diff --git a/scripts/HFIR_4Circle_Reduction.py b/scripts/HFIR_4Circle_Reduction.py new file mode 100644 index 0000000000000000000000000000000000000000..89742c6bfa9f75bb50d994cf3148f7d9e1c84476 --- /dev/null +++ b/scripts/HFIR_4Circle_Reduction.py @@ -0,0 +1,17 @@ +#pylint: disable=invalid-name +from HFIR_4Circle_Reduction import reduce4circleGUI +from PyQt4 import QtGui +import sys + +def qapp(): + if QtGui.QApplication.instance(): + _app = QtGui.QApplication.instance() + else: + _app = QtGui.QApplication(sys.argv) + return _app + +app = qapp() + +reducer = reduce4circleGUI.MainWindow() #the main ui class in this file is called MainWindow +reducer.show() +app.exec_() diff --git a/scripts/HFIR_4Circle_Reduction/reduce4circleControl.py b/scripts/HFIR_4Circle_Reduction/reduce4circleControl.py new file mode 100644 index 0000000000000000000000000000000000000000..69d7f214c3dd5477bd07e68cbda8fa1660dc3ded --- /dev/null +++ b/scripts/HFIR_4Circle_Reduction/reduce4circleControl.py @@ -0,0 +1,1234 @@ +#pylint: disable=C0302,C0103,R0902,R0904,R0913,W0212,W0621,R0912 +################################################################################ +# +# Controlling class +# +# == Data download and storage == +# - Local data storage (local-mode) +# - Download from internet to cache (download-mode) +# +################################################################################ +import os +import urllib2 + +import numpy + +import mantid +import mantid.simpleapi as api +from mantid.api import AnalysisDataService + +DebugMode = True + +DET_X_SIZE = 256 +DET_Y_SIZE = 256 + + +class PeakInfo(object): + """ Class containing a peak's information for GUI + In order to manage some operations for a peak + It does not contain peak workspace but will hold + """ + def __init__(self, parent): + """ Init + """ + assert isinstance(parent, CWSCDReductionControl) + + # Define class variable + self._myParent = parent + self._myHKL = [0, 0, 0] + + self._myExpNumber = None + self._myScanNumber = None + self._myPtNumber = None + + self._myPeakWSKey = (None, None, None) + self._myPeakIndex = None + self._myPeakWS = None + + self._myLastPeakUB = None + + return + + def get_peak_workspace(self): + """ + Get peak workspace related + :return: + """ + assert isinstance(self._myPeakWSKey, tuple) + exp_number, scan_number, pt_number = self._myPeakWSKey + return self._myParent.get_ub_peak_ws(exp_number, scan_number, pt_number)[1] + + def set_from_run_info(self, exp_number, scan_number, pt_number): + """ Set from run information with parent + :param exp_number: + :param scan_number: + :param pt_number: + :return: + """ + assert isinstance(exp_number, int) + assert isinstance(scan_number, int) + assert isinstance(pt_number, int) + + status, peak_ws = self._myParent.get_ub_peak_ws(exp_number, scan_number, pt_number) + if status is True: + self._myPeakWSKey = (exp_number, scan_number, pt_number) + self._myPeakIndex = 0 + self._myPeakWS = peak_ws.getPeak(0) + else: + error_message = peak_ws + return False, error_message + + self._myExpNumber = exp_number + self._myScanNumber = scan_number + self._myPtNumber = pt_number + + return True, '' + + def set_from_peak_ws(self, peak_ws, peak_index): + """ + Set from peak workspace + :param peak_ws: + :return: + """ + # Check + assert isinstance(peak_ws, mantid.dataobjects.PeaksWorkspace) + + # Get peak + try: + peak = peak_ws.getPeak(peak_index) + except RuntimeError as run_err: + raise RuntimeError(run_err) + + self._myPeakWS = peak + + return + + def set_hkl(self, h, k, l): + """ + Set HKL to this peak Info + :return: + """ + assert isinstance(h, float) + assert isinstance(k, float) + assert isinstance(l, float) + + self._myHKL[0] = h + self._myHKL[1] = k + self._myHKL[2] = l + + return + + def getExpInfo(self): + """ + + :return: 3-tuple of integer as experiment number, scan number and Pt number + """ + return self._myExpNumber, self._myScanNumber, self._myPtNumber + + def get_hkl_peak_ws(self): + """ Get HKL from PeakWorkspace + :return: + """ + hkl = self._myPeakWS.getHKL() + + return hkl.getX(), hkl.getY(), hkl.getZ() + + def getHKL(self): + """ + Get HKL set to this object by client + :return: 3-tuple of float as (H, K, L) + """ + hkl = self._myHKL + + return hkl[0], hkl[1], hkl[2] + + def getQSample(self): + """ + + :return: 3-tuple of floats as Qx, Qy, Qz + """ + q_sample = self._myPeakWS.getQSampleFrame() + return q_sample.getX(), q_sample.getY(), q_sample.getZ() + + +class CWSCDReductionControl(object): + """ Controlling class for reactor-based single crystal diffraction reduction + """ + def __init__(self, instrument_name=None): + """ init + """ + if isinstance(instrument_name, str): + self._instrumentName = instrument_name + elif instrument_name is None: + self._instrumentName = '' + else: + raise RuntimeError('Instrument name %s of type %s is not allowed.' % (str(instrument_name), + str(type(instrument_name)))) + + # Experiment number, data storage + # No Use/Confusing: self._expNumber = 0 + + self._dataDir = None + self._workDir = '/tmp' + + self._myServerURL = '' + + # Some set up + self._expNumber = None + + # Container for MDEventWorkspace for each Pt. + self._myPtMDDict = dict() + # Container for loaded workspaces + self._mySpiceTableDict = {} + # Container for loaded raw pt workspace + self._myRawDataWSDict = dict() + # Container for PeakWorkspaces for calculating UB matrix + self._myUBPeakWSDict = dict() + # Container for UB matrix + self._myUBMatrixDict = dict() + + # Peak Info + self._myPeakInfoDict = dict() + # Last UB matrix calculated + self._myLastPeakUB = None + # Flag for data storage + self._cacheDataOnly = False + + # A dictionary to manage all loaded and processed MDEventWorkspaces + # self._expDataDict = {} + + return + + def add_peak_info(self, exp_number, scan_number, pt_number): + """ Add a peak info for calculating UB matrix + :param exp_number: + :param scan_number: + :param pt_number: + :return: (boolean, PeakInfo/string) + """ + has_peak_ws, peak_ws = self.get_ub_peak_ws(exp_number, scan_number, pt_number) + if has_peak_ws is False: + err_msg = 'No peak workspace found for Exp %s Scan %s Pt %s' % ( + exp_number, scan_number, pt_number) + print '\n[DB] Fail to add peak info due to %s\n' % err_msg + return False, err_msg + + if peak_ws.rowCount() > 1: + err_msg = 'There are more than 1 peak in PeaksWorkspace.' + print '\n[DB] Fail to add peak info due to %s\n' % err_msg + return False, err_msg + + peak_info = PeakInfo(self) + peak_info.set_from_run_info(exp_number, scan_number, pt_number) + + # Add to data management + self._myPeakInfoDict[(exp_number, scan_number, pt_number)] = peak_info + + return True, peak_info + + def calculate_ub_matrix(self, peak_info_list, a, b, c, alpha, beta, gamma): + """ + Calculate UB matrix + + Set Miller index from raw data in Workspace2D. + :param peakws: + :param a: + :param b: + :param c: + :param alpha: + :param beta: + :param gamma: + :return: + """ + # Check + assert isinstance(peak_info_list, list) + for peak_info in peak_info_list: + if isinstance(peak_info, PeakInfo) is False: + raise NotImplementedError('Input PeakList is of type %s.' % str(type(peak_info_list[0]))) + assert isinstance(peak_info, PeakInfo) + + if len(peak_info_list) < 2: + return False, 'Too few peaks are input to calculate UB matrix. Must be >= 2.' + + # Construct a new peak workspace by combining all single peak + ub_peak_ws_name = 'Temp_UB_Peak' + ub_peak_ws = api.CloneWorkspace(InputWorkspace=peak_info_list[0].get_peak_workspace(), + OutputWorkspace=ub_peak_ws_name) + + for i_peak_info in xrange(1, len(peak_info_list)): + # Set HKL as optional + peak_ws = peak_info_list[i_peak_info].get_peak_workspace() + + # Combine peak workspace + ub_peak_ws = api.CombinePeaksWorkspaces(LHSWorkspace=ub_peak_ws, + RHSWorkspace=peak_ws, + CombineMatchingPeaks=False, + OutputWorkspace=ub_peak_ws_name) + # END-FOR(i_peak_info) + + # Calculate UB matrix + api.CalculateUMatrix(PeaksWorkspace=ub_peak_ws_name, + a=a, b=b, c=c, alpha=alpha, beta=beta, gamma=gamma) + # ub_peak_ws = AnalysisDataService.retrieve(ub_peak_ws_name) + + ub_matrix = ub_peak_ws.sample().getOrientedLattice().getUB() + + self._myLastPeakUB = ub_peak_ws + + return True, ub_matrix + + def does_raw_loaded(self, exp_no, scan_no, pt_no): + """ + Check whether the raw Workspace2D for a Pt. exists + :param exp_no: + :param scan_no: + :param pt_no: + :return: + """ + return (exp_no, scan_no, pt_no) in self._myRawDataWSDict + + def does_spice_loaded(self, exp_no, scan_no): + """ Check whether a SPICE file has been loaded + :param exp_no: + :param scan_no: + :return: + """ + return (exp_no, scan_no) in self._mySpiceTableDict + + def download_spice_file(self, exp_number, scan_number, over_write): + """ + Download a scan/pt data from internet + :param exp_number: experiment number + :param scan_number: + :return: + """ + # Check + if exp_number is None: + exp_number = self._expNumber + assert isinstance(exp_number, int) + assert isinstance(scan_number, int) + + # Generate the URL for SPICE data file + file_url = '%sexp%d/Datafiles/HB3A_exp%04d_scan%04d.dat' % (self._myServerURL, exp_number, + exp_number, scan_number) + file_name = '%s_exp%04d_scan%04d.dat' % (self._instrumentName, exp_number, scan_number) + file_name = os.path.join(self._dataDir, file_name) + if os.path.exists(file_name) is True and over_write is False: + return True, file_name + + # Download + try: + api.DownloadFile(Address=file_url, Filename=file_name) + except RuntimeError as run_err: + return False, str(run_err) + + # Check file exist? + if os.path.exists(file_name) is False: + return False, "Unable to locate downloaded file %s." % file_name + + return True, file_name + + def download_spice_xml_file(self, scan_no, pt_no, exp_no=None, overwrite=False): + """ Download a SPICE XML file for one measurement in a scan + :param scan_no: + :param pt_no: + :param exp_no: + :param overwrite: + :return: tuple (boolean, local file name/error message) + """ + # Experiment number + if exp_no is None: + exp_no = self._expNumber + + # Form the target file name and path + xml_file_name = '%s_exp%d_scan%04d_%04d.xml' % (self._instrumentName, exp_no, scan_no, pt_no) + local_xml_file_name = os.path.join(self._dataDir, xml_file_name) + if os.path.exists(local_xml_file_name) is True and overwrite is False: + return True, local_xml_file_name + + # Generate the URL for XML file + xml_file_url = '%sexp%d/Datafiles/%s' % (self._myServerURL, exp_no, xml_file_name) + + # Download + try: + api.DownloadFile(Address=xml_file_url, + Filename=local_xml_file_name) + except RuntimeError as run_err: + return False, 'Unable to download Detector XML file %s dur to %s.' % (xml_file_name, str(run_err)) + + # Check file exist? + if os.path.exists(local_xml_file_name) is False: + return False, "Unable to locate downloaded file %s."%(local_xml_file_name) + + return True, local_xml_file_name + + def download_data_set(self, scan_list, overwrite=False): + """ + Download data set including (1) spice file for a scan and (2) XML files for measurements + :param scan_list: + :return: + """ + # Check + if self._expNumber is None: + raise RuntimeError('Experiment number is not set up for controller.') + + error_message = '' + + for scan_no in scan_list: + # Download single spice file for a run + status, ret_obj = self.download_spice_file(exp_number=self._expNumber, + scan_number=scan_no, + over_write=overwrite) + + # Reject if SPICE file cannot download + if status is False: + error_message += '%s\n' % ret_obj + continue + + # Load SPICE file to Mantid + spice_file_name = ret_obj + status, ret_obj = self.load_spice_scan_file(self._expNumber, scan_no, spice_file_name) + if status is False: + error_message = ret_obj + return False, error_message + else: + spice_table = self._mySpiceTableDict[(self._expNumber, scan_no)] + assert spice_table + pt_no_list = self._get_pt_list_from_spice_table(spice_table) + + # Download all single-measurement file + for pt_no in pt_no_list: + status, ret_obj = self.download_spice_xml_file(scan_no, pt_no, overwrite=overwrite) + if status is False: + error_message += '%s\n' % ret_obj + # END-FOR + # END-FOR (scan_no) + + return True, error_message + + def existDataFile(self, scanno, ptno): + """ + Check whether data file for a scan or pt number exists + :param scanno: + :param ptno: + :return: + """ + # Check spice file + spice_file_name = '%s_exp%04d_scan%04d.dat'%(self._instrumentName, + self._expNumber, scanno) + spice_file_name = os.path.join(self._dataDir, spice_file_name) + if os.path.exists(spice_file_name) is False: + return False, 'Spice data file %s cannot be found.'% spice_file_name + + # Check xml file + xmlfilename = '%s_exp%d_scan%04d_%04d.xml'%(self._instrumentName, self._expNumber, + scanno, ptno) + xmlfilename = os.path.join(self._dataDir, xmlfilename) + if os.path.exists(xmlfilename) is False: + return (False, "Pt. XML file %s cannot be found."%(xmlfilename)) + + return True, "" + + def find_peak(self, exp_number, scan_number, pt_number): + """ Find 1 peak in sample Q space for UB matrix + :param scan_number: + :param pt_number: + :return:tuple as (boolean, object) such as (false, error message) and (true, PeakInfo object) + + This part will be redo as 11847_Load_HB3A_Experiment + """ + # Check + assert isinstance(exp_number, int) + assert isinstance(scan_number, int) + assert isinstance(pt_number, int) + + # Download or make sure data are there + status_sp, err_msg_sp = self.download_spice_file(exp_number, scan_number, over_write=False) + status_det, err_msg_det = self.download_spice_xml_file(scan_number, pt_number, exp_number, + overwrite=False) + if status_sp is False or status_det is False: + return False, 'Unable to access data (1) %s (2) %s' % (err_msg_sp, err_msg_det) + + # Collect reduction information: example + exp_info_ws_name = get_pt_info_ws_name(exp_number, scan_number) + virtual_instrument_info_table_name = get_virtual_instrument_table_name(exp_number, scan_number, pt_number) + api.CollectHB3AExperimentInfo( + ExperimentNumber=exp_number, + GenerateVirtualInstrument=False, + ScanList=[scan_number], + PtLists=[-1, pt_number], + DataDirectory=self._dataDir, + GetFileFromServer=False, + Detector2ThetaTolerance=0.01, + OutputWorkspace=exp_info_ws_name, + DetectorTableWorkspace=virtual_instrument_info_table_name) + + # Load XML file to MD + pt_md_ws_name = get_single_pt_md_name(exp_number, scan_number, pt_number) + api.ConvertCWSDExpToMomentum(InputWorkspace=exp_info_ws_name, + CreateVirtualInstrument=False, + OutputWorkspace=pt_md_ws_name, + Directory=self._dataDir) + + # Find peak in Q-space + pt_peak_ws_name = get_single_pt_peak_ws_name(exp_number, scan_number, pt_number) + api.FindPeaksMD(InputWorkspace=pt_md_ws_name, MaxPeaks=10, + DensityThresholdFactor=0.01, OutputWorkspace=pt_peak_ws_name) + peak_ws = AnalysisDataService.retrieve(pt_peak_ws_name) + pt_md_ws = AnalysisDataService.retrieve(pt_md_ws_name) + self._myPtMDDict[(exp_number, scan_number, pt_number)] = pt_md_ws + + num_peaks = peak_ws.getNumberPeaks() + if num_peaks != 1: + err_msg = 'Find %d peak from scan %d pt %d. ' \ + 'For UB matrix calculation, 1 and only 1 peak is allowed' % (num_peaks, scan_number, pt_number) + return False, err_msg + else: + self._add_ub_peak_ws(exp_number, scan_number, pt_number, peak_ws) + status, ret_obj = self.add_peak_info(exp_number, scan_number, pt_number) + if status is True: + pass + # peak_info = ret_obj + # peak_info.set_md_ws(pt_md_ws) + else: + err_msg = ret_obj + return False, err_msg + + return True, '' + + def get_experiment(self): + """ + Get experiment number + :return: + """ + return self._expNumber + + def get_pt_numbers(self, exp_no, scan_no, load_spice_scan=False): + """ Get Pt numbers (as a list) for a scan in an experiment + :param exp_no: + :param scan_no: + :param load_spice_scan: + :return: (Boolean, Object) as (status, pt number list/error message) + """ + # Check + if exp_no is None: + exp_no = self._expNumber + assert isinstance(exp_no, int) + assert isinstance(scan_no, int) + + # Get workspace + table_ws = self._get_spice_workspace(exp_no, scan_no) + if table_ws is None: + if load_spice_scan is False: + return False, 'Spice file for Exp %d Scan %d is not loaded.' % (exp_no, scan_no) + else: + status, error_message = self.load_spice_scan_file(exp_no, scan_no) + if status is True: + table_ws = self._get_spice_workspace(exp_no, scan_no) + if table_ws is None: + raise NotImplementedError('Logic error! Cannot happen!') + else: + return False, 'Unable to load Spice file for Exp %d Scan %d due to %s.' % ( + exp_no, scan_no, error_message) + + col_name_list = table_ws.getColumnNames() + i_pt = col_name_list.index('Pt.') + if i_pt < 0 or i_pt >= len(col_name_list): + return False, 'No column with name Pt. can be found in SPICE table.' + + pt_number_list = [] + num_rows = table_ws.rowCount() + for i in xrange(num_rows): + pt_number = table_ws.cell(i, i_pt) + pt_number_list.append(pt_number) + + return True, pt_number_list + + def get_raw_detector_counts(self, exp_no, scan_no, pt_no): + """ + Get counts on raw detector + :param scan_no: + :param pt_no: + :return: boolean, 2D numpy data + """ + # Get workspace (in memory or loading) + raw_ws = self.get_raw_data_workspace(exp_no, scan_no, pt_no) + if raw_ws is None: + return False, 'Raw data for Exp %d Scan %d Pt %d is not loaded.' % (exp_no, scan_no, pt_no) + + # Convert to numpy array + array2d = numpy.ndarray(shape=(DET_X_SIZE, DET_Y_SIZE), dtype='float') + for i in xrange(DET_X_SIZE): + for j in xrange(DET_Y_SIZE): + array2d[i][j] = raw_ws.readY(i * DET_X_SIZE + j)[0] + + return array2d + + def get_sample_log_value(self, exp_number, scan_number, pt_number, log_name): + """ + Get sample log's value + :param exp_number: + :param scan_number:167 + :param pt_number: + :param log_name: + :return: float + """ + assert isinstance(exp_number, int) + assert isinstance(scan_number, int) + assert isinstance(pt_number, int) + assert isinstance(log_name, str) + try: + md_ws = self._myPtMDDict[(exp_number, scan_number, pt_number)] + except KeyError as ke: + return 'Unable to find log value %s due to %s.' % (log_name, str(ke)) + + return md_ws.getExperimentInfo(0).run().getProperty(log_name).value + + def get_peak_info(self, exp_number, scan_number, pt_number): + """ + get peak information instance + :param exp_number: experiment number. if it is None, then use the current exp number + :param scan_number: + :param pt_number: + :return: + """ + # Check for type + if exp_number is None: + exp_number = self._expNumber + + assert isinstance(exp_number, int) + assert isinstance(scan_number, int) + assert isinstance(pt_number, int) + + # Check for existence + if (exp_number, scan_number, pt_number) not in self._myUBPeakWSDict: # self._myPeakInfoDict: + err_msg = 'Unable to find PeakInfo for Exp %d Scan %d Pt %d. ' \ + 'Existing keys are %s' % (exp_number, scan_number, pt_number, + str(self._myUBPeakWSDict.keys())) + return False, err_msg + + return True, self._myPeakInfoDict[(exp_number, scan_number, pt_number)] + + def get_ub_peak_ws(self, exp_number, scan_number, pt_number): + """ + Get peak workspace for the peak picked to calculate UB matrix + :param exp_number: + :param scan_number: + :param pt_number: + :return: + """ + assert isinstance(exp_number, int) + assert isinstance(scan_number, int) + assert isinstance(pt_number, int) + + if (exp_number, scan_number, pt_number) not in self._myUBPeakWSDict: + return False, 'Exp %d Scan %d Pt %d has no peak workspace.' % (exp_number, + scan_number, + pt_number) + + return True, self._myUBPeakWSDict[(exp_number, scan_number, pt_number)] + + def index_peak(self, ub_matrix, scan_number, pt_number): + """ Index peaks in a Pt. + :param ub_matrix: numpy.ndarray (3, 3) + :param scan_number: + :param pt_number: + :return: boolean, object (list of HKL or error message) + """ + # Check + assert isinstance(ub_matrix, numpy.ndarray) + assert ub_matrix.shape == (3, 3) + assert isinstance(scan_number, int) + assert isinstance(pt_number, int) + + # Find out the peak workspace + exp_num = self._expNumber + if self._myUBPeakWSDict.has_key((exp_num, scan_number, pt_number)) is False: + err_msg = 'No PeakWorkspace is found for exp %d scan %d pt %d' % ( + exp_num, scan_number, pt_number) + return False, err_msg + + peak_ws = self._myUBPeakWSDict[(exp_num, scan_number, pt_number)] + ub_1d = ub_matrix.reshape(9,) + print '[DB] UB matrix = ', ub_1d + + # Set UB + api.SetUB(Workspace=peak_ws, UB=ub_1d) + + # Note: IndexPeaks and CalcualtePeaksHKL do the same job + # while IndexPeaks has more control on the output + num_peak_index, error = api.IndexPeaks(PeaksWorkspace=peak_ws, + Tolerance=0.4, + RoundHKLs=False) + + if num_peak_index == 0: + return False, 'No peak can be indexed.' + elif num_peak_index > 1: + raise RuntimeError('Case for PeaksWorkspace containing more than 1 peak is not ' + 'considered. Contact developer for this issue.') + else: + hkl_v3d = peak_ws.getPeak(0).getHKL() + hkl = [hkl_v3d.X(), hkl_v3d.Y(), hkl_v3d.Z()] + + return True, (hkl, error) + + def load_spice_scan_file(self, exp_no, scan_no, spice_file_name=None): + """ + Load a SPICE scan file to table workspace and run information matrix workspace. + :param scan_no: + :param spice_file_name: + :return: status (boolean), error message (string) + """ + # Default for exp_no + if exp_no is None: + exp_no = self._expNumber + + # Check whether the workspace has been loaded + assert isinstance(exp_no, int) + assert isinstance(scan_no, int) + out_ws_name = get_spice_table_name(exp_no, scan_no) + if (exp_no, scan_no) in self._mySpiceTableDict: + return True, out_ws_name + + # Form standard name for a SPICE file if name is not given + if spice_file_name is None: + spice_file_name = os.path.join(self._dataDir, get_spice_file_name(exp_no, scan_no)) + + # Download SPICE file if necessary + if os.path.exists(spice_file_name) is False: + self.download_spice_file(exp_no, scan_no, over_write=True) + + try: + spice_table_ws, info_matrix_ws = api.LoadSpiceAscii(Filename=spice_file_name, + OutputWorkspace=out_ws_name, + RunInfoWorkspace='TempInfo') + api.DeleteWorkspace(Workspace=info_matrix_ws) + except RuntimeError as run_err: + return False, 'Unable to load SPICE data %s due to %s' % (spice_file_name, str(run_err)) + + # Store + self._add_spice_workspace(exp_no, scan_no, spice_table_ws) + + return True, out_ws_name + + def load_spice_xml_file(self, exp_no, scan_no, pt_no, xml_file_name=None): + """ + Load SPICE's XML file to + :param scan_no: + :param pt_no: + :return: + """ + # Form XMIL file as ~/../HB3A_exp355_scan%04d_%04d.xml'%(scan_no, pt) + if xml_file_name is None: + xml_file_name = os.path.join(self._dataDir, + 'HB3A_exp%d_scan%04d_%04d.xml' % (exp_no, scan_no, pt_no)) + + # Get spice table + spice_table_ws = self._get_spice_workspace(exp_no, scan_no) + assert isinstance(spice_table_ws, mantid.dataobjects.TableWorkspace) + spice_table_name = spice_table_ws.name() + + # Load SPICE Pt. file + # spice_table_name = 'Table_Exp%d_Scan%04d' % (exp_no, scan_no) + pt_ws_name = get_raw_data_workspace_name(exp_no, scan_no, pt_no) + try: + ret_obj = api.LoadSpiceXML2DDet(Filename=xml_file_name, + OutputWorkspace=pt_ws_name, + DetectorGeometry='256,256', + SpiceTableWorkspace=spice_table_name, + PtNumber=pt_no) + except RuntimeError as run_err: + return False, str(run_err) + + pt_ws = ret_obj + + # Add data storage + self._add_raw_workspace(exp_no, scan_no, pt_no, pt_ws) + + return True, pt_ws_name + + def group_workspaces(self, exp_number, group_name): + """ + + :return: + """ + # Find out the input workspace name + ws_names_str = '' + for key in self._myRawDataWSDict.keys(): + if key[0] == exp_number: + ws_names_str += '%s,' % self._myRawDataWSDict[key].name() + + for key in self._mySpiceTableDict.keys(): + if key[0] == exp_number: + ws_names_str += '%s,' % self._mySpiceTableDict[key].name() + + # Check + if len(ws_names_str) == 0: + return False, 'No workspace is found for experiment %d.' % exp_number + + # Remove last ',' + ws_names_str = ws_names_str[:-1] + + # Group + api.GroupWorkspaces(InputWorkspaces=ws_names_str, + OutputWorkspace=group_name) + + return + + def merge_pts_in_scan(self, exp_no, scan_no, target_ws_name, target_frame): + """ + Merge Pts in Scan + All the workspaces generated as internal results will be grouped + :param exp_no: + :param scan_no: + :param target_ws_name: + :param target_frame: + :return: (merged workspace name, workspace group name) + """ + # Check + if exp_no is None: + exp_no = self._expNumber + assert isinstance(exp_no, int) + assert isinstance(scan_no, int) + assert isinstance(target_frame, str) + assert isinstance(target_ws_name, str) + + ub_matrix_1d = None + + # Target frame + if target_frame.lower().startswith('hkl'): + target_frame = 'hkl' + ub_matrix_1d = self._myUBMatrixDict[self._expNumber].reshape(9,) + elif target_frame.lower().startswith('q-sample'): + target_frame = 'qsample' + + else: + raise RuntimeError('Target frame %s is not supported.' % target_frame) + + # Process data and save + status, pt_num_list = self.get_pt_numbers(exp_no, scan_no, True) + if status is False: + err_msg = pt_num_list + return False, err_msg + else: + print '[DB] Number of Pts for Scan %d is %d' % (scan_no, len(pt_num_list)) + print '[DB] Data directory: %s' % self._dataDir + max_pts = 0 + ws_names_str = '' + + for pt in pt_num_list: + try: + self.download_spice_xml_file(scan_no, pt, overwrite=False) + api.CollectHB3AExperimentInfo(ExperimentNumber=exp_no, ScanList='%d' % scan_no, PtLists='-1,%d' % pt, + DataDirectory=self._dataDir, + GenerateVirtualInstrument=False, + OutputWorkspace='ScanPtInfo_Exp%d_Scan%d' % (exp_no, scan_no), + DetectorTableWorkspace='MockDetTable') + + out_q_name = 'HB3A_Exp%d_Scan%d_Pt%d_MD' % (exp_no, scan_no, pt) + api.ConvertCWSDExpToMomentum(InputWorkspace='ScanPtInfo_Exp406_Scan%d' % scan_no, + CreateVirtualInstrument=False, + OutputWorkspace=out_q_name, + Directory=self._dataDir) + + if target_frame == 'hkl': + out_hkl_name = 'HKL_Scan%d_Pt%d' % (scan_no, pt) + api.ConvertCWSDMDtoHKL(InputWorkspace=out_q_name, + UBMatrix=ub_matrix_1d, + OutputWorkspace=out_hkl_name) + ws_names_str += out_hkl_name + ',' + else: + ws_names_str += out_q_name + ',' + + except RuntimeError as e: + print '[Error] Reducing scan %d pt %d due to %s' % (scan_no, pt, str(e)) + continue + + else: + max_pts = pt + # END-FOR + + # Merge + if target_frame == 'qsample': + out_ws_name = target_ws_name + '_QSample' + elif target_frame == 'hkl': + out_ws_name = target_ws_name + '_HKL' + else: + raise RuntimeError('Impossible to have target frame %s' % target_frame) + + ws_names_str = ws_names_str[:-1] + api.MergeMD(InputWorkspaces=ws_names_str, OutputWorkspace=out_ws_name, SplitInto=max_pts) + + # Group workspaces + group_name = 'Group_Exp406_Scan%d' % scan_no + api.GroupWorkspaces(InputWorkspaces=ws_names_str, OutputWorkspace=group_name) + spice_table_name = get_spice_table_name(exp_no, scan_no) + api.GroupWorkspaces(InputWorkspaces='%s,%s' % (group_name, spice_table_name), OutputWorkspace=group_name) + + ret_tup = out_ws_name, group_name + + return ret_tup + + def set_server_url(self, server_url): + """ + Set URL for server to download the data + :param server_url: + :return: + """ + # Server URL must end with '/' + self._myServerURL = str(server_url) + if self._myServerURL.endswith('/') is False: + self._myServerURL += '/' + + # Test URL valid or not + is_url_good = False + error_message = None + try: + result = urllib2.urlopen(self._myServerURL) + except urllib2.HTTPError, err: + error_message = str(err.code) + except urllib2.URLError, err: + error_message = str(err.args) + else: + is_url_good = True + result.close() + + if error_message is None: + error_message = '' + else: + error_message = 'Unable to open data server URL: %s due to %s.' % (server_url, error_message) + + return is_url_good, error_message + + def setWebAccessMode(self, mode): + """ + Set data access mode form server + :param mode: + :return: + """ + if isinstance(mode, str) is False: + raise RuntimeError('Input mode is not string') + + if mode == 'cache': + self._cacheDataOnly = True + elif mode == 'download': + self._cacheDataOnly = False + + return + + def set_local_data_dir(self, local_dir): + """ + Set local data storage + :param local_dir: + :return: + """ + # Get absolute path + if os.path.isabs(local_dir) is False: + # Input is relative path to current working directory + cwd = os.getcwd() + local_dir = os.path.join(cwd, local_dir) + + # Create cache directory if necessary + if os.path.exists(local_dir) is False: + try: + os.mkdir(local_dir) + except OSError as os_err: + return False, str(os_err) + + # Check whether the target is writable + if os.access(local_dir, os.W_OK) is False: + return False, 'Specified local data directory %s is not writable.' % local_dir + + # Successful + self._dataDir = local_dir + + return True, '' + + def set_ub_matrix(self, exp_number, ub_matrix): + """ + TODO/DOC + :param exp_number: + :param ub_matrix: + :return: + """ + # Check + if exp_number is None: + exp_number = self._expNumber + + assert isinstance(exp_number, int) + assert isinstance(ub_matrix, numpy.ndarray) + assert ub_matrix.shape == (3, 3) + + # Set up + self._myUBMatrixDict[exp_number] = ub_matrix + + def set_working_directory(self, work_dir): + """ + Set up the directory for working result + :return: (boolean, string) + """ + if os.path.exists(work_dir) is False: + try: + os.mkdir(work_dir) + except OSError as os_err: + return False, 'Unable to create working directory %s due to %s.' % (work_dir, str(os_err)) + elif os.access(work_dir, os.W_OK) is False: + return False, 'User specified working directory %s is not writable.' % work_dir + + self._workDir = work_dir + + return True, '' + + def set_instrument_name(self, instrument_name): + """ + Set instrument name + :param instrument_name: + :return: + """ + # Check + if isinstance(instrument_name, str) is False: + return False, 'Input instrument name is not a string but of type %s.' % str(type(instrument_name)) + if len(instrument_name) == 0: + return False, 'Input instrument name is an empty string.' + + self._instrumentName = instrument_name + + return True, '' + + def set_exp_number(self, exp_number): + """ Add experiment number + :param exp_number: + :return: + """ + assert isinstance(exp_number, int) + self._expNumber = exp_number + + return True + + def set_hkl_to_peak(self, exp_number, scan_number, pt_number): + """ + Get HKL as _h, _k, _l from MDEventWorkspace. It is for HB3A only + :return: + """ + status, peak_info = self.get_peak_info(exp_number, scan_number, pt_number) + if status is False: + err_msg = peak_info + return False, err_msg + + md_ws = self._myPtMDDict[(exp_number, scan_number, pt_number)] + assert md_ws.getNumExperimentInfo() == 1 + exp_info = md_ws.getExperimentInfo(0) + + try: + m_h = float(exp_info.run().getProperty('_h').value) + m_k = float(exp_info.run().getProperty('_k').value) + m_l = float(exp_info.run().getProperty('_l').value) + except RuntimeError as error: + return False, 'Unable to retrieve HKL due to %s.' % (str(error)) + + peak_ws = peak_info.get_peak_workspace() + peak = peak_ws.getPeak(0) + peak.setHKL(m_h, m_k, m_l) + + return True, (m_h, m_k, m_l) + + def _add_raw_workspace(self, exp_no, scan_no, pt_no, raw_ws): + """ Add raw Pt.'s workspace + :param exp_no: + :param scan_no: + :param pt_no: + :param raw_ws: workspace or name of the workspace + :return: None + """ + # Check + assert isinstance(exp_no, int) + assert isinstance(scan_no, int) + assert isinstance(pt_no, int) + + if isinstance(raw_ws, str): + # Given by name + matrix_ws = AnalysisDataService.retrieve(raw_ws) + else: + matrix_ws = raw_ws + assert isinstance(matrix_ws, mantid.dataobjects.Workspace2D) + + self._myRawDataWSDict[(exp_no, scan_no, pt_no)] = matrix_ws + + return + + def _add_md_workspace(self, exp_no, scan_no, pt_no, md_ws): + """ + Add MD workspace to storage + :param exp_no: + :param scan_no: + :param pt_no: + :param md_ws: + :return: + """ + # Check input + print '[DB] Type of md_ws is %s.' % str(type(md_ws)) + assert isinstance(md_ws, mantid.dataobjects.MDEventWorkspace) + + assert isinstance(exp_no, int) + assert isinstance(scan_no, int) + assert isinstance(pt_no) + + self._myPtMDDict[(exp_no, scan_no, pt_no)] = md_ws + + return + + def _add_ub_peak_ws(self, exp_number, scan_number, pt_number, peak_ws): + """ + Add peak workspace for UB matrix + :param exp_number: + :param scan_number: + :param pt_number: + :param peak_ws: + :return: + """ + # Check + assert isinstance(peak_ws, mantid.dataobjects.PeaksWorkspace) + + assert isinstance(exp_number, int) + assert isinstance(scan_number, int) + assert isinstance(pt_number, int) + + # Add + self._myUBPeakWSDict[(exp_number, scan_number, pt_number)] = peak_ws + + return + + def _add_spice_workspace(self, exp_no, scan_no, spice_table_ws): + """ + """ + assert isinstance(exp_no, int) + assert isinstance(scan_no, int) + assert isinstance(spice_table_ws, mantid.dataobjects.TableWorkspace) + self._mySpiceTableDict[(exp_no, scan_no)] = spice_table_ws + + return + + def _get_spice_workspace(self, exp_no, scan_no): + """ Get SPICE's scan table workspace + :param exp_no: + :param scan_no: + :return: Table workspace or None + """ + try: + ws = self._mySpiceTableDict[(exp_no, scan_no)] + except KeyError: + print '[DB] Keys to SPICE TABLE: %s' % str(self._mySpiceTableDict.keys()) + return None + + return ws + + def get_raw_data_workspace(self, exp_no, scan_no, pt_no): + """ Get raw workspace + """ + try: + ws = self._myRawDataWSDict[(exp_no, scan_no, pt_no)] + assert isinstance(ws, mantid.dataobjects.Workspace2D) + except KeyError: + return None + + return ws + + def _get_pt_list_from_spice_table(self, spice_table_ws): + """ + Get list of Pt. from a SPICE table workspace + :param spice_table_ws: SPICE table workspace + :return: list of Pt. + """ + numrows = spice_table_ws.rowCount() + ptlist = [] + for irow in xrange(numrows): + ptno = int(spice_table_ws.cell(irow, 0)) + ptlist.append(ptno) + + return ptlist + + +def get_spice_file_name(exp_number, scan_number): + """ + Get standard HB3A SPICE file name from experiment number and scan number + :param exp_number: + :param scan_num: + :return: + """ + file_name = 'HB3A_exp%04d_scan%04d.dat' % (exp_number, scan_number) + + return file_name + + +def get_spice_table_name(exp_number, scan_number): + """ Form the name of the table workspace for SPICE + :param exp_number: + :param scan_number: + :return: + """ + table_name = 'HB3A_%03d_%04d_SpiceTable' % (exp_number, scan_number) + + return table_name + + +def get_raw_data_workspace_name(exp_number, scan_number, pt_number): + """ Form the name of the matrix workspace to which raw pt. XML file is loaded + :param exp_number: + :param scan_number: + :param pt_number: + :return: + """ + ws_name = 'HB3A_exp%d_scan%04d_%04d' % (exp_number, scan_number, pt_number) + + return ws_name + + +def get_pt_info_ws_name(exp_number, scan_number): + """ + Information table workspace'name from CollectHB3AInfo + :param exp_number: + :param scan_number: + :param pt_number: + :return: + """ + ws_name = 'ScanPtInfo_Exp%d_Scan%d' % (exp_number, scan_number) + + return ws_name + + +def get_single_pt_peak_ws_name(exp_number, scan_number, pt_number): + """ + Form the name of the peak workspace + :param exp_number: + :param scan_number: + :param pt_number: + :return: + """ + ws_name = 'Peak_Exp%d_Scan%d_Pt%d' % (exp_number, scan_number, pt_number) + + return ws_name + + +def get_single_pt_md_name(exp_number, scan_number, pt_number): + """ Form the name of the MDEvnetWorkspace for a single Pt. measurement + :param exp_number: + :param scan_number: + :param pt_number: + :return: + """ + ws_name = 'HB3A_Exp%d_Scan%d_Pt%d_MD' % (exp_number, scan_number, pt_number) + + return ws_name + + +def get_virtual_instrument_table_name(exp_number, scan_number, pt_number): + """ + Generate the name of the table workspace containing the virtual instrument information + :param exp_number: + :param scan_number: + :param pt_number: + :return: + """ + ws_name = 'VirtualInstrument_Exp%d_Scan%d_Pt%d_Table' % (exp_number, scan_number, pt_number) + + return ws_name