1313import xarray as xr
1414import pandas as pd
1515from scipy .signal import find_peaks
16+ from scipy .ndimage import gaussian_filter
1617
1718from .base import SerializableMixin
1819from .cookiebox_deconvolve import TOFAnalogResponse
@@ -47,7 +48,7 @@ def search_offset(trace: np.ndarray, sigma: float=20) -> int:
4748 peak_idx = np .argmax (smoothened )
4849 return peak_idx - 200
4950
50- def search_roi (roi : np .ndarray ) -> np .ndarray :
51+ def search_roi (roi : np .ndarray ) -> Tuple [ np .ndarray , np . ndarray ] :
5152 """
5253 Find highest peaks in the 1D trace.
5354
@@ -56,9 +57,21 @@ def search_roi(roi: np.ndarray) -> np.ndarray:
5657
5758 Returns: Peak position.
5859 """
59- import scipy
60- p , _ = scipy .signal .find_peaks (roi , prominence = (0.25 * np .max (roi ), None ))
61- return p
60+ roi_smooth = gaussian_filter (roi [np .isfinite (roi )], sigma = 5 )
61+ p , prop = find_peaks (roi_smooth , prominence = 0.25 * np .max (roi_smooth ), width = 1 , height = 0 )
62+ idx = np .argsort (prop ["peak_heights" ])[::- 1 ]
63+ p = p [idx ]
64+ w = prop ["widths" ][idx ]
65+ if len (p ) == 0 :
66+ logging .info (f"Failed to find peaks." )
67+ return - 1 , 0
68+ if len (p ) == 1 :
69+ logging .info (f"Failed to find two peaks. If there is no Auger peak, set start_roi=0" )
70+ return - 1 , 0
71+ idx = np .argsort (p [:2 ])
72+ p = p [:2 ][idx ]
73+ w = w [:2 ][idx ]
74+ return p [- 1 ], w [- 1 ]
6275
6376def model (ts : np .ndarray , c : float , e0 : float , t0 : float ) -> np .ndarray :
6477 """
@@ -177,16 +190,13 @@ def calc_mean(itr: Tuple[int, int], scan: Scan, xgm_data: xr.DataArray, tof: Dic
177190 tof_data = tof_data .pulse_data (pulse_dim = 'pulseIndex' , parallel = parallel )
178191
179192 # select XGM
180- if xgm_threshold > 0 :
181- mask = xgm_data .coords ["trainId" ].isin (good_ids )
182- sel_xgm_data = xgm_data [mask ]
183- tof_data = tof_data .loc [sel_xgm_data > xgm_threshold , :]
184- tof_xgm_data = sel_xgm_data .loc [sel_xgm_data > xgm_threshold ]
193+ mask = xgm_data .coords ["trainId" ].isin (good_ids )
194+ sel_xgm_data = xgm_data [mask ]
195+ tof_data = tof_data .loc [sel_xgm_data > xgm_threshold , :]
196+ tof_xgm_data = sel_xgm_data .loc [sel_xgm_data > xgm_threshold ].to_numpy ()
185197
186- out_data = - tof_data .mean ('pulse' )
187- out_xgm = 0.0
188- if xgm_threshold > 0 :
189- out_xgm = tof_xgm_data .mean ('pulse' ).to_numpy ()
198+ out_data = - tof_data .mean ("pulse" )
199+ out_xgm = np .mean (tof_xgm_data )
190200 else :
191201 # option 2: count photon peaks
192202 # in this case, ignore the XGM, as it is only used for cleaning the data
@@ -198,9 +208,7 @@ def calc_mean(itr: Tuple[int, int], scan: Scan, xgm_data: xr.DataArray, tof: Dic
198208 out_data , _ = np .histogram (tof_data .edge , bins = bins , weights = - tof_data .amplitude )
199209
200210 out_data = xr .DataArray (out_data , dims = ('sample' ), coords = {'sample' : bins [:- 1 ]})
201- out_xgm = 0.0
202- if xgm_threshold > 0 :
203- out_xgm = xgm_data .mean ('pulse' ).to_numpy ()
211+ out_xgm = np .mean (xgm_data .to_numpy ())
204212
205213 if correction_fn is not None :
206214 out_data = correction_fn [tof_id ](out_data )
@@ -691,16 +699,13 @@ def update_roi(self, parallel=None):
691699 logging .info ("Reading calibration data ... (this takes a while)" )
692700 self .select_calibration_data (parallel )
693701 # find RoI if needed
694- if (self .auger_start_roi is None
695- or self .start_roi is None
696- or self .stop_roi is None ):
697- logging .info ("Finding RoI ..." )
698- logging .info ("(This may fail. If it does, please provide a `auger_start_roi`, `start_roi` and `stop_roi`.)" )
699- for tof_id in self .kwargs_adq .keys ():
702+ for tof_id in self .kwargs_adq .keys ():
703+ if (self .auger_start_roi [tof_id ] is None
704+ or self .start_roi [tof_id ] is None
705+ or self .stop_roi [tof_id ] is None ):
706+ logging .info (f"Finding RoI for TOF { tof_id } ..." )
700707 self .find_roi (tof_id )
701- logging .info (f"Auger start RoIs found: { self .auger_start_roi } " )
702- logging .info (f"Start RoIs found: { self .start_roi } " )
703- logging .info (f"Stop RoIs found: { self .stop_roi } " )
708+ logging .info (f"Start RoIs found: { self .start_roi [tof_id ]} " )
704709
705710 def update_fit_result (self ):
706711 """
@@ -781,38 +786,32 @@ def find_roi(self, tof_id: int):
781786 Args:
782787 tof_id: The eTOF ID.
783788 """
784- auger = list ()
785- roi = list ()
789+ p = list ()
790+ w = list ()
791+ N = self .calibration_data [tof_id ].shape [- 1 ]
786792 for e in range (self .calibration_data [tof_id ].shape [0 ]):
787793 d = self .calibration_data [tof_id ][e , :]
788- peaks = search_roi (d )
789- peaks = sorted (peaks )
790- if len (peaks ) < 2 :
791- logging .info (f"Failed to find peaks for eTOF { tof_id } , energy index { e } . "
792- f"Check the data quality." )
794+ peak , width = search_roi (d )
795+ if peak < 0 :
793796 continue
794- auger += [peaks [ 0 ] ]
795- roi += [peaks [ 1 ] ]
796- if len (auger ) == 0 or len ( roi ) == 0 :
797+ p += [peak ]
798+ w += [width ]
799+ if len (p ) == 0 :
797800 logging .info (f"No peaks found in eTOF { tof_id } . "
798801 f"Check the data quality. "
799802 f"I will set the RoI to collect non-sense,"
800803 f" so this TOF data will be meaningless. "
801804 f"It will also be masked." )
802805 self .auger_start_roi [tof_id ] = 0
803806 self .start_roi [tof_id ] = 100
804- self .stop_roi [tof_id ] = 200
807+ self .stop_roi [tof_id ] = N
805808 self .mask [tof_id ] = False
806809 return
807- a = min (auger )
808- b = min (roi )
809- dab = abs (b - a )
810- stop_roi = max (roi ) + int (dab / 2 )
811- auger_start_roi = int (a - dab / 2 )
812- start_roi = int (b - dab / 2 )
813- self .auger_start_roi [tof_id ] = auger_start_roi
814- self .start_roi [tof_id ] = start_roi
815- self .stop_roi [tof_id ] = stop_roi
810+ idx = np .argmin (p )
811+ pos = p [idx ] - 2 * w [idx ]
812+ self .auger_start_roi [tof_id ] = 0
813+ self .start_roi [tof_id ] = int (pos )
814+ self .stop_roi [tof_id ] = N
816815
817816 def plot_calibration_data (self ):
818817 """Plot data for checks.
@@ -966,9 +965,8 @@ def calculate_calibration_and_transmission(self, tof_id: int):
966965 detected = self .tof_fit_result [tof_id ].A
967966 #produced = self.calibration_mean_xgm[tof_id] * self.tof_fit_result[tof_id].Aa * dsig_dth
968967 #produced = self.tof_fit_result[tof_id].Aa * dsig_dth
969- produced = dsig_dth
970- if produced == 0 :
971- produced += 1e-1
968+ produced = dsig_dth * self .calibration_mean_xgm [tof_id ][mask ][eidx ]
969+ produced [produced == 0 ] = 0.1 # avoid division by zero
972970 en = detected [mask ][eidx ]/ produced
973971 # interpolate normalization
974972 self .normalization [tof_id ] = np .interp (self .energy_axis ,
0 commit comments