@@ -202,7 +202,7 @@ def _mask_prompt_pulses(self, ws, ispecs=None):
202
202
** mask_kwargs ,
203
203
)
204
204
205
- def _subtract_prompt_pulses (self , ws , tof_res = 0.002 ):
205
+ def _subtract_prompt_pulses (self , ws ):
206
206
"""
207
207
HRPD has a long flight path from the moderator resulting
208
208
in sharp peaks from the proton pulse that maintain their
@@ -235,71 +235,39 @@ def _subtract_prompt_pulses(self, ws, tof_res=0.002):
235
235
cen = npulse * PROMPT_PULSE_INTERVAL
236
236
xlo = cen - PROMPT_PULSE_LEFT_WIDTH - 10 # add extra to ensure get background on both sides of peak
237
237
xhi = cen + PROMPT_PULSE_RIGHT_WIDTH + 10
238
- comp_func = FunctionFactory .createInitialized (
239
- f"name=PearsonIV, Centre={ 8 } , Intensity=1,Sigma=8.5, Exponent=1.5, Skew=-5,"
240
- f"constraints=(0.2<Sigma,1.5<Exponent);name=FlatBackground, A0=0,constraints=(0<A0)"
241
- )
242
- comp_func [0 ].setAttributeValue ("CentreShift" , cen )
243
- comp_func [0 ].setHeight (ws .readY (ispec )[ws .yIndexOfX (cen )])
244
- comp_func [0 ].addConstraints (f"{ - 15 } <Centre<{ 15 } " )
245
- comp_func [0 ].addConstraints (f"{ 0 } <Intensity" )
246
- comp_func [1 ]["A0" ] = min (ws .readY (ispec )[ws .yIndexOfX (xlo )], ws .readY (ispec )[ws .yIndexOfX (xhi )])
238
+ comp_func = self ._setup_single_prompt_pulse_function (ws , ispec , cen , xlo , xhi )
247
239
func .add (comp_func )
248
240
func .setDomainIndex (ipulse , ipulse )
249
241
key_suffix = f"_{ ipulse } " if ipulse > 0 else ""
250
242
fit_kwargs ["InputWorkspace" + key_suffix ] = ws .name ()
251
243
fit_kwargs ["StartX" + key_suffix ] = xlo
252
244
fit_kwargs ["EndX" + key_suffix ] = xhi
253
245
fit_kwargs ["WorkspaceIndex" + key_suffix ] = ispec
254
- # work out how to exclude
255
- exclude = []
256
- diff_consts = si .diffractometerConstants (ispec )
257
- for dpk in dpks_bragg :
258
- tof_pk = UnitConversion .run ("dSpacing" , "TOF" , dpk , 0 , DeltaEModeType .Elastic , diff_consts )
259
- tof_pk_lo = tof_pk * (1 - tof_res )
260
- tof_pk_hi = tof_pk * (1 + tof_res )
261
- if xlo < tof_pk_lo < xhi or xlo < tof_pk_hi < xhi or (tof_pk_lo < xlo and tof_pk_hi > xhi ):
262
- exclude .extend ([tof_pk_lo , tof_pk_hi ])
246
+ # exclude x-ranges where Bragg peaks overlap prompt pulse within some fractional resolution
247
+ exclude = self ._get_tofs_to_exclude (si , ispec , dpks_bragg , xlo , xhi )
263
248
if exclude :
264
249
func .fixParameter (f"f{ ipulse } .f1.A0" ) # fix constant background - Bragg peak can mess with bg
265
250
fit_kwargs ["Exclude" + key_suffix ] = exclude
266
251
# check that at least one fit region has no overlapping prompt pulse
267
- if len ([key for key in fit_kwargs .keys () if "Exclude" in key ]) < len (npulses ):
268
- # tie peak parameters to be common for all prompt pulses
269
- for idomain in range (len (npulses ) - 1 ):
270
- for param_name in ["Intensity" , "Sigma" , "Exponent" , "Skew" , "Centre" ]:
271
- func .tie (f"f{ idomain } .f0.{ param_name } " , f"f{ len (npulses ) - 1 } .f0.{ param_name } " ) # tie to first
272
- # fix some peak parameters
273
- for param_name in ["Sigma" , "Exponent" , "Skew" ]:
274
- func .fixParameter (f"f{ len (npulses ) - 1 } .f0.{ param_name } " )
252
+ excluded_keys = [key for key in fit_kwargs .keys () if "Exclude" in key ]
253
+ if len (excluded_keys ) < len (npulses ):
254
+ self ._add_ties_to_multidomain_prompt_pulse_func (func )
275
255
fit_output = mantid .Fit (Function = func , ** fit_kwargs )
276
256
# subtract prompt pulse only from spectrum
277
- success = fit_output .OutputStatus == "success" or "Changes in function value are too small" in fit_output .OutputStatus
278
- if success :
257
+ is_success = fit_output .OutputStatus == "success"
258
+ is_small_change = "Changes in function value are too small" in fit_output .OutputStatus
259
+ if is_success or is_small_change :
279
260
func = fit_output .Function .function
280
- for ipulse in range (fit_output .Function .nDomains ):
281
- func .removeTie (f"f{ ipulse } .f0.Centre" )
282
- key_suffix = f"_{ ipulse } " if ipulse > 0 else ""
283
- if "Exclude" + key_suffix in fit_kwargs :
284
- func .fixParameter (f"f{ ipulse } .f0.Centre" ) # fix centre as can't fit independently with bragg
285
- else :
286
- fitted_cen = func [ipulse ][0 ]["Centre" ]
287
- func [ipulse ][0 ].addConstraints (
288
- f"{ fitted_cen - 5 } <Centre<{ fitted_cen + 5 } "
289
- ) # free centre but stricter constraint
261
+ self ._free_ties_of_multidomain_prompt_pulse_func (func , fit_kwargs )
290
262
fit_output_final = mantid .Fit (Function = func , ** fit_kwargs )
291
263
success_final = (
292
264
fit_output_final .OutputStatus == "success"
293
265
or "Changes in function value are too small" in fit_output_final .OutputStatus
294
266
)
295
267
if success_final :
296
- fit_output = fit_output_final
297
- # accumulate peak functions
298
- pk_func = FunctionWrapper (fit_output .Function .function [0 ][0 ])
299
- for ifunc in range (1 , fit_output .Function .nDomains ):
300
- pk_func = pk_func + FunctionWrapper (fit_output .Function .function [ifunc ][0 ])
301
- ws_eval = mantid .EvaluateFunction (InputWorkspace = ws , Function = pk_func , EnableLogging = False , StoreInADS = False )
302
- y_nopulse = ws .readY (ispec ) - ws_eval .readY (1 )
268
+ func = fit_output .Function .function
269
+ y_pulses = self ._eval_fitted_prompt_pulse_peaks_only (ws , func )
270
+ y_nopulse = ws .readY (ispec ) - y_pulses
303
271
y_nopulse [y_nopulse < 0 ] = 0 # can't have negative counts
304
272
ws .setY (ispec , y_nopulse )
305
273
else :
@@ -310,6 +278,61 @@ def _subtract_prompt_pulses(self, ws, tof_res=0.002):
310
278
# Mask other banks and spectra for which fit failed
311
279
self ._mask_prompt_pulses (ws , ispec_failed )
312
280
281
+ @staticmethod
282
+ def _eval_fitted_prompt_pulse_peaks_only (ws , func ):
283
+ # accumulate peak functions only (first function in individual composite functions)
284
+ pk_func = FunctionWrapper (func [0 ][0 ])
285
+ for ifunc in range (1 , func .nDomains ()):
286
+ pk_func = pk_func + FunctionWrapper (func [ifunc ][0 ])
287
+ ws_eval = mantid .EvaluateFunction (InputWorkspace = ws , Function = pk_func , EnableLogging = False , StoreInADS = False )
288
+ return ws_eval .readY (1 )
289
+
290
+ @staticmethod
291
+ def _free_ties_of_multidomain_prompt_pulse_func (func , fit_kwargs ):
292
+ for ipulse in range (func .nDomains ()):
293
+ func .removeTie (f"f{ ipulse } .f0.Centre" )
294
+ key_suffix = f"_{ ipulse } " if ipulse > 0 else ""
295
+ if "Exclude" + key_suffix in fit_kwargs :
296
+ func .fixParameter (f"f{ ipulse } .f0.Centre" ) # fix centre as can't fit independently with bragg
297
+ else :
298
+ fitted_cen = func [ipulse ][0 ]["Centre" ]
299
+ func [ipulse ][0 ].addConstraints (f"{ fitted_cen - 5 } <Centre<{ fitted_cen + 5 } " ) # free centre but stricter constraint
300
+
301
+ @staticmethod
302
+ def _add_ties_to_multidomain_prompt_pulse_func (func ):
303
+ # tie peak parameters to be common for all prompt pulses (tie to last peak func - seems to give better results than first)
304
+ for idomain in range (func .nDomains () - 1 ):
305
+ for param_name in ["Intensity" , "Sigma" , "Exponent" , "Skew" , "Centre" ]:
306
+ func .tie (f"f{ idomain } .f0.{ param_name } " , f"f{ func .nDomains () - 1 } .f0.{ param_name } " ) # tie to first
307
+ # fix some peak parameters
308
+ for param_name in ["Sigma" , "Exponent" , "Skew" ]:
309
+ func .fixParameter (f"f{ func .nDomains () - 1 } .f0.{ param_name } " )
310
+
311
+ @staticmethod
312
+ def _get_tofs_to_exclude (specinfo , ispec , dpks_bragg , xlo , xhi , tof_res = 0.002 ):
313
+ exclude = []
314
+ diff_consts = specinfo .diffractometerConstants (ispec )
315
+ for dpk in dpks_bragg :
316
+ tof_pk = UnitConversion .run ("dSpacing" , "TOF" , dpk , 0 , DeltaEModeType .Elastic , diff_consts )
317
+ tof_pk_lo = tof_pk * (1 - tof_res )
318
+ tof_pk_hi = tof_pk * (1 + tof_res )
319
+ if xlo < tof_pk_lo < xhi or xlo < tof_pk_hi < xhi or (tof_pk_lo < xlo and tof_pk_hi > xhi ):
320
+ exclude .extend ([tof_pk_lo , tof_pk_hi ])
321
+ return exclude
322
+
323
+ @staticmethod
324
+ def _setup_single_prompt_pulse_function (ws , ispec , cen , xlo , xhi ):
325
+ comp_func = FunctionFactory .createInitialized (
326
+ "name=PearsonIV, Centre=8, Intensity=1,Sigma=8.5, Exponent=1.5, Skew=-5,"
327
+ "constraints=(0.2<Sigma,1.5<Exponent);name=FlatBackground, A0=0,constraints=(0<A0)"
328
+ )
329
+ comp_func [0 ].setAttributeValue ("CentreShift" , cen )
330
+ comp_func [0 ].setHeight (ws .readY (ispec )[ws .yIndexOfX (cen )])
331
+ comp_func [0 ].addConstraints (f"{ - 15 } <Centre<{ 15 } " )
332
+ comp_func [0 ].addConstraints (f"{ 0 } <Intensity" )
333
+ comp_func [1 ]["A0" ] = min (ws .readY (ispec )[ws .yIndexOfX (xlo )], ws .readY (ispec )[ws .yIndexOfX (xhi )])
334
+ return comp_func
335
+
313
336
@staticmethod
314
337
def _find_bragg_peak_dspacings_and_prompt_pulses (ws , ispec_start = 0 , ispec_end = 60 , dspac_res = 0.001 ):
315
338
"""
0 commit comments