def test_zn(self): """Test pulse phase calculation, frequency only.""" ph = np.array([0, 1]) np.testing.assert_array_almost_equal(z_n(ph), 8) ph = np.array([]) np.testing.assert_array_almost_equal(z_n(ph), 0) ph = np.array([0.2, 0.7]) ph2 = np.array([0, 0.5]) np.testing.assert_array_almost_equal(z_n(ph), z_n(ph2))
def get_z2_label(phas, prof): good = phas < 1 z2 = z_n(phas[good], n=2, norm=prof[good]) z2_detlev = z2_n_detection_level(n=2) z2_label = r'$Z_2^2 = {:.1f} (90\% det. lev. {:.1f})$'.format( z2, z2_detlev) return z2_label
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_zn_2(self): np.testing.assert_almost_equal(z_n(np.arange(1), n=1, norm=1), 2) np.testing.assert_almost_equal(z_n(np.arange(1), n=2, norm=1), 4) np.testing.assert_almost_equal(z_n(np.arange(2), n=2, norm=1), 8) np.testing.assert_almost_equal(z_n(np.arange(2) + 0.5, n=2, norm=1), 8)
def test_zn_2(self): np.testing.assert_almost_equal(z_n(np.arange(1), n=1, norm=1), 2) np.testing.assert_almost_equal(z_n(np.arange(1), n=2, norm=1), 4) np.testing.assert_almost_equal(z_n(np.arange(2), n=2, norm=1), 8) np.testing.assert_almost_equal(z_n(np.arange(2)+0.5, n=2, norm=1), 8)
def efold_search_AandB(events_A, events_B, f_min, f_max, f_steps, fdots=None, time_intervals=None, nbin=32, pi_min=35, pi_max=260, return_peak=False, z_n=2): # Scan over frequency and do epoch folding. A_mask = np.sqrt( np.square(events_A.x - events_A.centroid[0]) + np.square(events_A.y - events_A.centroid[1])) <= events_A.radius B_mask = np.sqrt( np.square(events_B.x - events_B.centroid[0]) + np.square(events_B.y - events_B.centroid[1])) <= events_B.radius temp_time = np.concatenate([events_A.time[A_mask], events_B.time[B_mask]]) sorted_arg = np.argsort(temp_time) temp_time = temp_time[sorted_arg] temp_pi = np.concatenate([events_A.pi[A_mask], events_B.pi[B_mask]])[sorted_arg] joined_ev = EventList_ext(time=temp_time, gti=sting_gti.cross_two_gtis( events_A.gti, events_B.gti), pi=temp_pi) ref_time = joined_ev.time[0] f_arr = np.linspace(f_min, f_max, num=f_steps) # if fdots: # fgrid, fdgrid, z_stats = z_n_search(joined_ev.time, f_arr, nharm=z_n, nbin=nbin, gti=joined_ev.gti, fdots=fdots, segment_size=1e6) # else: # fgrid, z_stats = z_n_search(joined_ev.time, f_arr, nharm=z_n, nbin=nbin, gti=joined_ev.gti, fdots=fdots, segment_size=1e6) pi_mask = ((joined_ev.pi > pi_min) * (joined_ev.pi < pi_max)).astype(bool) # The times to actually fold into a profile fold_times = joined_ev.time[pi_mask] - ref_time z_stats = [] if fdots is not None: f_grid, fd_grid = np.meshgrid(f_arr, fdots) z_stats = np.zeros(f_grid.shape) for x in tqdm(range(f_steps)): for y in range(len(fdots)): # The phase of each folded event fold_phases = plsr.pulse_phase(fold_times, *[f_grid[y, x], fd_grid[y, x]]) z_stats[y, x] = plsr.z_n(fold_phases, n=z_n) z_prob = stats.z2_n_logprobability(z_stats, ntrial=(f_steps * len(fdots)), n=z_n) if return_peak: max_yx = np.unravel_index(np.argmax(z_stats, axis=None), z_stats.shape) phase_bins, profile, profile_err, _ = \ joined_ev.fold_events(*[f_grid[max_yx], fd_grid[max_yx]], time_intervals = time_intervals, \ nbin = nbin, ref_time = ref_time, region_filter=False, pi_min=pi_min, pi_max=pi_max, weight_pos=False, z_n=z_n) return f_grid, fd_grid, z_prob, z_stats, phase_bins, profile, profile_err else: return f_grid, fd_grid, z_prob, z_stats else: for f in tqdm(f_arr): # The phase of each folded event fold_phases = plsr.pulse_phase(fold_times, f) z_stat = plsr.z_n(fold_phases, n=z_n) # _, _, _, z_stat = \ # joined_ev.fold_events(f, time_intervals = time_intervals, \ # nbin = nbin, ref_time = joined_ev.time[0], region_filter=False, pi_min=pi_min, pi_max=pi_max, weight_pos=False, z_n=z_n) z_stats.append(z_stat) z_stats = np.array(z_stats) z_prob = stats.z2_n_logprobability(z_stats, ntrial=len(f_arr), n=z_n) if return_peak: phase_bins, profile, profile_err, _ = \ joined_ev.fold_events(f_arr[np.argmax(z_stats)], time_intervals = time_intervals, \ nbin = nbin, ref_time = ref_time, region_filter=False, pi_min=pi_min, pi_max=pi_max, weight_pos=False, z_n=z_n) return f_arr, z_prob, z_stats, phase_bins, profile, profile_err else: return f_arr, z_prob, z_stats
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