def _normaliseToSlits(self, ws): """Normalise ws to slit opening and update slit widths.""" # Update slit width in any case for later re-use. common.slitSizes(ws) slitNorm = self.getProperty(Prop.SLIT_NORM).value if slitNorm == SlitNorm.OFF: return ws elif slitNorm == SlitNorm.AUTO and self._instrumentName != 'D17': return ws run = ws.run() slit2width = run.get(common.SampleLogs.SLIT2WIDTH).value slit3width = run.get(common.SampleLogs.SLIT3WIDTH).value if slit2width == '-' or slit3width == '-': self.log().warning('Slit information not found in sample logs. Slit normalisation disabled.') return ws f = slit2width * slit3width normalisedWSName = self._names.withSuffix('normalised_to_slits') normalisedWS = Scale( InputWorkspace=ws, OutputWorkspace=normalisedWSName, Factor=1.0 / f, EnableLogging=self._subalgLogging ) self._cleanup.cleanup(ws) return normalisedWS
def _peakFitting(self, ws): """Add peak and foreground information to the sample logs.""" # Convert to wavelength l1 = ws.spectrumInfo().l1() hists = ws.getNumberHistograms() mindex = int(hists / 2) l2 = ws.spectrumInfo().l2(mindex) if not self.getProperty(Prop.LINE_POSITION).isDefault: linePosition = self.getProperty(Prop.LINE_POSITION).value else: # Fit peak position peakWSName = self._names.withSuffix('peak') xmin = self.getProperty(Prop.XMIN).value if not xmin == Property.EMPTY_DBL: xmin = self.getProperty(Prop.XMIN).value xmin = common.inTOF(xmin, l1, l2) xmax = self.getProperty(Prop.XMAX).value if not self.getProperty(Prop.XMAX).isDefault: xmax = self.getProperty(Prop.XMAX).value xmax = common.inTOF(xmax, l1, l2) peak = FindReflectometryLines(InputWorkspace=ws, OutputWorkspace=peakWSName, RangeLower=xmin, RangeUpper=xmax, StartWorkspaceIndex=self.getProperty( Prop.START_WS_INDEX).value, EndWorkspaceIndex=self.getProperty( Prop.END_WS_INDEX).value) linePosition = peak.LineCentre self._cleanup.cleanup(peak.OutputWorkspace) # Add the fractional workspace index of the beam position to the sample logs of ws. ws.run().addProperty(common.SampleLogs.LINE_POSITION, float(linePosition), True) return ws, linePosition
def PyExec(self): """Execute the algorithm.""" self._subalgLogging = self.getProperty( Prop.SUBALG_LOGGING).value == SubalgLogging.ON cleanupMode = self.getProperty(Prop.CLEANUP).value self._cleanup = common.WSCleanup(cleanupMode, self._subalgLogging) wsPrefix = self.getPropertyValue(Prop.OUTPUT_WS) self._names = common.WSNameSource(wsPrefix, cleanupMode) ws, beamPosWS = self._inputWS() ws, monWS = self._extractMonitors(ws) if beamPosWS is None: beamPosWS = self._findLine(ws) ws = self._addForegroundToLogs(ws, beamPosWS) self._outputBeamPosition(beamPosWS) ws = self._waterCalibration(ws) ws = self._normaliseToSlits(ws) ws = self._normaliseToFlux(ws, monWS) self._cleanup.cleanup(monWS) ws = self._subtractFlatBkg(ws, beamPosWS) self._cleanup.cleanup(beamPosWS) ws = self._convertToWavelength(ws) self._finalize(ws)
def _peakFitting(self, ws): """Add peak and foreground information to the sample logs.""" # Convert to wavelength l1 = ws.spectrumInfo().l1() hists = ws.getNumberHistograms() mindex = int(hists / 2) l2 = ws.spectrumInfo().l2(mindex) if not self.getProperty(Prop.LINE_POSITION).isDefault: linePosition = self.getProperty(Prop.LINE_POSITION).value else: # Fit peak position peakWSName = self._names.withSuffix('peak') xmin = self.getProperty(Prop.XMIN).value if not xmin == Property.EMPTY_DBL: xmin = self.getProperty(Prop.XMIN).value xmin = common.inTOF(xmin, l1, l2) xmax = self.getProperty(Prop.XMAX).value if not self.getProperty(Prop.XMAX).isDefault: xmax = self.getProperty(Prop.XMAX).value xmax = common.inTOF(xmax, l1, l2) peak = FindReflectometryLines( InputWorkspace=ws, OutputWorkspace=peakWSName, RangeLower=xmin, RangeUpper=xmax, StartWorkspaceIndex=self.getProperty(Prop.START_WS_INDEX).value, EndWorkspaceIndex=self.getProperty(Prop.END_WS_INDEX).value ) linePosition = peak.LineCentre self._cleanup.cleanup(peak.OutputWorkspace) # Add the fractional workspace index of the beam position to the sample logs of ws. ws.run().addProperty(common.SampleLogs.LINE_POSITION, float(linePosition), True) return ws, linePosition
def _correctForFractionalForegroundCentre(self, ws, summedForeground): """ This needs to be called after having summed the foreground but before transfering to momentum transfer. This needs to be called in both coherent and incoherent cases, regardless the angle calibration option. The reason for this is that up to this point is the fractional workspace index that correponds to the calibrated 2theta. However the momentum transfer calculation, which normally comes after summing the foreground, takes the 2theta from the spectrumInfo of the summed foreground workspace. Hence this code below translated the detector by the difference of the fractional and integer foreground centre along the detector plane. It also applies local rotation so that the detector continues to face the sample. Note that this translation has nothing to do with the difference of foreground centres in direct and reflected beams, which is handled already in pre-process algorithm. Here it's only about the difference of the fractional and integer foreground centre of the reflected beam with already calibrated angle no matter the option. Note also, that this could probably be avoided, if the loader placed the integer foreground at the given angle and not the fractional one. Fractional foreground centre only matter when calculating the difference between direct and reflected beams. But for the final Q (and sigma) calculation, it takes the position/angle from spectrumInfo()...(0), which corresponds to the centre of the pixel.""" foreground = self._foregroundIndices(ws) # integer foreground centre beamPosIndex = foreground[1] # fractional foreground centre linePosition = ws.run().getProperty( common.SampleLogs.LINE_POSITION).value l2 = ws.run().getProperty('L2').value instr = common.instrumentName(ws) pixelSize = common.pixelSize(instr) # the distance between the fractional and integer foreground centres along the detector plane dist = pixelSize * (linePosition - beamPosIndex) if dist != 0.: detPoint1 = ws.spectrumInfo().position(0) detPoint2 = ws.spectrumInfo().position(20) beta = numpy.math.atan2((detPoint2[0] - detPoint1[0]), (detPoint2[2] - detPoint1[2])) xvsy = numpy.math.sin(beta) * dist mz = numpy.math.cos(beta) * dist if instr == 'D17': mx = xvsy my = 0.0 rotationAxis = [0, 1, 0] else: mx = 0.0 my = xvsy rotationAxis = [-1, 0, 0] MoveInstrumentComponent(Workspace=summedForeground, ComponentName='detector', X=mx, Y=my, Z=mz, RelativePosition=True) angle_corr = numpy.arctan2(dist, l2) * 180 / numpy.pi RotateInstrumentComponent(Workspace=summedForeground, ComponentName='detector', X=rotationAxis[0], Y=rotationAxis[1], Z=rotationAxis[2], Angle=angle_corr, RelativeRotation=True) return summedForeground
def PyExec(self): """Execute the algorithm.""" self._subalgLogging = self.getProperty( Prop.SUBALG_LOGGING).value == SubalgLogging.ON cleanupMode = self.getProperty(Prop.CLEANUP).value self._cleanup = common.WSCleanup(cleanupMode, self._subalgLogging) wsPrefix = self.getPropertyValue(Prop.OUTPUT_WS) self._names = common.WSNameSource(wsPrefix, cleanupMode) ws, directWS, directForegroundWS = self._inputWS() ws = self._correctForChopperOpenings(ws, directWS) ws = self._convertToMomentumTransfer(ws, directWS) if directForegroundWS is not None: directForegroundWS = self._sameQAndDQ(ws, directForegroundWS, 'direct_') ws = self._toPointData(ws) ws = self._groupPoints(ws) if directForegroundWS is not None: directForegroundWS = self._toPointData(directForegroundWS, 'direct_') directForegroundWS = self._groupPoints(directForegroundWS, 'direct_') ws = self._divideByDirect(ws, directForegroundWS) self._finalize(ws)
def _moveDetector(self, ws, linePosition): """Perform detector position correction for direct and reflected beams.""" detectorMovedWSName = self._names.withSuffix('detectors_moved') twoTheta = ws.run().getProperty(common.SampleLogs.TWO_THETA).value args = { 'InputWorkspace': ws, 'OutputWorkspace': detectorMovedWSName, 'TwoTheta': twoTheta, 'EnableLogging': self._subalgLogging, 'DetectorComponentName': 'detector', 'PixelSize': common.pixelSize(self._instrumentName), 'DetectorCorrectionType': 'RotateAroundSample', 'DetectorFacesSample': True } if not self.getProperty(Prop.TWO_THETA).isDefault: # We should use user angle args['TwoTheta'] = self.getProperty(Prop.TWO_THETA).value # We need to subtract an offsetAngle from user given TwoTheta args['LinePosition'] = linePosition else: logs = ws.run() args['TwoTheta'] = twoTheta + common.deflectionAngle(logs) detectorMovedWS = SpecularReflectionPositionCorrect(**args) self._cleanup.cleanup(ws) return detectorMovedWS
def PyExec(self): """Execute the algorithm.""" self._subalgLogging = self.getProperty( Prop.SUBALG_LOGGING).value == SubalgLogging.ON cleanupMode = self.getProperty(Prop.CLEANUP).value self._cleanup = common.WSCleanup(cleanupMode, self._subalgLogging) wsPrefix = self.getPropertyValue(Prop.OUTPUT_WS) self._names = common.WSNameSource(wsPrefix, cleanupMode) ws = self._inputWS() processReflected = not self._directOnly() if processReflected: self._addBeamStatisticsToLogs(ws) sumType = self._sumType() if sumType == SumType.IN_LAMBDA: ws = self._sumForegroundInLambda(ws) self._addSumTypeToLogs(ws, SumType.IN_LAMBDA) if processReflected: ws = self._rebinToDirect(ws) else: if processReflected: ws = self._divideByDirect(ws) ws = self._sumForegroundInQ(ws) self._addSumTypeToLogs(ws, SumType.IN_Q) ws = self._applyWavelengthRange(ws) self._finalize(ws)
def PyExec(self): """Execute the algorithm.""" self._subalgLogging = self.getProperty( Prop.SUBALG_LOGGING).value == SubalgLogging.ON cleanupMode = self.getProperty(Prop.CLEANUP).value self._cleanup = common.WSCleanup(cleanupMode, self._subalgLogging) wsPrefix = self.getPropertyValue(Prop.OUTPUT_WS) self._names = common.WSNameSource(wsPrefix, cleanupMode) ws = self._inputWS() self._instrumentName = ws.getInstrument().getName() ws, monWS = self._extractMonitors(ws) ws, linePosition = self._peakFitting(ws) self._addSampleLogInfo(ws, linePosition) ws = self._moveDetector(ws, linePosition) ws = self._calibrateDetectorAngleByDirectBeam(ws) ws = self._waterCalibration(ws) ws = self._normaliseToSlits(ws) ws = self._normaliseToFlux(ws, monWS) self._cleanup.cleanup(monWS) ws = self._subtractFlatBkg(ws) ws = self._convertToWavelength(ws) self._finalize(ws)
def testD17SlitWidthLogEntry(self): ws = illhelpers.create_poor_mans_d17_workspace() mtd.add('ws', ws) illhelpers.add_slit_configuration_D17(ws, 0.03, 0.02) run = ws.run() instrName = common.instrumentName(ws) slit2width = run.get(common.slitSizeLogEntry(instrName, 1)) slit3width = run.get(common.slitSizeLogEntry(instrName, 2)) common.slitSizes(ws) self.assertEquals(slit2width.value, run.getProperty(common.SampleLogs.SLIT2WIDTH).value) self.assertEquals(slit3width.value, run.getProperty(common.SampleLogs.SLIT3WIDTH).value) mtd.clear()
def _convertToMomentumTransfer(self, ws): """Convert the X units of ws to momentum transfer.""" logs = ws.run() reflectedForeground = self._foreground(logs) instrumentName = common.instrumentName(ws) sumType = logs.getProperty(common.SampleLogs.SUM_TYPE).value pixelSize = common.pixelSize(instrumentName) detResolution = common.detectorResolution() chopperSpeed = common.chopperSpeed(logs, instrumentName) chopperOpening = common.chopperOpeningAngle(logs, instrumentName) chopperRadius = 0.36 if instrumentName == 'D17' else 0.305 chopperPairDist = common.chopperPairDistance(logs, instrumentName) slit1SizeLog = common.slitSizeLogEntry(instrumentName, 1) slit2SizeLog = common.slitSizeLogEntry(instrumentName, 2) tofBinWidth = self._TOFChannelWidth(logs) qWSName = self._names.withSuffix('in_momentum_transfer') qWS = ReflectometryMomentumTransfer( InputWorkspace=ws, OutputWorkspace=qWSName, SummationType=sumType, ReflectedForeground=reflectedForeground, PixelSize=pixelSize, DetectorResolution=detResolution, ChopperSpeed=chopperSpeed, ChopperOpening=chopperOpening, ChopperRadius=chopperRadius, ChopperPairDistance=chopperPairDist, FirstSlitName='slit2', FirstSlitSizeSampleLog=slit1SizeLog, SecondSlitName='slit3', SecondSlitSizeSampleLog=slit2SizeLog, TOFChannelWidth=tofBinWidth, EnableLogging=self._subalgLogging) self._cleanup.cleanup(ws) return qWS
def _correctForChopperOpenings(self, ws, directWS): """Corrects ws for different chopper opening angles.""" correctedWS = common.correctForChopperOpenings(ws, directWS, self._names, self._cleanup, self._subalgLogging) return correctedWS
def PyExec(self): """Execute the algorithm.""" self._subalgLogging = self.getProperty(Prop.SUBALG_LOGGING).value == SubalgLogging.ON cleanupMode = self.getProperty(Prop.CLEANUP).value self._cleanup = common.WSCleanup(cleanupMode, self._subalgLogging) wsPrefix = self.getPropertyValue(Prop.OUTPUT_WS) self._names = common.WSNameSource(wsPrefix, cleanupMode) ws = self._inputWS() ws = self._convertToMomentumTransfer(ws) ws = self._toPointData(ws) ws = self._groupPoints(ws) self._finalize(ws)
def _correctForChopperOpenings(self, ws, directWS): """Correct reflectivity values if chopper openings between RB and DB differ.""" def opening(instrumentName, logs, Xs): chopperGap = common.chopperPairDistance(logs, instrumentName) chopperPeriod = 60. / common.chopperSpeed(logs, instrumentName) openingAngle = common.chopperOpeningAngle(logs, instrumentName) return chopperGap * constants.m_n / constants.h / chopperPeriod * Xs * 1e-10 + openingAngle / 360. instrumentName = common.instrumentName(ws) Xbins = ws.readX(0) Xs = (Xbins[:-1] + Xbins[1:]) / 2. reflectedOpening = opening(instrumentName, ws.run(), Xs) directOpening = opening(instrumentName, directWS.run(), Xs) corFactorWSName = self._names.withSuffix( 'chopper_opening_correction_factors') corFactorWS = CreateWorkspace(OutputWorkspace=corFactorWSName, DataX=Xbins, DataY=directOpening / reflectedOpening, UnitX=ws.getAxis(0).getUnit().unitID(), ParentWorkspace=ws, EnableLogging=self._subalgLogging) correctedWSName = self._names.withSuffix( 'corrected_by_chopper_opening') correctedWS = Multiply(LHSWorkspace=ws, RHSWorkspace=corFactorWS, OutputWorkspace=correctedWSName, EnableLogging=self._subalgLogging) self._cleanup.cleanup(corFactorWS) self._cleanup.cleanup(ws) return correctedWS
def _convertToMomentumTransfer(self, ws): """Convert the X units of ws to momentum transfer.""" reflectedWS = self.getProperty(Prop.REFLECTED_WS).value reflectedForeground = self._foreground(reflectedWS.run()) directWS = self.getProperty(Prop.DIRECT_WS).value directForeground = self._foreground(directWS.run()) logs = ws.run() instrumentName = ws.getInstrument().getName() if instrumentName != 'D17' and instrumentName != 'Figaro': raise RuntimeError('Unrecognized instrument {}. Only D17 and Figaro are supported.'.format(instrumentName)) sumType = logs.getProperty(common.SampleLogs.SUM_TYPE).value polarized = self.getProperty(Prop.POLARIZED).value pixelSize = 0.001195 if instrumentName == 'D17' else 0.0012 detResolution = 0.0022 chopperSpeed = common.chopperSpeed(logs, instrumentName) chopperOpening = common.chopperOpeningAngle(logs, instrumentName) chopperRadius = 0.36 if instrumentName == 'D17' else 0.305 chopperPairDist = common.chopperPairDistance(logs, instrumentName) slit1SizeLog = 'VirtualSlitAxis.s2w_actual_width' if instrumentName == 'D17' else 'VirtualSlitAxis.S2H_actual_height' slit2SizeLog = 'VirtualSlitAxis.s3w_actual_width' if instrumentName == 'D17' else 'VirtualSlitAxis.S3H_actual_height' tofBinWidth = self._TOFChannelWidth(logs, instrumentName) qWSName = self._names.withSuffix('in_momentum_transfer') qWS = ReflectometryMomentumTransfer( InputWorkspace=ws, OutputWorkspace=qWSName, ReflectedBeamWorkspace=reflectedWS, ReflectedForeground=reflectedForeground, DirectBeamWorkspace=directWS, DirectForeground=directForeground, SummationType=sumType, Polarized=polarized, PixelSize=pixelSize, DetectorResolution=detResolution, ChopperSpeed=chopperSpeed, ChopperOpening=chopperOpening, ChopperRadius=chopperRadius, ChopperPairDistance=chopperPairDist, Slit1Name='slit2', Slit1SizeSampleLog=slit1SizeLog, Slit2Name='slit3', Slit2SizeSampleLog=slit2SizeLog, TOFChannelWidth=tofBinWidth, EnableLogging=self._subalgLogging) self._cleanup.cleanup(ws) return qWS
def PyExec(self): """Execute the algorithm.""" self._subalgLogging = self.getProperty( Prop.SUBALG_LOGGING).value == SubalgLogging.ON cleanupMode = self.getProperty(Prop.CLEANUP).value self._cleanup = common.WSCleanup(cleanupMode, self._subalgLogging) wsPrefix = self.getPropertyValue(Prop.OUTPUT_WS) self._names = common.WSNameSource(wsPrefix, cleanupMode) wss = self._inputWS() effWS = self._efficiencies(wss[0]) wss = self._commonBinning(wss) wss = self._correct(wss, effWS) self._finalize(wss)
def _normaliseToSlits(self, ws): """Normalise ws to slit opening.""" if self.getProperty(Prop.SLIT_NORM).value == SlitNorm.OFF: return ws r = ws.run() instrumentName = r.get('instrument.name').value slit2width = r.get(common.slitSizeLogEntry(instrumentName, 1)) slit3width = r.get(common.slitSizeLogEntry(instrumentName, 2)) if slit2width is None or slit3width is None: self.log().warning('Slit information not found in sample logs. Slit normalisation disabled.') return ws f = slit2width.value * slit3width.value normalisedWSName = self._names.withSuffix('normalised_to_slits') normalisedWS = Scale(InputWorkspace=ws, OutputWorkspace=normalisedWSName, Factor=1.0 / f, EnableLogging=self._subalgLogging) self._cleanup.cleanup(ws) return normalisedWS
def PyExec(self): """Execute the algorithm.""" self._subalgLogging = self.getProperty( Prop.SUBALG_LOGGING).value == SubalgLogging.ON cleanupMode = self.getProperty(Prop.CLEANUP).value self._cleanup = common.WSCleanup(cleanupMode, self._subalgLogging) wsPrefix = self.getPropertyValue(Prop.OUTPUT_WS) self._names = common.WSNameSource(wsPrefix, cleanupMode) ws = self._inputWS() sumType = self._sumType() if sumType == SumType.IN_LAMBDA: ws = self._sumForegroundInLambda(ws) ws = self._reflectivity(ws) else: raise RuntimeError('Summation in Q is not yet supported.') self._finalize(ws)
def _addBeamStatisticsToLogs(self, ws): """Calculate beam statistics and add the results to the sample logs.""" reflectedForeground = self._foregroundIndices(ws) directWS = self.getProperty(Prop.DIRECT_WS).value directForeground = self._foregroundIndices(directWS) instrumentName = common.instrumentName(ws) pixelSize = common.pixelSize(instrumentName) detResolution = common.detectorResolution() ReflectometryBeamStatistics( ReflectedBeamWorkspace=ws, ReflectedForeground=reflectedForeground, DirectLineWorkspace=directWS, DirectForeground=directForeground, PixelSize=pixelSize, DetectorResolution=detResolution, FirstSlitName='slit2', FirstSlitSizeSampleLog=common.SampleLogs.SLIT2WIDTH, SecondSlitName='slit3', SecondSlitSizeSampleLog=common.SampleLogs.SLIT3WIDTH, EnableLogging=self._subalgLogging)
def _addBeamStatisticsToLogs(self, ws): """Calculate beam statistics and add the results to the sample logs.""" reflectedForeground = self._foregroundIndices(ws) directWS = self.getProperty(Prop.DIRECT_WS).value directForeground = self._foregroundIndices(directWS) instrumentName = common.instrumentName(ws) pixelSize = common.pixelSize(instrumentName) detResolution = common.detectorResolution() firstSlitSizeLog = common.slitSizeLogEntry(instrumentName, 1) secondSlitSizeLog = common.slitSizeLogEntry(instrumentName, 2) ReflectometryBeamStatistics( ReflectedBeamWorkspace=ws, ReflectedForeground=reflectedForeground, DirectLineWorkspace=directWS, DirectForeground=directForeground, PixelSize=pixelSize, DetectorResolution=detResolution, FirstSlitName='slit2', FirstSlitSizeSampleLog=firstSlitSizeLog, SecondSlitName='slit3', SecondSlitSizeSampleLog=secondSlitSizeLog, EnableLogging=self._subalgLogging)
def _divideByDirect(self, ws): """Divide ws by the direct beam.""" ws = self._rebinToDirect(ws) directWS = self.getProperty(Prop.DIRECT_FOREGROUND_WS).value reflectivityWSName = self._names.withSuffix('reflectivity') reflectivityWS = Divide( LHSWorkspace=ws, RHSWorkspace=directWS, OutputWorkspace=reflectivityWSName, EnableLogging=self._subalgLogging) self._cleanup.cleanup(ws) reflectivityWS = common.correctForChopperOpenings(reflectivityWS, directWS, self._names, self._cleanup, self._subalgLogging) reflectivityWS.setYUnit('Reflectivity') reflectivityWS.setYUnitLabel('Reflectivity') return reflectivityWS
def _calibrateDetectorAngleByDirectBeam(self, ws): """Perform detector position correction for reflected beams.""" direct_line = self.getProperty('DirectBeamForegroundCentre').value calibratedWSName = self._names.withSuffix('reflected_beam_calibration') calibratedWS = SpecularReflectionPositionCorrect( InputWorkspace=ws, OutputWorkspace=calibratedWSName, DetectorComponentName='detector', LinePosition=direct_line, # yes, this is the direct line position! TwoTheta=2*self._theta_from_detector_angles(), PixelSize=common.pixelSize(self._instrumentName), DetectorCorrectionType='RotateAroundSample', DetectorFacesSample=True, EnableLogging=self._subalgLogging ) self._cleanup.cleanup(ws) return calibratedWS
def _calibrateDetectorAngleByDirectBeam(self, ws): """Perform detector position correction for reflected beams.""" if self.getProperty(Prop.DIRECT_LINE_WORKSPACE).isDefault: return ws calibratedWSName = self._names.withSuffix('reflected_beam_calibration') directLineWS = self.getProperty(Prop.DIRECT_LINE_WORKSPACE).value directLine = directLineWS.run().getProperty( common.SampleLogs.LINE_POSITION).value calibratedWS = SpecularReflectionPositionCorrect( InputWorkspace=ws, OutputWorkspace=calibratedWSName, EnableLogging=self._subalgLogging, DetectorComponentName='detector', DirectLineWorkspace=directLineWS, DirectLinePosition=directLine, PixelSize=common.pixelSize(self._instrumentName), DetectorCorrectionType='RotateAroundSample', DetectorFacesSample=True) self._cleanup.cleanup(ws) return calibratedWS
def _calibrateDetectorAngleByDirectBeam(self, ws): """Perform detector position correction for reflected beams.""" if self.getProperty(Prop.DIRECT_LINE_WORKSPACE).isDefault: return ws calibratedWSName = self._names.withSuffix('reflected_beam_calibration') directLineWS = self.getProperty(Prop.DIRECT_LINE_WORKSPACE).value directLine = directLineWS.run().getProperty(common.SampleLogs.LINE_POSITION).value calibratedWS = SpecularReflectionPositionCorrect( InputWorkspace=ws, OutputWorkspace=calibratedWSName, EnableLogging=self._subalgLogging, DetectorComponentName='detector', DirectLineWorkspace=directLineWS, DirectLinePosition=directLine, PixelSize=common.pixelSize(self._instrumentName), DetectorCorrectionType='RotateAroundSample', DetectorFacesSample=True ) self._cleanup.cleanup(ws) return calibratedWS
def _inputWS(self): """Return a raw input workspace.""" inputFiles = self.getPropertyValue(Prop.RUN) inputFiles = inputFiles.replace(',', '+') mergedWSName = self._names.withSuffix('merged') measurement_type = self.getPropertyValue('Measurement') load_options = { 'Measurement': measurement_type, 'XUnit': 'TimeOfFlight', 'FitStartWorkspaceIndex': self.getProperty(Prop.START_WS_INDEX).value, 'FitEndWorkspaceIndex': self.getProperty(Prop.END_WS_INDEX).value, 'FitRangeLower': self.getProperty(Prop.XMIN).value, 'FitRangeUpper': self.getProperty(Prop.XMAX).value } if measurement_type == 'ReflectedBeam': bragg_angle = None angle_option = self.getPropertyValue('AngleOption') first_run = self.getProperty(Prop.RUN).value[0] if angle_option == 'SampleAngle': bragg_angle = common.sample_angle(first_run) elif angle_option == 'DetectorAngle': bragg_angle = self._theta_from_detector_angles() # in this clause we still need to correct for the difference of foreground # centres between direct and reflected beams # but we need first to load the reflected beam to be able to do this elif angle_option == 'UserAngle': bragg_angle = self.getProperty('BraggAngle').value load_options['BraggAngle'] = bragg_angle # MergeRunsOptions are defined by the parameter files and will not be modified here! ws = LoadAndMerge( Filename=inputFiles, LoaderName='LoadILLReflectometry', LoaderOptions=load_options, OutputWorkspace=mergedWSName, EnableLogging=self._subalgLogging ) return ws
def _sumForegroundInLambda(self, ws): """Sum the foreground region into a single histogram.""" foreground = self._foregroundIndices(ws) sumIndices = [i for i in range(foreground[0], foreground[2] + 1)] beamPosIndex = foreground[1] foregroundWSName = self._names.withSuffix('grouped') foregroundWS = ExtractSingleSpectrum(InputWorkspace=ws, OutputWorkspace=foregroundWSName, WorkspaceIndex=beamPosIndex, EnableLogging=self._subalgLogging) maxIndex = ws.getNumberHistograms() - 1 foregroundYs = foregroundWS.dataY(0) foregroundEs = foregroundWS.dataE(0) numpy.square(foregroundEs, out=foregroundEs) for i in sumIndices: if i == beamPosIndex: continue if i < 0 or i > maxIndex: self.log().warning( 'Foreground partially out of the workspace.') addeeWSName = self._names.withSuffix('addee') addeeWS = ExtractSingleSpectrum(InputWorkspace=ws, OutputWorkspace=addeeWSName, WorkspaceIndex=i, EnableLogging=self._subalgLogging) addeeWS = RebinToWorkspace(WorkspaceToRebin=addeeWS, WorkspaceToMatch=foregroundWS, OutputWorkspace=addeeWSName, EnableLogging=self._subalgLogging) ys = addeeWS.readY(0) foregroundYs += ys es = addeeWS.readE(0) foregroundEs += es**2 self._cleanup.cleanup(addeeWS) self._cleanup.cleanup(ws) numpy.sqrt(foregroundEs, out=foregroundEs) # Move the detector to the fractional linePosition linePosition = ws.run().getProperty( common.SampleLogs.LINE_POSITION).value instr = common.instrumentName(ws) pixelSize = common.pixelSize(instr) dist = pixelSize * (linePosition - beamPosIndex) if dist != 0.: detPoint1 = ws.spectrumInfo().position(0) detPoint2 = ws.spectrumInfo().position(20) beta = numpy.math.atan2((detPoint2[0] - detPoint1[0]), (detPoint2[2] - detPoint1[2])) xvsy = numpy.math.sin(beta) * dist mz = numpy.math.cos(beta) * dist if instr == 'D17': mx = xvsy my = 0.0 rotationAxis = [0, 1, 0] else: mx = 0.0 my = xvsy rotationAxis = [-1, 0, 0] MoveInstrumentComponent(Workspace=foregroundWS, ComponentName='detector', X=mx, Y=my, Z=mz, RelativePosition=True) theta = foregroundWS.spectrumInfo().twoTheta(0) / 2. RotateInstrumentComponent(Workspace=foregroundWS, ComponentName='detector', X=rotationAxis[0], Y=rotationAxis[1], Z=rotationAxis[2], Angle=theta, RelativeRotation=True) return foregroundWS
def _theta_from_detector_angles(self): """Returns the bragg angle as half of detector angle difference""" first_run = self.getProperty(Prop.RUN).value[0] db_detector_angle = self.getProperty('DirectBeamDetectorAngle').value return (common.detector_angle(first_run) - db_detector_angle) / 2.
def opening(instrumentName, logs, Xs): chopperGap = common.chopperPairDistance(logs, instrumentName) chopperPeriod = 60. / common.chopperSpeed(logs, instrumentName) openingAngle = common.chopperOpeningAngle(logs, instrumentName) return chopperGap * constants.m_n / constants.h / chopperPeriod * Xs * 1e-10 + openingAngle / 360.