@@ -18,7 +18,7 @@ def compute_tof_in_each_cluster(
1818 da : StreakClusteredData [RunType ],
1919 mod_period : ModulationPeriod ,
2020) -> TofDetector [RunType ]:
21- ''' Fits a line through each cluster, the intercept of the line is t0.
21+ """ Fits a line through each cluster, the intercept of the line is t0.
2222 The line is fitted using linear regression with an outlier removal procedure.
2323
2424 The algorithm is:
@@ -30,15 +30,18 @@ def compute_tof_in_each_cluster(
3030 of the points in the cluster, and probably should belong to another cluster or
3131 are part of the background.
3232 3. Go back to 1) and iterate until convergence. A few iterations should be enough.
33- '''
33+ """
3434 if isinstance (da , sc .DataGroup ):
3535 return sc .DataGroup (
3636 {k : compute_tof_in_each_cluster (v , mod_period ) for k , v in da .items ()}
3737 )
3838
3939 max_distance_from_streak_line = mod_period / 3
4040 sin_theta_L = sc .sin (da .bins .coords ['two_theta' ] / 2 ) * da .bins .coords ['Ltotal' ]
41- t = da .bins .coords ['event_time_offset' ]
41+ t = time_of_arrival (
42+ da .bins .coords ['event_time_offset' ],
43+ da .coords ['tc' ].to (unit = da .bins .coords ['event_time_offset' ].unit ),
44+ )
4245 for _ in range (15 ):
4346 s , t0 = _linear_regression_by_bin (sin_theta_L , t , da .data )
4447
@@ -57,10 +60,10 @@ def compute_tof_in_each_cluster(
5760def _linear_regression_by_bin (
5861 x : sc .Variable , y : sc .Variable , w : sc .Variable
5962) -> tuple [sc .Variable , sc .Variable ]:
60- ''' Performs a weighted linear regression of the points
63+ """ Performs a weighted linear regression of the points
6164 in the binned variables ``x`` and ``y`` weighted by ``w``.
6265 Returns ``b1`` and ``b0`` such that ``y = b1 * x + b0``.
63- '''
66+ """
6467 w = sc .values (w )
6568 tot_w = w .bins .sum ()
6669
@@ -77,7 +80,7 @@ def _linear_regression_by_bin(
7780
7881
7982def _compute_d (
80- event_time_offset : sc .Variable ,
83+ time_of_arrival : sc .Variable ,
8184 theta : sc .Variable ,
8285 dhkl_list : sc .Variable ,
8386 pulse_length : sc .Variable ,
@@ -87,15 +90,15 @@ def _compute_d(
8790 given a list of known peaks."""
8891 # Source: https://www2.mcstas.org/download/components/3.4/contrib/NPI_tof_dhkl_detector.comp
8992 sinth = sc .sin (theta )
90- t = event_time_offset
93+ t = time_of_arrival
9194
9295 d = sc .empty (dims = sinth .dims , shape = sinth .shape , unit = dhkl_list [0 ].unit )
9396 d [:] = sc .scalar (float ('nan' ), unit = dhkl_list [0 ].unit )
9497 dtfound = sc .empty (dims = sinth .dims , shape = sinth .shape , dtype = 'float64' , unit = t .unit )
9598 dtfound [:] = sc .scalar (float ('nan' ), unit = t .unit )
9699
97100 const = (2 * sinth * L0 / (scipp .constants .h / scipp .constants .m_n )).to (
98- unit = f'{ event_time_offset .unit } /angstrom'
101+ unit = f'{ time_of_arrival .unit } /angstrom'
99102 )
100103
101104 for dhkl in dhkl_list :
@@ -112,36 +115,49 @@ def _compute_d(
112115 return d
113116
114117
115- def _tof_from_dhkl (
118+ def time_of_arrival (
116119 event_time_offset : sc .Variable ,
120+ tc : sc .Variable ,
121+ ):
122+ """Does frame unwrapping for pulse shaping chopper modes.
123+
124+ Events before the "cutoff time" `tc` are assumed to come from the previous pulse."""
125+ _eto = event_time_offset
126+ T = sc .scalar (1 / 14 , unit = 's' ).to (unit = _eto .unit )
127+ tc = tc .to (unit = _eto .unit )
128+ return sc .where (_eto >= tc , _eto , _eto + T )
129+
130+
131+ def _tof_from_dhkl (
132+ time_of_arrival : sc .Variable ,
117133 theta : sc .Variable ,
118134 coarse_dhkl : sc .Variable ,
119135 Ltotal : sc .Variable ,
120136 mod_period : sc .Variable ,
121137 time0 : sc .Variable ,
122138) -> sc .Variable :
123- ''' Computes tof for BEER given the dhkl peak that the event belongs to'''
139+ """ Computes tof for BEER given the dhkl peak that the event belongs to"""
124140 # Source: https://www2.mcstas.org/download/components/3.4/contrib/NPI_tof_dhkl_detector.comp
125141 # tref = 2 * d_hkl * sin(theta) / hm * Ltotal
126- # tc = event_time_zero - time0 - tref
142+ # tc = time_of_arrival - time0 - tref
127143 # dt = floor(tc / mod_period + 0.5) * mod_period + time0
128- # tof = event_time_offset - dt
144+ # tof = time_of_arrival - dt
129145 c = (- 2 * 1.0 / (scipp .constants .h / scipp .constants .m_n )).to (
130- unit = f'{ event_time_offset .unit } /m/angstrom'
146+ unit = f'{ time_of_arrival .unit } /m/angstrom'
131147 )
132148 out = sc .sin (theta )
133149 out *= c
134150 out *= coarse_dhkl
135151 out *= Ltotal
136- out += event_time_offset
152+ out += time_of_arrival
137153 out -= time0
138154 out /= mod_period
139155 out += 0.5
140156 sc .floor (out , out = out )
141157 out *= mod_period
142158 out += time0
143159 out *= - 1
144- out += event_time_offset
160+ out += time_of_arrival
145161 return out
146162
147163
@@ -151,20 +167,23 @@ def tof_from_known_dhkl_graph(
151167 time0 : WavelengthDefinitionChopperDelay ,
152168 dhkl_list : DHKLList ,
153169) -> TofCoordTransformGraph :
170+ """Graph computing ``tof`` in modulation chopper modes using
171+ list of peak positions."""
172+
154173 def _compute_coarse_dspacing (
155- event_time_offset ,
174+ time_of_arrival : sc . Variable ,
156175 theta : sc .Variable ,
157176 pulse_length : sc .Variable ,
158- L0 ,
177+ L0 : sc . Variable ,
159178 ):
160- ''' To capture dhkl_list, otherwise it causes an error when
179+ """ To capture dhkl_list, otherwise it causes an error when
161180 ``.transform_coords`` is called unless it is called with
162181 ``keep_indermediates=False``.
163182 The error happens because data arrays do not allow coordinates
164183 with dimensions not present on the data.
165- '''
184+ """
166185 return _compute_d (
167- event_time_offset = event_time_offset ,
186+ time_of_arrival = time_of_arrival ,
168187 theta = theta ,
169188 pulse_length = pulse_length ,
170189 L0 = L0 ,
@@ -176,19 +195,56 @@ def _compute_coarse_dspacing(
176195 'mod_period' : lambda : mod_period ,
177196 'time0' : lambda : time0 ,
178197 'tof' : _tof_from_dhkl ,
198+ 'time_of_arrival' : time_of_arrival ,
179199 'coarse_dhkl' : _compute_coarse_dspacing ,
180200 'theta' : lambda two_theta : two_theta / 2 ,
181201 }
182202
183203
184- def compute_tof_from_known_peaks (
204+ def t0_estimate (
205+ wavelength_estimate : sc .Variable ,
206+ L0 : sc .Variable ,
207+ Ltotal : sc .Variable ,
208+ ) -> sc .Variable :
209+ """Estimates the time-at-chopper by assuming the wavelength."""
210+ return (
211+ sc .constants .m_n
212+ / sc .constants .h
213+ * wavelength_estimate
214+ * (L0 - Ltotal ).to (unit = wavelength_estimate .unit )
215+ ).to (unit = 's' )
216+
217+
218+ def _tof_from_t0 (
219+ time_of_arrival : sc .Variable ,
220+ t0 : sc .Variable ,
221+ ) -> sc .Variable :
222+ """Computes time-of-flight by subtracting a start time."""
223+ return time_of_arrival - t0
224+
225+
226+ def tof_from_t0_estimate_graph () -> TofCoordTransformGraph :
227+ """Graph for computing ``tof`` in pulse shaping chopper modes."""
228+ return {
229+ 't0' : t0_estimate ,
230+ 'tof' : _tof_from_t0 ,
231+ 'time_of_arrival' : time_of_arrival ,
232+ }
233+
234+
235+ def compute_tof (
185236 da : RawDetector [RunType ], graph : TofCoordTransformGraph
186237) -> TofDetector [RunType ]:
238+ """Uses the transformation graph to compute ``tof``."""
187239 return da .transform_coords (('tof' ,), graph = graph )
188240
189241
190242convert_from_known_peaks_providers = (
191243 tof_from_known_dhkl_graph ,
192- compute_tof_from_known_peaks ,
244+ compute_tof ,
245+ )
246+ convert_pulse_shaping = (
247+ tof_from_t0_estimate_graph ,
248+ compute_tof ,
193249)
194250providers = (compute_tof_in_each_cluster ,)
0 commit comments