def test_init_bad_unit(self): # bad units on int try: Phase(2 * u.Unit("m"), 0.3) except u.core.UnitConversionError: pass else: print("Exception not thrown") raise u.core.UnitConversionError # bad units on frac try: Phase(2, 0.3 * u.Unit("m")) except u.core.UnitConversionError: pass else: print("Exception not thrown") raise u.core.UnitConversionError # bad units on int and frac try: Phase(2 * u.Unit("m"), 0.3 * u.Unit("m")) except u.core.UnitConversionError: pass else: print("Exception not thrown") raise u.core.UnitConversionError
def test_vector_addition(self): phase1 = Phase([0, 2, 2, 2], [0, 0.3, 0.3, 0]) phase2 = Phase([0, 1, 1, 1], [0, 0.1, 0.2, -0.5]) phasesum = phase1 + phase2 assert isinstance(phasesum, Phase) assert_equal(phasesum.int, u.Quantity([0, 3, 4, 3])) assert_equal(phasesum.frac, u.Quantity([0, 0.4, -0.5, -0.5]))
def test_scalar_addition(self, ii1, ff1, ii2, ff2, sumi, sumf): phase1 = Phase(ii1, ff1) phase2 = Phase(ii2, ff2) phasesum = phase1 + phase2 assert isinstance(phasesum, Phase) assert_equal(phasesum.int, u.Quantity(sumi)) assert_equal(phasesum.frac, u.Quantity(sumf))
def test_commutative_vector_addition(self): phase1 = Phase([0, 2, 2, 2], [0, 0.3, 0.3, 0]) phase2 = Phase([0, 1, 1, 1], [0, 0.1, 0.2, -0.5]) sum1 = phase1 + phase2 sum2 = phase2 + phase1 assert_equal(sum1.int, sum2.int) assert_equal(sum1.frac, sum2.frac)
def test_commutative_scalar_addition(self): phase1 = Phase(2, 0.5) phase2 = Phase(1, 0.3) sum1 = phase1 + phase2 sum2 = phase2 + phase1 assert_equal(sum1.int, sum2.int) assert_equal(sum1.frac, sum2.frac)
def calc_phase_resids(self): """Return timing model residuals in pulse phase.""" # Read any delta_pulse_numbers that are in the TOAs table. # These are for PHASE statements, -padd flags, as well as user-inserted phase jumps # Check for the column, and if not there then create it as zeros try: delta_pulse_numbers = Phase(self.toas.table["delta_pulse_number"]) except: self.toas.table["delta_pulse_number"] = np.zeros( len(self.toas.get_mjds())) delta_pulse_numbers = Phase(self.toas.table["delta_pulse_number"]) # Track on pulse numbers, if requested if self.track_mode == "use_pulse_numbers": pulse_num = self.toas.get_pulse_numbers() if pulse_num is None: raise ValueError( "Pulse numbers missing from TOAs but track_mode requires them" ) # Compute model phase. For pulse numbers tracking # we need absolute phases, since TZRMJD serves as the pulse # number reference. modelphase = (self.model.phase(self.toas, abs_phase=True) + delta_pulse_numbers) # First assign each TOA to the correct relative pulse number, including # and delta_pulse_numbers (from PHASE lines or adding phase jumps in GUI) residualphase = modelphase - Phase(pulse_num, np.zeros_like(pulse_num)) # This converts from a Phase object to a np.float128 full = residualphase.int + residualphase.frac # If not tracking then do the usual nearest pulse number calculation else: # Compute model phase modelphase = self.model.phase(self.toas) + delta_pulse_numbers # Here it subtracts the first phase, so making the first TOA be the # reference. Not sure this is a good idea. if self.subtract_mean: modelphase -= Phase(modelphase.int[0], modelphase.frac[0]) # Here we discard the integer portion of the residual and replace it with 0 # This is effectively selecting the nearst pulse to compute the residual to. residualphase = Phase(np.zeros_like(modelphase.frac), modelphase.frac) # This converts from a Phase object to a np.float128 full = residualphase.int + residualphase.frac # If we are using pulse numbers, do we really want to subtract any kind of mean? if not self.subtract_mean: return full if not self.use_weighted_mean: mean = full.mean() else: # Errs for weighted sum. Units don't matter since they will # cancel out in the weighted sum. if np.any(self.toas.get_errors() == 0): raise ValueError( "Some TOA errors are zero - cannot calculate residuals") w = 1.0 / (self.toas.get_errors().value**2) mean, err = weighted_mean(full, w) return full - mean
def phase(self, toas, abs_phase=False): """Return the model-predicted pulse phase for the given TOAs.""" # First compute the delays to "pulsar time" delay = self.delay(toas) phase = Phase(np.zeros(toas.ntoas), np.zeros(toas.ntoas)) # Then compute the relevant pulse phases for pf in self.phase_funcs: phase += Phase(pf(toas, delay)) # If the absolute phase flag is on, use the TZR parameters to compute # the absolute phase. if abs_phase: if "AbsPhase" not in list(self.components.keys()): # if no absolute phase (TZRMJD), add the component to the model and calculate it from pint.models import absolute_phase self.add_component(absolute_phase.AbsPhase()) self.make_TZR_toa( toas ) # TODO:needs timfile to get all toas, but model doesn't have access to timfile. different place for this? tz_toa = self.get_TZR_toa(toas) tz_delay = self.delay(tz_toa) tz_phase = Phase(np.zeros(len(toas.table)), np.zeros(len(toas.table))) for pf in self.phase_funcs: tz_phase += Phase(pf(tz_toa, tz_delay)) return phase - tz_phase else: return phase
def test_associative_vector_addition(self): phase1 = Phase([0, 2, 2, 2], [0, 0.3, 0.3, 0]) phase2 = Phase([0, 1, 1, 1], [0, 0.1, 0.2, -0.5]) phase3 = Phase([1, 5, 2, 3], [0.2, 0.4, -0.3, 0.3]) sum1 = phase1 + (phase2 + phase3) sum2 = (phase1 + phase2) + phase3 assert_equal(sum1.int, sum2.int) assert_equal(sum1.frac, sum2.frac)
def test_associative_scalar_addition(self): phase1 = Phase(2, 0.5) phase2 = Phase(1, 0.3) phase3 = Phase(3, -0.1) sum1 = phase1 + (phase2 + phase3) sum2 = (phase1 + phase2) + phase3 assert_equal(sum1.int, sum2.int) assert_equal(sum1.frac, sum2.frac)
def test_vector_negation(self): phase1 = Phase([1, -2, -3, 4], [0.1, -0.3, 0.4, -0.2]) phase2 = -phase1 sum = phase1 + phase2 assert_equal(sum.int, u.Quantity(0)) assert_equal(sum.frac, u.Quantity(0)) phase01 = -Phase([0, 0], [0, 0]) assert_equal(phase01.int, u.Quantity(0)) assert_equal(phase01.frac, u.Quantity(0))
def test_scalar_negation(self): phase1 = Phase(2, 0.3) phase2 = -phase1 sum = phase1 + phase2 assert_equal(sum.int, u.Quantity(0)) assert_equal(sum.frac, u.Quantity(0)) phase01 = -Phase(0, 0) assert_equal(phase01.int, u.Quantity(0)) assert_equal(phase01.frac, u.Quantity(0))
def test_vector_addition_with_scalar(self): vecphase = Phase([0, 2, 2, 2], [0, 0.3, 0.3, 0]) scalarphase = Phase(1, 0.1) sum1 = vecphase + scalarphase assert isinstance(sum1, Phase) assert_equal(sum1.int, u.Quantity([1, 3, 3, 3])) assert_equal(sum1.frac, u.Quantity([0.1, 0.4, 0.4, 0.1])) # check commutivity sum2 = scalarphase + vecphase assert isinstance(sum2, Phase) assert_equal(sum1.int, sum2.int) assert_equal(sum1.frac, sum2.frac)
def evalabsphase(self, t): """Return the phase at time t, computed with this polyco entry""" dt = (data2longdouble(t) - self.tmid.value) * MIN_PER_DAY # Compute polynomial by factoring out the dt's phase = Phase(self.coeffs[self.ncoeff - 1]) # Compute phase using two long double for i in range(self.ncoeff - 2, -1, -1): pI = Phase(dt * phase.int) pF = Phase(dt * phase.frac) c = Phase(self.coeffs[i]) phase = pI + pF + c # Add DC term phase += self.rphase + Phase(dt * 60.0 * self.f0) return phase
def __init__(self, tmid, mjdspan, rph_int, rph_frac, f0, ncoeff, coeffs): self.tmid = data2longdouble(tmid) * u.day self.mjdspan = data2longdouble(mjdspan / MIN_PER_DAY) * u.day self.tstart = self.tmid - (self.mjdspan / 2) self.tstop = self.tmid + (self.mjdspan / 2) self.f0 = data2longdouble(f0) self.ncoeff = ncoeff self.rphase = Phase(rph_int, rph_frac) self.coeffs = data2longdouble(coeffs)
def test_vector_multiplication(self): phase = Phase([2, 1, -3], [0.1, -0.4, 0.2]) product1 = phase * 0 assert isinstance(product1, Phase) assert_equal(product1.int, u.Quantity([0, 0, 0])) assert_equal(product1.frac, u.Quantity([0, 0, 0])) product2 = phase * 1 assert_equal(product2.int, phase.int) assert_equal(product2.frac, phase.frac) product3 = phase * 2 assert_equal(product3.int, u.Quantity([4, 1, -6])) assert_equal(product3.frac, u.Quantity([0.2, 0.2, 0.4]))
def test_scalar_multiplication(self): phase = Phase(2, 0.1) product1 = phase * 0 assert isinstance(product1, Phase) assert_equal(product1.int, u.Quantity(0)) assert_equal(product1.frac, u.Quantity(0)) product2 = phase * 1 assert_equal(product2.int, phase.int) assert_equal(product2.frac, phase.frac) product3 = phase * 2 assert_equal(product3.int, u.Quantity(4)) assert_equal(product3.frac, u.Quantity(0.2))
def __init__(self, tmid, mjdspan, rphaseInt, rphaseFrac, f0, ncoeff, coeffs, obs): self.tmid = tmid * u.day self.mjdspan = mjdspan * u.day self.tstart = data2longdouble( self.tmid) - data2longdouble(self.mjdspan) / 2.0 self.tstop = data2longdouble( self.tmid) + data2longdouble(self.mjdspan) / 2.0 self.rphase = Phase(rphaseInt, rphaseFrac) self.f0 = data2longdouble(f0) self.ncoeff = ncoeff self.coeffs = data2longdouble(coeffs) self.obs = obs
def calc_phase_resids(self, weighted_mean=True, set_pulse_nums=False): """Return timing model residuals in pulse phase.""" rs = self.model.phase(self.toas) rs -= Phase(rs.int[0], rs.frac[0]) try: delta_pulse_numbers = Phase(self.toas.table["delta_pulse_number"]) except: self.toas.table["delta_pulse_number"] = np.zeros(len(self.toas.get_mjds())) delta_pulse_numbers = Phase(self.toas.table["delta_pulse_number"]) if set_pulse_nums: self.toas.table["delta_pulse_number"] = np.zeros(len(self.toas.get_mjds())) delta_pulse_numbers = Phase(self.toas.table["delta_pulse_number"]) full = Phase(np.zeros_like(rs.frac), rs.frac) + delta_pulse_numbers full = full.int + full.frac # Track on pulse numbers, if necessary if getattr(self.model, "TRACK").value == "-2": pulse_num = self.toas.get_pulse_numbers() if pulse_num is None: raise ValueError( "Pulse numbers missing from TOAs but TRACK -2 requires them" ) pn_act = np.trunc(full) addPhase = pn_act - pulse_num full -= pn_act full += addPhase if not weighted_mean: full -= full.mean() else: w = 1.0 / (np.array(self.toas.get_errors()) ** 2) wm = (full * w).sum() / w.sum() full -= wm return full if not weighted_mean: full -= full.mean() else: # Errs for weighted sum. Units don't matter since they will # cancel out in the weighted sum. if np.any(self.toas.get_errors() == 0): raise ValueError("TOA errors are zero - cannot calculate residuals") w = 1.0 / (np.array(self.toas.get_errors()) ** 2) wm = (full * w).sum() / w.sum() full -= wm return full
def eval_abs_phase(self, t): """ Polyco evaluate absolute phase for a time array. Parameters --------- t: numpy.ndarray or a single number. An time array in MJD. Time sample should be in order Returns --------- out: PINT Phase class Polyco evaluated absolute phase for t. phase = refPh + DT*60*F0 + COEFF(1) + COEFF(2)*DT + COEFF(3)*DT**2 + ... """ if not isinstance(t, (np.ndarray, list)): t = np.array([t]) entryIndex = self.find_entry(t) phaseInt = () phaseFrac = () # Compute phase for time in each entry for i in range(len(self.polycoTable)): mask = np.where( entryIndex == i) # Build mask for time in each entry t_in_entry = t[mask] if len(t_in_entry) == 0: continue # Calculate the phase as an array absp = self.polycoTable["entry"][i].evalabsphase(t_in_entry) phaseInt += (absp.int, ) phaseFrac += (absp.frac, ) # Maybe add sort function here, since the time has been masked. phaseInt = np.hstack(phaseInt).value phaseFrac = np.hstack(phaseFrac).value absPhase = Phase(phaseInt, phaseFrac) return absPhase
def test_init_scalar(self, inti, fraci, intf, fracf): phase = Phase(inti, fraci) assert isinstance(phase, Phase) assert_equal(phase.int, u.Quantity(intf)) assert_equal(phase.frac, u.Quantity(fracf))
def random_models( fitter, rs_mean, ledge_multiplier=4, redge_multiplier=4, iter=1, npoints=100 ): """Uses the covariance matrix to produce gaussian weighted random models. Returns fake toas for plotting and a list of the random models' phase resid objects. rs_mean determines where in residual phase the lines are plotted, edge_multipliers determine how far beyond the selected toas the random models are plotted. This uses an approximate method based on the cov matrix, it doesn't use MCMC. Parameters ---------- fitter fitter object with model and toas to vary from rs_mean average phase residual for toas in fitter object, used to plot random models ledge_multiplier how far the lines will plot to the left in multiples of the fit toas span, default 4 redge_multiplier how far the lines will plot to the right in multiples of the fit toas span, default 4 iter how many random models will be computed, default 1 npoints how many fake toas will be reated for the random lines, default 100 Returns ------- TOAs object containing the evenly spaced fake toas to plot the random lines with list of residual objects for the random models (one residual object each) """ params = fitter.model.get_params_dict("free", "num") mean_vector = params.values() # remove the first column and row (absolute phase) cov_matrix = (((fitter.covariance_matrix.matrix[1:]).T)[1:]).T fac = fitter.fac[1:] f_rand = deepcopy(fitter) mrand = f_rand.model # scale by fac log.debug("errors", np.sqrt(np.diag(cov_matrix))) log.debug("mean vector", mean_vector) mean_vector = np.array(list(mean_vector)) * fac cov_matrix = ((cov_matrix * fac).T * fac).T toa_mjds = fitter.toas.get_mjds() minMJD, maxMJD = toa_mjds.min(), toa_mjds.max() spanMJDs = maxMJD - minMJD # ledge and redge _multiplier control how far the fake toas extend # in either direction of the selected points x = simulation.make_fake_toas_uniform( minMJD - spanMJDs * ledge_multiplier, maxMJD + spanMJDs * redge_multiplier, npoints, mrand, ) x2 = simulation.make_fake_toas_uniform(minMJD, maxMJD, npoints, mrand) rss = [] random_models = [] for i in range(iter): # create a set of randomized parameters based on mean vector and covariance matrix rparams_num = np.random.multivariate_normal(mean_vector, cov_matrix) # scale params back to real units for j in range(len(mean_vector)): rparams_num[j] /= fac[j] rparams = OrderedDict(zip(params.keys(), rparams_num)) # print("randomized parameters",rparams) f_rand.set_params(rparams) rs = mrand.phase(x, abs_phase=True) - fitter.model.phase(x, abs_phase=True) rs2 = mrand.phase(x2, abs_phase=True) - fitter.model.phase(x2, abs_phase=True) # from calc_phase_resids in residuals rs -= Phase(0.0, rs2.frac.mean() - rs_mean) # TODO: use units here! rs = ((rs.int + rs.frac).value / fitter.model.F0.value) * 10 ** 6 rss.append(rs) random_models.append(deepcopy(mrand)) return x, rss, random_models
def tempo_polyco_table_reader(filename): """Read tempo style polyco file to an astropy table. Tempo style: The polynomial ephemerides are written to file 'polyco.dat'. Entries are listed sequentially within the file. The file format is:: ==== ======= ============================================ Line Columns Item ==== ======= ============================================ 1 1-10 Pulsar Name 11-19 Date (dd-mmm-yy) 20-31 UTC (hhmmss.ss) 32-51 TMID (MJD) 52-72 DM 74-79 Doppler shift due to earth motion (10^-4) 80-86 Log_10 of fit rms residual in periods 2 1-20 Reference Phase (RPHASE) 21-38 Reference rotation frequency (F0) 39-43 Observatory number 44-49 Data span (minutes) 50-54 Number of coefficients 55-75 Observing frequency (MHz) 76-80 Binary phase 3* 1-25 Coefficient 1 (COEFF(1)) 26-50 Coefficient 2 (COEFF(2)) 51-75 Coefficient 3 (COEFF(3)) ==== ======= ============================================ * Subsequent lines have three coefficients each, up to NCOEFF One polyco file could include more then one entrie The pulse phase and frequency at time T are then calculated as:: DT = (T-TMID)*1440 PHASE = RPHASE + DT*60*F0 + COEFF(1) + DT*COEFF(2) + DT^2*COEFF(3) + .... FREQ(Hz) = F0 + (1/60)*(COEFF(2) + 2*DT*COEFF(3) + 3*DT^2*COEFF(4) + ....) Parameters ---------- filename : str Name of the input poloco file. References ---------- http://tempo.sourceforge.net/ref_man_sections/tz-polyco.txt """ f = open(filename, "r") # Read entries to the end of file entries = [] while True: # Read first line line1 = f.readline() if len(line1) == 0: break fields = line1.split() psrname = fields[0].strip() date = fields[1].strip() utc = fields[2] tmid = np.longdouble(fields[3]) dm = float(fields[4]) doppler = float(fields[5]) logrms = float(fields[6]) # Read second line line2 = f.readline() fields = line2.split() refPhaseInt, refPhaseFrac = fields[0].split(".") refPhaseInt = data2longdouble(refPhaseInt) refPhaseFrac = data2longdouble("." + refPhaseFrac) if refPhaseInt < 0: refPhaseFrac = -refPhaseFrac refF0 = data2longdouble(fields[1]) obs = fields[2] mjdSpan = data2longdouble( fields[3]) / MIN_PER_DAY # Here change to constant nCoeff = int(fields[4]) obsfreq = float(fields[5].strip()) try: binaryPhase = data2longdouble(fields[6]) except ValueError: binaryPhase = data2longdouble(0.0) # Read coefficients nCoeffLines = int(np.ceil(nCoeff / 3)) # if nCoeff%3>0: # nCoeffLines += 1 coeffs = [] for i in range(nCoeffLines): line = f.readline() for c in line.split(): coeffs.append(data2longdouble(c)) coeffs = np.array(coeffs) tmid = tmid * u.day mjdspan = mjdSpan * u.day tstart = data2longdouble(tmid) - data2longdouble(mjdspan) / 2.0 tstop = data2longdouble(tmid) + data2longdouble(mjdspan) / 2.0 rphase = Phase(refPhaseInt, refPhaseFrac) refF0 = data2longdouble(refF0) coeffs = data2longdouble(coeffs) entry = PolycoEntry(tmid, mjdspan, refPhaseInt, refPhaseFrac, refF0, nCoeff, coeffs, obs) entries.append(( psrname, date, utc, tmid.value, dm, doppler, logrms, binaryPhase, mjdspan, tstart, tstop, obs, obsfreq, entry, )) entry_list = [] for ii in range(len(entries[0])): entry_list.append([t[ii] for t in entries]) # Construct the polyco data table pTable = table.Table( entry_list, names=( "psr", "date", "utc", "tmid", "dm", "doppler", "logrms", "binary_phase", "mjd_span", "t_start", "t_stop", "obs", "obsfreq", "entry", ), meta={"name": "Polyco Data Table"}, ) pTable["index"] = np.arange(len(entries)) return pTable
def test_precision(self): phase = Phase(1e5, 0.1) phase2 = phase + Phase(0, 1e-9) assert_equal(phase2.int, u.Quantity(1e5)) assert_equal(phase2.frac, u.Quantity(0.100000001))
def calc_phase_resids(self, weighted_mean=True, set_pulse_nums=False): """Return timing model residuals in pulse phase.""" # Please define what set_pulse_nums means! # Read any delta_pulse_numbers that are in the TOAs table. # These are for PHASE statements, -padd flags, as well as user-inserted phase jumps # Check for the column, and if not there then create it as zeros try: delta_pulse_numbers = Phase(self.toas.table["delta_pulse_number"]) except: self.toas.table["delta_pulse_number"] = np.zeros(len(self.toas.get_mjds())) delta_pulse_numbers = Phase(self.toas.table["delta_pulse_number"]) # I have no idea what this is trying to do. It just sets delta_pulse_number to zero # This will wipe out any PHASE or -padd commands from the .tim file!!! if set_pulse_nums: self.toas.table["delta_pulse_number"] = np.zeros(len(self.toas.get_mjds())) delta_pulse_numbers = Phase(self.toas.table["delta_pulse_number"]) # Compute model phase rs = self.model.phase(self.toas) # Track on pulse numbers, if requested if getattr(self.model, "TRACK").value == "-2": pulse_num = self.toas.get_pulse_numbers() if pulse_num is None: raise ValueError( "Pulse numbers missing from TOAs but TRACK -2 requires them" ) # Compute model phase. For pulse numbers tracking # we need absolute phases, since TZRMJD serves as the pulse # number reference. rs = self.model.phase(self.toas, abs_phase=True) + delta_pulse_numbers # First assign each TOA to the correct relative pulse number rs -= Phase(pulse_num, np.zeros_like(pulse_num)) # Then subtract the constant offset since that is irrelevant rs -= Phase(rs.int[0], rs.frac[0]) full = rs.int + rs.frac # If not tracking then do the usual nearest pulse number calculation else: # Compute model phase rs = self.model.phase(self.toas) + delta_pulse_numbers # Here it subtracts the first phase, so making the first TOA be the # reference. Not sure this is a good idea. rs -= Phase(rs.int[0], rs.frac[0]) # What exactly is full? full = Phase(np.zeros_like(rs.frac), rs.frac) # This converts full from a Phase object to a np.float128 full = full.int + full.frac # If we are using pulse numbers, do we really want to subtract any kind of mean? # Perhaps there should be an option to not subtract any mean? if not weighted_mean: full -= full.mean() else: # Errs for weighted sum. Units don't matter since they will # cancel out in the weighted sum. if np.any(self.toas.get_errors() == 0): raise ValueError( "Some TOA errors are zero - cannot calculate residuals" ) w = 1.0 / (np.array(self.toas.get_errors()) ** 2) wm = (full * w).sum() / w.sum() full -= wm return full
def test_init_array(self): phase = Phase([0, 2, -4, 1.2, 5], [0, 0.3, 0.5, 0, 1.4]) assert isinstance(phase, Phase) assert_equal(phase.int, u.Quantity([0, 2, -3, 1, 6])) assert_equal(phase.frac, u.Quantity([0, 0.3, -0.5, 0.2, 0.4]))