def PyExec(self): config['default.facility'] = "SNS" config['default.instrument'] = self._long_inst self._doIndiv = self.getProperty("DoIndividual").value self._etBins = self.getProperty( "EnergyBins").value / MICROEV_TO_MILLIEV self._qBins = self.getProperty("MomentumTransferBins").value self._noMonNorm = self.getProperty("NoMonitorNorm").value self._maskFile = self.getProperty("MaskFile").value self._groupDetOpt = self.getProperty("GroupDetectors").value self._normalizeToFirst = self.getProperty("NormalizeToFirst").value self._normalizeToVanadium = self.getProperty("GroupDetectors").value self._doNorm = self.getProperty("DivideByVanadium").value datasearch = config["datasearch.searcharchive"] if datasearch != "On": config["datasearch.searcharchive"] = "On" # Handle masking file override if necessary self._overrideMask = bool(self._maskFile) if not self._overrideMask: config.appendDataSearchDir(DEFAULT_MASK_GROUP_DIR) self._maskFile = DEFAULT_MASK_FILE api.LoadMask(Instrument='BASIS', OutputWorkspace='BASIS_MASK', InputFile=self._maskFile) # Work around length issue _dMask = api.ExtractMask('BASIS_MASK') self._dMask = _dMask[1] api.DeleteWorkspace(_dMask[0]) ############################ ## Process the Vanadium ## ############################ norm_runs = self.getProperty("NormRunNumbers").value if self._doNorm and bool(norm_runs): if ";" in norm_runs: raise SyntaxError("Normalization does not support run groups") self._doNorm = self.getProperty("NormalizationType").value self.log().information("Divide by Vanadium with normalization" + self._doNorm) # The following steps are common to all types of Vanadium normalization # norm_runs encompasses a single set, thus _getRuns returns # a list of only one item norm_set = self._getRuns(norm_runs, doIndiv=False)[0] self._normWs = self._sum_and_calibrate(norm_set, extra_extension="_norm") # This rebin integrates counts onto a histogram of a single bin if self._doNorm == "by detectorID": normRange = self.getProperty("NormWavelengthRange").value self._normRange = [ normRange[0], normRange[1] - normRange[0], normRange[1] ] api.Rebin(InputWorkspace=self._normWs, OutputWorkspace=self._normWs, Params=self._normRange) # FindDetectorsOutsideLimits to be substituted by MedianDetectorTest api.FindDetectorsOutsideLimits(InputWorkspace=self._normWs, OutputWorkspace="BASIS_NORM_MASK") # additional reduction steps when normalizing by Q slice if self._doNorm == "by Q slice": self._normWs = self._group_and_SofQW(self._normWs, self._etBins, isSample=False) ########################## ## Process the sample ## ########################## self._run_list = self._getRuns(self.getProperty("RunNumbers").value, doIndiv=self._doIndiv) for run_set in self._run_list: self._samWs = self._sum_and_calibrate(run_set) self._samWsRun = str(run_set[0]) # Mask detectors with insufficient Vanadium signal if self._doNorm: api.MaskDetectors(Workspace=self._samWs, MaskedWorkspace='BASIS_NORM_MASK') # Divide by Vanadium if self._doNorm == "by detector ID": api.Divide(LHSWorkspace=self._samWs, RHSWorkspace=self._normWs, OutputWorkspace=self._samWs) # additional reduction steps self._samSqwWs = self._group_and_SofQW(self._samWs, self._etBins, isSample=True) # Divide by Vanadium if self._doNorm == "by Q slice": api.Integration(InputWorkspace=self._normWs, OutputWorkspace=self._normWs, RangeLower=DEFAULT_VANADIUM_ENERGY_RANGE[0], RangeUpper=DEFAULT_VANADIUM_ENERGY_RANGE[1]) api.Divide(LHSWorkspace=self._samSqwWs, RHSWorkspace=self._normWs, OutputWorkspace=self._samSqwWs) # Clear mask from reduced file. Needed for binary operations # involving this S(Q,w) api.ClearMaskFlag(Workspace=self._samSqwWs) # Scale so that elastic line has Y-values ~ 1 if self._normalizeToFirst: self._ScaleY(self._samSqwWs) # Output Dave and Nexus files extension = "_divided.dat" if self._doNorm else ".dat" dave_grp_filename = self._makeRunName(self._samWsRun, False) + extension api.SaveDaveGrp(Filename=dave_grp_filename, InputWorkspace=self._samSqwWs, ToMicroEV=True) extension = "_divided_sqw.nxs" if self._doNorm else "_sqw.nxs" processed_filename = self._makeRunName(self._samWsRun, False) + extension api.SaveNexus(Filename=processed_filename, InputWorkspace=self._samSqwWs)
import mantid.simpleapi as mantid # == Set parameters for calibration == filename = 'MAP14919.raw' # Calibration run ( found in \\isis\inst$\NDXMAPS\Instrument\data\cycle_09_5 ) # Set what we want to calibrate (e.g whole instrument or one door ) CalibratedComponent = 'D2_window' # Calibrate D2 window # Get calibration raw file and integrate it rawCalibInstWS = mantid.Load( filename) # 'raw' in 'rawCalibInstWS' means unintegrated. print("Integrating Workspace") rangeLower = 2000 # Integrate counts in each spectra from rangeLower to rangeUpper rangeUpper = 10000 # CalibInstWS = mantid.Integration(rawCalibInstWS, RangeLower=rangeLower, RangeUpper=rangeUpper) mantid.DeleteWorkspace(rawCalibInstWS) print( "Created workspace (CalibInstWS) with integrated data from run and instrument to calibrate" ) # == Create Objects needed for calibration == # The positions of the shadows and ends here are an intelligent guess. # First array gives positions in Metres and second array gives type 1=Gaussian peak 2=edge. knownPos = [-0.65, -0.22, -0.00, 0.22, 0.65] funcForm = [2, 1, 1, 1, 2] # Get fitting parameters
def CalibrateWish(run_per_panel_list): ''' :param run_per_panel_list: is a list of tuples with the run number and the associated panel run_per_panel_list = [ (17706, 'panel01'), (17705, 'panel02'), (17701, 'panel03'), (17702, 'panel04'), (17695, 'panel05')] ''' # == Set parameters for calibration == previousDefaultInstrument = mantid.config['default.instrument'] mantid.config['default.instrument'] = "WISH" # definition of the parameters static for the calibration lower_tube = numpy.array( [-0.41, -0.31, -0.21, -0.11, -0.02, 0.09, 0.18, 0.28, 0.39]) upper_tube = numpy.array(lower_tube + 0.003) funcForm = 9 * [1] # 9 gaussian peaks margin = 15 low_range = list(range(0, 76)) high_range = list(range(76, 152)) kwargs = {'margin': margin} # it will copy all the data from the runs to have a single instrument with the calibrated data. whole_instrument = mantid.LoadRaw(str(run_per_panel_list[0][0])) whole_instrument = mantid.Integration(whole_instrument) for (run_number, panel_name) in run_per_panel_list: panel_name = str(panel_name) run_number = str(run_number) # load your data and integrate it ws = mantid.LoadRaw(run_number, OutputWorkspace=panel_name) ws = mantid.Integration(ws, 1, 20000, OutputWorkspace=panel_name) # use the TubeSpec object to be able to copy the data to the whole_instrument tube_set = TubeSpec(ws) tube_set.setTubeSpecByString(panel_name) # update kwargs argument before calling calibrate kwargs['rangeList'] = low_range # calibrate only the lower tubes calibrationTable = tube.calibrate(ws, tube_set, lower_tube, funcForm, **kwargs) # update kwargs kwargs[ 'calibTable'] = calibrationTable # append calib to calibrationtable kwargs['rangeList'] = high_range # calibrate only the upper tubes calibrationTable = tube.calibrate(ws, tube_set, upper_tube, funcForm, **kwargs) kwargs['calibTable'] = calibrationTable mantid.ApplyCalibration(ws, calibrationTable) # copy data from the current panel to the whole_instrument for i in range(tube_set.getNumTubes()): for spec_num in tube_set.getTube(i): whole_instrument.setY(spec_num, ws.dataY(spec_num)) # calibrate the whole_instrument with the last calibrated panel which has the calibration accumulation # of all the others mantid.CopyInstrumentParameters(run_per_panel_list[-1][1], whole_instrument) mantid.config['default.instrument'] = previousDefaultInstrument
# Here we run the calibration of WISH panel03 # We base the ideal tube on one tube of this door. # from __future__ import absolute_import, division, print_function import tube reload(tube) # noqa from tube_spec import TubeSpec import tube_calib #from tube_calib import constructIdealTubeFromRealTube from tube_calib_fit_params import TubeCalibFitParams import mantid.simpleapi as mantid filename = 'WISH00017701.raw' # Calibration run ( found in \\isis\inst$\NDXWISH\Instrument\data\cycle_11_1 ) rawCalibInstWS = mantid.Load(filename) #'raw' in 'rawCalibInstWS' means unintegrated. CalibInstWS = mantid.Integration( rawCalibInstWS, RangeLower=1, RangeUpper=20000 ) mantid.DeleteWorkspace(rawCalibInstWS) print("Created workspace (CalibInstWS) with integrated data from run and instrument to calibrate") CalibratedComponent = 'WISH/panel03/tube038' # Set fitting parameters eP = [65.0, 113.0, 161.0, 209.0, 257.0, 305.0, 353.0, 401.0, 449.0] ExpectedHeight = 2000.0 # Expected Height of Gaussian Peaks (initial value of fit parameter) ExpectedWidth = 32.0 # Expected width of Gaussian peaks in pixels (initial value of fit parameter) fitPar = TubeCalibFitParams( eP, ExpectedHeight, ExpectedWidth ) fitPar.setAutomatic(True) print("Created objects needed for calibration.") func_form = 9*[1] # Use first tube as ideal tube
def PyExec(self): config['default.facility'] = 'SNS' config['default.instrument'] = 'ARCS' self._runs = self.getProperty('RunNumbers').value self._vanfile = self.getProperty('Vanadium').value self._ecruns = self.getProperty('EmptyCanRunNumbers').value self._ebins_str = self.getProperty('EnergyBins').value self._qbins_str = self.getProperty('MomentumTransferBins').value self._snorm = self.getProperty('NormalizeSlices').value self._clean = self.getProperty('CleanWorkspaces').value wn_sqes = self.getPropertyValue("OutputWorkspace") # workspace names prefix = '' if self._clean: prefix = '__' # Sample files wn_data = prefix + 'data' wn_van = prefix + 'vanadium' wn_reduced = prefix + 'reduced' wn_ste = prefix + 'S_theta_E' wn_van_st = prefix + 'vanadium_S_theta' wn_sten = prefix + 'S_theta_E_normalized' wn_steni = prefix + 'S_theta_E_normalized_interp' wn_sqe = prefix + 'S_Q_E' wn_sqeb = prefix + 'S_Q_E_binned' wn_sqesn = prefix + wn_sqes + '_norm' # Empty can files wn_ec_data = prefix + 'ec_data' wn_ec_reduced = prefix + 'ec_reduced' wn_ec_ste = prefix + 'ec_S_theta_E' datasearch = config["datasearch.searcharchive"] if datasearch != "On": config["datasearch.searcharchive"] = "On" # Load several event files into a sinle workspace. The nominal incident # energy should be the same to avoid difference in energy resolution api.Load(Filename=self._runs, OutputWorkspace=wn_data) # Load the vanadium file, assume to be preprocessed, meaning that # for every detector all events whithin a particular wide wavelength # range have been rebinned into a single histogram api.Load(Filename=self._vanfile, OutputWorkspace=wn_van) # Load empty can event files, if present if self._ecruns: api.Load(Filename=self._ecruns, OutputWorkspace=wn_ec_data) # Retrieve the mask from the vanadium workspace, and apply it to the data # (and empty can, if submitted) api.MaskDetectors(Workspace=wn_data, MaskedWorkspace=wn_van) if self._ecruns: api.MaskDetectors(Workspace=wn_ec_data, MaskedWorkspace=wn_van) # Obtain incident energy as the mean of the nominal Ei values. # There is one nominal value per events file. ws_data = api.mtd[wn_data] Ei = ws_data.getRun()['EnergyRequest'].getStatistics().mean Ei_std = ws_data.getRun()['EnergyRequest'].getStatistics( ).standard_deviation # Verify empty can runs were obtained at similar energy if self._ecruns: ws_ec_data = api.mtd[wn_ec_data] ec_Ei = ws_ec_data.getRun()['EnergyRequest'].getStatistics().mean if abs(Ei - ec_Ei) > Ei_std: raise RuntimeError( 'Empty can runs were obtained at a significant' + ' different incident energy than the sample runs') # Obtain energy range self._ebins = [ float(x) for x in re.compile(r'\d+[\.\d+]*').findall(self._ebins_str) ] if len(self._ebins) == 1: ws_data = api.mtd[wn_data] Ei = ws_data.getRun()['EnergyRequest'].getStatistics().mean self._ebins.insert(0, -0.5 * Ei) # prepend self._ebins.append(0.95 * Ei) # append # Enforce that the elastic energy (E=0) lies in the middle of the # central bin with an appropriate small shift in the energy range Ei_min_reduced = self._ebins[0] / self._ebins[1] remainder = Ei_min_reduced - int(Ei_min_reduced) if remainder >= 0.0: erange_shift = self._ebins[1] * (0.5 - remainder) else: erange_shift = self._ebins[1] * (-0.5 - remainder) self._ebins[0] += erange_shift # shift minimum energy self._ebins[-1] += erange_shift # shift maximum energy # Convert to energy transfer. Normalize by proton charge. # The output workspace is S(detector-id,E) factor = 0.1 # a fine energy bin Erange = '{0},{1},{2}'.format(self._ebins[0], factor * self._ebins[1], self._ebins[2]) api.DgsReduction(SampleInputWorkspace=wn_data, EnergyTransferRange=Erange, OutputWorkspace=wn_reduced) if self._ecruns: api.DgsReduction(SampleInputWorkspace=wn_ec_data, EnergyTransferRange=Erange, IncidentBeamNormalisation='ByCurrent', OutputWorkspace=wn_ec_reduced) # Obtain maximum and minimum |Q| values, as well as dQ if none passed self._qbins = [ float(x) for x in re.compile(r'\d+[\.\d+]*').findall(self._qbins_str) ] if len(self._qbins) < 3: if not self._qbins: # insert dQ if empty qbins dE = self._ebins[1] self._qbins.append( numpy.sqrt((Ei + dE) / ENERGY_TO_WAVEVECTOR) - numpy.sqrt(Ei / ENERGY_TO_WAVEVECTOR)) mins, maxs = api.ConvertToMDMinMaxLocal(wn_reduced, Qdimensions='|Q|', dEAnalysisMode='Direct') self._qbins.insert(0, mins[0]) # prepend minimum Q self._qbins.append(maxs[0]) # append maximum Q # Clean up the events files. They take a lot of space in memory api.DeleteWorkspace(wn_data) if self._ecruns: api.DeleteWorkspace(wn_ec_data) # Convert to S(theta,E) ki = numpy.sqrt(Ei / ENERGY_TO_WAVEVECTOR) factor = 1. / 5 # a reasonable (heuristic) value # If dE is the smallest energy transfer considered, # then dQ/ki is the smallest dtheta (in radians) dtheta = factor * self._qbins[1] / ki * (180.0 / numpy.pi) # very small dtheta (<0.15 degrees) prevents interpolation dtheta = max(0.15, dtheta) group_file_os_handle, group_file_name = mkstemp(suffix='.xml') group_file_handle = os.fdopen(group_file_os_handle, 'w') api.GenerateGroupingPowder(InputWorkspace=wn_reduced, AngleStep=dtheta, GroupingFilename=group_file_name) group_file_handle.close() api.GroupDetectors(InputWorkspace=wn_reduced, MapFile=group_file_name, OutputWorkspace=wn_ste) if self._ecruns: api.GroupDetectors(InputWorkspace=wn_ec_reduced, MapFile=group_file_name, OutputWorkspace=wn_ec_ste) # Substract the empty can from the can+sample api.Minus(LHSWorkspace=wn_ste, RHSWorkspace=wn_ec_ste, OutputWorkspace=wn_ste) # Normalize by the vanadium intensity, but before that we need S(theta) # for the vanadium. Recall every detector has all energies into a single # bin, so we get S(theta) instead of S(theta,E) api.GroupDetectors(InputWorkspace=wn_van, MapFile=group_file_name, OutputWorkspace=wn_van_st) os.remove(group_file_name) # no need for this file api.Divide(wn_ste, wn_van_st, OutputWorkspace=wn_sten) api.ClearMaskFlag(Workspace=wn_sten) max_i_theta = 0.0 min_i_theta = 0.0 # Linear interpolation # First, find minimum theta index with a non-zero histogram ws_sten = api.mtd[wn_sten] for i_theta in range(ws_sten.getNumberHistograms()): if ws_sten.dataY(i_theta).any(): min_i_theta = i_theta break # second, find maximum theta with a non-zero histogram for i_theta in range(ws_sten.getNumberHistograms() - 1, -1, -1): if ws_sten.dataY(i_theta).any(): max_i_theta = i_theta break # Scan the region [min_i_theta, max_i_theta] and apply interpolation to # theta angles with no signal whatsoever, S(theta*, E)=0.0 for all energies api.CloneWorkspace(InputWorkspace=wn_sten, OutputWorkspace=wn_steni) ws_steni = api.mtd[wn_steni] i_theta = 1 + min_i_theta while i_theta < max_i_theta: if not ws_steni.dataY(i_theta).any(): nonnull_i_theta_start = i_theta - 1 # angle index of non-null histogram # scan until we find a non-null histogram while not ws_steni.dataY(i_theta).any(): i_theta += 1 nonnull_i_theta_end = i_theta # angle index of non-null histogram # The range [1+nonnull_i_theta_start, nonnull_i_theta_end] # contains only null-histograms. Interpolate! y_start = ws_steni.dataY(nonnull_i_theta_start) y_end = ws_steni.dataY(nonnull_i_theta_end) intercept = y_start slope = (y_end - y_start) / (nonnull_i_theta_end - nonnull_i_theta_start) for null_i_theta in range(1 + nonnull_i_theta_start, nonnull_i_theta_end): ws_steni.dataY(null_i_theta)[:] = intercept + slope * ( null_i_theta - nonnull_i_theta_start) i_theta += 1 # Convert S(theta,E) to S(Q,E), then rebin in |Q| and E to MD workspace api.ConvertToMD(InputWorkspace=wn_steni, QDimensions='|Q|', dEAnalysisMode='Direct', OutputWorkspace=wn_sqe) Qmin = self._qbins[0] Qmax = self._qbins[-1] dQ = self._qbins[1] Qrange = '|Q|,{0},{1},{2}'.format(Qmin, Qmax, int((Qmax - Qmin) / dQ)) Ei_min = self._ebins[0] Ei_max = self._ebins[-1] dE = self._ebins[1] deltaErange = 'DeltaE,{0},{1},{2}'.format(Ei_min, Ei_max, int((Ei_max - Ei_min) / dE)) api.BinMD(InputWorkspace=wn_sqe, AxisAligned=1, AlignedDim0=Qrange, AlignedDim1=deltaErange, OutputWorkspace=wn_sqeb) # Slice the data by transforming to a Matrix2Dworkspace, with deltaE along the vertical axis api.ConvertMDHistoToMatrixWorkspace( InputWorkspace=wn_sqeb, Normalization='NumEventsNormalization', OutputWorkspace=wn_sqes) # Shift the energy axis, since the reported values should be the center # of the bins, instead of the minimum bin boundary ws_sqes = api.mtd[wn_sqes] Eaxis = ws_sqes.getAxis(1) e_shift = self._ebins[1] / 2.0 for i in range(Eaxis.length()): Eaxis.setValue(i, Eaxis.getValue(i) + e_shift) # Normalize each slice if self._snorm: api.Integration(InputWorkspace=wn_sqes, OutputWorkspace=wn_sqesn) api.Divide(LHSWorkspace=wn_sqes, RHSWorkspace=wn_sqesn, OutputWorkspace=wn_sqes) # Clean up workspaces from intermediate steps if self._clean: for name in (wn_van, wn_reduced, wn_ste, wn_van_st, wn_sten, wn_steni, wn_sqe, wn_sqeb, wn_sqesn): api.DeleteWorkspace(name) if api.mtd.doesExist('PreprocessedDetectorsWS'): api.DeleteWorkspace('PreprocessedDetectorsWS') # Ouput some info message = '\n****** SOME OUTPUT INFORMATION ***' + \ '\nEnergy bins: ' + ', '.join(['{0:.2f}'.format(x) for x in self._ebins]) + \ '\nQ bins: ' + ', '.join(['{0:.2f}'.format(x) for x in self._qbins]) + \ '\nTheta bins: {0:.2f} {1:.2f} {2:.2f}'.format(min_i_theta * dtheta, dtheta, max_i_theta * dtheta) logger.notice(message) self.setProperty("OutputWorkspace", api.mtd[wn_sqes])
def CalibrateMerlin(RunNumber): # == Set parameters for calibration == previousDefaultInstrument = mantid.config['default.instrument'] mantid.config['default.instrument'] = "MERLIN" filename = str(RunNumber) # Name of calibration run. rangeLower = 3000 # Integrate counts in each spectra from rangeLower to rangeUpper rangeUpper = 20000 # # Set parameters for ideal tube. Left = 2.0 # Where the left end of tube should be in pixels (target for AP) Centre = 512.5 # Where the centre of the tube should be in pixels (target for CP) Right = 1023.0 # Where the right of the tube should be in pixels (target for BP) ActiveLength = 2.9 # Active length of tube in Metres # Set initial parameters for peak finding ExpectedHeight = 1000.0 # Expected Height of Gaussian Peaks (initial value of fit parameter) ExpectedWidth = 32.0 # Expected width of centre peak in Pixels (initial value of fit parameter) ExpectedPositions = [ 35.0, 512.0, 989.0 ] # Expected positions of the edges and peak in pixels (initial values of fit parameters) # Set what we want to calibrate (e.g whole instrument or one door ) CalibratedComponent = 'MERLIN' # Calibrate door 2 # Get calibration raw file and integrate it print(filename) rawCalibInstWS = mantid.LoadRaw(filename) # 'raw' in 'rawCalibInstWS' means unintegrated. print("Integrating Workspace") CalibInstWS = mantid.Integration(rawCalibInstWS, RangeLower=rangeLower, RangeUpper=rangeUpper) mantid.DeleteWorkspace(rawCalibInstWS) print( "Created workspace (CalibInstWS) with integrated data from run and instrument to calibrate" ) # == Create Objects needed for calibration == ## In the merlin case, the positions are usually given in pixels, instead of being given in ## meters, to convert to meter and put the origin in the center, we have to apply the following ## transformation: ## ## pos = pixel * length/npixels - length/2 = length (pixel/npixels - 1/2) ## ## for merlin: npixels = 1024 knownPos = ActiveLength * (numpy.array([Left, Centre, Right]) / 1024.0 - 0.5) funcForm = 3 * [1] # Get fitting parameters fitPar = TubeCalibFitParams(ExpectedPositions, ExpectedHeight, ExpectedWidth, margin=40) print("Created objects needed for calibration.") # == Get the calibration and put results into calibration table == # also put peaks into PeakFile calibrationTable, peakTable = tube.calibrate(CalibInstWS, CalibratedComponent, knownPos, funcForm, outputPeak=True, fitPar=fitPar, plotTube=list( range(0, 280, 20))) print( "Got calibration (new positions of detectors) and put slit peaks into file TubeDemoMerlin01.txt" ) # == Apply the Calibation == mantid.ApplyCalibration(Workspace=CalibInstWS, CalibrationTable=calibrationTable) print("Applied calibration") # == Save workspace == # mantid.SaveNexusProcessed(CalibInstWS, 'TubeCalibDemoMerlinResult.nxs', "Result of Running TubeCalibDemoMerlin_Simple.py") # print("saved calibrated workspace (CalibInstWS) into Nexus file TubeCalibDemoMerlinResult.nxs") # == Reset default instrument == mantid.config['default.instrument'] = previousDefaultInstrument
def normalise_workspace(ws_name): tmp_norm = sapi.Integration(ws_name) sapi.Divide(LHSWorkspace=ws_name,RHSWorkspace="tmp_norm",OutputWorkspace=ws_name) safe_delete_ws(tmp_norm)
def generate_plots(run_number, workspace, options=None): """ Generate diagnostics plots """ n_x = int( workspace.getInstrument().getNumberParameter("number-of-x-pixels")[0]) n_y = int( workspace.getInstrument().getNumberParameter("number-of-y-pixels")[0]) # X-TOF plot tof_min = workspace.getTofMin() tof_max = workspace.getTofMax() workspace = api.Rebin(workspace, params="%s, 50, %s" % (tof_min, tof_max)) direct_summed = api.RefRoi(InputWorkspace=workspace, IntegrateY=True, NXPixel=n_x, NYPixel=n_y, ConvertToQ=False, YPixelMin=0, YPixelMax=n_y, OutputWorkspace="direct_summed") signal = np.log10(direct_summed.extractY()) tof_axis = direct_summed.extractX()[0] / 1000.0 x_tof_plot = _plot2d(z=signal, y=np.arange(signal.shape[0]), x=tof_axis, x_label="TOF (ms)", y_label="X pixel", title="r%s" % run_number) # X-Y plot _workspace = api.Integration(workspace) signal = np.log10(_workspace.extractY()) z = np.reshape(signal, (n_x, n_y)) xy_plot = _plot2d(z=z.T, x=np.arange(n_x), y=np.arange(n_y), title="r%s" % run_number) # Count per X pixel integrated = api.Integration(direct_summed) integrated = api.Transpose(integrated) signal_y = integrated.readY(0) signal_x = np.arange(len(signal_y)) peak_pixels = _plot1d(signal_x, signal_y, x_label="X pixel", y_label="Counts", title="r%s" % run_number) # TOF distribution workspace = api.SumSpectra(workspace) signal_x = workspace.readX(0) / 1000.0 signal_y = workspace.readY(0) tof_dist = _plot1d(signal_x, signal_y, x_range=None, x_label="TOF (ms)", y_label="Counts", title="r%s" % run_number) return [xy_plot, x_tof_plot, peak_pixels, tof_dist]
def PyExec(self): self._runs = self.getProperty('RunNumbers').value self._vanfile = self.getProperty('Vanadium').value self._ecruns = self.getProperty('EmptyCanRunNumbers').value self._ebins = (self.getProperty('EnergyBins').value).tolist() self._qbins = (self.getProperty('MomentumTransferBins').value).tolist() self._snorm = self.getProperty('NormalizeSlices').value self._clean = self.getProperty('CleanWorkspaces').value wn_sqes = self.getPropertyValue("OutputWorkspace") # workspace names prefix = '' if self._clean: prefix = '__' # "wn" denotes workspace name wn_data = prefix + 'data' # Accumulated data events wn_data_mon = prefix + 'data_monitors' # Accumulated monitors for data wn_van = prefix + 'vanadium' # White-beam vanadium wn_van_st = prefix + 'vanadium_S_theta' wn_reduced = prefix + 'reduced' # data after DGSReduction wn_ste = prefix + 'S_theta_E' # data after grouping by theta angle wn_sten = prefix + 'S_theta_E_normalized' wn_steni = prefix + 'S_theta_E_interp' wn_sqe = prefix + 'S_Q_E' wn_sqeb = prefix + 'S_Q_E_binned' wn_sqesn = prefix + wn_sqes + '_norm' # Empty can files wn_ec_data = prefix + 'ec_data' # Accumulated empty can data wn_ec_data_mon = prefix + 'ec_data_monitors' # Accumulated monitors for empty can wn_ec_reduced = prefix + 'ec_reduced' # empty can data after DGSReduction wn_ec_ste = prefix + 'ec_S_theta_E' # empty can data after grouping by theta angle # Save current configuration facility = config['default.facility'] instrument = config['default.instrument'] datasearch = config["datasearch.searcharchive"] # Allows searching for ARCS run numbers config['default.facility'] = 'SNS' config['default.instrument'] = 'ARCS' config["datasearch.searcharchive"] = "On" try: # Load the vanadium file, assumed to be preprocessed, meaning that # for every detector all events within a particular wide wavelength # range have been rebinned into a single histogram self._load(self._vanfile, wn_van) # Check for white-beam vanadium, true if the vertical chopper is absent (vChTrans==2) if api.mtd[wn_van].run().getProperty('vChTrans').value[0] != 2: raise ValueError("White-vanadium is required") # Load several event files into a single workspace. The nominal incident # energy should be the same to avoid difference in energy resolution self._load(self._runs, wn_data) # Load empty can event files, if present if self._ecruns: self._load(self._ecruns, wn_ec_data) finally: # Recover the default configuration config['default.facility'] = facility config['default.instrument'] = instrument config["datasearch.searcharchive"] = datasearch # Obtain incident energy as the mean of the nominal Ei values. # There is one nominal value for each run number. ws_data = sapi.mtd[wn_data] Ei = ws_data.getRun()['EnergyRequest'].getStatistics().mean Ei_std = ws_data.getRun()['EnergyRequest'].getStatistics( ).standard_deviation # Verify empty can runs were obtained at similar energy if self._ecruns: ws_ec_data = sapi.mtd[wn_ec_data] ec_Ei = ws_ec_data.getRun()['EnergyRequest'].getStatistics().mean if abs(Ei - ec_Ei) > Ei_std: raise RuntimeError( 'Empty can runs were obtained at a significant' + ' different incident energy than the sample runs') # Obtain energy range. If user did not supply a triad # [Estart, Ewidth, Eend] but only Ewidth, then estimate # Estart and End from the nominal energies if len(self._ebins) == 1: ws_data = sapi.mtd[wn_data] Ei = ws_data.getRun()['EnergyRequest'].getStatistics().mean self._ebins.insert(0, -0.5 * Ei) # prepend self._ebins.append(0.95 * Ei) # append # Enforce that the elastic energy (E=0) lies in the middle of the # central bin with an appropriate small shift in the energy range Ei_min_reduced = self._ebins[0] / self._ebins[1] remainder = Ei_min_reduced - int(Ei_min_reduced) if remainder >= 0.0: erange_shift = self._ebins[1] * (0.5 - remainder) else: erange_shift = self._ebins[1] * (-0.5 - remainder) self._ebins[0] += erange_shift # shift minimum energy self._ebins[-1] += erange_shift # shift maximum energy # Convert to energy transfer. Normalize by proton charge. # The output workspace is S(detector-id,E) factor = 0.1 # use a finer energy bin than the one passed (self._ebins[1]) Erange = '{0},{1},{2}'.format(self._ebins[0], factor * self._ebins[1], self._ebins[2]) Ei_calc, T0 = sapi.GetEiT0atSNS(MonitorWorkspace=wn_data_mon, IncidentEnergyGuess=Ei) sapi.MaskDetectors(Workspace=wn_data, MaskedWorkspace=wn_van) # Use vanadium mask sapi.DgsReduction(SampleInputWorkspace=wn_data, SampleInputMonitorWorkspace=wn_data_mon, IncidentEnergyGuess=Ei_calc, UseIncidentEnergyGuess=1, TimeZeroGuess=T0, EnergyTransferRange=Erange, IncidentBeamNormalisation='ByCurrent', OutputWorkspace=wn_reduced) if self._ecruns: sapi.MaskDetectors(Workspace=wn_ec_data, MaskedWorkspace=wn_van) sapi.DgsReduction(SampleInputWorkspace=wn_ec_data, SampleInputMonitorWorkspace=wn_ec_data_mon, IncidentEnergyGuess=Ei_calc, UseIncidentEnergyGuess=1, TimeZeroGuess=T0, EnergyTransferRange=Erange, IncidentBeamNormalisation='ByCurrent', OutputWorkspace=wn_ec_reduced) # Obtain maximum and minimum |Q| values, as well as dQ if none passed if len(self._qbins) < 3: if not self._qbins: # insert dQ if empty qbins. The minimal momentum transfer # is the result on an event where the initial energy was # Ei and the final energy was Ei+dE. dE = self._ebins[1] self._qbins.append( numpy.sqrt((Ei + dE) / ENERGY_TO_WAVEVECTOR) - numpy.sqrt(Ei / ENERGY_TO_WAVEVECTOR)) mins, maxs = sapi.ConvertToMDMinMaxLocal(wn_reduced, Qdimensions='|Q|', dEAnalysisMode='Direct') self._qbins.insert(0, mins[0]) # prepend minimum Q self._qbins.append(maxs[0]) # append maximum Q # Delete sample and empty can event workspaces to free memory. if self._clean: sapi.DeleteWorkspace(wn_data) if self._ecruns: sapi.DeleteWorkspace(wn_ec_data) # Convert to S(theta,E) ki = numpy.sqrt(Ei / ENERGY_TO_WAVEVECTOR) # If dE is the smallest energy transfer considered, # then dQ/ki is the smallest dtheta (in radians) dtheta = self._qbins[1] / ki * (180.0 / numpy.pi) # Use a finer dtheta that the nominal smallest value factor = 1. / 5 # a reasonable (heuristic) value dtheta *= factor # Fix: a very small dtheta (<0.15 degrees) prevents correct interpolation dtheta = max(0.15, dtheta) # Group detectors according to theta angle for the sample runs group_file_os_handle, group_file_name = mkstemp(suffix='.xml') group_file_handle = os.fdopen(group_file_os_handle, 'w') sapi.GenerateGroupingPowder(InputWorkspace=wn_reduced, AngleStep=dtheta, GroupingFilename=group_file_name) group_file_handle.close() sapi.GroupDetectors(InputWorkspace=wn_reduced, MapFile=group_file_name, OutputWorkspace=wn_ste) # Group detectors according to theta angle for the emtpy can run if self._ecruns: sapi.GroupDetectors(InputWorkspace=wn_ec_reduced, MapFile=group_file_name, OutputWorkspace=wn_ec_ste) # Subtract the empty can from the can+sample sapi.Minus(LHSWorkspace=wn_ste, RHSWorkspace=wn_ec_ste, OutputWorkspace=wn_ste) # Normalize by the vanadium intensity, but before that we need S(theta) # for the vanadium. Recall every detector has all energies into a single # bin, so we get S(theta) instead of S(theta,E) sapi.GroupDetectors(InputWorkspace=wn_van, MapFile=group_file_name, OutputWorkspace=wn_van_st) # Divide by vanadium. Make sure it is integrated in the energy domain sapi.Integration(wn_van_st, OutputWorkspace=wn_van_st) sapi.Divide(wn_ste, wn_van_st, OutputWorkspace=wn_sten) sapi.ClearMaskFlag(Workspace=wn_sten) # Temporary file generated by GenerateGroupingPowder to be removed os.remove(group_file_name) # no need for this file os.remove(os.path.splitext(group_file_name)[0] + ".par") max_i_theta = 0.0 min_i_theta = 0.0 # Linear interpolation for those theta values with low intensity # First, find minimum theta index with a non-zero histogram ws_sten = sapi.mtd[wn_sten] for i_theta in range(ws_sten.getNumberHistograms()): if ws_sten.dataY(i_theta).any(): min_i_theta = i_theta break # second, find maximum theta with a non-zero histogram for i_theta in range(ws_sten.getNumberHistograms() - 1, -1, -1): if ws_sten.dataY(i_theta).any(): max_i_theta = i_theta break # Scan a range of theta angles and apply interpolation to those theta angles # with considerably low intensity (gaps) delta_theta = max_i_theta - min_i_theta gaps = self._findGaps(wn_sten, int(min_i_theta + 0.1 * delta_theta), int(max_i_theta - 0.1 * delta_theta)) sapi.CloneWorkspace(InputWorkspace=wn_sten, OutputWorkspace=wn_steni) for gap in gaps: self._interpolate(wn_steni, gap) # interpolate this gap # Convert S(theta,E) to S(Q,E), then rebin in |Q| and E to MD workspace sapi.ConvertToMD(InputWorkspace=wn_steni, QDimensions='|Q|', dEAnalysisMode='Direct', OutputWorkspace=wn_sqe) Qmin = self._qbins[0] Qmax = self._qbins[-1] dQ = self._qbins[1] Qrange = '|Q|,{0},{1},{2}'.format(Qmin, Qmax, int((Qmax - Qmin) / dQ)) Ei_min = self._ebins[0] Ei_max = self._ebins[-1] dE = self._ebins[1] deltaErange = 'DeltaE,{0},{1},{2}'.format(Ei_min, Ei_max, int((Ei_max - Ei_min) / dE)) sapi.BinMD(InputWorkspace=wn_sqe, AxisAligned=1, AlignedDim0=Qrange, AlignedDim1=deltaErange, OutputWorkspace=wn_sqeb) # Slice the data by transforming to a Matrix2Dworkspace, # with deltaE along the vertical axis sapi.ConvertMDHistoToMatrixWorkspace( InputWorkspace=wn_sqeb, Normalization='NumEventsNormalization', OutputWorkspace=wn_sqes) # Ensure correct units sapi.mtd[wn_sqes].getAxis(0).setUnit("MomentumTransfer") sapi.mtd[wn_sqes].getAxis(1).setUnit("DeltaE") # Shift the energy axis, since the reported values should be the center # of the bins, instead of the minimum bin boundary ws_sqes = sapi.mtd[wn_sqes] Eaxis = ws_sqes.getAxis(1) e_shift = self._ebins[1] / 2.0 for i in range(Eaxis.length()): Eaxis.setValue(i, Eaxis.getValue(i) + e_shift) # Normalize each slice, if requested if self._snorm: sapi.Integration(InputWorkspace=wn_sqes, OutputWorkspace=wn_sqesn) sapi.Divide(LHSWorkspace=wn_sqes, RHSWorkspace=wn_sqesn, OutputWorkspace=wn_sqes) # Clean up workspaces from intermediate steps if self._clean: for name in (wn_van, wn_reduced, wn_ste, wn_van_st, wn_sten, wn_steni, wn_sqe, wn_sqeb, wn_sqesn, 'PreprocessedDetectorsWS'): if sapi.mtd.doesExist(name): sapi.DeleteWorkspace(name) # Ouput some info as a Notice in the log ebins = ', '.join(['{0:.2f}'.format(x) for x in self._ebins]) qbins = ', '.join(['{0:.2f}'.format(x) for x in self._qbins]) tbins = '{0:.2f} {1:.2f} {2:.2f}'.format(min_i_theta * dtheta, dtheta, max_i_theta * dtheta) message = '\n****** SOME OUTPUT INFORMATION ***' + \ '\nEnergy bins: ' + ebins + \ '\nQ bins: ' + qbins + \ '\nTheta bins: '+tbins kapi.logger.notice(message) self.setProperty("OutputWorkspace", sapi.mtd[wn_sqes])
def CalibrateWish(RunNumber, PanelNumber): ''' :param RunNumber: is the run number of the calibration. :param PanelNumber: is a string of two-digit number of the panel being calibrated ''' # == Set parameters for calibration == previousDefaultInstrument = mantid.config['default.instrument'] mantid.config['default.instrument'] = "WISH" filename = str(RunNumber) CalibratedComponent = 'WISH/panel' + PanelNumber # Get calibration raw file and integrate it print("Loading", filename) rawCalibInstWS = mantid.Load( filename) # 'raw' in 'rawCalibInstWS' means unintegrated. CalibInstWS = mantid.Integration(rawCalibInstWS, RangeLower=1, RangeUpper=20000) mantid.DeleteWorkspace(rawCalibInstWS) print( "Created workspace (CalibInstWS) with integrated data from run and instrument to calibrate" ) # Give y-positions of slit points (gotten for converting first tube's slit point to Y) # WISH instrument has a particularity. It is composed by a group of upper tubes and lower tubes, # they are disposed 3 milimiters in difference one among the other lower_tube = numpy.array( [-0.41, -0.31, -0.21, -0.11, -0.02, 0.09, 0.18, 0.28, 0.39]) upper_tube = numpy.array(lower_tube + 0.003) funcForm = 9 * [1] # 9 gaussian peaks print("Created objects needed for calibration.") # Get the calibration and put it into the calibration table # calibrate the lower tubes calibrationTable, peakTable = tube.calibrate(CalibInstWS, CalibratedComponent, lower_tube, funcForm, rangeList=list(range(0, 76)), outputPeak=True) # calibrate the upper tubes calibrationTable, peakTable = tube.calibrate( CalibInstWS, CalibratedComponent, upper_tube, funcForm, rangeList=list(range(76, 152)), calibTable=calibrationTable, # give the calibration table to append data outputPeak=peakTable # give peak table to append data ) print("Got calibration (new positions of detectors)") # Apply the calibration mantid.ApplyCalibration(Workspace=CalibInstWS, PositionTable=calibrationTable) print("Applied calibration") # == Save workspace == # uncomment these lines to save the workspace # nexusName = "TubeCalibDemoWish"+PanelNumber+"Result.nxs" # SaveNexusProcessed( CalibInstWS, 'TubeCalibDemoWishResult.nxs',"Result of Running TubeCalibWishMerlin_Simple.py") # print "saved calibrated workspace (CalibInstWS) into Nexus file",nexusName # == Reset dafault instrument == mantid.config['default.instrument'] = previousDefaultInstrument
def calibrateMerlin(filename): # == Set parameters for calibration == rangeLower = 3000 # Integrate counts in each spectra from rangeLower to rangeUpper rangeUpper = 20000 # # Get calibration raw file and integrate it rawCalibInstWS = mantid.LoadRaw(filename) # 'raw' in 'rawCalibInstWS' means unintegrated. print("Integrating Workspace") CalibInstWS = mantid.Integration(rawCalibInstWS, RangeLower=rangeLower, RangeUpper=rangeUpper) mantid.DeleteWorkspace(rawCalibInstWS) print("Created workspace (CalibInstWS) with integrated data from run and instrument to calibrate") # the known positions are given in pixels inside the tubes and transformed to provide the positions # with the center of the tube as the origin knownPositions = 2.92713867188 * (numpy.array([27.30074322, 92.5, 294.65178585, 362.37861919, 512.77103043, 663.41425323, 798.3223896, 930.9, 997.08480835]) / 1024 - 0.5) funcForm = numpy.array([2, 2, 1, 1, 1, 1, 1, 2, 2], numpy.int8) # The calibration will follow different steps for sets of tubes # For the door9, the best points to define the known positions are the 1st edge, 5 peaks, last edge. points7 = knownPositions[[0, 2, 3, 4, 5, 6, 8]] points7func = funcForm[[0, 2, 3, 4, 5, 6, 8]] door9pos = points7 door9func = points7func CalibratedComponent = 'MERLIN/door9' # door9 # == Get the calibration and put results into calibration table == # also put peaks into PeakFile calibrationTable, peakTable = tube.calibrate(CalibInstWS, CalibratedComponent, door9pos, door9func, outputPeak=True, margin=30, rangeList=list(range(20)) # because 20, 21, 22, 23 are defective detectors ) print("Got calibration (new positions of detectors) and put slit peaks into file TubeDemoMerlin01.txt") analisePeakTable(peakTable, 'door9_tube1_peaks') # For the door8, the best points to define the known positions are the 1st edge, 5 peaks, last_edge door8pos = points7 door8func = points7func CalibratedComponent = 'MERLIN/door8' calibrationTable, peakTable = tube.calibrate(CalibInstWS, CalibratedComponent, door8pos, door8func, outputPeak=True, # change to peakTable to append to peakTable calibTable=calibrationTable, margin=30) analisePeakTable(peakTable, 'door8_peaks') # For the doors 7,6,5,4, 2, 1 we may use the 9 points doorpos = knownPositions doorfunc = funcForm CalibratedComponent = ['MERLIN/door%d' % (i) for i in [7, 6, 5, 4, 2, 1]] calibrationTable, peakTable = tube.calibrate(CalibInstWS, CalibratedComponent, doorpos, doorfunc, outputPeak=True, calibTable=calibrationTable, margin=30) analisePeakTable(peakTable, 'door1to7_peaks') # The door 3 is a special case, because it is composed by diffent kind of tubes. # door 3 tubes: 5_8, 5_7, 5_6, 5_5, 5_4, 5_3, 5_2, 5_1, 4_8, 4_7, 4_6, 4_5, 4_4, 4_3, 4_2, 4_1, 3_8, 3_7, 3_6, 3_5, 3_4 # obeys the same rules as the doors 7, 6, 5, 4, 2, 1 # For the tubes 3_3, 3_2, 3_1 -> it is better to skip the central peak # For the tubes 1_x (smaller tube below), it is better to take the final part of known positions: peak4,peak5,edge6,edge7 # For the tubes 2_x (smaller tube above, it is better to take the first part of known positions: edge1, edge2, peak1,peak2 # NOTE: the smaller tubes they have length = 1.22879882813, but 1024 detectors # so we have to correct the known positiosn by multiplying by its length and dividing by the longer dimension from tube_calib_fit_params import TubeCalibFitParams # calibrating tubes 1_x CalibratedComponent = ['MERLIN/door3/tube_1_%d' % (i) for i in range(1, 9)] half_diff_center = ( 2.92713867188 - 1.22879882813) / 2 # difference among the expected center position for # both tubes here a little bit of attempts is necessary. # The effective center position and length is different for the calibrated tube, that is the reason, # the calibrated values of the smaller tube does not seems aligned with the others. By, finding the # 'best' half_diff_center value, the alignment occurs nicely. half_diff_center = 0.835 # # the knownpositions were given with the center of the bigger tube as origin, to convert # to the center of the upper tube as origin is necessary to subtract them with the half_diff_center doorpos = knownPositions[[5, 6, 7, 8]] - half_diff_center doorfunc = [1, 1, 2, 2] # for the smal tubes, automatically searching for the peak position in pixel was not working quite well, # so we will give the approximate position for these tubes through fitPar argument fitPar = TubeCalibFitParams([216, 527, 826, 989]) fitPar.setAutomatic(True) calibrationTable, peakTable = tube.calibrate(CalibInstWS, CalibratedComponent, doorpos, doorfunc, outputPeak=True, fitPar=fitPar, calibTable=calibrationTable, margin=30) analisePeakTable(peakTable, 'door3_tube1_peaks') # calibrating tubes 2_x CalibratedComponent = ['MERLIN/door3/tube_2_%d' % (i) for i in range(1,9)] # the knownpositions were given with the center of the bigger tube as origin, to convert # to the center of the lower tube as origin is necessary to sum them with (len_big - len_small)/2 doorpos = knownPositions[[0, 1, 2, 3]] + half_diff_center doorfunc = [2, 2, 1, 1] # for the smal tubes, automatically searching for the peak position in pixel was not working quite well, # so we will give the approximate position for these tubes through fitPar argument fitPar = TubeCalibFitParams([50, 202, 664, 815]) fitPar.setAutomatic(True) calibrationTable, peakTable = tube.calibrate(CalibInstWS, CalibratedComponent, doorpos, doorfunc, outputPeak=True, calibTable=calibrationTable, fitPar=fitPar, margin=30) analisePeakTable(peakTable, 'door3_tube2_peaks') # calibrating tubes 3_3,3_2,3_1 CalibratedComponent = ['MERLIN/door3/tube_3_%d' % (i) for i in [1, 2, 3]] doorpos = knownPositions[[0, 1, 2, 3, 5, 6, 7, 8]] doorfunc = funcForm[[0, 1, 2, 3, 5, 6, 7, 8]] calibrationTable, peakTable = tube.calibrate(CalibInstWS, CalibratedComponent, doorpos, doorfunc, outputPeak=True, calibTable=calibrationTable, margin=30) analisePeakTable(peakTable, 'door3_123_peaks') # calibrating others inside door3 # 5_8, 5_7, 5_6, 5_5, 5_4, 5_3, 5_2, 5_1, 4_8, 4_7, 4_6, 4_5, 4_4, 4_3, 4_2, 4_1, 3_8, 3_7, 3_6, 3_5, 3_4 part_3 = ['MERLIN/door3/tube_3_%d' % (i) for i in [4, 5, 6, 7, 8]] part_4 = ['MERLIN/door3/tube_4_%d' % (i) for i in range(1, 9)] part_5 = ['MERLIN/door3/tube_5_%d' % (i) for i in range(1, 9)] CalibratedComponent = part_3 + part_4 + part_5 doorpos = knownPositions doorfunc = funcForm calibrationTable, peakTable = tube.calibrate(CalibInstWS, CalibratedComponent, doorpos, doorfunc, outputPeak=True, calibTable=calibrationTable, margin=30) analisePeakTable(peakTable, 'door3_peaks') # == Apply the Calibation == mantid.ApplyCalibration(Workspace=CalibInstWS, CalibrationTable=calibrationTable) print("Applied calibration")