def folding(eventfile, Porb, nbins): """ Folding the events by some orbital period """ times = fits.open(eventfile)[1].data['TIME'] #getting array of times gtis_data = fits.open(eventfile)[2].data #getting GTIs T = sum([ gtis_data[i]['STOP'] - gtis_data[i]['START'] for i in range(len(gtis_data)) ]) #exposure time gtis_conform = [] for i in range(len(gtis_data)): gtis_conform.append([gtis_data[i][0], gtis_data[i][1] ]) #conform to the input that Stingray uses phase_sr, prof_sr, err_sr = fold_events(times, 1 / Porb, gtis=np.array(gtis_conform), ref_time=times[0], nbin=nbins) phase_sr_expo, prof_sr_expo, err_sr_expo = fold_events( times, 1 / Porb, gtis=np.array(gtis_conform), ref_time=times[0], expocorr=True, nbin=nbins) total_phase_sr = list(phase_sr) + list(phase_sr + 1) total_prof_sr = list(prof_sr) * 2 total_err_sr = list(err_sr) * 2 total_phase_sr_expo = list(phase_sr_expo) + list(phase_sr_expo + 1) total_prof_sr_expo = list(prof_sr_expo) * 2 total_err_sr_expo = list(err_sr_expo) * 2 plt.figure() plt.errorbar(x=total_phase_sr, y=total_prof_sr / T, yerr=total_err_sr / T, color='r', drawstyle='steps-mid') plt.errorbar(x=total_phase_sr_expo, y=total_prof_sr_expo / T, yerr=total_err_sr_expo / T, color='b', drawstyle='steps-mid') plt.legend(('Folded profile', 'Exposure-corrected'), loc='best', fontsize=12) plt.title(str(pathlib.Path(eventfile).name) + ', exposure-corrected (using Stingray fold_events)', fontsize=12) plt.xlabel('Phase', fontsize=12) plt.ylabel('Counts/s', fontsize=12) return total_phase_sr_expo, total_prof_sr_expo / T, total_err_sr_expo / T
def test_plot_profile(self): import matplotlib.pyplot as plt phase, prof, _ = fold_events(self.event_times, self.pulse_frequency) ax = plot_profile(phase, prof) plt.savefig('profile_direct.png') plt.close(plt.gcf())
def test_plot_profile_existing_ax(self): import matplotlib.pyplot as plt fig = plt.figure('Pulse profile') ax = plt.subplot() phase, prof, _ = fold_events(self.event_times, self.pulse_frequency, ax=ax) ax = plot_profile(phase, prof, ax=ax) plt.savefig('profile_existing_ax.png') plt.close(fig)
def test_plot_profile_existing_ax(self): import matplotlib.pyplot as plt fig = plt.figure('Pulse profile') ax = plt.subplot() phase, prof, _ = fold_events(self.event_times, self.pulse_frequency, ax=ax) ax = plot_profile(phase, prof, ax=ax) plt.savefig('profile_existing_ax.png') plt.close(fig)
def test_pulse_profile1(self): nbin = 16 times = np.arange(0, 1, 1/nbin) period = 1 ph, p, pe = fold_events(times, 1, nbin=nbin) np.testing.assert_array_almost_equal(p, np.ones(nbin)) np.testing.assert_array_almost_equal(ph, np.arange(nbin)/nbin + 0.5/nbin) np.testing.assert_array_almost_equal(pe, np.ones(nbin))
def test_pulse_profile2(self): nbin = 16 dt = 1/nbin times = np.arange(0, 2, dt) gtis = np.array([[-0.5*dt, 2 + 0.5*dt]]) period = 1 ph, p, pe = fold_events(times, 1, nbin=nbin, expocorr=True, gtis=gtis) np.testing.assert_array_almost_equal(ph, np.arange(nbin)/nbin + 0.5/nbin) np.testing.assert_array_almost_equal(p, 2 * np.ones(nbin)) np.testing.assert_array_almost_equal(pe, 2**0.5 * np.ones(nbin))
def test_pulse_profile3(self): nbin = 16 dt = 1 / nbin times = np.arange(0, 2 - dt, dt) gtis = np.array([[-0.5 * dt, 2 - dt]]) ph, p, pe = fold_events(times, 1, nbin=nbin, expocorr=True, gtis=gtis) np.testing.assert_array_almost_equal( ph, np.arange(nbin) / nbin + 0.5 / nbin) np.testing.assert_array_almost_equal(p, 2 * np.ones(nbin)) expected_err = 2**0.5 * np.ones(nbin) expected_err[-1] = 2 # Because of the change of exposure np.testing.assert_array_almost_equal(pe, expected_err)
def test_pulse_profile3(self): nbin = 16 dt = 1/nbin times = np.arange(0, 2 - dt, dt) gtis = np.array([[-0.5*dt, 2 - dt]]) ph, p, pe = fold_events(times, 1, nbin=nbin, expocorr=True, gtis=gtis) np.testing.assert_array_almost_equal(ph, np.arange(nbin)/nbin + 0.5/nbin) np.testing.assert_array_almost_equal(p, 2 * np.ones(nbin)) expected_err = 2**0.5 * np.ones(nbin) expected_err[-1] = 2 # Because of the change of exposure np.testing.assert_array_almost_equal(pe, expected_err)
def pulsar_events_mp(length, period, ctrate, pulsed_fraction, mean_obs, bkg_ctrate, detlev, nbin=128): nustar_orb = 5808 dt = period / 20 # The total length of the time series should be the number of pointings times the time per orbit. # Add one orbit for buffer. N_orb = int(round(length / mean_obs, 0)) tot_len = (N_orb + 1) * nustar_orb # The orbital period is 5808s. Every 5808s, a continuous observation with min_obs < length < max_obs begins start_t = numpy.multiply( numpy.arange(N_orb), numpy.random.normal(loc=nustar_orb, scale=60, size=N_orb)) point_t = numpy.random.uniform(low=mean_obs - 500, high=mean_obs + 500, size=N_orb) end_t = numpy.add(start_t, point_t) times = numpy.arange(dt / 2, tot_len + dt / 2, dt) cont_lc = numpy.random.poisson( (ctrate * (1 + pulsed_fraction * numpy.cos(2 * numpy.pi / period * times)) * dt)) + numpy.random.poisson(bkg_ctrate * dt) lc = Lightcurve(time=times, counts=cont_lc, gti=numpy.column_stack((start_t, end_t)), dt=dt) exposure = numpy.sum(point_t) events = EventList() events.gti = lc.gti events.simulate_times(lc) phase = numpy.arange(0, 1, 1 / nbin) zsq = z_n(phase, n=2, norm=fold_events(events.time, 1 / period, nbin=nbin)[1]) detected = zsq > detlev return (detected, exposure)
def test_plot_profile(self): import matplotlib.pyplot as plt phase, prof, _ = fold_events(self.event_times, self.pulse_frequency) ax = plot_profile(phase, prof) plt.savefig('profile_direct.png') plt.close(plt.gcf())
def get_TOAs_from_events(events, folding_length, *frequency_derivatives, **kwargs): """Get TOAs of pulsation. Parameters ---------- events : array-like event arrival times folding_length : float length of sub-intervals to fold *frequency_derivatives : floats pulse frequency, first derivative, second derivative, etc. Other parameters ---------------- pepoch : float, default None Epoch of timing solution, in the same units as ev_times. If none, the first event time is used. mjdref : float, default None Reference MJD template : array-like, default None The pulse template nbin : int, default 16 The number of bins in the profile (overridden by the dimension of the template) timfile : str, default 'out.tim' file to save the TOAs to (if PINT is installed) gti: [[g0_0, g0_1], [g1_0, g1_1], ...] Good time intervals. Defaults to None quick: bool If True, use a quicker fitting algorithms for TOAs. Defaults to False position: `astropy.SkyCoord` object Position of the object Returns ------- toas : array-like list of times of arrival. If ``mjdref`` is specified, they are expressed as MJDs, otherwise in MET toa_err : array-like errorbars on TOAs, in the same units as TOAs. """ import matplotlib.pyplot as plt template = kwargs['template'] if 'template' in kwargs else None mjdref = kwargs['mjdref'] if 'mjdref' in kwargs else None nbin = kwargs['nbin'] if 'nbin' in kwargs else 16 pepoch = kwargs['pepoch'] if 'pepoch' in kwargs else None timfile = kwargs['timfile'] if 'timfile' in kwargs else 'out.tim' gti = kwargs['gti'] if 'gti' in kwargs else None label = kwargs['label'] if 'label' in kwargs else None quick = kwargs['quick'] if 'quick' in kwargs else False position = kwargs['position'] if 'position' in kwargs else None pepoch = assign_value_if_none(pepoch, events[0]) gti = assign_value_if_none(gti, [[events[0], events[-1]]]) # run exposure correction only if there are less than 1000 pulsations # in the interval length = gti.max() - gti.min() expocorr = folding_length < (1000 / frequency_derivatives[0]) if template is not None: nbin = len(template) additional_phase = np.argmax(template) / nbin else: phase, profile, profile_err = \ fold_events(copy.deepcopy(events), *frequency_derivatives, ref_time=pepoch, gtis=copy.deepcopy(gti), expocorr=expocorr, nbin=nbin) fit_pars_save, _, _ = \ fit_profile_with_sinusoids(profile, profile_err, nperiods=1, baseline=True) template = std_fold_fit_func(fit_pars_save, phase) fig = plt.figure() plt.plot(phase, profile, drawstyle='steps-mid') plt.plot(phase, template, drawstyle='steps-mid') plt.savefig(timfile.replace('.tim', '') + '.png') plt.close(fig) # start template from highest bin! # template = np.roll(template, -np.argmax(template)) template *= folding_length / length template_fine = std_fold_fit_func(fit_pars_save, np.arange(0, 1, 0.001)) additional_phase = np.argmax(template_fine) / len(template_fine) starts = np.arange(gti[0, 0], gti[-1, 1], folding_length) toas = [] toa_errs = [] for start in show_progress(starts): stop = start + folding_length good = (events >= start) & (events < stop) events_tofold = events[good] if len(events_tofold) < nbin: continue gtis_tofold = \ copy.deepcopy(gti[(gti[:, 0] < stop) & (gti[:, 1] > start)]) gtis_tofold[0, 0] = start gtis_tofold[-1, 1] = stop local_f = frequency_derivatives[0] for i_f, f in enumerate(frequency_derivatives[1:]): local_f += 1 / np.math.factorial(i_f + 1) * (start - pepoch)**(i_f + 1) * f fder = copy.deepcopy(list(frequency_derivatives)) fder[0] = local_f phase, profile, profile_err = \ fold_events(events_tofold, *fder, ref_time=start, gtis=gtis_tofold, expocorr=expocorr, nbin=nbin) # BAD!BAD!BAD! # [[Pay attention to time reference here. # We are folding wrt pepoch, and calculating TOAs wrt start]] toa, toaerr = \ get_TOA(profile, 1/frequency_derivatives[0], start, template=template, additional_phase=additional_phase, quick=quick, debug=True) toas.append(toa) toa_errs.append(toaerr) toas, toa_errs = np.array(toas), np.array(toa_errs) if mjdref is not None: toas = toas / 86400 + mjdref toa_errs = toa_errs * 1e6 if HAS_PINT: label = assign_value_if_none(label, 'hendrics') toa_list = _load_and_prepare_TOAs(toas, errs_us=toa_errs) # workaround until PR #368 is accepted in pint toa_list.table['clkcorr'] = 0 toa_list.write_TOA_file(timfile, name=label, format='Tempo2') print('TOA(MJD) TOAerr(us)') else: print('TOA(MET) TOAerr(us)') for t, e in zip(toas, toa_errs): print(t, e) return toas, toa_errs
def fold_events(self, *frequency_derivatives, time_intervals=None, pi_min=35, pi_max=1909, region_filter=False, centroid=None, radius=None, ref_time=None, nbin=64, weights=1, gtis=None, expocorr=False, weight_pos=False, z_n=2): # Epoch folding without livetime correction. # Includes region and energy filtering, position weighting, and custom weighting. if region_filter or weight_pos: if centroid == None: centroid = self.centroid if radius == None: radius = self.radius if time_intervals == None: time_intervals = [[self.time[0], self.time[-1]]] if ref_time == None: ref_time = self.time[0] if gtis == None: gtis = self.gti time_mask = np.zeros(np.shape(self.time)) if np.shape(time_intervals)[-1] != 2: print('The array of time intervals has the wrong shape') return None for interval in time_intervals: start_time, end_time = interval if (start_time < np.min(self.time)): print('Invalid start time') return None elif (end_time > np.max(self.time)): print('Invalid end time') return None time_mask = time_mask + ((self.time >= start_time) * (self.time <= end_time)) time_mask = time_mask.astype(bool) pi_mask = ((self.pi > pi_min) * (self.pi < pi_max)).astype(bool) reg_mask = np.ones(np.shape(self.time)).astype(bool) p_weights = 1.0 if region_filter: # if weight_pos: # print('Region filtering overrides position weighting.') reg_mask = (np.sqrt( np.square(self.x - centroid[0]) + np.square(self.y - centroid[1])) < radius).astype(bool) elif weight_pos: # print('Make sure you have called set_xy_weights') p_weights = self.xy_weights[time_mask * pi_mask * reg_mask] # print([g.x_mean, g.y_mean, g.amplitude, g.x_stddev, g.y_stddev]) # The times to actually fold into a profile fold_times = self.time[time_mask * pi_mask * reg_mask] # The phase of each folded event fold_phases = plsr.pulse_phase(fold_times, *frequency_derivatives) phase_bins, profile, profile_err = plsr.fold_events(fold_times, *frequency_derivatives, ref_time=ref_time, \ nbin=nbin, weights=weights * p_weights, gtis=gtis, expocorr=expocorr) z_stat = plsr.z_n(fold_phases, n=z_n, norm=weights * p_weights) return phase_bins, profile, profile_err, z_stat
def fold_events_ltcorr(self, *frequency_derivatives, time_intervals= None, pi_min=35, pi_max=1909, \ region_filter=False, centroid = None, radius=None, ref_time=None, nbin = 64, weights = 1, gtis = None, expocorr=False, weight_pos=False): # Do Epoch folding to look for pulsations while also correction for livetime variations. This is important for high count rates and high pulse fractions. # Includes region and energy filtering, position weighting, and custom weighting. if centroid == None: centroid = self.centroid if radius == None: radius = self.radius if time_intervals == None: time_intervals = [[self.time[0], self.time[-1]]] if ref_time == None: ref_time = self.time[0] if gtis == None: gtis = self.gti time_mask = np.zeros(np.shape(self.time)) if np.shape(time_intervals)[-1] != 2: print('The array of time intervals has the wrong shape') return None for interval in time_intervals: start_time, end_time = interval if (start_time < np.min(self.time)): print('Invalid start time') return None elif (end_time > np.max(self.time)): print('Invalid end time') return None time_mask = time_mask + ((self.time >= start_time) * (self.time <= end_time)) time_mask = time_mask.astype(bool) pi_mask = ((self.pi > pi_min) * (self.pi < pi_max)).astype(bool) reg_mask = np.ones(np.shape(self.time)).astype(bool) p_weights = 1.0 if region_filter: if weight_pos: print('Region filtering overrides position weighting.') reg_mask = (np.sqrt( np.square(self.x - centroid[0]) + np.square(self.y - centroid[1])) < radius).astype(bool) elif weight_pos: # print('Make sure you have called set_xy_weights') p_weights = self.xy_weights[time_mask * pi_mask * reg_mask] # print([g.x_mean, g.y_mean, g.amplitude, g.x_stddev, g.y_stddev]) # The times to actually fold into a profile fold_times = self.time[time_mask * pi_mask * reg_mask] # The phase of each folded event fold_phases = plsr.pulse_phase(fold_times, *frequency_derivatives) # We should use PRIOR from every event though temp_times = self.time[time_mask] temp_prior = self.prior[time_mask] temp_phases = plsr.pulse_phase(temp_times, *frequency_derivatives) # print(frequency_derivatives) p_derivs = plsr.p_to_f(*frequency_derivatives) p_t = np.zeros(np.shape(temp_times)) # Taylor expand P(t) for i in range(len(p_derivs)): p_t = p_t + (p_derivs[i] * np.power(temp_times - ref_time, i) / scipy.special.factorial(i, exact=True)) phase_bins, profile, profile_err = plsr.fold_events(fold_times, *frequency_derivatives, ref_time=ref_time, \ nbin=nbin, weights=weights * p_weights, gtis=gtis, expocorr=expocorr) livetime_profile = np.zeros(np.shape(profile)) # During which phase bin did PRIOR start counting before each event? start_bins = np.floor(nbin * (temp_phases - (temp_prior / p_t))) # What phase bin is each event at? end_bins = np.floor(nbin * temp_phases) # Add 1 to every phase bin for which PRIOR was active. for i in range(len(start_bins)): start = start_bins[i] end = end_bins[i] # print(start) # If PRIOR started counting in a previous cycle, add 1 to each full cycle and to the partial cycles if start < 0: for j in range(int(np.floor(np.abs(start) / nbin))): livetime_profile = livetime_profile + 1 livetime_profile[int(start % nbin):] = livetime_profile[int(start % nbin):] + 1 livetime_profile[:int(end)] = livetime_profile[:int(end)] + 1 # Else, just add 1 to the portion of this cycle during which PRIOR was counting else: livetime_profile[int(start):int( end)] = livetime_profile[int(start):int(end)] + 1 # livetime corresponding to the phase bin for each photon fold_lts = np.array( [livetime_profile[int(b)] for b in np.floor(nbin * fold_phases)]) z_stat = plsr.z_n(fold_phases, n=2, norm=weights * p_weights * np.max(livetime_profile) / fold_lts) # livetime_profile = livetime_profile/np.max(livetime_profile) return phase_bins, profile, profile_err, livetime_profile, z_stat
color='b', drawstyle='steps-mid') plt.title(str(pathlib.Path(eventfile_xmm).name) + ', exposure-corrected (using Lv2_phase)', fontsize=12) plt.xlabel('Phase', fontsize=12) plt.ylabel('Counts/s', fontsize=12) plt.legend(('Folded profile', 'Exposure-corrected profile'), loc='best', fontsize=12) ##### Using stingray.pulse.pulsar's fold_events phase_sr, prof_sr, err_sr = fold_events(times_xmm, 1 / pb, freqdot, freqdotdot, gtis=np.array(gtis_conform), ref_time=times_xmm[0] - phaseoff * pb, nbin=nbins) phase_sr_expo, prof_sr_expo, err_sr_expo = fold_events( times_xmm, 1 / pb, freqdot, freqdotdot, gtis=np.array(gtis_conform), ref_time=times_xmm[0] - phaseoff * pb, expocorr=True, nbin=nbins) total_phase_sr = np.array(list(phase_sr) + list(phase_sr + 1)) total_prof_sr = np.array(list(prof_sr) * 2)
for i in range(len(gtis))]) #exposure time gtis_conform = [] for i in range(len(gtis)): gtis_conform.append([gtis[i][0], gtis[i][1]]) #conform to the input that Stingray uses freq = 622.12202499673376738 freqdot = -6.5114520166830696246e-15 freqdotdot = 0 nbins = 20 phase_sr, prof_sr, err_sr = fold_events(times, freq, freqdot, freqdotdot, gtis=np.array(gtis_conform), ref_time=times[0], nbin=nbins) phase_sr_expo, prof_sr_expo, err_sr_expo = fold_events( times, freq, freqdot, freqdotdot, gtis=np.array(gtis_conform), ref_time=times[0], expocorr=True, nbin=nbins) total_phase_sr = list(phase_sr) + list(phase_sr + 1) total_prof_sr = list(prof_sr) * 2
def profile(self): ph, profile, profile_err = fold_events(self.times, self.pulse_frequency, nbin=self.nbin, gtis=self.T_star_stop) return (ph, profile, profile_err)