diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/SANSRunWindow.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/SANSRunWindow.cpp index c1be652ce1893086dde17f8be70bff29af6f2bf9..10c42a32c5867dcb7f7a617adea1f1523b80f563 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/SANSRunWindow.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/SANSRunWindow.cpp @@ -920,10 +920,16 @@ bool SANSRunWindow::loadUserFile() // from the ticket #5942 both detectors have center coordinates dbl_param = runReduceScriptFunction( "print i.ReductionSingleton().get_beam_center('rear')[0]").toDouble(); - m_uiForm.rear_beam_x->setText(QString::number(dbl_param*1000.0)); + // get the scale factor1 for the beam centre to scale it correctly + double dbl_paramsf = runReduceScriptFunction( + "print i.ReductionSingleton().get_beam_center_scale_factor1()").toDouble(); + m_uiForm.rear_beam_x->setText(QString::number(dbl_param*dbl_paramsf)); + // get scale factor2 for the beam centre to scale it correctly + dbl_paramsf = runReduceScriptFunction( + "print i.ReductionSingleton().get_beam_center_scale_factor2()").toDouble(); dbl_param = runReduceScriptFunction( "print i.ReductionSingleton().get_beam_center('rear')[1]").toDouble(); - m_uiForm.rear_beam_y->setText(QString::number(dbl_param*1000.0)); + m_uiForm.rear_beam_y->setText(QString::number(dbl_param*dbl_paramsf)); // front dbl_param = runReduceScriptFunction( "print i.ReductionSingleton().get_beam_center('front')[0]").toDouble(); diff --git a/Code/Mantid/instrument/LARMOR_Parameters.xml b/Code/Mantid/instrument/LARMOR_Parameters.xml index 7d8452b19e2cddf0ce2ec8da034e230ae67f2147..269f48964a125ff254df06f2e4735dfcac7475c5 100644 --- a/Code/Mantid/instrument/LARMOR_Parameters.xml +++ b/Code/Mantid/instrument/LARMOR_Parameters.xml @@ -46,10 +46,32 @@ <parameter name="centre-finder-step-size"> -<!-- this is the initial step for the beam centre finder in metres --> +<!-- this is the initial step for the beam centre finder degrees (X value)--> +<!-- 5mm at 4m = 0.07deg --> + <value val="0.07"/> +</parameter> + +<parameter name="beam-centre-scale-factor1"> +<!-- This is a scale factor to allow the beam centre coordinates to be set in meaningful units (X value)--> +<!-- e.g. mm in the mask file means this should be 1000.0 --> +<!-- If no value is supplied then a default of 1000.0 is assumed for backward compatibility --> +<!-- For Larmor X is in degrees so does not need a multiplier --> + <value val="1000.0"/> +</parameter> + +<parameter name="centre-finder-step-size2"> +<!-- this is the second initial step for the beam centre finder if using angle and displacement (Y value)--> <value val="0.005"/> </parameter> +<parameter name="beam-centre-scale-factor2"> +<!-- This is a scale factor to allow the beam centre coordinates to be set in meaningful units (Y value)--> +<!-- e.g. mm in the mask file means this should be 1000.0 --> +<!-- If no value is supplied then a default of 1000.0 is assumed for backward compatibility --> +<!-- For Larmor Y is in mm --> + <value val="1000.0"/> +</parameter> + </component-link> </parameter-file> diff --git a/Code/Mantid/scripts/SANS/ISISCommandInterface.py b/Code/Mantid/scripts/SANS/ISISCommandInterface.py index b453bb6065b28543b8198a481c2e35285e5a2163..127cc5410f9793efc32eb66af44bf6a30f0c2379 100644 --- a/Code/Mantid/scripts/SANS/ISISCommandInterface.py +++ b/Code/Mantid/scripts/SANS/ISISCommandInterface.py @@ -316,9 +316,12 @@ def SetCentre(xcoord, ycoord, bank = 'rear'): Introduced #5942 """ _printMessage('SetCentre(' + str(xcoord) + ', ' + str(ycoord) + ')') + # use the scale factors from the parameter file to scale correctly + XSF = ReductionSingleton().inst.beam_centre_scale_factor1 + YSF = ReductionSingleton().inst.beam_centre_scale_factor2 ReductionSingleton().set_beam_finder(isis_reduction_steps.BaseBeamFinder(\ - float(xcoord)/1000.0, float(ycoord)/1000.0), bank) + float(xcoord)/XSF, float(ycoord)/YSF), bank) def GetMismatchedDetList(): """ @@ -561,56 +564,56 @@ def _fitRescaleAndShift(rAnds, frontData, rearData): Fit rear data to FRONTnew(Q) = ( FRONT(Q) + SHIFT )xRESCALE, FRONT(Q) is the frontData argument. Returns scale and shift - Note SHIFT is shift of a constant back, not the Shift parameter in - TabulatedFunction. - @param rAnds: A DetectorBank -> _RescaleAndShift structure @param frontData: Reduced front data @param rearData: Reduced rear data """ if rAnds.fitScale==False and rAnds.fitShift==False: return rAnds.scale, rAnds.shift - + #TODO: we should allow the user to add constraints? if rAnds.fitScale==False: if rAnds.qRangeUserSelected: Fit(InputWorkspace=rearData, Function='name=TabulatedFunction, Workspace="'+str(frontData)+'"' - +";name=FlatBackground", - Ties='f0.Scaling='+str(rAnds.scale)+',f0.Shift=0.0', + +";name=FlatBackground", Ties='f0.Scaling='+str(rAnds.scale), Output="__fitRescaleAndShift", StartX=rAnds.qMin, EndX=rAnds.qMax) else: Fit(InputWorkspace=rearData, Function='name=TabulatedFunction, Workspace="'+str(frontData)+'"' - +";name=FlatBackground", - Ties='f0.Scaling='+str(rAnds.scale)+',f0.Shift=0.0', + +";name=FlatBackground", Ties='f0.Scaling='+str(rAnds.scale), Output="__fitRescaleAndShift") elif rAnds.fitShift==False: if rAnds.qRangeUserSelected: + function_input = 'name=TabulatedFunction, Workspace="'+str(frontData)+'"' +";name=FlatBackground" + ties = 'f1.A0='+str(rAnds.shift*rAnds.scale) + logger.warning('function input ' + str(function_input)) + Fit(InputWorkspace=rearData, Function='name=TabulatedFunction, Workspace="'+str(frontData)+'"' - +";name=FlatBackground", - Ties='f1.A0='+str(rAnds.shift*rAnds.scale)+',f0.Shift=0.0', + +";name=FlatBackground", Ties='f1.A0='+str(rAnds.shift*rAnds.scale), Output="__fitRescaleAndShift", StartX=rAnds.qMin, EndX=rAnds.qMax) else: Fit(InputWorkspace=rearData, Function='name=TabulatedFunction, Workspace="'+str(frontData)+'"' - +";name=FlatBackground", - Ties='f1.A0='+str(rAnds.shift*rAnds.scale)+',f0.Shift=0.0', + +";name=FlatBackground", Ties='f1.A0='+str(rAnds.shift*rAnds.scale), Output="__fitRescaleAndShift") else: if rAnds.qRangeUserSelected: Fit(InputWorkspace=rearData, Function='name=TabulatedFunction, Workspace="'+str(frontData)+'"' - +";name=FlatBackground", Ties=',f0.Shift=0.0', + +";name=FlatBackground", Output="__fitRescaleAndShift", StartX=rAnds.qMin, EndX=rAnds.qMax) else: Fit(InputWorkspace=rearData, Function='name=TabulatedFunction, Workspace="'+str(frontData)+'"' - +";name=FlatBackground", Ties=',f0.Shift=0.0', Output="__fitRescaleAndShift") + +";name=FlatBackground",Output="__fitRescaleAndShift") param = mtd['__fitRescaleAndShift_Parameters'] - scale = param.row(0).items()[1][1] - chiSquared = param.row(3).items()[1][1] + row1 = param.row(0).items() + row2 = param.row(1).items() + row3 = param.row(2).items() + scale = row1[1][1] + chiSquared = row3[1][1] fitSuccess = True if not chiSquared > 0: @@ -623,7 +626,7 @@ def _fitRescaleAndShift(rAnds, frontData, rearData): if fitSuccess == False: return rAnds.scale, rAnds.shift - shift = param.row(2).items()[1][1] / scale + shift = row2[1][1] / scale delete_workspaces('__fitRescaleAndShift_Parameters') delete_workspaces('__fitRescaleAndShift_NormalisedCovarianceMatrix') @@ -823,7 +826,8 @@ def SetPhiLimit(phimin, phimax, use_mirror=True): #a beam centre of [0,0,0] makes sense if the detector has been moved such that beam centre is at [0,0,0] ReductionSingleton().mask.set_phi_limit(phimin, phimax, use_mirror) -def SetDetectorOffsets(bank, x, y, z, rot, radius, side): +def SetDetectorOffsets(bank, x, y, z, rot, radius, side, xtilt=0.0, ytilt=0.0 ): + # 10/03/15 RKH added 2 more parameters - xtilt & ytilt """ Adjust detector position away from position defined in IDF. On SANS2D the detector banks can be moved around. This method allows fine adjustments of detector bank position @@ -841,10 +845,12 @@ def SetDetectorOffsets(bank, x, y, z, rot, radius, side): @param rot: shift in degrees @param radius: shift in mm @param side: shift in mm + @param side: xtilt in degrees + @param side: ytilt in degrees """ _printMessage("SetDetectorOffsets(" + str(bank) + ', ' + str(x) + ','+str(y) + ',' + str(z) + ',' + str(rot) - + ',' + str(radius) + ',' + str(side) + ')') + + ',' + str(radius) + ',' + str(side) + ',' + str(xtilt)+ ',' + str(ytilt) +')') detector = ReductionSingleton().instrument.getDetector(bank) detector.x_corr = x @@ -853,7 +859,24 @@ def SetDetectorOffsets(bank, x, y, z, rot, radius, side): detector.rot_corr = rot detector.radius_corr = radius detector.side_corr = side + # 10/03/15 RKH add 2 more + detector.x_tilt = xtilt + detector.y_tilt = ytilt +def SetCorrectionFile(bank, filename): + # 10/03/15 RKH, create a new routine that allows change of "direct beam file" = correction file, for a given + # detector, this simplify the iterative process used to adjust it. Will still have to keep changing the name of the file + # 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. + # Modelled this on SetDetectorOffsets above ... + """ + @param bank: Must be either 'front' or 'rear' (not case sensitive) + @param filename: self explanatory + """ + _printMessage("SetCorrectionFile(" + str(bank) + ', ' + filename +')') + + detector = ReductionSingleton().instrument.getDetector(bank) + detector.correction_file = filename + def LimitsR(rmin, rmax, quiet=False, reducer=None): if reducer == None: reducer = ReductionSingleton().reference() @@ -1038,7 +1061,10 @@ def FindBeamCentre(rlow, rupp, MaxIter = 10, xstart = None, ystart = None, toler @return: the best guess for the beam centre point """ XSTEP = ReductionSingleton().inst.cen_find_step - YSTEP = ReductionSingleton().inst.cen_find_step + YSTEP = ReductionSingleton().inst.cen_find_step2 + + XSF = ReductionSingleton().inst.beam_centre_scale_factor1 + YSF = ReductionSingleton().inst.beam_centre_scale_factor2 original = ReductionSingleton().get_instrument().cur_detector_position(ReductionSingleton().get_sample().get_wksp_name()) @@ -1077,6 +1103,7 @@ def FindBeamCentre(rlow, rupp, MaxIter = 10, xstart = None, ystart = None, toler XNEW = xstart + XSTEP YNEW = ystart + YSTEP graph_handle = None + it = 0 for i in range(1, MaxIter+1): it = i @@ -1123,7 +1150,7 @@ def FindBeamCentre(rlow, rupp, MaxIter = 10, xstart = None, ystart = None, toler ReductionSingleton().set_beam_finder( isis_reduction_steps.BaseBeamFinder(XNEW, YNEW), det_bank) - centre.logger.notice("Centre coordinates updated: [" + str(XNEW)+ ", "+ str(YNEW) + ']') + centre.logger.notice("Centre coordinates updated: [" + str(XNEW*XSF) + ", " + str(YNEW*YSF) + ']') return XNEW, YNEW diff --git a/Code/Mantid/scripts/SANS/centre_finder.py b/Code/Mantid/scripts/SANS/centre_finder.py index a3f2e4ca4658fcde0f1f2e1ce244e11dcd9e063e..b179ee8328d451445d135384b4818756b3e47ab3 100644 --- a/Code/Mantid/scripts/SANS/centre_finder.py +++ b/Code/Mantid/scripts/SANS/centre_finder.py @@ -21,6 +21,8 @@ class CentreFinder(object): self.logger = Logger("CentreFinder") self._last_pos = guess_centre self.detector = None + self.XSF = 1.0 + self.YSF = 1.0 def SeekCentre(self, setup, trial): """ @@ -33,6 +35,10 @@ class CentreFinder(object): self.detector = setup.instrument.cur_detector().name() + # populate the x and y scale factor values at this point for the text box + self.XSF = setup.instrument.beam_centre_scale_factor1 + self.YSF = setup.instrument.beam_centre_scale_factor2 + self.move(setup, trial[0]-self._last_pos[0], trial[1]-self._last_pos[1]) #phi masking will remove areas of the detector that we need @@ -83,8 +89,9 @@ class CentreFinder(object): @param y_res: asymmetry in y @return: a human readable string """ - x_str = str(self._last_pos[0]*1000.).ljust(10)[0:9] - y_str = str(self._last_pos[1]*1000.).ljust(10)[0:9] + + x_str = str(self._last_pos[0] * self.XSF).ljust(10)[0:9] + y_str = str(self._last_pos[1] * self.YSF).ljust(10)[0:9] x_res = ' SX='+str(x_res).ljust(7)[0:6] y_res = ' SY='+str(y_res).ljust(7)[0:6] return 'Itr '+str(iter)+': ('+x_str+', '+y_str+')'+x_res+y_res diff --git a/Code/Mantid/scripts/SANS/isis_instrument.py b/Code/Mantid/scripts/SANS/isis_instrument.py index 743145fa0b79f6349c077742193b01cdf73872b2..088d87dd5f3481a27bb9cc82a476b8dc892dd9c0 100644 --- a/Code/Mantid/scripts/SANS/isis_instrument.py +++ b/Code/Mantid/scripts/SANS/isis_instrument.py @@ -10,6 +10,7 @@ import xml.dom.minidom from mantid.simpleapi import * from mantid.api import WorkspaceGroup, Workspace, ExperimentInfo from mantid.kernel import Logger +from mantid.kernel import V3D import SANSUtility as su sanslog = Logger("SANS") @@ -219,6 +220,9 @@ class DetectorBank(object): #23/3/12 RKH add 2 more variables self._radius_corr = 0.0 self._side_corr =0.0 + # 10/03/15 RKH add 2 more, valid for all detectors. WHY do some of the above have an extra leading underscore?? Seems they are the optional ones sorted below + self.x_tilt = 0.0 + self.y_tilt = 0.0 # hold rescale and shift object _RescaleAndShift self.rescaleAndShift = self._RescaleAndShift() @@ -427,8 +431,28 @@ class ISISInstrument(BaseInstrument): #the spectrum with this number is used to normalize the workspace data self._incid_monitor = int(self.definition.getNumberParameter( 'default-incident-monitor-spectrum')[0]) - self.cen_find_step = float(self.definition.getNumberParameter( - 'centre-finder-step-size')[0]) + self.cen_find_step = float(self.definition.getNumberParameter('centre-finder-step-size')[0]) + # see if a second step size is defined. If not set the second value to the first for compatibility + #logger.warning("Trying to find centre-finder-step-size2") + try: + self.cen_find_step2 = float(self.definition.getNumberParameter('centre-finder-step-size2')[0]) + except: + #logger.warning("Failed to find centre-finder-step-size2") + self.cen_find_step2 = self.cen_find_step + + logger.warning("Trying to find beam-centre-scale-factor1") + try: + self.beam_centre_scale_factor1 = float(self.definition.getNumberParameter('beam-centre-scale-factor1')[0]) + except: + logger.warning("Failed to find beam-centre-scale-factor1") + self.beam_centre_scale_factor1 = 1000.0 + + logger.warning("Trying to find beam-centre-scale-factor2") + try: + self.beam_centre_scale_factor2 = float(self.definition.getNumberParameter('beam-centre-scale-factor2')[0]) + except: + logger.warning("Failed to find beam-centre-scale-factor2") + self.beam_centre_scale_factor2 = 1000.0 firstDetect = DetectorBank(self.definition, 'low-angle') #firstDetect.disable_y_and_rot_corrs() @@ -463,6 +487,10 @@ class ISISInstrument(BaseInstrument): self.REAR_DET_Z = 0.0 self.REAR_DET_X = 0 + # LOG files for Larmor will have these encoder readings + # why are these not defined in Larmor + self.BENCH_ROT = 0.0 + #spectrum number of the monitor used to as the incidient in the transmission calculations self.default_trans_spec = int(self.definition.getNumberParameter( 'default-transmission-monitor-spectrum')[0]) @@ -896,7 +924,15 @@ class SANS2D(ISISInstrument): FRONT_DET_Z, FRONT_DET_X, FRONT_DET_ROT, REAR_DET_Z, REAR_DET_X = self.getDetValues(ws) # Deal with front detector - # 9/1/2 this all dates to Richard Heenan & Russell Taylor's original python development for SANS2d + # 10/03/15 RKH need to add tilt of detector, in degrees, with respect to the horizontal or vertical of the detector plane + # this time we can rotate about the detector's own axis so can use RotateInstrumentComponent, ytilt rotates about x axis, xtilt rotates about z axis + # + if frontDet.y_tilt != 0.0: + RotateInstrumentComponent(Workspace=ws,ComponentName= self.getDetector('front').name(), X = "1.", Y = "0.", Z = "0.", Angle = frontDet.y_tilt) + if frontDet.x_tilt != 0.0: + RotateInstrumentComponent(Workspace=ws,ComponentName= self.getDetector('front').name(), X = "0.", Y = "0.", Z = "1.", Angle = frontDet.x_tilt) + # + # 9/1/12 this all dates to Richard Heenan & Russell Taylor's original python development for SANS2d # the rotation axis on the SANS2d front detector is actually set front_det_radius = 306mm behind the detector. # Since RotateInstrumentComponent will only rotate about the centre of the detector, we have to to the rest here. # rotate front detector according to value in log file and correction value provided in user file @@ -925,6 +961,14 @@ class SANS2D(ISISInstrument): # deal with rear detector + # 10/03/15 RKH need to add tilt of detector, in degrees, with respect to the horizontal or vertical of the detector plane + # Best to do the tilts first, while the detector is still centred on the z axis, ytilt rotates about x axis, xtilt rotates about z axis + # NOTE the beam centre coordinates may change + if rearDet.y_tilt != 0.0: + RotateInstrumentComponent(Workspace=ws,ComponentName= rearDet.name(), X = "1.", Y = "0.", Z = "0.", Angle = rearDet.y_tilt) + if rearDet.x_tilt != 0.0: + RotateInstrumentComponent(Workspace=ws,ComponentName= rearDet.name(), X = "0.", Y = "0.", Z = "1.", Angle = rearDet.x_tilt) + xshift = -xbeam yshift = -ybeam zshift = (REAR_DET_Z + rearDet.z_corr)/1000. @@ -1140,10 +1184,16 @@ class SANS2D(ISISInstrument): class LARMOR(ISISInstrument): _NAME = 'LARMOR' - WAV_RANGE_MIN = 2.2 - WAV_RANGE_MAX = 10.0 + WAV_RANGE_MIN = 0.5 + WAV_RANGE_MAX = 13.5 def __init__(self): super(LARMOR,self).__init__('LARMOR_Definition.xml') + self._marked_dets = [] + # set to true once the detector positions have been moved to the locations given in the sample logs + self.corrections_applied = False + # a warning is issued if the can logs are not the same as the sample + self._can_logs = {} + self.monitor_names = dict() for i in range(1,6): @@ -1161,82 +1211,259 @@ class LARMOR(ISISInstrument): second.set_orien('Horizontal') second.place_after(first) - def move_components(self, ws, xbeam, ybeam): - self.move_all_components(ws) + def getDetValues(self, ws_name): + """ + Retrive the values of Bench_Rot from the workspace. If it does not find the value at the run info, + it takes as default value the self.BENCH_ROT, which are extracted from the sample workspace + at apply_detector_log. + This is done to allow the function move_components to use the correct values and not to use + all the values for TRANS ans SAMPLE the same, as sometimes, this assumption is not valid. + The reason for this method is explained at the ticket http://trac.mantidproject.org/mantid/ticket/7314. + """ + # set the default value for these variables + values = [self.BENCH_ROT] + # get these variables from the workspace run + run_info = mtd[str(ws_name)].run() + ind = 0 + name = 'Bench_Rot' + try: + var = run_info.get(name).value + if hasattr(var, '__iter__'): + var = var[-1] + values[ind] = float(var) + except: + pass # ignore, because we do have a default value + ind += 1 + #return these variables + return tuple(values) - detBanch = self.getDetector('rear') + def get_detector_log(self, wksp): + """ + Reads information about the state of the instrument on the information + stored in the sample + @param logs: a workspace pointer + @return the values that were read as a dictionary + """ + #logger.warning("Entering get_detector_log") + self._marked_dets = [] + wksp = su.getWorkspaceReference(wksp) + #assume complete log information is stored in the first entry, it isn't stored in the group workspace itself + if isinstance(wksp, WorkspaceGroup): + wksp = wksp[0] - xshift = -xbeam - yshift = -ybeam - #zshift = ( detBanch.z_corr)/1000. - #zshift -= self.REAR_DET_DEFAULT_SD_M - zshift = 0 - sanslog.notice("Setup move " + str(xshift*1000) + " " + str(yshift*1000) + " " + str(zshift*1000)) - MoveInstrumentComponent(ws, ComponentName=detBanch.name(), X=xshift, - Y=yshift, Z=zshift) - # beam centre, translation - return [0.0, 0.0], [-xbeam, -ybeam] + samp = wksp.getRun() - def cur_detector_position(self, ws_name): - """Return the position of the center of the detector bank""" - ws = mtd[ws_name] - pos = ws.getInstrument().getComponentByName(self.cur_detector().name()).getPos() + logvalues = {} + logvalues['Bench_Rot'] = self._get_const_num(samp, 'Bench_Rot') + #logger.warning(str(logvalues)) - return [-pos.getX(), -pos.getY()] + return logvalues + def _get_const_num(self, log_data, log_name): + """ + Get a the named entry from the log object. If the entry is a + time series it's assumed to contain unchanging data and the first + value is used. The answer must be convertible to float otherwise + this throws. + @param log_data: the sample object from a workspace + @param log_name: a string with the name of the individual entry to load + @return: the floating point number + @raise TypeError: if that log entry can't be converted to a float + """ + try: + # return the log value if it stored as a single number + return float(log_data.getLogData(log_name).value) + except TypeError: + # Python 2.4 doesn't have datetime.strptime... + def format_date(date_string, format, date_str_len): + if len(date_string)>date_str_len: + date_string = date_string[:date_str_len] + from datetime import datetime + if sys.version_info[0] == 2 and sys.version_info[1] < 5: + import time + return datetime(*(time.strptime(date_string, format)[0:6])) + else: + return datetime.strptime(date_string, format) -class LARMOR(ISISInstrument): - _NAME = 'LARMOR' - WAV_RANGE_MIN = 2.2 - WAV_RANGE_MAX = 10.0 - def __init__(self): - super(LARMOR,self).__init__('LARMOR_Definition.xml') - self.monitor_names = dict() + # if the value was stored as a time series we have an array here + property = log_data.getLogData(log_name) - for i in range(1,6): - self.monitor_names[i] = 'monitor'+str(i) + size = len(property.value) + if size == 1: + return float(log_data.getLogData(log_name).value[0]) - def set_up_for_run(self, base_runno): + start = log_data.getLogData('run_start') + dt_0 = format_date(start.value,"%Y-%m-%dT%H:%M:%S",19) + for i in range(0, size): + dt = format_date(str(property.times[i]),"%Y-%m-%dT%H:%M:%S",19) + if dt > dt_0: + if i == 0: + return float(log_data.getLogData(log_name).value[0]) + else: + return float(log_data.getLogData(log_name).value[i-1]) + + # this gets executed if all entries is before the start-time + return float(log_data.getLogData(log_name).value[size-1]) + + def apply_detector_logs(self, logvalues): + #apply the corrections that came from the logs + self.BENCH_ROT = float(logvalues['Bench_Rot']) + self.corrections_applied = True + if len(self._can_logs) > 0: + self.check_can_logs(self._can_logs) + + def check_can_logs(self, new_logs): """ - Needs to run whenever a sample is loaded + Tests if applying the corrections from the passed logvalues + would give the same result as the corrections that were + already made + @param new_logs: the new values to check are equivalent + @return: True if the are the same False if not """ - first = self.DETECTORS['low-angle'] - second = self.DETECTORS['high-angle'] + #logger.warning("Entering check_can_logs") - first.set_orien('Horizontal') - first.set_first_spec_num(10) - second.set_orien('Horizontal') - second.place_after(first) + if not self.corrections_applied: + #the check needs to wait until there's something to compare against + self._can_logs = new_logs + + if len(new_logs) == 0: + return False + + existing_values = [] + existing_values.append(self.BENCH_ROT) + + new_values = [] + new_values.append(float(new_logs['Bench_Rot'])) + + errors = 0 + corr_names = ['Bench_Rot'] + for i in range(0, len(existing_values)): + if math.fabs(existing_values[i] - new_values[i]) > 5e-04: + sanslog.warning('values differ between sample and can runs: Sample ' + corr_names[i] + ' = ' + str(existing_values[i]) + \ + ', can value is ' + str(new_values[i])) + errors += 1 + + self.append_marked(corr_names[i]) + + #the check has been done clear up + self._can_logs = {} + + return errors == 0 def move_components(self, ws, xbeam, ybeam): + #logger.warning("Entering move_components") self.move_all_components(ws) + #logger.warning("Back from move_all_components") - detBanch = self.getDetector('rear') + detBench = self.getDetector('rear') - xshift = -xbeam + # get the bench rotation value from the instrument log + BENCH_ROT = self.getDetValues(ws)[0] + + # use the scale factors from the parameter file to scale appropriately + XSF = self.beam_centre_scale_factor1 + YSF = self.beam_centre_scale_factor2 + + # in this case the x shift is actually a value of 2theta rotated about the sample stack centre + # so... we need to do two moves first a shift in y and then a rotation yshift = -ybeam #zshift = ( detBanch.z_corr)/1000. #zshift -= self.REAR_DET_DEFAULT_SD_M + xshift = 0 zshift = 0 - sanslog.notice("Setup move " + str(xshift*1000) + " " + str(yshift*1000) + " " + str(zshift*1000)) - MoveInstrumentComponent(ws, ComponentName=detBanch.name(), X=xshift, - Y=yshift, Z=zshift) + sanslog.notice("Setup move " + str(xshift*XSF) + " " + str(yshift*YSF) + " " + str(zshift*1000)) + MoveInstrumentComponent(ws, ComponentName=detBench.name(), X=xshift, Y=yshift, Z=zshift) + # in order to avoid rewriting old mask files from initial commisioning during 2014. + ws_ref=mtd[ws] + try: + run_num = ws_ref.getRun().getLogData('run_number').value + except: + run_num = int(re.findall(r'\d+',str(ws_name))[-1]) + + # The angle value + # Note that the x position gets converted from mm to m when read from the user file so we need to reverse this if X is now an angle + if(int(run_num) < 2217): + # Initial commisioning before run 2217 did not pay much attention to making sure the bench_rot value was meaningful + xshift = -xbeam + sanslog.notice("Setup move " + str(xshift*XSF) + " " + str(0.0) + " " + str(0.0)) + MoveInstrumentComponent(ws, ComponentName=detBench.name(), X=xshift, Y=0.0, Z=0.0) + else: + xshift = BENCH_ROT-xbeam*XSF + sanslog.notice("Setup move " + str(xshift*XSF) + " " + str(0.0) + " " + str(0.0)) + RotateInstrumentComponent(ws, ComponentName=detBench.name(), X=0, Y=1, Z=0, Angle=xshift) + #logger.warning("Back from RotateInstrumentComponent") + # beam centre, translation return [0.0, 0.0], [-xbeam, -ybeam] + def append_marked(self, detNames): + self._marked_dets.append(detNames) + + def get_marked_dets(self): + return self._marked_dets + def load_transmission_inst(self, ws_trans, ws_direct, beamcentre): """ - Not required for SANS2D + Larmor requires centralisation of the detectors of the transmission + as well as the sample and can. """ - pass + self.move_components(ws_trans, beamcentre[0], beamcentre[1]) + if ws_trans != ws_direct: + self.move_components(ws_direct, beamcentre[0], beamcentre[1]) def cur_detector_position(self, ws_name): """Return the position of the center of the detector bank""" + """Unforunately getting the angle of the bench does not work so we have to get bench and detector""" + + #logger.warning("Entering cur_detector_position") ws = mtd[ws_name] - pos = ws.getInstrument().getComponentByName(self.cur_detector().name()).getPos() + # define the vector along the beam axis + a1 = V3D(0,0,1) + # position of the detector itself + pos = ws.getInstrument().getComponentByName('LARMORSANSDetector').getPos() + # position of the bench + pos2 = ws.getInstrument().getComponentByName(self.cur_detector().name()).getPos() + # take the difference + posdiff = pos-pos2 + deg2rad = 4.0*math.atan(1.0)/180.0 + # now finally find the angle between the vector for the difference and the beam axis + angle = posdiff.angle(a1)/deg2rad + + # return the angle and the y displacement + #logger.warning("Blah: angle=" + str(angle) + " Y displacement=" +str(-pos2.getY()) ) + return [-angle, -pos2.getY()] - return [-pos.getX(), -pos.getY()] + def on_load_sample(self, ws_name, beamcentre, isSample): + """For Larmor in addition to the operations defined in on_load_sample of ISISInstrument + it has to deal with the log, which defines some offsets for the movement of the + detector bank. + """ + #logger.warning("Entering on_load_sample") + ws_ref = mtd[str(ws_name)] + # in order to avoid problems with files from initial commisioning during 2014. + # these didn't have the required log entries for the detector position + try: + run_num = ws_ref.getRun().getLogData('run_number').value + except: + run_num = int(re.findall(r'\d+',str(ws_name))[-1]) + if(int(run_num) >= 2217): + try: + #logger.warning("Trying get_detector_log") + log = self.get_detector_log(ws_ref) + if log == "": + raise "Invalid log" + except: + if isSample: + raise RuntimeError('Sample logs cannot be loaded, cannot continue') + else: + logger.warning("Can logs could not be loaded, using sample values.") + + if isSample: + self.apply_detector_logs(log) + else: + self.check_can_logs(log) + ISISInstrument.on_load_sample(self, ws_name, beamcentre, isSample) if __name__ == '__main__': pass diff --git a/Code/Mantid/scripts/SANS/isis_reducer.py b/Code/Mantid/scripts/SANS/isis_reducer.py index d22422c814008ebce2db02e81f23b520e081d81b..8b9195ee640a7dce0f32a1119cbd9efa08d64533 100644 --- a/Code/Mantid/scripts/SANS/isis_reducer.py +++ b/Code/Mantid/scripts/SANS/isis_reducer.py @@ -622,6 +622,18 @@ class ISISReducer(Reducer): else: return self._beam_finder.get_beam_center() + def get_beam_center_scale_factor1(self): + """ + Return the beam center scale factor 1 defined in the parameter file. + """ + return self.instrument.beam_centre_scale_factor1 + + def get_beam_center_scale_factor2(self): + """ + Return the beam center scale factor 2 defined in the parameter file. + """ + return self.instrument.beam_centre_scale_factor2 + def getCurrSliceLimit(self): if not self._slices_def: self._slices_def = su.sliceParser("") diff --git a/Code/Mantid/scripts/SANS/isis_reduction_steps.py b/Code/Mantid/scripts/SANS/isis_reduction_steps.py index 308d8e5e8ec0becac8cbb06ce0879eef0bb94e9a..65bf57953fed2fbf5700accf26ae28a7c81927c0 100644 --- a/Code/Mantid/scripts/SANS/isis_reduction_steps.py +++ b/Code/Mantid/scripts/SANS/isis_reduction_steps.py @@ -1533,9 +1533,9 @@ class CalculateNormISIS(object): """ detector = detector.upper() - if detector in ("FRONT","HAB","FRONT-DETECTOR-BANK"): + if detector in ("FRONT", "HAB", "FRONT-DETECTOR-BANK"): self._high_angle_pixel_file = filename - if detector in ("REAR","MAIN","","MAIN-DETECTOR-BANK"): + if detector in ("REAR", "MAIN", "", "MAIN-DETECTOR-BANK", "DETECTORBENCH"): self._low_angle_pixel_file = filename def getPixelCorrFile(self, detector ): @@ -1546,9 +1546,9 @@ class CalculateNormISIS(object): """ detector = detector.upper() - if detector in ("FRONT","HAB","FRONT-DETECTOR-BANK", "FRONT-DETECTOR"): + if detector in ("FRONT", "HAB", "FRONT-DETECTOR-BANK", "FRONT-DETECTOR"): return self._high_angle_pixel_file - elif detector in ("REAR","MAIN","MAIN-DETECTOR-BANK","", "REAR-DETECTOR"): + elif detector in ("REAR","MAIN", "MAIN-DETECTOR-BANK", "", "REAR-DETECTOR", "DETECTORBENCH"): return self._low_angle_pixel_file else : logger.warning("Request of pixel correction file with unknown detector ("+ str(detector)+")") @@ -1998,19 +1998,23 @@ class UserFile(ReductionStep): hab_str_pos = upper_line.find('HAB') x_pos = 0.0 y_pos = 0.0 + # use the scale factors supplied in the parameter file + XSF = reducer.inst.beam_centre_scale_factor1 + YSF = reducer.inst.beam_centre_scale_factor2 + if main_str_pos > 0: values = upper_line[main_str_pos+5:].split() #remov the SET CENTRE/MAIN - x_pos = float(values[0])/1000.0 - y_pos = float(values[1])/1000.0 + x_pos = float(values[0])/XSF + y_pos = float(values[1])/YSF elif hab_str_pos > 0: values = upper_line[hab_str_pos+4:].split() # remove the SET CENTRE/HAB print ' convert values ',values - x_pos = float(values[0])/1000.0 - y_pos = float(values[1])/1000.0 + x_pos = float(values[0])/XSF + y_pos = float(values[1])/YSF else: values = upper_line.split() - x_pos = float(values[2])/1000.0 - y_pos = float(values[3])/1000.0 + x_pos = float(values[2])/XSF + y_pos = float(values[3])/YSF if hab_str_pos > 0: print 'Front values = ',x_pos,y_pos reducer.set_beam_finder(BaseBeamFinder(x_pos, y_pos),'front') @@ -2085,7 +2089,7 @@ class UserFile(ReductionStep): else: _issueWarning('FIT/MONITOR line specific to LOQ instrument. Line ignored') - elif upper_line == 'SANS2D' or upper_line == 'LOQ': + elif upper_line == 'SANS2D' or upper_line == 'LOQ' or upper_line == 'LARMOR': self._check_instrument(upper_line, reducer) elif upper_line.startswith('PRINT '): @@ -2338,6 +2342,11 @@ class UserFile(ReductionStep): detector.radius_corr = shift elif det_axis == 'SIDE': detector.side_corr = shift + # 10/03/15 RKH add 2 more variables + elif det_axis == 'XTILT': + detector.x_tilt = shift + elif det_axis == 'YTILT': + detector.y_tilt = shift else: raise NotImplemented('Detector correction on "'+det_axis+'" is not supported')