From 7c29c473020de48bf4cc9e1b4abf6dcd64f3a330 Mon Sep 17 00:00:00 2001 From: Israel Martinez Date: Mon, 26 Feb 2024 11:51:34 -0500 Subject: [PATCH 1/3] Fix exposure calculation to account for edge effects and GTI --- src/gdt/core/data_primitives.py | 2 +- src/gdt/core/tte.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/gdt/core/data_primitives.py b/src/gdt/core/data_primitives.py index aecb4f8..5fc474e 100644 --- a/src/gdt/core/data_primitives.py +++ b/src/gdt/core/data_primitives.py @@ -640,7 +640,7 @@ def bin(self, method, *args, tstart=None, tstop=None, overflow_counts = counts[:, -1] deadtime = counts.sum(axis=1) * event_deadtime + \ overflow_counts * (overflow_deadtime - event_deadtime) - exposure = (hi_edges - lo_edges) - deadtime + exposure = (np.minimum(hi_edges, tstop) - np.maximum(lo_edges, tstart)) - deadtime if self.ebounds is None: bins = TimeChannelBins(counts, lo_edges, hi_edges, exposure, diff --git a/src/gdt/core/tte.py b/src/gdt/core/tte.py index e5bedb0..0156aa2 100644 --- a/src/gdt/core/tte.py +++ b/src/gdt/core/tte.py @@ -331,6 +331,12 @@ def to_phaii(self, bin_method, *args, time_range=None, energy_range=None, obj = obj.slice_time(time_range) # do the time binning to create the TimeEnergyBins or TimeChannelBins + # Use GTI start tstart/tstop by default + if 'tstart' not in kwargs: + kwargs['tstart'] = self.gti[0].tstart + if 'tstop' not in kwargs: + kwargs['tstop'] = self.gti[-1].tstop + bins = obj.data.bin(bin_method, *args, **kwargs) if (energy_range is not None) and (self.ebounds is not None): bins = bins.slice_energy(*energy_range) From 5400f5fbb2b15ae57cebf759fa20feb68984f4cb Mon Sep 17 00:00:00 2001 From: Israel Martinez Date: Thu, 29 May 2025 15:15:01 -0400 Subject: [PATCH 2/3] Use obj instead of self, since self could have been previously sliced in energy or time and stored into obj --- src/gdt/core/tte.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gdt/core/tte.py b/src/gdt/core/tte.py index 0156aa2..cad43eb 100644 --- a/src/gdt/core/tte.py +++ b/src/gdt/core/tte.py @@ -333,9 +333,9 @@ def to_phaii(self, bin_method, *args, time_range=None, energy_range=None, # do the time binning to create the TimeEnergyBins or TimeChannelBins # Use GTI start tstart/tstop by default if 'tstart' not in kwargs: - kwargs['tstart'] = self.gti[0].tstart + kwargs['tstart'] = obj.gti[0].tstart if 'tstop' not in kwargs: - kwargs['tstop'] = self.gti[-1].tstop + kwargs['tstop'] = obj.gti[-1].tstop bins = obj.data.bin(bin_method, *args, **kwargs) if (energy_range is not None) and (self.ebounds is not None): From ed791530c55aea7a89628b322a5b86744ebce318 Mon Sep 17 00:00:00 2001 From: Israel Martinez Date: Fri, 30 May 2025 10:44:15 -0400 Subject: [PATCH 3/3] Updating tests to take into account tstart and tstop in the exposure. Adding test to check the default tstart and tstop. --- tests/core/test_primitives.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/tests/core/test_primitives.py b/tests/core/test_primitives.py index be2146e..bba09ff 100644 --- a/tests/core/test_primitives.py +++ b/tests/core/test_primitives.py @@ -1948,9 +1948,10 @@ def test_times(self): 7.790, 9.602, 9.726, 10.45, 10.61]) def test_bin(self): - + # full range, no deadtime, bin into 1 s starting at 0 - bins = self.ev.bin(bin_by_time, 1.0, time_ref=0.0, event_deadtime=0.0, + bins = self.ev.bin(bin_by_time, 1.0, time_ref=0.0, event_deadtime=0.0, + tstart = 0., tstop = 11., overflow_deadtime=0.0) self.assertEqual(bins.num_chans, 4) self.assertEqual(bins.num_times, 11) @@ -1961,8 +1962,18 @@ def test_bin(self): self.assertListEqual(bins.counts[i].tolist(), counts[i]) self.assertListEqual(bins.exposure.tolist(), [1.0]*11) + # By default, tstart and tstop are the time of the first and last event + bins = self.ev.bin(bin_by_time, 1.0, time_ref=0.0, event_deadtime=0.0, + overflow_deadtime=0.0) + + exposure = [1.0] * 11 + exposure[0] -= self.ev.times[0] + exposure[-1] -= 11 - self.ev.times[-1] + self.assertListEqual(bins.exposure.tolist(), exposure) + # full range, event deadtime, no overflow deadtime - bins = self.ev.bin(bin_by_time, 1.0, time_ref=0.0, event_deadtime=0.1, + bins = self.ev.bin(bin_by_time, 1.0, time_ref=0.0, event_deadtime=0.1, + tstart=0., tstop=11., overflow_deadtime=0.0) self.assertEqual(bins.num_chans, 4) self.assertEqual(bins.num_times, 11) @@ -1971,7 +1982,8 @@ def test_bin(self): 0.8]) # full range, event deadtime, and overflow deadtime - bins = self.ev.bin(bin_by_time, 1.0, time_ref=0.0, event_deadtime=0.1, + bins = self.ev.bin(bin_by_time, 1.0, time_ref=0.0, event_deadtime=0.1, + tstart=0., tstop=11., overflow_deadtime=0.2) self.assertEqual(bins.num_chans, 4) self.assertEqual(bins.num_times, 11) @@ -1980,7 +1992,7 @@ def test_bin(self): 0.8]) # bin starting at tstart - bins = self.ev.bin(bin_by_time, 1.0, time_ref=2.0, event_deadtime=0.0, + bins = self.ev.bin(bin_by_time, 1.0, time_ref=2.0, event_deadtime=0.0, overflow_deadtime=0.0, tstart=2.0) self.assertEqual(bins.num_chans, 4) self.assertEqual(bins.num_times, 9) @@ -1991,7 +2003,7 @@ def test_bin(self): self.assertListEqual(bins.counts[i].tolist(), counts[i]) # bin ending at tstop - bins = self.ev.bin(bin_by_time, 1.0, time_ref=0.0, event_deadtime=0.0, + bins = self.ev.bin(bin_by_time, 1.0, time_ref=0.0, event_deadtime=0.0, overflow_deadtime=0.0, tstop=9.0) self.assertEqual(bins.num_chans, 4) self.assertEqual(bins.num_times, 9) @@ -2023,7 +2035,8 @@ def test_bin(self): # no ebounds ev = EventList(times=self.ev.times, channels=self.ev.channels) - bins = ev.bin(bin_by_time, 1.0, time_ref=0.0, event_deadtime=0.0, + bins = ev.bin(bin_by_time, 1.0, time_ref=0.0, event_deadtime=0.0, + tstart=0., tstop=11., overflow_deadtime=0.0) assert isinstance(bins, TimeChannelBins) self.assertEqual(bins.num_chans, 4)