def __init__(self, filename, **kwargs): self.filename = filename log.debug( "Loading {0} observatory clock correction file {1}".format( self.format, filename ) ) mjd, clk, self.header = self.load_tempo2_clock_file(filename) # NOTE Clock correction file has a time far in the future as ending point with warnings.catch_warnings(): warnings.simplefilter("ignore", ErfaWarning) self._time = Time(mjd, format="pulsar_mjd", scale="utc") self._clock = clk * u.s
def change_pepoch(self, new_epoch, toas=None, delay=None): """Move PEPOCH to a new time and change the related paramters. Parameters ---------- new_epoch: float or `astropy.Time` object The new PEPOCH value. toas: `toa` object, optional. If current PEPOCH is not provided, the first pulsar frame toa will be treated as PEPOCH. delay: `numpy.array` object If current PEPOCH is not provided, it is required for computing the first pulsar frame toa. """ if isinstance(new_epoch, Time): new_epoch = Time(new_epoch, scale="tdb", precision=9) else: new_epoch = Time(new_epoch, scale="tdb", format="mjd", precision=9) if self.PEPOCH.value is None: if toas is None or delay is None: raise ValueError( "`PEPOCH` is not in the model, thus, 'toa' and" " 'delay' shoule be givne." ) tbl = toas.table phsepoch_ld = (tbl["tdb"][0] - delay[0]).tdb.mjd_long else: phsepoch_ld = self.PEPOCH.quantity.tdb.mjd_long dt = (new_epoch.tdb.mjd_long - phsepoch_ld) * u.day fterms = [0.0 * u.Unit("")] + self.get_spin_terms() # rescale the fterms for n in range(len(fterms) - 1): f_par = getattr(self, "F{}".format(n)) f_par.value = taylor_horner_deriv( dt.to(u.second), fterms, deriv_order=n + 1 ) self.PEPOCH.value = new_epoch
def get_iers_b_up_to_date(mjd): """Update the IERS B table to include MJD if necessary""" if Time.now().mjd <= mjd: raise ValueError("IERS B data requested for future MJD {}".format(mjd)) might_be_old = is_url_in_cache(IERS_B_URL) iers_b = IERS_B.open(download_file(IERS_B_URL, cache=True)) if might_be_old and iers_b[-1]["MJD"].to_value(u.d) < mjd: # Try wiping the download and re-downloading clear_download_cache(IERS_B_URL) iers_b = IERS_B.open(download_file(IERS_B_URL, cache=True)) if iers_b[-1]["MJD"].to_value(u.d) < mjd: raise ValueError( "IERS B data not yet available for MJD {}".format(mjd)) return iers_b
def get_TDBs(self, t, method="default", ephem=None, options=None, grp=None): """This is a high level function for converting TOAs to TDB time scale. Different method can be applied to obtain the result. Current supported methods are ['astropy', 'ephemeris'] Parameters ---------- t: astropy.time.Time object The time need for converting toas method: str or callable, optional Method of computing TDB default Astropy time.Time object built-in converter, uses FB90. SpacecraftObs will include a topocentric correction term. ephemeris JPL ephemeris included TDB-TT correction. ephem: str, optional The ephemeris to get he TDB-TT correction. Required for the 'ephemeris' method. """ if t.isscalar: t = Time([t]) if t.scale == "tdb": return t # Check the method. This pattern is from numpy minize if callable(method): meth = "_custom" else: meth = method.lower() if options is None: options = {} if meth == "_custom": options = dict(options) return method(t, **options) if meth == "default": return self._get_TDB_default(t, ephem, grp) elif meth == "ephemeris": if ephem is None: raise ValueError( "A ephemeris file should be provided to get" " the TDB-TT corrections." ) return self._get_TDB_ephem(t, ephem) else: raise ValueError("Unknown method '%s'." % method)
def __init__(self, filename, obscode=None, **kwargs): self.filename = filename self.obscode = obscode log.debug( "Loading {0} observatory ({1}) clock correction file {2}".format( self.format, obscode, filename)) mjd, clk = self.load_tempo1_clock_file(filename, site=obscode) # NOTE Clock correction file has a time far in the future as ending point # We are swithing off astropy warning only for gps correction. with warnings.catch_warnings(): warnings.simplefilter("ignore", ErfaWarning) try: self._time = Time(mjd, format="pulsar_mjd", scale="utc") except ValueError: log.error("Filename {0}, site {1}: Bad MJD {2}".format( filename, obscode, mjd)) raise self._clock = clk * u.us
def main(argv=None): parser = argparse.ArgumentParser( description="PINT tool for command-line barycentering calculations.") parser.add_argument("time", help="MJD (UTC, by default)") parser.add_argument( "--timescale", default="utc", help="Time scale for MJD argument ('utc', 'tt', 'tdb'), default=utc", ) parser.add_argument( "--format", help= ("Format for time argument ('mjd' or any astropy.Time format " "(e.g. 'isot'), see <http://docs.astropy.org/en/stable/time/#time-format>)" ), default="mjd", ) parser.add_argument("--freq", type=float, default=np.inf, help="Frequency to use, MHz") parser.add_argument("--obs", default="Geocenter", help="Observatory code (default = Geocenter)") parser.add_argument("--parfile", help="par file to read model from", default=None) parser.add_argument( "--ra", help="RA to use (e.g. '12h22m33.2s' if not read from par file)") parser.add_argument( "--dec", help="Decl. to use (e.g. '19d21m44.2s' if not read from par file)") parser.add_argument("--dm", help="DM to use (if not read from par file)", type=float, default=0.0) parser.add_argument("--ephem", default="DE421", help="Ephemeris to use") parser.add_argument( "--use_gps", default=False, action="store_true", help="Apply GPS to UTC clock corrections", ) parser.add_argument( "--use_bipm", default=False, action="store_true", help="Use TT(BIPM) instead of TT(TAI)", ) args = parser.parse_args(argv) if args.format in ("mjd", "jd", "unix"): # These formats require conversion from string to long double first fmt = args.format # Never allow format == 'mjd' because it fails when scale is 'utc' # Change 'mjd' to 'pulsar_mjd' to deal with this. if fmt == "mjd": fmt = "pulsar_mjd" t = Time(np.longdouble(args.time), scale=args.timescale, format=fmt, precision=9) print(t) else: t = Time(args.time, scale=args.timescale, format=args.format, precision=9) log.debug(t.iso) t = toa.TOA(t, freq=args.freq, obs=args.obs) # Build TOAs and compute TDBs and positions from ephemeris ts = toa.get_TOAs_list( [t], ephem=args.ephem, include_bipm=args.use_bipm, include_gps=args.use_gps, planets=False, ) if args.parfile is not None: m = pint.models.get_model(args.parfile) else: # Construct model by hand m = pint.models.StandardTimingModel # Should check if 12:13:14.2 syntax is used and support that as well! m.RAJ.quantity = Angle(args.ra) m.DECJ.quantity = Angle(args.dec) m.DM.quantity = args.dm * u.parsec / u.cm**3 tdbtimes = m.get_barycentric_toas(ts) print("{0:.16f}".format(tdbtimes[0].value)) return
def main(argv=None): import argparse parser = argparse.ArgumentParser( description= "Use PINT to compute event phases and make plots of photon event files." ) parser.add_argument( "eventfile", help= "Photon event FITS file name (e.g. from NICER, RXTE, XMM, Chandra).", ) parser.add_argument("parfile", help="par file to construct model from") parser.add_argument("--orbfile", help="Name of orbit file", default=None) parser.add_argument("--maxMJD", help="Maximum MJD to include in analysis", default=None) parser.add_argument("--plotfile", help="Output figure file name (default=None)", default=None) parser.add_argument( "--addphase", help="Write FITS file with added phase column", default=False, action="store_true", ) parser.add_argument( "--addorbphase", help="Write FITS file with added orbital phase column", default=False, action="store_true", ) parser.add_argument( "--absphase", help="Write FITS file with integral portion of pulse phase (ABS_PHASE)", default=False, action="store_true", ) parser.add_argument( "--barytime", help= "Write FITS file with a column containing the barycentric time as double precision MJD.", default=False, action="store_true", ) parser.add_argument( "--outfile", help="Output FITS file name (default=same as eventfile)", default=None, ) parser.add_argument("--ephem", help="Planetary ephemeris to use (default=DE421)", default="DE421") parser.add_argument( "--tdbmethod", help="Method for computing TT to TDB (default=astropy)", default="default", ) parser.add_argument("--plot", help="Show phaseogram plot.", action="store_true", default=False) parser.add_argument( "--use_gps", default=False, action="store_true", help="Apply GPS to UTC clock corrections", ) parser.add_argument( "--use_bipm", default=False, action="store_true", help="Use TT(BIPM) instead of TT(TAI)", ) # parser.add_argument("--fix",help="Apply 1.0 second offset for NICER", action='store_true', default=False) args = parser.parse_args(argv) # If outfile is specified, that implies addphase if args.outfile is not None: args.addphase = True # If plotfile is specified, that implies plot if args.plotfile is not None: args.plot = True # Read event file header to figure out what instrument is is from hdr = pyfits.getheader(args.eventfile, ext=1) log.info("Event file TELESCOPE = {0}, INSTRUMENT = {1}".format( hdr["TELESCOP"], hdr["INSTRUME"])) if hdr["TELESCOP"] == "NICER": # Instantiate NICERObs once so it gets added to the observatory registry if args.orbfile is not None: log.info("Setting up NICER observatory") NICERObs(name="NICER", FPorbname=args.orbfile, tt2tdb_mode="pint") # Read event file and return list of TOA objects try: tl = load_NICER_TOAs(args.eventfile) except KeyError: log.error( "Observatory not recognized. This probably means you need to provide an orbit file or barycenter the event file." ) sys.exit(1) elif hdr["TELESCOP"] == "XTE": # Instantiate RXTEObs once so it gets added to the observatory registry if args.orbfile is not None: # Determine what observatory type is. log.info("Setting up RXTE observatory") RXTEObs(name="RXTE", FPorbname=args.orbfile, tt2tdb_mode="pint") # Read event file and return list of TOA objects tl = load_RXTE_TOAs(args.eventfile) elif hdr["TELESCOP"].startswith("XMM"): # Not loading orbit file here, since that is not yet supported. tl = load_XMM_TOAs(args.eventfile) elif hdr["TELESCOP"].lower().startswith("nustar"): if args.orbfile is not None: log.info("Setting up NuSTAR observatory") NuSTARObs(name="NuSTAR", FPorbname=args.orbfile, tt2tdb_mode="pint") tl = load_NuSTAR_TOAs(args.eventfile) else: log.error( "FITS file not recognized, TELESCOPE = {0}, INSTRUMENT = {1}". format(hdr["TELESCOP"], hdr["INSTRUME"])) sys.exit(1) # Now convert to TOAs object and compute TDBs and posvels if len(tl) == 0: log.error("No TOAs, exiting!") sys.exit(0) # Read in model modelin = pint.models.get_model(args.parfile) use_planets = False if "PLANET_SHAPIRO" in modelin.params: if modelin.PLANET_SHAPIRO.value: use_planets = True if "AbsPhase" not in modelin.components: log.error( "TimingModel does not include AbsPhase component, which is required " "for computing phases. Make sure you have TZR* parameters in your par file!" ) raise ValueError("Model missing AbsPhase component.") if args.addorbphase and (not hasattr(modelin, "binary_model_name")): log.error( "TimingModel does not include a binary model, which is required for " "computing orbital phases. Make sure you have BINARY and associated " "model parameters in your par file!") raise ValueError("Model missing BINARY component.") # Discard events outside of MJD range if args.maxMJD is not None: tlnew = [] print("pre len : ", len(tl)) maxT = Time(float(args.maxMJD), format="mjd") print("maxT : ", maxT) for tt in tl: if tt.mjd < maxT: tlnew.append(tt) tl = tlnew print("post len : ", len(tlnew)) ts = toa.get_TOAs_list( tl, ephem=args.ephem, include_bipm=args.use_bipm, include_gps=args.use_gps, planets=use_planets, tdb_method=args.tdbmethod, ) ts.filename = args.eventfile # if args.fix: # ts.adjust_TOAs(TimeDelta(np.ones(len(ts.table))*-1.0*u.s,scale='tt')) print(ts.get_summary()) mjds = ts.get_mjds() print(mjds.min(), mjds.max()) # Compute model phase for each TOA iphss, phss = modelin.phase(ts, abs_phase=True) # ensure all postive negmask = phss < 0.0 phases = np.where(negmask, phss + 1.0, phss) h = float(hm(phases)) print("Htest : {0:.2f} ({1:.2f} sigma)".format(h, h2sig(h))) if args.plot: phaseogram_binned(mjds, phases, bins=100, plotfile=args.plotfile) # Compute orbital phases for each photon TOA if args.addorbphase: delay = modelin.delay(ts) orbits = modelin.binary_instance.orbits() # These lines are already in orbits.orbit_phase() in binary_orbits.py. # What is the correct syntax is to call this function here? norbits = np.array(np.floor(orbits), dtype=np.long) orbphases = orbits - norbits # fractional phase if args.addphase or args.addorbphase: # Read input FITS file (again). # If overwriting, open in 'update' mode if args.outfile is None: hdulist = pyfits.open(args.eventfile, mode="update") else: hdulist = pyfits.open(args.eventfile) datacol = [] data_to_add = {} if args.addphase: if len(hdulist[1].data) != len(phases): raise RuntimeError( "Mismatch between length of FITS table ({0}) and length of phase array ({1})!" .format(len(hdulist[1].data), len(phases))) data_to_add["PULSE_PHASE"] = [phases, "D"] if args.absphase: data_to_add["ABS_PHASE"] = [iphss - negmask, "K"] if args.barytime: bats = modelin.get_barycentric_toas(ts) data_to_add["BARY_TIME"] = [bats, "D"] if args.addorbphase: if len(hdulist[1].data) != len(orbphases): raise RuntimeError( "Mismatch between length of FITS table ({0}) and length of orbital phase array ({1})!" .format(len(hdulist[1].data), len(orbphases))) data_to_add["ORBIT_PHASE"] = [orbphases, "D"] # End if args.addorbphase for key in data_to_add.keys(): if key in hdulist[1].columns.names: log.info("Found existing %s column, overwriting..." % key) # Overwrite values in existing Column hdulist[1].data[key] = data_to_add[key][0] else: # Construct and append new column, preserving HDU header and name log.info("Adding new %s column." % key) datacol.append( pyfits.ColDefs([ pyfits.Column( name=key, format=data_to_add[key][1], array=data_to_add[key][0], ) ])) if len(datacol) > 0: cols = hdulist[1].columns for c in datacol: cols = cols + c bt = pyfits.BinTableHDU.from_columns(cols, header=hdulist[1].header, name=hdulist[1].name) hdulist[1] = bt if args.outfile is None: # Overwrite the existing file log.info("Overwriting existing FITS file " + args.eventfile) hdulist.flush(verbose=True, output_verify="warn") else: # Write to new output file log.info("Writing output FITS file " + args.outfile) hdulist.writeto(args.outfile, overwrite=True, checksum=True, output_verify="warn")
def main(argv=None): parser = argparse.ArgumentParser( description= "Use PINT to compute H-test and plot Phaseogram from a Fermi FT1 event file." ) parser.add_argument("eventfile", help="Fermi event FITS file name.") parser.add_argument("parfile", help="par file to construct model from") parser.add_argument( "weightcol", help="Column name for event weights (or 'CALC' to compute them)") parser.add_argument("--ft2", help="Path to FT2 file.", default=None) parser.add_argument( "--addphase", help="Write FT1 file with added phase column", default=False, action="store_true", ) parser.add_argument("--plot", help="Show phaseogram plot.", action="store_true", default=False) parser.add_argument("--plotfile", help="Output figure file name (default=None)", default=None) parser.add_argument("--maxMJD", help="Maximum MJD to include in analysis", default=None) parser.add_argument( "--outfile", help="Output figure file name (default is to overwrite input file)", default=None, ) parser.add_argument( "--planets", help="Use planetary Shapiro delay in calculations (default=False)", default=False, action="store_true", ) parser.add_argument("--ephem", help="Planetary ephemeris to use (default=DE421)", default="DE421") args = parser.parse_args(argv) # If outfile is specified, that implies addphase if args.outfile is not None: args.addphase = True # Read in model modelin = pint.models.get_model(args.parfile) if "ELONG" in modelin.params: tc = SkyCoord( modelin.ELONG.quantity, modelin.ELAT.quantity, frame="barycentrictrueecliptic", ) else: tc = SkyCoord(modelin.RAJ.quantity, modelin.DECJ.quantity, frame="icrs") if args.ft2 is not None: # Instantiate FermiObs once so it gets added to the observatory registry FermiObs(name="Fermi", ft2name=args.ft2) # Read event file and return list of TOA objects tl = load_Fermi_TOAs(args.eventfile, weightcolumn=args.weightcol, targetcoord=tc) # Discard events outside of MJD range if args.maxMJD is not None: tlnew = [] print("pre len : ", len(tl)) maxT = Time(float(args.maxMJD), format="mjd") print("maxT : ", maxT) for tt in tl: if tt.mjd < maxT: tlnew.append(tt) tl = tlnew print("post len : ", len(tlnew)) # Now convert to TOAs object and compute TDBs and posvels # For Fermi, we are not including GPS or TT(BIPM) corrections ts = toa.get_TOAs_list( tl, include_gps=False, include_bipm=False, planets=args.planets, ephem=args.ephem, ) ts.filename = args.eventfile print(ts.get_summary()) mjds = ts.get_mjds() print(mjds.min(), mjds.max()) # Compute model phase for each TOA iphss, phss = modelin.phase(ts, abs_phase=True) # ensure all postive phases = np.where(phss < 0.0 * u.cycle, phss + 1.0 * u.cycle, phss) mjds = ts.get_mjds() weights = np.array([w["weight"] for w in ts.table["flags"]]) h = float(hmw(phases, weights)) print("Htest : {0:.2f} ({1:.2f} sigma)".format(h, h2sig(h))) if args.plot: log.info("Making phaseogram plot with {0} photons".format(len(mjds))) phaseogram(mjds, phases, weights, bins=100, plotfile=args.plotfile) if args.addphase: # Read input FITS file (again). # If overwriting, open in 'update' mode if args.outfile is None: hdulist = pyfits.open(args.eventfile, mode="update") else: hdulist = pyfits.open(args.eventfile) event_hdu = hdulist[1] event_hdr = event_hdu.header event_dat = event_hdu.data if len(event_dat) != len(phases): raise RuntimeError( "Mismatch between length of FITS table ({0}) and length of phase array ({1})!" .format(len(event_dat), len(phases))) if "PULSE_PHASE" in event_hdu.columns.names: log.info("Found existing PULSE_PHASE column, overwriting...") # Overwrite values in existing Column event_dat["PULSE_PHASE"] = phases else: # Construct and append new column, preserving HDU header and name log.info("Adding new PULSE_PHASE column.") phasecol = pyfits.ColDefs( [pyfits.Column(name="PULSE_PHASE", format="D", array=phases)]) bt = pyfits.BinTableHDU.from_columns(event_hdu.columns + phasecol, header=event_hdr, name=event_hdu.name) hdulist[1] = bt if args.outfile is None: # Overwrite the existing file log.info("Overwriting existing FITS file " + args.eventfile) hdulist.flush(verbose=True, output_verify="warn") else: # Write to new output file log.info("Writing output FITS file " + args.outfile) hdulist.writeto(args.outfile, overwrite=True, checksum=True, output_verify="warn") return 0
def year(self): """ Return the decimal year for all the TOAs of this pulsar """ t = Time(self.all_toas.get_mjds(), format="mjd") return np.asarray(t.decimalyear) << u.year
def test_times_against_tempo2(): log.setLevel("ERROR") # for nice output info, set the following instead # log.setLevel('INFO') ls = u.def_unit("ls", const.c * 1.0 * u.s) log.info("Reading TOAs into PINT") ts = toa.get_TOAs(datadir + "/testtimes.tim", include_bipm=False, usepickle=False) if log.level < 25: ts.print_summary() ts.table.sort("index") log.info("Calling TEMPO2") # cmd = 'tempo2 -output general2 -f tests/testtimes.par tests/testtimes.tim -s "XXX {clock0} {clock1} {clock2} {clock3} {tt} {t2tb} {telSSB} {telVel} {Ttt}\n"' # cmd = 'tempo2 -output general2 -f ' + datadir+'/testtimes.par ' + datadir + \ # '/testtimes.tim -s "XXX {clock0} {clock1} {clock2} {clock3} {tt} {t2tb} {earth_ssb1} {earth_ssb2} {earth_ssb3} {earth_ssb4} {earth_ssb5} {earth_ssb6} {telEpos} {telEVel} {Ttt}\n"' # args = shlex.split(cmd) # # tout = subprocess.check_output(args) # goodlines = [x for x in tout.split("\n") if x.startswith("XXX")] # # assert(len(goodlines)==len(ts.table)) # t2result = numpy.genfromtxt('datafile/testtimes.par' + '.tempo2_test', names=True, comments = '#') f = open(datadir + "/testtimes.par" + ".tempo2_test") lines = f.readlines() goodlines = lines[1:] # Get the output lines from the TOAs for line, TOA in zip(goodlines, ts.table): assert (len(line.split()) == 19 ), "tempo2 general2 does not support all needed outputs" ( oclk, gps_utc, tai_utc, tt_tai, ttcorr, tt2tb, ep0, ep1, ep2, ev0, ev1, ev2, tp0, tp1, tp2, tv0, tv1, tv2, Ttt, ) = (float(x) for x in line.split()) t2_epv = PosVel( numpy.asarray([ep0, ep1, ep2]) * ls, numpy.asarray([ev0, ev1, ev2]) * ls / u.s, ) t2_opv = PosVel( numpy.asarray([tp0, tp1, tp2]) * ls, numpy.asarray([tv0, tv1, tv2]) * ls / u.s, ) t2_ssb2obs = t2_epv + t2_opv # print time_to_mjd_string(TOA.mjd.tt), line.split()[-1] tempo_tt = Time(line.split()[-1], format="mjd_string", scale="tt") # Ensure that the clock corrections are accurate to better than 0.1 ns assert (math.fabs( (oclk * u.s + gps_utc * u.s - TOA["flags"]["clkcorr"]).to( u.ns).value) < 0.1) log.info("TOA in tt difference is: %.2f ns" % ((TOA["mjd"].tt - tempo_tt.tt).sec * u.s).to(u.ns).value) pint_opv = erfautils.gcrs_posvel_from_itrf(Observatory.get( TOA["obs"]).earth_location_itrf(), TOA, obsname=TOA["obs"]) pint_opv = PosVel(pint_opv.pos, pint_opv.vel) # print " obs T2:", t2_opv.pos.to(u.m).value, t2_opv.vel.to(u.m/u.s) # print " obs PINT:", pint_opv.pos.to(u.m), pint_opv.vel.to(u.m/u.s) dopv = pint_opv - t2_opv dpos = numpy.sqrt(numpy.dot(dopv.pos.to(u.m), dopv.pos.to(u.m))) dvel = numpy.sqrt( numpy.dot(dopv.vel.to(u.mm / u.s), dopv.vel.to(u.mm / u.s))) log.info(" obs diff: %.2f m, %.3f mm/s" % (dpos, dvel)) assert dpos < 2.0 and dvel < 0.02 pint_ssb2obs = PosVel( numpy.asarray(TOA["ssb_obs_pos"]) * u.km, numpy.asarray(TOA["ssb_obs_vel"]) * u.km / u.s, origin="SSB", obj="OBS", ) # print " topo T2:", t2_ssb2obs.pos.to(u.km), t2_ssb2obs.vel.to(u.km/u.s) # print " topo PINT:", pint_ssb2obs.pos.to(u.km), pint_ssb2obs.vel.to(u.km/u.s) dtopo = pint_ssb2obs - t2_ssb2obs dpos = numpy.sqrt(numpy.dot(dtopo.pos.to(u.m), dtopo.pos.to(u.m))) dvel = numpy.sqrt( numpy.dot(dtopo.vel.to(u.mm / u.s), dtopo.vel.to(u.mm / u.s))) log.info(" topo diff: %.2f m, %.3f m/s" % (dpos, dvel)) assert dpos < 2.0 and dvel < 0.02
def fit(self, selected, iters=1): """ Run a fit using the specified fitter """ # Select all the TOAs if none are explicitly set if not any(selected): selected = ~selected if self.fitted: self.prefit_model = self.postfit_model self.prefit_resids = self.postfit_resids if self.fitter == Fitters.POWELL: fitter = pint.fitter.PowellFitter(self.selected_toas, self.prefit_model) elif self.fitter == Fitters.WLS: fitter = pint.fitter.WLSFitter(self.selected_toas, self.prefit_model) elif self.fitter == Fitters.GLS: fitter = pint.fitter.GLSFitter(self.selected_toas, self.prefit_model) chi2 = self.prefit_resids.chi2 wrms = self.prefit_resids.rms_weighted() print("Pre-Fit Chi2:\t\t%.8g" % chi2) print("Pre-Fit Weighted RMS:\t%.8g us" % wrms.to(u.us).value) fitter.fit_toas(maxiter=1) self.postfit_model = fitter.model self.postfit_resids = Residuals(self.all_toas, self.postfit_model) self.fitted = True self.write_fit_summary() # TODO: delta_pulse_numbers need some work. They serve both for PHASE and -padd functions from the TOAs # as well as for phase jumps added manually in the GUI. They really should not be zeroed out here because # that will wipe out preexisting values self.all_toas.table["delta_pulse_numbers"] = np.zeros(self.all_toas.ntoas) self.selected_toas.table["delta_pulse_number"] = np.zeros( self.selected_toas.ntoas ) # plot the prefit without jumps pm_no_jumps = copy.deepcopy(self.prefit_model) for param in pm_no_jumps.params: if param.startswith("JUMP"): getattr(pm_no_jumps, param).value = 0.0 getattr(pm_no_jumps, param).frozen = True self.prefit_resids_no_jumps = Residuals(self.selected_toas, pm_no_jumps) f = copy.deepcopy(fitter) no_jumps = [ False if "jump" in dict.keys() else True for dict in f.toas.table["flags"] ] f.toas.select(no_jumps) selectedMJDs = self.selected_toas.get_mjds() if all(no_jumps): q = list(self.all_toas.get_mjds()) index = q.index( [i for i in self.all_toas.get_mjds() if i > selectedMJDs.min()][0] ) rs_mean = ( Residuals(self.all_toas, f.model) .phase_resids[index : index + len(selectedMJDs)] .mean() ) else: rs_mean = self.prefit_resids_no_jumps.phase_resids[no_jumps].mean() # determines how far on either side fake toas go # TODO: hard limit on how far fake toas can go --> can get clkcorr # errors if go before GBT existed, etc. minMJD, maxMJD = selectedMJDs.min(), selectedMJDs.max() spanMJDs = maxMJD - minMJD if spanMJDs < 30 * u.d: redge = ledge = 4 npoints = 400 elif spanMJDs < 90 * u.d: redge = ledge = 2 npoints = 300 elif spanMJDs < 200 * u.d: redge = ledge = 1 npoints = 300 elif spanMJDs < 400 * u.d: redge = ledge = 0.5 npoints = 200 else: redge = ledge = 1.0 npoints = 250 # Check to see if too recent nowish = (Time.now().mjd - 40) * u.d if maxMJD + spanMJDs * redge > nowish: redge = (nowish - maxMJD) / spanMJDs if redge < 0.0: redge = 0.0 f_toas = make_fake_toas_uniform( minMJD - spanMJDs * ledge, maxMJD + spanMJDs * redge, npoints, f.model ) rs = calculate_random_models( f, f_toas, Nmodels=10, keep_models=False, return_time=True ) # subtract the mean residual of each random model from the respective residual set # based ONLY on the mean of the random residuals in the real data range start_index = np.where(abs(f_toas.get_mjds() - minMJD) < 1 * u.d) end_index = np.where(abs(f_toas.get_mjds() - maxMJD) < 1 * u.d) for i in range(len(rs)): # use start_index[0][0] since np.where returns np.array([], dtype), extract index from list in array rs_mean = rs[i][start_index[0][0] : end_index[0][0]].mean() rs[i][:] = [resid - rs_mean for resid in rs[i]] self.random_resids = rs self.fake_toas = f_toas
def year(self): """ Return the decimal year for all the TOAs of this pulsar """ t = Time(self.selected_toas.get_mjds(), format="mjd") return (t.decimalyear) * u.year
def fit(self, selected, iters=1): """ Run a fit using the specified fitter """ # Select all the TOAs if none are explicitly set if not any(selected): selected = ~selected """JUMP check, TODO: put in fitter?""" if "PhaseJump" in self.prefit_model.components: # if attempted fit (selected) # A) contains only jumps, don't do the fit and return an error # B) excludes a jump, turn that jump off # C) partially contains a jump, redefine that jump only with the overlap fit_jumps = [] for param in self.prefit_model.params: if getattr(self.prefit_model, param).frozen == False and param.startswith("JUMP"): fit_jumps.append(int(param[4:])) numjumps = self.prefit_model.components[ "PhaseJump"].get_number_of_jumps() if numjumps == 0: log.warn( "There are no jumps (maskParameter objects) in PhaseJump. Please delete the PhaseJump object and try again. " ) return None # boolean array to determine if all selected toas are jumped jumps = [ True if "jump" in dict.keys() and dict["jump"] in fit_jumps else False for dict in self.all_toas.table["flags"][selected] ] # check if par file jumps in PhaseJump object if not any(jumps): # for every jump, set appropriate flag for TOAs it jumps for jump_par in self.prefit_model.components[ "PhaseJump"].get_jump_param_objects(): # find TOAs jump applies to mask = jump_par.select_toa_mask(self.all_toas) # apply to dictionaries for future use for dict in self.all_toas.table["flags"][mask]: dict["jump"] = jump_par.index jumps = [ True if "jump" in dict.keys() and dict["jump"] in fit_jumps else False for dict in self.all_toas.table["flags"][selected] ] if all(jumps): log.warn( "toas being fit must not all be jumped. Remove or uncheck at least one jump in the selected toas before fitting." ) return None # numerical array of selected jump flags sel_jump_nums = [ dict["jump"] if "jump" in dict.keys() else np.nan for dict in self.all_toas.table["flags"][selected] ] # numerical array of all jump flags full_jump_nums = [ dict["jump"] if "jump" in dict.keys() else np.nan for dict in self.all_toas.table["flags"] ] for num in range(1, numjumps + 1): num = int(num) if num not in sel_jump_nums: getattr(self.prefit_model, "JUMP" + str(num)).frozen = True continue jump_select = [num == jump_num for jump_num in full_jump_nums] overlap = [a and b for a, b in zip(jump_select, selected)] # remove the jump flags for that num for dict in self.all_toas.table["flags"]: if "jump" in dict.keys() and dict["jump"] == num: del dict["jump"] # re-add the jump using overlap as 'selected' for dict in self.all_toas.table["flags"][overlap]: dict["jump"] = num if self.fitted: self.prefit_model = self.postfit_model self.prefit_resids = self.postfit_resids if self.fitter == Fitters.POWELL: fitter = pint.fitter.PowellFitter(self.selected_toas, self.prefit_model) elif self.fitter == Fitters.WLS: fitter = pint.fitter.WLSFitter(self.selected_toas, self.prefit_model) elif self.fitter == Fitters.GLS: fitter = pint.fitter.GLSFitter(self.selected_toas, self.prefit_model) chi2 = self.prefit_resids.chi2 wrms = np.sqrt(chi2 / self.selected_toas.ntoas) print("Pre-Fit Chi2:\t\t%.8g us^2" % chi2) print("Pre-Fit Weighted RMS:\t%.8g us" % wrms) fitter.fit_toas(maxiter=1) self.postfit_model = fitter.model self.postfit_resids = Residuals(self.all_toas, self.postfit_model) self.fitted = True self.write_fit_summary() # TODO: delta_pulse_numbers need some work. They serve both for PHASE and -padd functions from the TOAs # as well as for phase jumps added manually in the GUI. They really should not be zeroed out here because # that will wipe out preexisting values self.fulltoas.table["delta_pulse_numbers"] = np.zeros( self.fulltoas.ntoas) self.selected_toas.table["delta_pulse_number"] = np.zeros( self.selected_toas.ntoas) # plot the prefit without jumps pm_no_jumps = copy.deepcopy(self.postfit_model) for param in pm_no_jumps.params: if param.startswith("JUMP"): getattr(pm_no_jumps, param).value = 0.0 getattr(pm_no_jumps, param).frozen = True self.prefit_resids_no_jumps = Residuals(self.selected_toas, pm_no_jumps) f = copy.deepcopy(fitter) no_jumps = [ False if "jump" in dict.keys() else True for dict in f.toas.table["flags"] ] f.toas.select(no_jumps) selectedMJDs = self.selected_toas.get_mjds() if all(no_jumps): q = list(self.all_toas.get_mjds()) index = q.index([ i for i in self.all_toas.get_mjds() if i > selectedMJDs.min() ][0]) rs_mean = (Residuals( self.all_toas, f.model).phase_resids[index:index + len(selectedMJDs)].mean()) else: rs_mean = self.prefit_resids_no_jumps.phase_resids[no_jumps].mean() # determines how far on either side fake toas go # TODO: hard limit on how far fake toas can go --> can get clkcorr # errors if go before GBT existed, etc. minMJD, maxMJD = selectedMJDs.min(), selectedMJDs.max() spanMJDs = maxMJD - minMJD if spanMJDs < 30 * u.d: redge = ledge = 4 npoints = 400 elif spanMJDs < 90 * u.d: redge = ledge = 2 npoints = 300 elif spanMJDs < 200 * u.d: redge = ledge = 1 npoints = 300 elif spanMJDs < 400 * u.d: redge = ledge = 0.5 npoints = 200 else: redge = ledge = 0.2 npoints = 150 # Check to see if too recent nowish = (Time.now().mjd - 40) * u.d if maxMJD + spanMJDs * redge > nowish: redge = (nowish - maxMJD) / spanMJDs if redge < 0.0: redge = 0.0 f_toas, rs, mrands = random_models( f, rs_mean=rs_mean, redge_multiplier=redge, ledge_multiplier=ledge, npoints=npoints, iter=10, ) self.random_resids = rs self.fake_toas = f_toas
def setUpClass(cls): os.chdir(datadir) cls.test_obs = ["aro", "ao", "chime", "drao"] cls.test_time = Time(np.linspace(55000, 58000, num=100), scale="utc", format="pulsar_mjd")
def old_gcrs_posvel_from_itrf(loc, toas, obsname="obs"): """Return a list of PosVel instances for the observatory at the TOA times. Observatory location should be given in the loc argument as an astropy EarthLocation object. This location will be in the ITRF frame (i.e. co-rotating with the Earth). The optional obsname argument will be used as label in the returned PosVel instance. This routine returns a list of PosVel instances, containing the positions (m) and velocities (m / s) at the times of the toas and referenced to the Earth-centered Inertial (ECI, aka GCRS) coordinates. This routine is basically SOFA's pvtob() [Position and velocity of a terrestrial observing station] with an extra rotation from c2ixys() [Form the celestial to intermediate-frame-of-date matrix given the CIP X,Y and the CIO locator s]. """ unpack = False # If the input is a single TOA (i.e. a row from the table), # then put it into a list if type(toas) == table.row.Row: ttoas = Time([toas["mjd"]]) unpack = True elif type(toas) == table.table.Table: ttoas = toas["mjd"] elif isinstance(toas, Time): if toas.isscalar: ttoas = Time([toas]) unpack = True else: ttoas = toas else: if np.isscalar(toas): ttoas = Time([toas], format="mjd") unpack = True else: ttoas = toas N = len(ttoas) if len(ttoas.shape) != 1: raise ValueError("At most one-dimensional array of times possible, " "shape was {}".format(ttoas.shape)) # Get various times from the TOAs as arrays tts = np.asarray([(t.jd1, t.jd2) for t in ttoas.tt]).T ut1s = np.asarray([(t.jd1, t.jd2) for t in ttoas.ut1]).T mjds = np.asarray(ttoas.mjd) iers_b = get_iers_b_up_to_date(mjds.max()) # Get x, y coords of Celestial Intermediate Pole and CIO locator s X, Y, S = erfa.xys00a(*tts) # Get dX and dY from IERS A in arcsec and convert to radians # dX = np.interp(mjds, iers_tab['MJD'], iers_tab['dX_2000A_B']) * asec2rad # dY = np.interp(mjds, iers_tab['MJD'], iers_tab['dY_2000A_B']) * asec2rad # Get dX and dY from IERS B in arcsec and convert to radians dX = np.interp(mjds, iers_b["MJD"].to_value(u.d), iers_b["dX_2000A"].to_value(u.rad)) dY = np.interp(mjds, iers_b["MJD"].to_value(u.d), iers_b["dY_2000A"].to_value(u.rad)) # Get GCRS to CIRS matrices rc2i = erfa.c2ixys(X + dX, Y + dY, S) # Gets the TIO locator s' sp = erfa.sp00(*tts) # Get X and Y from IERS A in arcsec and convert to radians # xp = np.interp(mjds, iers_tab['MJD'], iers_tab['PM_X_B']) * asec2rad # yp = np.interp(mjds, iers_tab['MJD'], iers_tab['PM_Y_B']) * asec2rad # Get X and Y from IERS B in arcsec and convert to radians xp = np.interp(mjds, iers_b["MJD"].to_value(u.d), iers_b["PM_x"].to_value(u.rad)) yp = np.interp(mjds, iers_b["MJD"].to_value(u.d), iers_b["PM_y"].to_value(u.rad)) # Get the polar motion matrices rpm = erfa.pom00(xp, yp, sp) # Observatory geocentric coords in m xyzm = np.array([a.to_value(u.m) for a in loc.geocentric]) x, y, z = np.dot(xyzm, rpm).T # Functions of Earth Rotation Angle theta = erfa.era00(*ut1s) s, c = np.sin(theta), np.cos(theta) sx, cx = s * x, c * x sy, cy = s * y, c * y # Initial positions and velocities iposs = np.asarray([cx - sy, sx + cy, z]).T ivels = np.asarray([OM * (-sx - cy), OM * (cx - sy), np.zeros_like(x)]).T # There is probably a way to do this with np.einsum or something... # and here it is . poss = np.empty((N, 3), dtype=np.float64) vels = np.empty((N, 3), dtype=np.float64) poss = np.einsum("ij,ijk->ik", iposs, rc2i) vels = np.einsum("ij,ijk->ik", ivels, rc2i) r = PosVel(poss.T * u.m, vels.T * u.m / u.s, obj=obsname, origin="earth") if unpack: return r[0] else: return r
def fit(self, selected, iters=1): """ Run a fit using the specified fitter """ # Select all the TOAs if none are explicitly set if not any(selected): selected = ~selected """JUMP check, TODO: put in fitter?""" if "PhaseJump" in self.prefit_model.components: # if attempted fit (selected) # A) contains only jumps, don't do the fit and return an error # B) excludes a jump, turn that jump off # C) partially contains a jump, redefine that jump only with the overlap fit_jumps = [] for param in self.prefit_model.params: if getattr(self.prefit_model, param).frozen == False and param.startswith("JUMP"): fit_jumps.append(int(param[4:])) jumps = [ True if "jump" in dict.keys() and dict["jump"] in fit_jumps else False for dict in self.selected_toas.table["flags"] ] if all(jumps): log.warn( "toas being fit must not all be jumped. Remove or uncheck at least one jump in the selected toas before fitting." ) return None sel_jump_nums = [ dict["jump"] if "jump" in dict.keys() else np.nan for dict in self.selected_toas.table["flags"] ] full_jump_nums = [ dict["jump"] if "jump" in dict.keys() else np.nan for dict in self.all_toas.table["flags"] ] for num in range(1, int(np.nanmax(full_jump_nums) + 1)): num = int(num) if num not in sel_jump_nums: getattr(self.prefit_model, "JUMP" + str(num)).frozen = True continue jump_select = [num == jump_num for jump_num in full_jump_nums] overlap = [a and b for a, b in zip(jump_select, selected)] # remove the jump flags for that num for dict in self.all_toas.table["flags"]: if "jump" in dict.keys() and dict["jump"] == num: del dict["jump"] # re-add the jump using overlap as 'selected' for dict in self.all_toas.table["flags"][overlap]: dict["jump"] = num if self.fitted: self.prefit_model = self.postfit_model self.prefit_resids = self.postfit_resids if self.fitter == Fitters.POWELL: fitter = pint.fitter.PowellFitter(self.selected_toas, self.prefit_model) elif self.fitter == Fitters.WLS: fitter = pint.fitter.WLSFitter(self.selected_toas, self.prefit_model) elif self.fitter == Fitters.GLS: fitter = pint.fitter.GLSFitter(self.selected_toas, self.prefit_model) chi2 = self.prefit_resids.chi2 wrms = np.sqrt(chi2 / self.selected_toas.ntoas) print("Pre-Fit Chi2:\t\t%.8g us^2" % chi2) print("Pre-Fit Weighted RMS:\t%.8g us" % wrms) fitter.fit_toas(maxiter=1) self.postfit_model = fitter.model self.postfit_resids = Residuals(self.all_toas, self.postfit_model, set_pulse_nums=True) self.fitted = True self.write_fit_summary() # TODO: set pulse nums above not working to reset delta pulse nums, have to force it here # self.fulltoas.table['delta_pulse_numbers'] = np.zeros(self.fulltoas.ntoas) self.selected_toas.table["delta_pulse_number"] = np.zeros( self.selected_toas.ntoas) # plot the prefit without jumps pm_no_jumps = copy.deepcopy(self.postfit_model) for param in pm_no_jumps.params: if param.startswith("JUMP"): getattr(pm_no_jumps, param).value = 0.0 getattr(pm_no_jumps, param).frozen = True self.prefit_resids_no_jumps = Residuals(self.all_toas, pm_no_jumps, set_pulse_nums=True) f = copy.deepcopy(fitter) no_jumps = [ False if "jump" in dict.keys() else True for dict in f.toas.table["flags"] ] f.toas.select(no_jumps) selectedMJDs = self.selected_toas.get_mjds() if all(no_jumps): q = list(self.all_toas.get_mjds()) index = q.index([ i for i in self.all_toas.get_mjds() if i > selectedMJDs.min() ][0]) rs_mean = (Residuals( self.all_toas, f.model, set_pulse_nums=True).phase_resids[index:index + len(selectedMJDs)].mean()) else: rs_mean = self.prefit_resids_no_jumps.phase_resids[no_jumps].mean() # determines how far on either side fake toas go # TODO: hard limit on how far fake toas can go --> can get clkcorr # errors if go before GBT existed, etc. minMJD, maxMJD = selectedMJDs.min(), selectedMJDs.max() spanMJDs = maxMJD - minMJD if spanMJDs < 30 * u.d: redge = ledge = 4 npoints = 400 elif spanMJDs < 90 * u.d: redge = ledge = 2 npoints = 300 elif spanMJDs < 200 * u.d: redge = ledge = 1 npoints = 300 elif spanMJDs < 400 * u.d: redge = ledge = 0.5 npoints = 200 else: redge = ledge = 0.2 npoints = 150 # Check to see if too recent nowish = (Time.now().mjd - 40) * u.d if maxMJD + spanMJDs * redge > nowish: redge = (nowish - maxMJD) / spanMJDs if redge < 0.0: redge = 0.0 f_toas, rs = random_models( f, rs_mean=rs_mean, redge_multiplier=redge, ledge_multiplier=ledge, npoints=npoints, iter=10, ) self.random_resids = rs self.fake_toas = f_toas