Ticket #10569: ISISCommandInterface_RKH.py

File ISISCommandInterface_RKH.py, 53.4 KB (added by Anders Markvardsen, 6 years ago)

Richard update emailed 20th Nov 2014

Line 
1"""
2    Enables the SANS commands (listed at http://www.mantidproject.org/SANS) to
3    be run
4"""
5import isis_instrument
6from reducer_singleton import ReductionSingleton
7from mantid.kernel import Logger
8sanslog = Logger("SANS")
9
10import isis_reduction_steps
11import isis_reducer
12from centre_finder import CentreFinder as CentreFinder
13#import SANSReduction
14from mantid.simpleapi import *
15from mantid.api import WorkspaceGroup
16import copy
17from SANSadd2 import *
18import SANSUtility as su
19from SANSUtility import deprecated
20
21# disable plotting if running outside Mantidplot
22try:
23    import mantidplot
24except:
25    mantidplot = None
26    #this should happen when this is called from outside Mantidplot and only then, the result is that attempting to plot will raise an exception
27    pass
28
29try:
30    from PyQt4.QtGui import qApp
31    def appwidgets():
32        return qApp.allWidgets()
33except ImportError:
34    def appwidgets():
35        return []
36
37_VERBOSE_ = False
38LAST_SAMPLE = None
39def SetVerboseMode(state):
40#TODO: this needs to be on the reducer
41    _VERBOSE_ = state
42
43# Print a message and log it if the
44def _printMessage(msg, log = True, no_console=False):
45    if log == True and _VERBOSE_ == True:
46        sanslog.notice(msg)
47    if not no_console:
48        print msg
49
50def issueWarning(msg):
51    """
52        Issues a Mantid message
53        @param msg: message to be issued
54    """
55    isis_reduction_steps._issueWarning(msg)
56
57def _refresh_singleton():
58    ReductionSingleton.clean(isis_reducer.ISISReducer)
59    ReductionSingleton().remove_settings()
60
61def Clean():
62    """
63    An exposed command to allow cleaning of the reducer, and any related
64    settings.
65    """
66    _refresh_singleton()
67
68def SANS2D(idf_path=None):
69    """
70        Initialises the instrument settings for SANS2D
71        @param idf_path :: optionally specify the path to the SANS2D IDF to use.
72                           Uses default if none specified.
73        @return True on success
74    """
75    _printMessage('SANS2D()')
76    try:
77        instrument = isis_instrument.SANS2D(idf_path)
78
79        ReductionSingleton().set_instrument(instrument)
80        config['default.instrument']='SANS2D'
81    except:
82        return False
83    return True
84
85def SANS2DTUBES():
86    """
87    Quick, temporary workaround for the IDF problem we're fixing in #9367.
88    Simply pass the correct IDF to SANS2D().
89    """
90    return SANS2D("SANS2D_Definition_Tubes.xml")
91
92def LOQ():
93    """
94        Initialises the instrument settings for LOQ
95        @return True on success
96    """
97    _printMessage('LOQ()')
98    try:
99        instrument = isis_instrument.LOQ()
100
101        ReductionSingleton().set_instrument(instrument)
102        config['default.instrument']='LOQ'
103    except:
104        return False
105    return True
106
107def LARMOR():
108    """
109    Initialises the instrument settings for LARMOR
110    @return True on success
111    """
112    _printMessage('LARMOR()')
113    try:
114        instrument = isis_instrument.LARMOR()
115
116        ReductionSingleton().set_instrument(instrument)
117        config['default.instrument']='LARMOR'
118    except:
119        return False
120    return True
121
122def Detector(det_name):
123    """
124        Sets the detector bank to use for the reduction e.g. 'front-detector'. The
125        main detector is assumed if this line is not given
126        @param det_name: the detector's name
127    """
128    _printMessage('Detector("' + det_name + '")')
129    ReductionSingleton().instrument.setDetector(det_name)
130
131def Mask(details):
132    """
133        Specify regions of the detector to mask using the same syntax
134        as used in the user file
135        @param details: a string that specifies masking as it would appear in a mask file
136    """
137    _printMessage('Mask("' + details + '")')
138    ReductionSingleton().mask.parse_instruction(ReductionSingleton().instrument.name(),details)
139
140def MaskFile(file_name):
141    """
142        Loads the settings file. The settings are loaded as soon as this line is encountered
143        and are overridden by other Python commands
144        @param file_name: the settings file
145    """
146    _printMessage('#Opening "'+file_name+'"')
147
148    # ensure that no slice string is kept from previous executions.
149    ReductionSingleton().setSlicesLimits("")
150
151    ReductionSingleton().user_settings = isis_reduction_steps.UserFile(
152        file_name)
153    status = ReductionSingleton().user_settings.execute(
154        ReductionSingleton(), None)
155    _printMessage('#Success reading "'+file_name+'"'+' is '+str(status))
156    return status
157
158def SetMonitorSpectrum(specNum, interp=False):
159    """
160        Specifies the spectrum number of the spectrum that will be used to
161        for monitor normalisation
162        @param specNum: a spectrum number (1 or greater)
163        @param interp: when rebinning the wavelength bins to match the main workspace, if use interpolation default no interpolation
164    """
165    ReductionSingleton().set_monitor_spectrum(specNum, interp)
166
167def SetTransSpectrum(specNum, interp=False):
168    ReductionSingleton().set_trans_spectrum(specNum, interp)
169
170def SetSampleOffset(value):
171    ReductionSingleton().instrument.set_sample_offset(value)
172
173def Gravity(flag):
174    _printMessage('Gravity(' + str(flag) + ')')
175    ReductionSingleton().to_Q.set_gravity(flag)
176
177def SetFrontDetRescaleShift(scale=1.0, shift=0.0, fitScale=False, fitShift=False, qMin=None, qMax=None):
178    """
179        Stores property about the detector which is used to rescale and shift
180        data in the bank after data have been reduced
181        @param scale: Default to 1.0. Value to multiply data with
182        @param shift: Default to 0.0. Value to add to data
183        @param fitScale: Default is False. Whether or not to try and fit this param
184        @param fitShift: Default is False. Whether or not to try and fit this param
185        @param qMin: When set to None (default) then for fitting use the overlapping q region of front and rear detectors
186        @param qMax: When set to None (default) then for fitting use the overlapping q region of front and rear detectors
187    """
188    ReductionSingleton().instrument.getDetector('FRONT').rescaleAndShift = ReductionSingleton().instrument.getDetector('FRONT')._RescaleAndShift(
189        scale, shift, fitScale, fitShift, qMin, qMax)
190    _printMessage('#Set front detector rescale/shift values')
191
192def TransFit(mode,lambdamin=None,lambdamax=None, selector='BOTH'):
193    """
194        Sets the fit method to calculate the transmission fit and the wavelength range
195        over which to do the fit. These arguments are passed to the algorithm
196        CalculateTransmission. If mode is set to 'Off' then the unfitted workspace is
197        used and lambdamin and max have no effect
198        @param mode: can be 'Logarithmic' ('YLOG', 'LOG') 'OFF' ('CLEAR') or 'LINEAR' (STRAIGHT', LIN'), 'POLYNOMIAL2', 'POLYNOMIAL3', ...
199        @param lambdamin: the lowest wavelength to use in any fit
200        @param lambdamax: the end of the fit range
201        @param selector: define for which transmission this fit specification is valid (BOTH, SAMPLE, CAN)
202    """
203    mode = str(mode).strip().upper()
204    message = mode
205    if lambdamin: message += ', ' + str(lambdamin)
206    if lambdamax: message += ', ' + str(lambdamax)
207    message += ', selector=' + selector
208    _printMessage("TransFit(\"" + message + "\")")
209
210    ReductionSingleton().set_trans_fit(lambdamin, lambdamax, mode, selector)
211
212def TransWorkspace(sample, can = None):
213    """
214        Use a given workpspace that contains pre-calculated transmissions
215        @param sample the workspace to use for the sample
216        @param can calculated transmission for the can
217    """
218    ReductionSingleton().transmission_calculator.calculated_samp = sample
219    ReductionSingleton().transmission_calculator.calculated_can = can
220
221def _return_old_compatibility_assign_methods(ws_name):
222    """For backward compatibility, AssignCan and AssignSample returns a tuple
223    with workspace name and the log entry if available.
224
225    In the future, those methods should return just workspace name
226    """
227    logs = ""
228    if isinstance(ReductionSingleton().instrument, isis_instrument.SANS2D):
229        try:
230            logs = ReductionSingleton().instrument.get_detector_log(ws_name)
231        except:
232            pass
233    return ws_name, logs
234
235def AssignCan(can_run, reload = True, period = isis_reduction_steps.LoadRun.UNSET_PERIOD):
236    """
237        The can is a scattering run under the same conditions as the experimental run but the
238        only the sample container is in the sample position. Hence allowing the effect of the
239        container to be removed. The run is specified using instrumentrunnumber.extension,
240        e.g. SANS2D7777.nxs. On calling this function the run is loaded to a workspace and the
241        detector banks and other components moved as applicable. Currently only reload=true is
242        supported.
243        @param can_run: run number to analysis e.g. SANS2D7777.nxs
244        @param reload: must be set to True
245        @param period: the period (entry) number to load, default is the first period
246    """
247    mes = 'AssignCan("' + str(can_run) + '"'
248    if period != isis_reduction_steps.LoadRun.UNSET_PERIOD:
249        mes += ', ' + str(period)
250    mes += ')'
251    _printMessage(mes)
252
253    ReductionSingleton().set_can(can_run, reload, period)
254    return _return_old_compatibility_assign_methods(
255        ReductionSingleton().get_can().wksp_name)
256
257def TransmissionSample(sample, direct, reload = True, period_t = -1, period_d = -1):
258    """
259        Specify the transmission and direct runs for the sample
260        @param sample: the transmission run
261        @param direct: direct run
262        @param reload: if to replace the workspace if it is already there
263        @param period_t: the entry number of the transmission run (default single entry file)
264        @param period_d: the entry number of the direct run (default single entry file)
265    """
266    _printMessage('TransmissionSample("' + str(sample) + '","' + str(direct) + '")')
267    ReductionSingleton().set_trans_sample(sample, direct, reload, period_t, period_d)
268    return ReductionSingleton().samp_trans_load.execute(
269                                        ReductionSingleton(), None)
270
271def TransmissionCan(can, direct, reload = True, period_t = -1, period_d = -1):
272    """
273        Specify the transmission and direct runs for the can
274        @param can: the transmission run
275        @param direct: direct run
276        @param reload: if to replace the workspace if it is already there
277        @param period_t: the entry number of the transmission run (default single entry file)
278        @param period_d: the entry number of the direct run (default single entry file)
279    """
280    _printMessage('TransmissionCan("' + str(can) + '","' + str(direct) + '")')
281    ReductionSingleton().set_trans_can(can, direct, reload, period_t, period_d)
282    return ReductionSingleton().can_trans_load.execute(
283                                            ReductionSingleton(), None)
284
285def AssignSample(sample_run, reload = True, period = isis_reduction_steps.LoadRun.UNSET_PERIOD):
286    """
287        Specifies the run to analyse using the format instrumentrunnumber.extension,
288        e.g. SANS2D7777.nxs. This is one of the few commands that executes Mantid algorithms
289        when called. Currently only reload=true is supported.
290        @param sample_run: run number to analysis e.g. SANS2D7777.nxs
291        @param reload: must be set to True
292        @param period: the period (entry) number to load, default is the first period
293    """
294    mes = 'AssignSample("' + str(sample_run) + '"'
295    if period != isis_reduction_steps.LoadRun.UNSET_PERIOD:
296        mes += ', ' + str(period)
297    mes += ')'
298    _printMessage(mes)
299
300    ReductionSingleton().set_sample(sample_run, reload, period)
301
302    global LAST_SAMPLE
303    LAST_SAMPLE = ReductionSingleton().get_sample().wksp_name
304    return _return_old_compatibility_assign_methods(LAST_SAMPLE)
305
306def SetCentre(xcoord, ycoord, bank = 'rear'):
307    """
308    Configure the Beam Center position. It support the configuration of the centre for
309    the both detectors bank (low-angle bank and high-angle bank detectors)
310
311    It allows defining the position for both detector banks.
312    :param xcoord: X position of beam center in the user coordinate system.
313    :param ycoord: Y position of beam center in the user coordinate system.
314    :param bank: The selected bank ('rear' - low angle or 'front' - high angle)
315    Introduced #5942
316    """
317    _printMessage('SetCentre(' + str(xcoord) + ', ' + str(ycoord) + ')')
318
319    ReductionSingleton().set_beam_finder(isis_reduction_steps.BaseBeamFinder(
320                                float(xcoord)/1000.0, float(ycoord)/1000.0), bank)
321
322def GetMismatchedDetList():
323    """
324        Return the list of mismatched detector names
325    """
326    return ReductionSingleton().instrument.get_marked_dets()
327
328def WavRangeReduction(wav_start=None, wav_end=None, full_trans_wav=None, name_suffix=None, combineDet=None, resetSetup=True, out_fit_settings = dict()):
329    """
330        Run reduction from loading the raw data to calculating Q. Its optional arguments allows specifics
331        details to be adjusted, and optionally the old setup is reset at the end. Note if FIT of RESCALE or SHIFT
332        is selected then both REAR and FRONT detectors are both reduced EXCEPT if only the REAR detector is selected
333        to be reduced
334
335        @param wav_start: the first wavelength to be in the output data
336        @param wav_end: the last wavelength in the output data
337        @param full_trans_wav: if to use a wide wavelength range, the instrument's default wavelength range, for the transmission correction, false by default
338        @param name_suffix: append the created output workspace with this
339        @param combineDet: combineDet can be one of the following:
340                           'rear'                (run one reduction for the 'rear' detector data)
341                           'front'               (run one reduction for the 'front' detector data, and rescale+shift 'front' data)
342                           'both'                (run both the above two reductions)
343                           'merged'              (run the same reductions as 'both' and additionally create a merged data workspace)
344                            None                 (run one reduction for whatever detector has been set as the current detector
345                                                  before running this method. If front apply rescale+shift)
346        @param resetSetup: if true reset setup at the end
347        @param out_fit_settings: An output parameter. It is used, specially when resetSetup is True, in order to remember the 'scale and fit' of the fitting algorithm.
348        @return Name of one of the workspaces created
349    """
350    _printMessage('WavRangeReduction(' + str(wav_start) + ', ' + str(wav_end) + ', '+str(full_trans_wav)+')')
351    # these flags indicate if it is necessary to reduce the front bank, the rear bank and if it is supposed to merge them
352    reduce_rear_flag = False
353    reduce_front_flag = False
354    merge_flag = False
355
356    retWSname_rear, retWSname_front, retWSname_merged = ["", "", ""]
357
358    # combineDet from None to 'rear' or 'front'
359    if combineDet is None:
360        if ReductionSingleton().instrument.cur_detector().isAlias('FRONT'):
361            combineDet = 'front'
362        else:
363            combineDet = 'rear'
364
365    if not full_trans_wav is None:
366        ReductionSingleton().full_trans_wav = full_trans_wav
367
368    ReductionSingleton().to_wavelen.set_range(wav_start, wav_end)
369
370    rAnds = ReductionSingleton().instrument.getDetector('FRONT').rescaleAndShift
371    # check if fit is required.
372    fitRequired = False
373    if rAnds.fitScale or rAnds.fitShift:
374        fitRequired = True
375
376    com_det_option = combineDet.lower()
377
378    # the only special case where reduce rear is not required is
379    # if the user chose to reduce front and does not require fit
380    if not (com_det_option == 'front' and not fitRequired):
381        reduce_rear_flag = True
382    if (com_det_option != 'rear'):
383        reduce_front_flag = True
384    if (com_det_option == 'merged'):
385        merge_flag = True
386
387    #The shift and scale is always on the front detector.
388    if not reduce_front_flag:
389        fitRequired = False
390
391    #To backup value of singleton which are temporarily modified in this method
392    toRestoreAfterAnalysis = ReductionSingleton().instrument.cur_detector().name()
393    toRestoreOutputParts = ReductionSingleton().to_Q.outputParts
394
395    # if 'merged' then when cross section is calculated output the two individual parts
396    # of the cross section. These additional outputs are required to calculate
397    # the merged workspace
398    if merge_flag:
399        ReductionSingleton().to_Q.outputParts = True
400
401    # do reduce rear bank data
402    if reduce_rear_flag:
403        ReductionSingleton().instrument.setDetector('rear')
404        retWSname_rear = _WavRangeReduction(name_suffix)
405        retWSname = retWSname_rear
406
407    # do reduce front bank
408    if reduce_front_flag:
409        # it is necessary to replace the Singleton if a reduction was done before
410        if (reduce_rear_flag):
411            # In this case, it is necessary to reload the files, in order to move the components to the
412            # correct position defined by its get_beam_center. (ticket #5942)
413
414            # first copy the settings
415            ReductionSingleton.replace(ReductionSingleton().cur_settings())
416
417            # for the LOQ instrument, if the beam centers are different, we have to reload the data.
418            if (ReductionSingleton().instrument._NAME == 'LOQ' and
419               (ReductionSingleton().get_beam_center('rear') != ReductionSingleton().get_beam_center('front'))):
420
421                # It is necessary to reload sample, transmission and can files.
422                #reload sample
423                issueWarning('Trying to reload workspaces')
424                ReductionSingleton().instrument.setDetector('front')
425                ReductionSingleton()._sample_run.reload(ReductionSingleton())
426                #reassign can
427                if ReductionSingleton().get_can():
428                    ReductionSingleton().get_can().reload(ReductionSingleton())
429                if ReductionSingleton().samp_trans_load:
430                    #refresh Transmission
431                    ReductionSingleton().samp_trans_load.execute(ReductionSingleton(), None)
432                if ReductionSingleton().can_trans_load:
433                    ReductionSingleton().can_trans_load.execute(ReductionSingleton(),None)
434
435        ReductionSingleton().instrument.setDetector('front')
436
437        retWSname_front = _WavRangeReduction(name_suffix)
438        retWSname = retWSname_front
439
440    # do fit and scale if required
441    if fitRequired:
442        scale, shift = _fitRescaleAndShift(rAnds, retWSname_front, retWSname_rear)
443        ReductionSingleton().instrument.getDetector('FRONT').rescaleAndShift.shift = shift
444        ReductionSingleton().instrument.getDetector('FRONT').rescaleAndShift.scale = scale
445        if scale < 0:
446            issueWarning("Fit returned SCALE negative")
447
448    shift = ReductionSingleton().instrument.getDetector('FRONT').rescaleAndShift.shift
449    scale = ReductionSingleton().instrument.getDetector('FRONT').rescaleAndShift.scale
450
451    # apply the merge algorithm
452    if merge_flag:
453        retWSname_merged = retWSname_rear
454        if retWSname_merged.count('rear') == 1:
455          retWSname_merged = retWSname_merged.replace('rear', 'merged')
456        else:
457          retWSname_merged = retWSname_merged + "_merged"
458
459        Nf = mtd[retWSname_front+"_sumOfNormFactors"]
460        Nr = mtd[retWSname_rear+"_sumOfNormFactors"]
461        Cf = mtd[retWSname_front+"_sumOfCounts"]
462        Cr = mtd[retWSname_rear+"_sumOfCounts"]
463        consider_can = True
464        try:
465            Nf_can = mtd[retWSname_front+"_can_tmp_sumOfNormFactors"]
466            Nr_can = mtd[retWSname_rear+"_can_tmp_sumOfNormFactors"]
467            Cf_can = mtd[retWSname_front+"_can_tmp_sumOfCounts"]
468            Cr_can = mtd[retWSname_rear+"_can_tmp_sumOfCounts"]
469            if Cr_can is None:
470                consider_can = False
471        except KeyError :
472            #The CAN was not specified
473            consider_can = False
474
475
476        fisF = mtd[retWSname_front]
477        fisR = mtd[retWSname_rear]
478
479        minQ = min(min(fisF.dataX(0)), min(fisR.dataX(0)))
480        maxQ = max(max(fisF.dataX(0)), max(fisR.dataX(0)))
481
482        if maxQ > minQ:
483            #preparing the sample
484            Nf = CropWorkspace(InputWorkspace=Nf, OutputWorkspace=Nf, XMin=minQ, XMax=maxQ)
485            Nr = CropWorkspace(InputWorkspace=Nr, OutputWorkspace=Nr, XMin=minQ, XMax=maxQ)
486            Cf = CropWorkspace(InputWorkspace=Cf, OutputWorkspace=Cf, XMin=minQ, XMax=maxQ)
487            Cr = CropWorkspace(InputWorkspace=Cr, OutputWorkspace=Cr, XMin=minQ, XMax=maxQ)
488            if consider_can:
489                #preparing the can
490                Nf_can = CropWorkspace(InputWorkspace=Nf_can, OutputWorkspace=Nf_can, XMin=minQ, XMax=maxQ)
491                Nr_can = CropWorkspace(InputWorkspace=Nr_can, OutputWorkspace=Nr_can, XMin=minQ, XMax=maxQ)
492                Cf_can = CropWorkspace(InputWorkspace=Cf_can, OutputWorkspace=Cf_can, XMin=minQ, XMax=maxQ)
493                Cr_can = CropWorkspace(InputWorkspace=Cr_can, OutputWorkspace=Cr_can, XMin=minQ, XMax=maxQ)
494
495            mergedQ = (Cf+shift*Nf+Cr)/(Nf/scale + Nr)
496            if consider_can:
497                mergedQ -= (Cf_can+Cr_can)/(Nf_can/scale + Nr_can)
498
499            RenameWorkspace(InputWorkspace=mergedQ,OutputWorkspace= retWSname_merged)
500
501            # save the properties Transmission and TransmissionCan inside the merged workspace
502            # get these values from the rear_workspace because they are the same value as the front one.
503            # ticket #6929
504            rear_ws = mtd[retWSname_rear]
505            for prop in ['Transmission','TransmissionCan']:
506                if rear_ws.getRun().hasProperty(prop):
507                    ws_name = rear_ws.getRun().getLogData(prop).value
508                    if mtd.doesExist(ws_name): # ensure the workspace has not been deleted
509                        AddSampleLog(Workspace=retWSname_merged,LogName= prop, LogText=ws_name)
510        else:
511            issueWarning('rear and front data has no overlapping q-region. Merged workspace no calculated')
512
513        delete_workspaces(retWSname_rear+"_sumOfCounts")
514        delete_workspaces(retWSname_rear+"_sumOfNormFactors")
515        delete_workspaces(retWSname_front+"_sumOfCounts")
516        delete_workspaces(retWSname_front+"_sumOfNormFactors")
517        if consider_can:
518            delete_workspaces(retWSname_front+"_can_tmp_sumOfNormFactors")
519            delete_workspaces(retWSname_rear+"_can_tmp_sumOfNormFactors")
520            delete_workspaces(retWSname_front+"_can_tmp_sumOfCounts")
521            delete_workspaces(retWSname_rear+"_can_tmp_sumOfCounts")
522
523        retWSname = retWSname_merged
524
525    #applying scale and shift on the front detector reduced data
526    if reduce_front_flag:
527        frontWS = mtd[retWSname_front]
528        frontWS = (frontWS+shift)*scale
529        RenameWorkspace(InputWorkspace=frontWS,OutputWorkspace= retWSname_front)
530
531    # finished calculating cross section so can restore these value
532    ReductionSingleton().to_Q.outputParts = toRestoreOutputParts
533    ReductionSingleton().instrument.setDetector(toRestoreAfterAnalysis)
534
535    # update the scale and shift values of out_fit_settings
536    out_fit_settings['scale'] = ReductionSingleton().instrument.getDetector('FRONT').rescaleAndShift.scale
537    out_fit_settings['shift'] = ReductionSingleton().instrument.getDetector('FRONT').rescaleAndShift.shift
538
539    if resetSetup:
540        _refresh_singleton()
541
542    # Relabel the YUnit of the resulting workspaces before we return anything.
543    # Depending on the given options, we may have rear, front and merged
544    # workspaces to handle.  These may also be WorkspaceGroups.
545    for ws_name in [retWSname_rear, retWSname_front, retWSname_merged]:
546        if not ws_name in mtd:
547            continue
548        ws = mtd[ws_name]
549        if isinstance(ws, WorkspaceGroup):
550            relabel_ws_list = [mtd[name] for name in ws.getNames()]
551        else:
552            relabel_ws_list = [ws]
553        for relabel_ws in relabel_ws_list:
554            relabel_ws.setYUnitLabel("I(q) (cm-1)")
555
556    return retWSname
557
558def _fitRescaleAndShift(rAnds, frontData, rearData):
559    """
560        Fit rear data to FRONTnew(Q) = ( FRONT(Q) + SHIFT )xRESCALE,
561        FRONT(Q) is the frontData argument. Returns scale and shift
562
563        @param rAnds: A DetectorBank -> _RescaleAndShift structure
564        @param frontData: Reduced front data
565        @param rearData: Reduced rear data
566    """
567    if rAnds.fitScale==False and rAnds.fitShift==False:
568        return rAnds.scale, rAnds.shift
569    #TODO: we should allow the user to add constraints?
570    if rAnds.fitScale==False:
571        if rAnds.qRangeUserSelected:
572            Fit(InputWorkspace=rearData,
573                Function='name=TabulatedFunction, Workspace="'+str(frontData)+'"'
574                +";name=FlatBackground", Ties='f0.Scaling='+str(rAnds.scale),
575                Output="__fitRescaleAndShift", StartX=rAnds.qMin, EndX=rAnds.qMax)
576        else:
577            Fit(InputWorkspace=rearData,
578                Function='name=TabulatedFunction, Workspace="'+str(frontData)+'"'
579                +";name=FlatBackground", Ties='f0.Scaling='+str(rAnds.scale),
580                Output="__fitRescaleAndShift")
581    elif rAnds.fitShift==False:
582        if rAnds.qRangeUserSelected:
583            function_input = 'name=TabulatedFunction, Workspace="'+str(frontData)+'"' +";name=FlatBackground"
584            ties = 'f1.A0='+str(rAnds.shift*rAnds.scale)
585            logger.warning('function input ' + str(function_input))
586
587            Fit(InputWorkspace=rearData,
588                Function='name=TabulatedFunction, Workspace="'+str(frontData)+'"'
589                +";name=FlatBackground", Ties='f1.A0='+str(rAnds.shift*rAnds.scale),
590                Output="__fitRescaleAndShift", StartX=rAnds.qMin, EndX=rAnds.qMax)
591        else:
592            Fit(InputWorkspace=rearData,
593                Function='name=TabulatedFunction, Workspace="'+str(frontData)+'"'
594                +";name=FlatBackground", Ties='f1.A0='+str(rAnds.shift*rAnds.scale),
595                Output="__fitRescaleAndShift")
596    else:
597        if rAnds.qRangeUserSelected:
598            Fit(InputWorkspace=rearData,
599                Function='name=TabulatedFunction, Workspace="'+str(frontData)+'"'
600                +";name=FlatBackground",
601                Output="__fitRescaleAndShift", StartX=rAnds.qMin, EndX=rAnds.qMax)
602        else:
603            Fit(InputWorkspace=rearData, Function='name=TabulatedFunction, Workspace="'+str(frontData)+'"'
604                +";name=FlatBackground",Output="__fitRescaleAndShift")
605
606    param = mtd['__fitRescaleAndShift_Parameters']
607
608    row1 = param.row(0).items()
609    row2 = param.row(1).items()
610    row3 = param.row(2).items()
611    scale = row1[1][1]
612    chiSquared = row3[1][1]
613
614    fitSuccess = True
615    if not chiSquared > 0:
616        issueWarning("Can't fit front detector RESCALE or SHIFT. Use non fitted values")
617        fitSuccess = False
618    if scale == 0.0:
619        issueWarning("front detector RESCALE fitted to zero. Use non fitted values")
620        fitSuccess = False
621
622    if fitSuccess == False:
623        return rAnds.scale, rAnds.shift
624
625    shift = row2[1][1] / scale
626
627    delete_workspaces('__fitRescaleAndShift_Parameters')
628    delete_workspaces('__fitRescaleAndShift_NormalisedCovarianceMatrix')
629    delete_workspaces('__fitRescaleAndShift_Workspace')
630
631    return scale, shift
632
633def _WavRangeReduction(name_suffix=None):
634    """
635        Run a reduction that has been set up, from loading the raw data to calculating Q
636    """
637    def _setUpPeriod(period):
638        assert(ReductionSingleton().get_sample().loader.move2ws(period))
639        can = ReductionSingleton().get_can()
640        if can and can.loader.periods_in_file > 1:
641            can.loader.move2ws(period)
642
643        for trans in [ReductionSingleton().samp_trans_load, ReductionSingleton().can_trans_load]:
644            if trans and trans.direct.periods_in_file > 1 and trans.trans.periods_in_file > 1:
645                trans.direct.move2ws(period)
646                trans.trans.move2next(period)
647        return
648
649    def _applySuffix(result, name_suffix):
650        if name_suffix:
651            old = result
652            result += name_suffix
653            RenameWorkspace(InputWorkspace=old,OutputWorkspace= result)
654        return result
655
656    def _common_substring(val1, val2):
657        l = []
658        for i in range(len(val1)):
659            if val1[i]==val2[i]: l.append(val1[i])
660            else:
661                return ''.join(l)
662
663    def _group_workspaces(list_of_values, outputname):
664        allnames = ','.join(list_of_values)
665        GroupWorkspaces(InputWorkspaces=allnames, OutputWorkspace=outputname)
666
667    def _reduceAllSlices():
668        if ReductionSingleton().getNumSlices() > 1:
669            slices = []
670            for index in range(ReductionSingleton().getNumSlices()):
671                ReductionSingleton().setSliceIndex(index)
672                slices.append(ReductionSingleton()._reduce())
673            ReductionSingleton().setSliceIndex(0)
674            group_name = _common_substring(slices[0], slices[1])
675            if group_name[-2] == "_":
676                group_name = group_name[:-2]
677            _group_workspaces(slices, group_name)
678            return group_name
679        else:
680            return ReductionSingleton()._reduce()
681
682
683
684    result = ""
685    if ReductionSingleton().get_sample().loader.periods_in_file == 1:
686        result = _reduceAllSlices()
687        return _applySuffix(result, name_suffix)
688
689    calculated = []
690    try:
691        for period in ReductionSingleton().get_sample().loader.entries:
692            _setUpPeriod(period)
693            calculated.append(_reduceAllSlices())
694
695    finally:
696        if len(calculated) > 0:
697            result = ReductionSingleton().get_out_ws_name(show_period=False)
698            _group_workspaces(calculated, result)
699
700    return _applySuffix(result, name_suffix)
701
702def delete_workspaces(workspaces):
703    """
704        Delete the list of workspaces if possible but fail siliently if there is
705        a problem
706        @param workspaces: the list to delete
707    """
708    if type(workspaces) != type(list()):
709        if type(workspaces) != type(tuple()):
710            workspaces = [workspaces]
711
712    for wksp in workspaces:
713        if wksp and wksp in mtd:
714            try:
715                DeleteWorkspace(Workspace=wksp)
716            except:
717                #we're only deleting to save memory, if the workspace really won't delete leave it
718                pass
719
720def CompWavRanges(wavelens, plot=True, combineDet=None, resetSetup=True):
721    """
722        Compares the momentum transfer results calculated from different wavelength ranges. Given
723        the list of wave ranges [a, b, c] it reduces for wavelengths a-b, b-c and a-c.
724        @param wavelens: the list of wavelength ranges
725        @param plot: set this to true to plot the result (must be run in Mantid), default is true
726        @param combineDet: see description in WavRangeReduction
727        @param resetSetup: if true reset setup at the end
728    """
729
730    _printMessage('CompWavRanges( %s,plot=%s)'%(str(wavelens),plot))
731
732    #this only makes sense for 1D reductions
733    if ReductionSingleton().to_Q.output_type == '2D':
734        issueWarning('This wave ranges check is a 1D analysis, ignoring 2D setting')
735        _printMessage('Set1D()')
736        ReductionSingleton().to_Q.output_type = '1D'
737
738    if type(wavelens) != type([]) or len(wavelens) < 2:
739        if type(wavelens) != type((1,)):
740            raise RuntimeError('Error CompWavRanges() requires a list of wavelengths between which reductions will be performed.')
741
742
743    calculated = [WavRangeReduction(wav_start=wavelens[0], wav_end=wavelens[len(wavelens)-1], combineDet=combineDet,resetSetup=False)]
744    for i in range(0, len(wavelens)-1):
745        calculated.append(WavRangeReduction(wav_start=wavelens[i], wav_end=wavelens[i+1], combineDet=combineDet, resetSetup=False))
746
747    if resetSetup:
748        _refresh_singleton()
749
750    if plot and mantidplot:
751        mantidplot.plotSpectrum(calculated, 0)
752
753    #return just the workspace name of the full range
754    return calculated[0]
755
756def PhiRanges(phis, plot=True):
757    """
758        Given a list of phi ranges [a, b, c, d] it reduces in the phi ranges a-b and c-d
759        @param phis: the list of phi ranges
760        @param plot: set this to true to plot the result (must be run in Mantid), default is true
761    """
762
763    _printMessage('PhiRanges( %s,plot=%s)'%(str(phis),plot))
764
765    #todo covert their string into Python array
766
767    if len(phis)/2 != float(len(phis))/2.:
768        raise RuntimeError('Phi ranges must be given as pairs')
769
770    try:
771        #run the reductions, calculated will be an array with the names of all the workspaces produced
772        calculated = []
773        for i in range(0, len(phis), 2):
774            SetPhiLimit(phis[i],phis[i+1])
775            #reducedResult = ReductionSingleton()._reduce()
776            #RenameWorkspace(reducedResult,'bob')
777            #calculated.append(reducedResult)
778            calculated.append(ReductionSingleton()._reduce())
779            ReductionSingleton.replace(ReductionSingleton().cur_settings())
780    finally:
781        _refresh_singleton()
782
783    if plot and mantidplot:
784        mantidplot.plotSpectrum(calculated, 0)
785
786    #return just the workspace name of the full range
787    return calculated[0]
788
789def Reduce():
790    try:
791        result = ReductionSingleton()._reduce()
792    finally:
793        _refresh_singleton()
794
795    return result
796
797def _SetWavelengthRange(start, end):
798    ReductionSingleton().to_wavelen.set_range(start, end)
799
800def Set1D():
801    _printMessage('Set1D()')
802    ReductionSingleton().set_Q_output_type('1D')
803
804def Set2D():
805    _printMessage('Set2D()')
806    ReductionSingleton().set_Q_output_type('2D')
807
808def SetDetectorFloodFile(filename, detector_name="REAR"):
809    ReductionSingleton().prep_normalize.setPixelCorrFile(filename, detector_name)
810
811def SetPhiLimit(phimin, phimax, use_mirror=True):
812    """
813        Call this function to restrict the analyse segments of the detector. Phimin and
814        phimax define the limits of the segment where phi=0 is the -x axis and phi = 90
815        is the y-axis. Setting use_mirror to true includes a second segment to be included
816        it is the same as the first but rotated 180 degrees.
817        @param phimin: the minimum phi angle to include
818        @param phimax: the upper limit on phi for the segment
819        @param use_mirror: when True (default) another segment is included, rotated 180 degrees from the first
820    """
821    _printMessage("SetPhiLimit(" + str(phimin) + ', ' + str(phimax) + ',use_mirror='+str(use_mirror)+')')
822    #a beam centre of [0,0,0] makes sense if the detector has been moved such that beam centre is at [0,0,0]
823    ReductionSingleton().mask.set_phi_limit(phimin, phimax, use_mirror)
824
825def SetDetectorOffsets(bank, x, y, z, rot, radius, side, xtilt, ytilt ):
826    # 19/11/14 RKH added more xtilt & ytilt
827    """
828        Adjust detector position away from position defined in IDF. On SANS2D the detector
829        banks can be moved around. This method allows fine adjustments of detector bank position
830        in the same way as the DET/CORR userfile command works. Hence please see
831        http://www.mantidproject.org/SANS_User_File_Commands#DET for details.
832
833        Note, for now, this command will only have an effect on runs loaded
834        after this command have been executed (because it is when runs are loaded
835        that components are moved away from the positions set in the IDF)
836
837        @param bank: Must be either 'front' or 'rear' (not case sensitive)
838        @param x: shift in mm
839        @param y: shift in mm
840        @param z: shift in mm
841        @param rot: shift in degrees
842        @param radius: shift in mm
843        @param side: shift in mm
844        @param side: xtilt in degrees
845        @param side: ytilt in degrees
846    """
847    _printMessage("SetDetectorOffsets(" + str(bank) + ', ' + str(x)
848                  + ','+str(y) + ',' + str(z) + ',' + str(rot)
849                  + ',' + str(radius) + ',' + str(side) + ',' + str(xtilt)+ ',' + str(ytilt) +')')
850
851    detector = ReductionSingleton().instrument.getDetector(bank)
852    detector.x_corr = x
853    detector.y_corr = y
854    detector.z_corr = z
855    detector.rot_corr = rot
856    detector.radius_corr = radius
857    detector.side_corr = side
858        # 11/11/14 RKH add 2 more
859    detector.x_tilt = xtilt
860    detector.y_tilt = ytilt
861
862def SetCorrectionFile(bank, filename):
863    # 20/11/14 RKH, create a new routine that allows change of "direct beam file" = correction file, for a given
864    # detector, this simplify the iterative process used to adjust it. Will still have to keep changing the name of the file
865    # for each iteratiom to avoid Mantid using a cached version, but can then use only a single user (=mask) file for each set of iterations.
866    # Modelled this on SetDetectorOffsets above ...
867    """
868        @param bank: Must be either 'front' or 'rear' (not case sensitive)
869        @param filename: self explanatory
870    """
871    _printMessage("SetCorrectionFile(" + str(bank) + ', ' + filename +')')
872
873    detector = ReductionSingleton().instrument.getDetector(bank)
874    detector.correction_file = filename
875   
876def LimitsR(rmin, rmax, quiet=False, reducer=None):
877    if reducer == None:
878        reducer = ReductionSingleton().reference()
879
880    if not quiet:
881        _printMessage('LimitsR(' + str(rmin) + ', ' +str(rmax) + ')', reducer)
882
883    reducer.mask.set_radi(rmin, rmax)
884    reducer.CENT_FIND_RMIN = float(rmin)/1000.
885    reducer.CENT_FIND_RMAX = float(rmax)/1000.
886
887def LimitsWav(lmin, lmax, step, bin_type):
888    _printMessage('LimitsWav(' + str(lmin) + ', ' + str(lmax) + ', ' + str(step) + ', '  + bin_type + ')')
889
890    if ( bin_type.upper().strip() == 'LINEAR'): bin_type = 'LIN'
891    if ( bin_type.upper().strip() == 'LOGARITHMIC'): bin_type = 'LOG'
892    if bin_type == 'LOG':
893        bin_sym = '-'
894    else:
895        bin_sym = ''
896
897    ReductionSingleton().to_wavelen.set_rebin(lmin, bin_sym + str(step), lmax)
898
899def LimitsQXY(qmin, qmax, step, type):
900    """
901        To set the bin parameters for the algorithm Qxy()
902        @param qmin: the first Q value to include
903        @param qmaz: the last Q value to include
904        @param step: bin width
905        @param type: pass LOG for logarithmic binning
906    """
907    _printMessage('LimitsQXY(' + str(qmin) + ', ' + str(qmax) +', ' + str(step) + ', ' + str(type) + ')')
908    settings = ReductionSingleton().user_settings
909    if settings is None:
910        raise RuntimeError('MaskFile() first')
911
912    settings.readLimitValues('L/QXY ' + str(qmin) + ' ' + str(qmax) + ' ' + str(step) + '/'  + type, ReductionSingleton())
913
914def SetEventSlices(input_str):
915    """
916    """
917    ReductionSingleton().setSlicesLimits(input_str)
918
919
920def PlotResult(workspace, canvas=None):
921    """
922        Draws a graph of the passed workspace. If the workspace is 2D (has many spectra
923        a contour plot is written
924        @param workspace: a workspace name or handle to plot
925        @param canvas: optional handle to an existing graph to write the plot to
926        @return: a handle to the graph that was written to
927    """
928    if not mantidplot:
929        issueWarning('Plot functions are not available, is this being run from outside Mantidplot?')
930        return
931
932    #ensure that we are dealing with a workspace handle rather than its name
933    workspace = mtd[str(workspace)]
934    if isinstance(workspace, WorkspaceGroup):
935        numSpecs = workspace[0].getNumberHistograms()
936    else:
937        numSpecs = workspace.getNumberHistograms()
938
939    if numSpecs == 1:
940        graph = mantidplot.plotSpectrum(workspace,0)
941    else:
942        graph = mantidplot.importMatrixWorkspace(workspace.getName()).plotGraph2D()
943
944    if not canvas is None:
945        #we were given a handle to an existing graph, use it
946        mantidplot.mergePlots(canvas, graph)
947        graph = canvas
948
949    return graph
950
951##################### View mask details #####################################################
952
953def DisplayMask(mask_worksp=None):
954    """
955        Displays masking by applying it to a workspace and displaying
956        it in instrument view. If no workspace is passed a copy of the
957        sample workspace is used, unless no sample was loaded and then
958        an empty instrument will be shown
959        @param mask_worksp: optional this named workspace will be modified and should be from the currently selected instrument
960        @return the name of the workspace that was displayed
961    """
962    #this will be copied from a sample work space if one exists
963    counts_data = None
964    instrument = ReductionSingleton().instrument
965
966    if not mask_worksp:
967        mask_worksp = '__CurrentMask'
968        samp = LAST_SAMPLE
969
970        if samp:
971            CloneWorkspace(InputWorkspace=samp, OutputWorkspace=mask_worksp)
972
973            if su.isEventWorkspace(samp):
974                assert samp + "_monitors" in mtd
975                CloneWorkspace(InputWorkspace=samp + "_monitors",
976                               OutputWorkspace=mask_worksp + "_monitors")
977                su.fromEvent2Histogram(mask_worksp, mtd[mask_worksp + "_monitors"])
978
979            counts_data = '__DisplayMasked_tempory_wksp'
980            Integration(InputWorkspace=mask_worksp,OutputWorkspace= counts_data)
981
982        else:
983            instrument.load_empty(mask_worksp)
984            instrument.set_up_for_run('emptyInstrument')
985
986    ReductionSingleton().mask.display(mask_worksp, ReductionSingleton(), counts_data)
987    if counts_data:
988        DeleteWorkspace(counts_data)
989
990    return mask_worksp
991
992# Print a test script for Colette if asked
993def createColetteScript(inputdata, format, reduced, centreit , plotresults, csvfile = '', savepath = ''):
994    script = ''
995    if csvfile != '':
996        script += '[COLETTE]  @ ' + csvfile + '\n'
997    file_1 = inputdata['sample_sans'] + format
998    script += '[COLETTE]  ASSIGN/SAMPLE ' + file_1 + '\n'
999    file_1 = inputdata['sample_trans'] + format
1000    file_2 = inputdata['sample_direct_beam'] + format
1001    if file_1 != format and file_2 != format:
1002        script += '[COLETTE]  TRANSMISSION/SAMPLE/MEASURED ' + file_1 + ' ' + file_2 + '\n'
1003    file_1 = inputdata['can_sans'] + format
1004    if file_1 != format:
1005        script +='[COLETTE]  ASSIGN/CAN ' + file_1 + '\n'
1006    file_1 = inputdata['can_trans'] + format
1007    file_2 = inputdata['can_direct_beam'] + format
1008    if file_1 != format and file_2 != format:
1009        script += '[COLETTE]  TRANSMISSION/CAN/MEASURED ' + file_1 + ' ' + file_2 + '\n'
1010    if centreit:
1011        script += '[COLETTE]  FIT/MIDDLE'
1012    # Parameters
1013    script += '[COLETTE]  LIMIT/RADIUS ' + str(ReductionSingleton().mask.min_radius)
1014    script += ' ' + str(ReductionSingleton().mask.max_radius) + '\n'
1015    script += '[COLETTE]  LIMIT/WAVELENGTH ' + ReductionSingleton().to_wavelen.get_range() + '\n'
1016    if ReductionSingleton().DWAV <  0:
1017        script += '[COLETTE]  STEP/WAVELENGTH/LOGARITHMIC ' + str(ReductionSingleton().to_wavelen.w_step)[1:] + '\n'
1018    else:
1019        script += '[COLETTE]  STEP/WAVELENGTH/LINEAR ' + str(ReductionSingleton().to_wavelen.w_step) + '\n'
1020    # For the moment treat the rebin string as min/max/step
1021    qbins = ReductionSingleton().Q_REBEIN.split(",")
1022    nbins = len(qbins)
1023    if ReductionSingleton().to_Q.output_type == '1D':
1024        script += '[COLETTE]  LIMIT/Q ' + str(qbins[0]) + ' ' + str(qbins[nbins-1]) + '\n'
1025        dq = float(qbins[1])
1026        if dq <  0:
1027            script += '[COLETTE]  STEP/Q/LOGARITHMIC ' + str(dq)[1:] + '\n'
1028        else:
1029            script += '[COLETTE]  STEP/Q/LINEAR ' + str(dq) + '\n'
1030    else:
1031        script += '[COLETTE]  LIMIT/QXY ' + str(0.0) + ' ' + str(ReductionSingleton().QXY2) + '\n'
1032        if ReductionSingleton().DQXY <  0:
1033            script += '[COLETTE]  STEP/QXY/LOGARITHMIC ' + str(ReductionSingleton().DQXY)[1:] + '\n'
1034        else:
1035            script += '[COLETTE]  STEP/QXY/LINEAR ' + str(ReductionSingleton().DQXY) + '\n'
1036
1037    # Correct
1038    script += '[COLETTE] CORRECT\n'
1039    if plotresults:
1040        script += '[COLETTE]  DISPLAY/HISTOGRAM ' + reduced + '\n'
1041    if savepath != '':
1042        script += '[COLETTE]  WRITE/LOQ ' + reduced + ' ' + savepath + '\n'
1043
1044    return script
1045
1046def FindBeamCentre(rlow, rupp, MaxIter = 10, xstart = None, ystart = None, tolerance=1.251e-4):
1047    """
1048        Estimates the location of the effective beam centre given a good initial estimate. For more
1049        information go to this page
1050        mantidproject.org/Using_the_SANS_GUI_Beam_Centre_Finder
1051        @param rlow: mask around the (estimated) centre to this radius (in millimetres)
1052        @param rupp: don't include further out than this distance (mm) from the centre point
1053        @param MaxInter: don't calculate more than this number of iterations (default = 10)
1054        @param xstart: initial guess for the horizontal distance of the beam centre from the detector centre in meters (default the values in the mask file)
1055        @param ystart: initial guess for the distance of the beam centre from the detector centre vertically in metres (default the values in the mask file)
1056    @param tolerance: define the precision of the search. If the step is smaller than the tolerance, it will be considered stop searching the centre (default=1.251e-4 or 1.251um)
1057        @return: the best guess for the beam centre point
1058    """
1059    XSTEP = ReductionSingleton().inst.cen_find_step
1060    YSTEP = ReductionSingleton().inst.cen_find_step
1061
1062    original = ReductionSingleton().get_instrument().cur_detector_position(ReductionSingleton().get_sample().get_wksp_name())
1063
1064    if ReductionSingleton().instrument.lowAngDetSet:
1065        det_bank = 'rear'
1066    else:
1067        det_bank = 'front'
1068
1069    if xstart or ystart:
1070        ReductionSingleton().set_beam_finder(
1071            isis_reduction_steps.BaseBeamFinder(
1072            float(xstart), float(ystart)),det_bank)
1073
1074    beamcoords = ReductionSingleton().get_beam_center()
1075    XNEW = beamcoords[0]
1076    YNEW = beamcoords[1]
1077    xstart = beamcoords[0]
1078    ystart = beamcoords[1]
1079
1080
1081    #remove this if we know running the Reducer() doesn't change i.e. all execute() methods are const
1082    centre_reduction = copy.deepcopy(ReductionSingleton().reference())
1083    LimitsR(str(float(rlow)), str(float(rupp)), quiet=True, reducer=centre_reduction)
1084
1085    centre = CentreFinder(original)
1086    centre.logger.notice("xstart,ystart="+str(XNEW*1000.)+" "+str(YNEW*1000.))
1087    centre.logger.notice("Starting centre finding routine ...")
1088    #this function moves the detector to the beam center positions defined above and returns an estimate of where the beam center is relative to the new center
1089    resX_old, resY_old = centre.SeekCentre(centre_reduction, [XNEW, YNEW])
1090    centre_reduction = copy.deepcopy(ReductionSingleton().reference())
1091    LimitsR(str(float(rlow)), str(float(rupp)), quiet=True, reducer=centre_reduction)
1092
1093    logger.notice(centre.status_str(0, resX_old, resY_old))
1094
1095    # take first trial step
1096    XNEW = xstart + XSTEP
1097    YNEW = ystart + YSTEP
1098    graph_handle = None
1099    for i in range(1, MaxIter+1):
1100        it = i
1101
1102        centre_reduction.set_beam_finder(
1103            isis_reduction_steps.BaseBeamFinder(XNEW, YNEW), det_bank)
1104
1105        resX, resY = centre.SeekCentre(centre_reduction, [XNEW, YNEW])
1106        centre_reduction = copy.deepcopy(ReductionSingleton().reference())
1107        LimitsR(str(float(rlow)), str(float(rupp)), quiet=True, reducer=centre_reduction)
1108
1109        centre.logger.notice(centre.status_str(it, resX, resY))
1110
1111        if mantidplot:
1112            try :
1113                if not graph_handle:
1114                    #once we have a plot it will be updated automatically when the workspaces are updated
1115                    graph_handle = mantidplot.plotSpectrum(centre.QUADS, 0)
1116                graph_handle.activeLayer().setTitle(
1117                        centre.status_str(it, resX, resY))
1118            except :
1119                #if plotting is not available it probably means we are running outside a GUI, in which case do everything but don't plot
1120                pass
1121
1122        #have we stepped across the y-axis that goes through the beam center?
1123        if resX > resX_old:
1124            # yes with stepped across the middle, reverse direction and half the step size
1125            XSTEP = -XSTEP/2.
1126        if resY > resY_old:
1127            YSTEP = -YSTEP/2.
1128        if abs(XSTEP) < tolerance and abs(YSTEP) < tolerance :
1129            # this is the success criteria, we've close enough to the center
1130            centre.logger.notice("Converged - check if stuck in local minimum!")
1131            break
1132
1133        resX_old = resX
1134        resY_old = resY
1135        XNEW += XSTEP
1136        YNEW += YSTEP
1137
1138    if it == MaxIter:
1139        centre.logger.notice("Out of iterations, new coordinates may not be the best!")
1140        XNEW -= XSTEP
1141        YNEW -= YSTEP
1142
1143    ReductionSingleton().set_beam_finder(
1144        isis_reduction_steps.BaseBeamFinder(XNEW, YNEW), det_bank)
1145    centre.logger.notice("Centre coordinates updated: [" + str(XNEW)+ ", "+ str(YNEW) + ']')
1146
1147    return XNEW, YNEW
1148
1149###############################################################################
1150######################### Start of Deprecated Code ############################
1151###############################################################################
1152
1153@deprecated
1154def UserPath(path):
1155    """
1156        Sets the directory in which Mantid should look for the mask file if a
1157        full path was not specified
1158        @param path: the full path to the directory
1159    """
1160    _printMessage('UserPath("' + path + '") #Will look for mask file here')
1161    ReductionSingleton().user_file_path = path
1162
1163@deprecated
1164def DataPath(path):
1165    """
1166        Sets an extra directory for Mantid to look for run files
1167        @param path: the full path to a directory containing the run files to analyse
1168    """
1169    ReductionSingleton().set_data_path(path)
1170
1171@deprecated
1172def CropToDetector(inputWSname, outputWSname=None):
1173    """
1174        Crops the workspace so that it only contains the spectra that correspond
1175        to the detectors used in the reduction
1176        @param inputWSname: name of the workspace to crop
1177        @param outputWSname: name the workspace will take (default is the inputWSname)
1178    """
1179    if not outputWSname:
1180        outputWSname = inputWSname
1181
1182    ReductionSingleton().instrument.cur_detector().crop_to_detector(inputWSname, outputWSname)
1183
1184@deprecated
1185def SetRearEfficiencyFile(filename):
1186    rear_det = ReductionSingleton().instrument.getDetector('rear')
1187    rear_det.correction_file = filename
1188
1189@deprecated
1190def SetFrontEfficiencyFile(filename):
1191    front_det = ReductionSingleton().instrument.getDetector('front')
1192    front_det.correction_file = filename
1193
1194@deprecated
1195def displayUserFile():
1196    print '-- Mask file defaults --'
1197    print ReductionSingleton().to_wavlen
1198    print ReductionSingleton().Q_string()
1199#    print correction_files()
1200    print '    direct beam file rear:',
1201    print ReductionSingleton().instrument.detector_file('rear')
1202    print '    direct beam file front:',
1203    print ReductionSingleton().instrument.detector_file('front')
1204    print ReductionSingleton().mask
1205
1206@deprecated
1207def displayMaskFile():
1208    displayUserFile()
1209
1210@deprecated
1211def displayGeometry():
1212    [x, y] = ReductionSingleton().get_beam_center()
1213    print 'Beam centre: [' + str(x) + ',' + str(y) + ']'
1214    print ReductionSingleton().get_sample().geometry
1215
1216@deprecated
1217def LimitsQ(*args):
1218    settings = ReductionSingleton().user_settings
1219    if settings is None:
1220        raise RuntimeError('MaskFile() first')
1221
1222    # If given one argument it must be a rebin string
1223    if len(args) == 1:
1224        val = args[0]
1225        if type(val) == str:
1226            _printMessage("LimitsQ(" + val + ")")
1227            settings.readLimitValues("L/Q " + val, ReductionSingleton())
1228        else:
1229            issueWarning("LimitsQ can only be called with a single string or 4 values")
1230    elif len(args) == 4:
1231        qmin,qmax,step,step_type = args
1232        _printMessage('LimitsQ(' + str(qmin) + ', ' + str(qmax) +', ' + str(step) + ','  + str(step_type) + ')')
1233        settings.readLimitValues('L/Q ' + str(qmin) + ' ' + str(qmax) + ' ' + str(step) + '/'  + step_type, ReductionSingleton())
1234    else:
1235        issueWarning("LimitsQ called with " + str(len(args)) + " arguments, 1 or 4 expected.")
1236
1237@deprecated
1238def ViewCurrentMask():
1239    """
1240        In MantidPlot this opens InstrumentView to display the masked
1241        detectors in the bank in a different colour
1242    """
1243    ReductionSingleton().ViewCurrentMask()
1244
1245###############################################################################
1246########################## End of Deprecated Code #############################
1247###############################################################################
1248
1249#this is like a #define I'd like to get rid of it because it seems meaningless here
1250DefaultTrans = 'True'
1251NewTrans = 'False'
1252
1253_refresh_singleton()
1254
1255if __name__ == '__main__':
1256    SetVerboseMode(True)
1257    SANS2D()
1258    MaskFile('MASKSANS2D_123T_4m_Xpress_8mm.txt')
1259    Set1D()
1260    AssignSample('SANS2D00002500.nxs')
1261    Gravity(True)
1262    wav1 = 2.0
1263    wav2 = wav1 + 2.0
1264    reduced = WavRangeReduction(wav1, wav2, DefaultTrans)