Beispiel #1
0
def test_parse_and_fit_to_line():
    """
    Tests fitting a line to fake RA and DEC data which has errors calculated from the real intermediate data
    from Hip1, Hip2, and GaiaDR2. This only fits a line to the first 11 points.
    """
    stars = ['049699', '027321', '027321']
    parsers = [GaiaeDR3, GaiaData, HipparcosOriginalData, HipparcosRereductionDVDBook]
    subdirectories = ['GaiaeDR3', 'GaiaDR2', 'Hip1', 'Hip2']
    base_directory = os.path.join(os.getcwd(), 'htof/test/data_for_tests')
    for star, parser, subdirectory in zip(stars, parsers, subdirectories):
        test_data_directory = os.path.join(base_directory, subdirectory)
        data = parser()
        data.parse(star_id=star,
                   intermediate_data_directory=test_data_directory)
        data.calculate_inverse_covariance_matrices()
        fitter = AstrometricFitter(inverse_covariance_matrices=data.inverse_covariance_matrix,
                                   epoch_times=np.linspace(0, 10, num=11))
        solution_vector = fitter.fit_line(ra_vs_epoch=np.linspace(30, 40, num=11),
                                          dec_vs_epoch=np.linspace(20, 30, num=11))
        ra0, dec0, mu_ra, mu_dec = solution_vector

        assert np.isclose(ra0, 30)
        assert np.isclose(dec0, 20)
        assert np.isclose(mu_ra, 1)
        assert np.isclose(mu_dec, 1)
Beispiel #2
0
 def test_fitting_to_cubic_astrometric_data_with_parallax(self):
     real_plx = 100
     cntr_dec, cntr_ra = Angle(45, unit='degree'), Angle(45, unit='degree')
     astrometric_data = generate_astrometric_data(acc=True, jerk=True)
     jyear_epochs = Time(astrometric_data['epoch_delta_t'] + 2012,
                         format='decimalyear').jyear
     ra_pert, dec_pert = parallactic_motion(jyear_epochs,
                                            cntr_dec.mas,
                                            cntr_ra.mas,
                                            'mas',
                                            2012,
                                            parallax=1)
     astrometric_data['dec'] += dec_pert * real_plx
     astrometric_data['ra'] += ra_pert * real_plx
     fitter = AstrometricFitter(
         inverse_covariance_matrices=astrometric_data[
             'inverse_covariance_matrix'],
         epoch_times=astrometric_data['epoch_delta_t'],
         use_parallax=True,
         parallactic_pertubations={
             'ra_plx': ra_pert,
             'dec_plx': dec_pert
         },
         fit_degree=3)
     solution, errors, chisq, resids = fitter.fit_line(
         astrometric_data['ra'], astrometric_data['dec'], return_all=True)
     assert np.allclose(solution[1:],
                        astrometric_data['nonlinear_solution'],
                        atol=0,
                        rtol=1E-4)
     assert np.allclose(solution[0], real_plx)
Beispiel #3
0
 def test_optimal_central_epoch_raises_error(self):
     astrometric_data = generate_astrometric_data()
     fitter = AstrometricFitter(
         inverse_covariance_matrices=astrometric_data[
             'inverse_covariance_matrix'],
         epoch_times=astrometric_data['epoch_delta_t'])
     with pytest.raises(ValueError):
         fitter.find_optimal_central_epoch(
             'incorrect_choice'), fitter.find_optimal_central_epoch('dec')
Beispiel #4
0
 def test_errors_on_linear_astrometric_data(self):
     astrometric_data = generate_astrometric_data()
     fitter = AstrometricFitter(
         inverse_covariance_matrices=astrometric_data[
             'inverse_covariance_matrix'],
         epoch_times=astrometric_data['epoch_delta_t'])
     sol, errs, chisq, resids = fitter.fit_line(astrometric_data['ra'],
                                                astrometric_data['dec'],
                                                return_all=True)
     assert errs.size == 4
     assert np.isclose(0, chisq, atol=1e-7)
Beispiel #5
0
 def test_optimal_central_epoch_on_linear_data(self):
     astrometric_data = generate_astrometric_data()
     fitter = AstrometricFitter(
         inverse_covariance_matrices=astrometric_data[
             'inverse_covariance_matrix'],
         epoch_times=astrometric_data['epoch_delta_t'])
     central_epoch_ra, central_epoch_dec = fitter.find_optimal_central_epoch(
         'ra'), fitter.find_optimal_central_epoch('dec')
     cov_matrix = fitter.evaluate_cov_matrix(central_epoch_ra,
                                             central_epoch_dec)
     assert np.allclose([cov_matrix[0, 2], cov_matrix[1, 3]], 0)
Beispiel #6
0
 def test_optimal_central_epoch_on_cubic_data(self):
     astrometric_data = generate_astrometric_data(acc=True, jerk=True)
     fitter = AstrometricFitter(
         inverse_covariance_matrices=astrometric_data[
             'inverse_covariance_matrix'],
         epoch_times=astrometric_data['epoch_delta_t'],
         use_parallax=False,
         fit_degree=3)
     central_epoch_ra, central_epoch_dec = fitter.find_optimal_central_epoch(
         'ra'), fitter.find_optimal_central_epoch('dec')
     cov_matrix = fitter.evaluate_cov_matrix(central_epoch_ra,
                                             central_epoch_dec)
     assert np.allclose([cov_matrix[0, 2], cov_matrix[1, 3]], 0)
Beispiel #7
0
 def test_chi2_matrix_many_epoch(self, fake_chi2_matrix_per_epoch):
     ivar = np.ones((2, 2))
     fitter = AstrometricFitter(
         inverse_covariance_matrices=[ivar, ivar, ivar],
         epoch_times=[1, 2, 3],
         astrometric_solution_vector_components=[],
         use_parallax=True,
         fit_degree=3)
     assert np.allclose(
         np.ones((9, 9)) * 3,
         fitter._init_astrometric_chi_squared_matrix(3)[0])
     fake_chi2_matrix_per_epoch.return_value = np.ones((7, 7))
     assert np.allclose(
         np.ones((7, 7)) * 3,
         fitter._init_astrometric_chi_squared_matrix(2)[0])
Beispiel #8
0
 def test_chi2_vector(self, mock_ra_vec, mock_dec_vec):
     covariance_matrix = np.array([[5, 1], [12, 2]])
     expected_c = 4 * np.ones(4)
     fitter = AstrometricFitter(inverse_covariance_matrices=np.array([
         np.linalg.pinv(covariance_matrix),
         np.linalg.pinv(covariance_matrix)
     ]),
                                epoch_times=np.array([1, 2]),
                                astrometric_chi_squared_matrices=[],
                                fit_degree=1,
                                use_parallax=False)
     assert np.allclose(
         expected_c,
         fitter._chi2_vector(ra_vs_epoch=np.array([1, 1]),
                             dec_vs_epoch=np.array([1, 1])))
Beispiel #9
0
 def test_fitting_with_nonzero_central_epoch(self):
     ra_cnt = np.random.randint(1, 5)
     dec_cnt = np.random.randint(1, 5)
     astrometric_data = generate_astrometric_data()
     expected_vec = astrometric_data['linear_solution']
     expected_vec[0] += ra_cnt * expected_vec[
         2]  # r0 = ra_central_time * mu_ra
     expected_vec[1] += dec_cnt * expected_vec[
         3]  # dec0 = dec_central_time * mu_dec
     fitter = AstrometricFitter(
         inverse_covariance_matrices=astrometric_data[
             'inverse_covariance_matrix'],
         epoch_times=astrometric_data['epoch_delta_t'],
         central_epoch_dec=dec_cnt,
         central_epoch_ra=ra_cnt)
     assert np.allclose(
         fitter.fit_line(astrometric_data['ra'], astrometric_data['dec']),
         expected_vec)
Beispiel #10
0
 def test_init_epochs(self):
     fitter = AstrometricFitter(astrometric_solution_vector_components=[],
                                central_epoch_dec=1.5,
                                central_epoch_ra=1,
                                epoch_times=np.array([1, 2, 3]),
                                astrometric_chi_squared_matrices=[],
                                fit_degree=1,
                                use_parallax=False)
     assert np.allclose(fitter.dec_epochs, [-0.5, 0.5, 1.5])
     assert np.allclose(fitter.ra_epochs, [0, 1, 2])
Beispiel #11
0
 def test_fitter_removes_parallax(self):
     astrometric_data = generate_astrometric_data()
     fitter = AstrometricFitter(
         inverse_covariance_matrices=astrometric_data[
             'inverse_covariance_matrix'],
         epoch_times=astrometric_data['epoch_delta_t'],
         use_parallax=False,
         fit_degree=3)
     assert fitter._chi2_matrix.shape == (8, 8)
     assert fitter.astrometric_solution_vector_components['ra'][
         0].shape == (8, )
     assert fitter.astrometric_solution_vector_components['dec'][
         0].shape == (8, )
Beispiel #12
0
def refit_hip_fromdata(data: DataParser, fit_degree, cntr_RA=Angle(0, unit='degree'), cntr_Dec=Angle(0, unit='degree'),
                       use_parallax=False):
    data.calculate_inverse_covariance_matrices()
    # generate parallax motion
    jyear_epoch = time.Time(data.julian_day_epoch(), format='jd', scale='tcb').jyear
    # note that ra_motion and dec_motion are in degrees here.
    # generate sky path
    year_epochs = jyear_epoch - time.Time(1991.25, format='decimalyear', scale='tcb').jyear
    ra_motion, dec_motion = parallactic_motion(jyear_epoch, cntr_RA.degree, cntr_Dec.degree, 'degree',
                                               time.Time(1991.25, format='decimalyear', scale='tcb').jyear,
                                               ephemeris=earth_ephemeris)  # Hipparcos was in a geostationary orbit.

    ra_resid = Angle(data.residuals.values * np.sin(data.scan_angle.values), unit='mas')
    dec_resid = Angle(data.residuals.values * np.cos(data.scan_angle.values), unit='mas')
    # instantiate fitter
    fitter = AstrometricFitter(data.inverse_covariance_matrix, year_epochs,
                               use_parallax=use_parallax, fit_degree=fit_degree,
                               parallactic_pertubations={'ra_plx': Angle(ra_motion, 'degree').mas,
                                                         'dec_plx': Angle(dec_motion, 'degree').mas})
    fit_coeffs, errors, chisq, refit_residuals = fitter.fit_line(ra_resid.mas, dec_resid.mas, return_all=True)
    parallax_factors = ra_motion * np.sin(data.scan_angle.values) + dec_motion * np.cos(data.scan_angle.values)
    # technically this could be data.parallax_factors.values, but then we have to deal with
    # getting the RA and Dec components of that.
    if not use_parallax:
        fit_coeffs = np.hstack([[0], fit_coeffs])
        errors = np.hstack([[0], errors])
        parallax_factors = np.zeros_like(parallax_factors)
    # pad so coeffs and errors are 9 long.
    errors = np.pad(errors, (0, 9 - len(fit_coeffs)))
    fit_coeffs = np.pad(fit_coeffs, (0, 9 - len(fit_coeffs)))
    # calculate the chisquared partials
    sin_scan = np.sin(data.scan_angle.values)
    cos_scan = np.cos(data.scan_angle.values)
    dt = data.epoch - 1991.25
    chi2_vector = (2 * data.residuals.values / data.along_scan_errs.values ** 2 * np.array(
        [parallax_factors, sin_scan, cos_scan, dt * sin_scan, dt * cos_scan])).T
    chi2_partials = np.sum(chi2_vector, axis=0) ** 2
    return fit_coeffs, errors, chisq, chi2_partials
Beispiel #13
0
    def __init__(self, data_choice, star_id, intermediate_data_directory, fitter=None, data=None,
                 central_epoch_ra=0, central_epoch_dec=0, format='jd', fit_degree=1,
                 use_parallax=False, central_ra=None, central_dec=None,
                 use_catalog_parallax_factors=False, along_scan_error_scaling=1.0, **kwargs):
        self.along_scan_error_scaling = along_scan_error_scaling
        if data_choice.lower() == 'hip2recalibrated':
            warnings.warn(f'You have selected {data_choice}, the recalibrated Hipparcos 2 data. Note that for this,'
                          f' you should be feeding in the filepaths to the Hip21 (Hip2 java tool data), because'
                          f' htof applies the recalibration on-the-fly for each file. As well, be sure to read'
                          f' Brandt et al. 2022 to understand the limitations of using the recalibrated data. ')     # pragma: no cover

        if data is None:
            ThisDataParser = self.parsers[data_choice.lower()]
            data = ThisDataParser.parse_and_instantiate(star_id=star_id,
                                                        intermediate_data_directory=intermediate_data_directory)
            data.scale_along_scan_errs(self.along_scan_error_scaling)
            data.calculate_inverse_covariance_matrices()

        parallactic_pertubations = None
        if use_parallax:
            if not use_catalog_parallax_factors:
                # recompute the parallax factors at the new central_ra and central_ra epoch.
                if not (isinstance(central_ra, Angle) and isinstance(central_dec, Angle)):
                    raise ValueError('Cannot compute parallax factors. central_ra and central_dec must be instances'
                                     ' of astropy.coordinates.Angle.')     # pragma: no cover
                if central_epoch_dec != central_epoch_ra:
                    warnings.warn('central_epoch_dec != central_epoch_ra. '
                                  'Using central_epoch_ra as the central_epoch to compute the parallax motion.',
                                  UserWarning)    # pragma: no cover
                ra_motion, dec_motion = parallactic_motion(Time(data.julian_day_epoch(), format='jd').jyear,
                                                           central_ra.mas, central_dec.mas, 'mas',
                                                           Time(central_epoch_ra, format=format).jyear,
                                                           ephemeris=self.ephemeri[data_choice.lower()])
            else:
                ra_motion, dec_motion = to_ra_dec_basis(data.parallax_factors.values, data.scan_angle.values)
            parallactic_pertubations = {'ra_plx': ra_motion, 'dec_plx': dec_motion}

        if fitter is None and data is not None:
            fitter = AstrometricFitter(inverse_covariance_matrices=data.inverse_covariance_matrix,
                                       epoch_times=Time(Time(data.julian_day_epoch(), format='jd'), format=format).value,
                                       central_epoch_dec=Time(central_epoch_dec, format=format).value,
                                       central_epoch_ra=Time(central_epoch_ra, format=format).value,
                                       fit_degree=fit_degree,
                                       use_parallax=use_parallax,
                                       parallactic_pertubations=parallactic_pertubations)
        self.data = data
        self.fitter = fitter
Beispiel #14
0
    def parse(self,
              star_id,
              intermediate_data_directory,
              attempt_adhoc_rejection=True,
              reject_known=True,
              **kwargs):
        # important that the 2007 error inflation is turned off (error_inflate=False)
        header, raw_data = super(Hipparcos2Recalibrated, self).parse(
            star_id,
            intermediate_data_directory,
            error_inflate=False,
            attempt_adhoc_rejection=attempt_adhoc_rejection,
            reject_known=reject_known,
            **kwargs)
        apply_calibrations = True
        if not (self.meta['catalog_soltype'] == 5
                or self.meta['catalog_soltype'] == 7
                or self.meta['catalog_soltype'] == 9):
            warnings.warn(
                f'This source has a solution type of {self.meta["catalog_soltype"]}. '
                f'htof will only recalibrate 5, 7, and 9 parameter solutions currently. '
                f'No recalibration will be performed.')
            apply_calibrations = False
        if attempt_adhoc_rejection is False or reject_known is False:
            warnings.warn(
                'We have not tested recalibration without rejecting any of the flagged or bugged '
                'observations. No recalibration will be performed.')
            apply_calibrations = False

        if apply_calibrations:
            # apply the calibrations
            self.residuals += self.residual_offset  # note that this modifies the raw_data column also.
            self.along_scan_errs = np.sqrt(self.along_scan_errs**2 +
                                           self.cosmic_dispersion**2)
            self.calculate_inverse_covariance_matrices()
            # munge the parallax factors into the correct form. Note that we are using
            # the parallax factors from the catalog here to keep everything consistent.
            ra_motion, dec_motion = to_ra_dec_basis(
                self.parallax_factors.values, self.scan_angle.values)
            parallactic_perturbations = {
                'ra_plx': ra_motion,
                'dec_plx': dec_motion
            }
            # refit the data, calculate the new residuals, and parameters.
            fit_degree = {5: 1, 7: 2, 9: 3}[int(self.meta['catalog_soltype'])]
            fitter = AstrometricFitter(
                inverse_covariance_matrices=self.inverse_covariance_matrix,
                epoch_times=Time(Time(self.julian_day_epoch(), format='jd'),
                                 format='jyear').value,
                central_epoch_dec=1991.25,
                central_epoch_ra=1991.25,
                fit_degree=fit_degree,
                use_parallax=True,
                parallactic_pertubations=parallactic_perturbations)
            # get residuals in ra and dec.
            ra = Angle(self.residuals.values * np.sin(self.scan_angle.values),
                       unit='mas')
            dec = Angle(self.residuals.values * np.cos(self.scan_angle.values),
                        unit='mas')
            # fit the residuals
            coeffs, errors, Q, new_residuals = fitter.fit_line(ra.mas,
                                                               dec.mas,
                                                               return_all=True)
            # compute the along-scan residuals
            new_residuals = to_along_scan_basis(new_residuals[:, 0],
                                                new_residuals[:, 1],
                                                self.scan_angle.values)
            self.residuals = Series(new_residuals, index=self.residuals.index)
            """
            update the header with the new statistics
            """
            ntransits, nparam = len(self), int(self.meta['catalog_soltype'])
            header['second']['NOB'] = len(self)
            header['second'][
                'NR'] = 0  # because we automatically remove any "flagged as rejected" observations.
            header['first']['F1'] = 0
            header['first']['NRES'] = len(self)
            header['first']['F2'] = special.erfcinv(
                stats.chi2.sf(Q, ntransits - nparam) * 2) * np.sqrt(2)
            if np.isfinite(header['first']['F2']):
                header['first']['F2'] = np.round(header['first']['F2'], 4)
            # update the best fit parameters with the new values
            # dpmRA  dpmDE  e_dpmRA  e_dpmDE  ddpmRA  ddpmDE  e_ddpmRA  e_ddpmDE
            for i, key, dp, in zip(np.arange(nparam), [
                    'Plx', 'RAdeg', 'DEdeg', 'pm_RA', 'pm_DE', 'dpmRA',
                    'dpmDE', 'ddpmRA', 'ddpmDE'
            ], [2, 8, 8, 2, 2, 2, 2, 2, 2]):
                if key != 'RAdeg' and key != 'DEdeg':
                    header['third'][key] = np.round(
                        header['third'][key] + coeffs[i], dp)
                else:
                    # convert the perturbation from mas to degrees.
                    header['third'][key] = np.round(
                        header['third'][key] +
                        (coeffs[i] * u.mas).to(u.degree).value, dp)
            # update the errors with the new errors
            for i, key, dp, in zip(np.arange(nparam), [
                    'e_Plx', 'e_RA', 'e_DE', 'e_pmRA', 'e_pmDE', 'e_dpmRA',
                    'e_dpmDE', 'e_ddpmRA', 'e_ddpmDE'
            ], [2, 2, 2, 2, 2, 2, 2, 2, 2]):
                header['third'][key] = np.round(errors[i], dp)
            # save the modified header to the class, because these will be
            # used by self.write_as_javatool_format()
            self.recalibrated_header = deepcopy(header)
            """
            update the raw_data columns with the new data. Note that rejected/bugged epochs are already
            taken care of.
            """
            # data order in Java tool data: IORB   EPOCH    PARF    CPSI    SPSI     RES   SRES
            recalibrated_data = DataFrame({
                '1':
                self._iorb,
                '2':
                self._epoch - 1991.25,
                '3':
                self.parallax_factors,
                '4':
                self._cpsi,
                '5':
                self._spsi,
                '6':
                np.round(self.residuals.values, 3),
                '7':
                np.round(self.along_scan_errs.values, 3)
            })
            self.recalibrated_data = recalibrated_data
            header, raw_data = None, None  # the raw header and raw data have been modified, so clear them.
        return header, raw_data