예제 #1
0
def test_is_Bluesky_specfile():

    folder = join("polartools", "tests", "data_for_test")

    # Tests with spec loading within polartools.
    result = load_data.is_Bluesky_specfile("pressure_calibration.dat",
                                           folder=folder)
    assert result is False

    result = load_data.is_Bluesky_specfile("bluesky_spec.dat", folder=folder)
    assert result is True

    result = load_data.is_Bluesky_specfile("absorption.dat", folder=folder)
    assert result is False

    # Tests loading the spec file here.
    spec_file = SpecDataFile(join(folder, "pressure_calibration.dat"))
    result = load_data.is_Bluesky_specfile(spec_file)
    assert result is False

    spec_file = SpecDataFile(join(folder, "bluesky_spec.dat"))
    result = load_data.is_Bluesky_specfile(spec_file)
    assert result is True

    spec_file = SpecDataFile(join(folder, "absorption.dat"))
    result = load_data.is_Bluesky_specfile(spec_file)
    assert result is False
예제 #2
0
    def load_spec_file(self):

        if self.spec_fname == '':
            self.status.showMessage('No file was loaded')
            return

        self.scan.scans_box.clear()
        try:
            self._spec_file = SpecDataFile(self.spec_fname)

            self._commands_list = self._spec_file.getScanCommands()
            for i in range(len(self._commands_list)):
                if '#S' in self._commands_list[i]:
                    self._commands_list[i] = self._commands_list[i].strip(
                        '#S ')
                self.scan.scans_box.addItem(self._commands_list[i])

            self.scan.scans_box.setCurrentIndex(len(self._commands_list) - 1)

            self.selected_scan(self._commands_list[-1])
            self.make_plot()
            self.pressure.print_pressure.setText('')
        except:
            self.status.showMessage('{} is not a spec file!!'.format(
                self.spec.fname.text()))
예제 #3
0
def load_spec(scan_id, spec_file, folder=""):
    """
    Load data from spec file.

    If `spec_file` is the file name, it will load the spec file internally
    which is time consuming.

    Parameters
    ----------
    scan_id : int
        Scan_id of the scan to be retrieved.
    spec_file : string or spec2nexus.spec.SpecDataFile
        Either the spec file name or a SpecDataFile instance.
    folder : string, optional
        Folder where spec file is located.

    Returns
    -------
    data : pandas.DataFrame
        Table with the data from scan.

    See also
    --------
    :func:`spec2nexus.spec.SpecDataFile`
    """

    if isinstance(spec_file, str):
        path = join(folder, spec_file)
        spec_file = SpecDataFile(path)

    return DataFrame(spec_file.getScan(scan_id).data)
예제 #4
0
 def openFile(self):
     fileName = qtWidgets.QFileDialog.getOpenFileName(
         None, "Open Spec File")[0]
     self.specFile = SpecDataFile(fileName)
     self.setWindowTitle(APP_NAME + ' - ' + str(fileName))
     firstScan = self.specFile.getFirstScanNumber()
     self.positionerSelector.loadPositioners(
         self.specFile.scans[firstScan].positioner)
예제 #5
0
 def testLoadScans(self):
     specFile = os.path.join(self.dataPath, FLUORESCENCE_FILE)
     print("SpecDataFile %s" % specFile)
     specData = SpecDataFile(specFile)
     self.scanBrowser.loadScans(specData.scans, newFile=True)
     self.assertEqual(len(self.spyLoaded), 1)
     self.assertEqual(len(self.spySelected), 0)
     self.assertEqual(self.scanBrowser.scanList.rowCount(),
                      len(specData.getScanNumbers()))
예제 #6
0
    def testName(self):
        spec_data = SpecDataFile(self.fname)
        self.assertTrue(isinstance(spec_data, SpecDataFile))
        scan = spec_data.getScan(1)

        with self.assertRaises(spec2nexus.plugins.uxml.UXML_Error) as context:
            scan.interpret()
        received = str(context.exception)
        expected = "UXML error: Element 'group': "
        expected += "Character content other than whitespace is not allowed "
        expected += "because the content type is 'element-only'."
        self.assertTrue(received.startswith(expected))
예제 #7
0
 def setUp(self):
     self.specDisplay = SpecDisplay()
     self.dataPath = os.environ.get(DATAPATH)
     specFileName1 = os.path.join(self.dataPath, FLUORESCENCE_FILE)
     try:
         self.specFile1 = SpecDataFile(specFileName1)
     except Exception as ex:
         self.fail(ex)
     specFileName2 = os.path.join(self.dataPath, FLUORESCENCE_FILE)
     try:
         self.specFile2 = SpecDataFile(specFileName2)
     except Exception as ex:
         self.fail(ex)
예제 #8
0
def test_xrd_calibrate_pressure():
    path = join('polartools', 'tests', 'data_for_test')

    # Au calibrant
    pressure = xrd_calibrate_pressure(419,
                                      'pressure_calibration.dat',
                                      temperature=9,
                                      energy=12.0,
                                      positioner='4C Two Theta',
                                      detector='CyberMag',
                                      monitor='IC3',
                                      folder=path)

    assert allclose(pressure, 0.743387, atol=1e-5)

    # Ag calibrant
    spec_file = SpecDataFile(join(path, 'pressure_calibration.dat'))
    pressure = xrd_calibrate_pressure(419,
                                      spec_file,
                                      temperature=9,
                                      energy=12.0,
                                      calibrant='Ag',
                                      positioner='4C Two Theta',
                                      detector='CyberMag',
                                      monitor='IC3')

    assert allclose(pressure, 0.818770, atol=1e-5)
예제 #9
0
class PositionerSelectorExample(BaseExample):
    def __init__(self, parent=None):
        super(PositionerSelectorExample, self).__init__(parent)
        self.positionerSelector = PositionerSelector()
        menuBar = self.menuBar()
        dataMenu = menuBar.addMenu("Data")
        self.dumpAction = qtWidgets.QAction("DumpSelected", self)
        dataMenu.addAction(self.dumpAction)
        self.dumpAction.triggered.connect(self.dumpSelectedData)
        self.setCentralWidget(self.positionerSelector)
        self.connectOpenFileAction(self.openFile)
        self.setWindowTitle(APP_NAME)
        self.show()

    @qtCore.pyqtSlot()
    def openFile(self):
        fileName = qtWidgets.QFileDialog.getOpenFileName(
            None, "Open Spec File")[0]
        self.specFile = SpecDataFile(fileName)
        self.setWindowTitle(APP_NAME + ' - ' + str(fileName))
        firstScan = self.specFile.getFirstScanNumber()
        self.positionerSelector.loadPositioners(
            self.specFile.scans[firstScan].positioner)

    def dumpSelectedData(self):
        print(self.positionerSelector.getSelectedItems())
예제 #10
0
 def testScanSelected(self):
     selectedScan = 5
     specFile = os.path.join(self.dataPath, FLUORESCENCE_FILE)
     spyLoaded = QSignalSpy(self.scanBrowser.scanLoaded)
     specData = SpecDataFile(specFile)
     self.scanBrowser.loadScans(specData.scans)
     self.assertEqual(len(spyLoaded), 1)
     self.assertEqual(len(self.spySelected), 0)
     self.scanBrowser.setCurrentScan(selectedScan)
     self.assertEqual(len(spyLoaded), 1)
     self.assertEqual(len(self.spySelected), 1)
     self.assertEqual(self.scanBrowser.getCurrentScan(), '6')
     self.scanBrowser.filterByScanTypes(specData.scans, SCAN_TYPES[2])
     self.assertEqual(len(spyLoaded), 2)
     self.assertEqual(len(self.spySelected), 1)
     # Doesn't register a selection change since row has not changed
     self.scanBrowser.setCurrentScan(selectedScan)
     self.assertEqual(len(spyLoaded), 2)
     self.assertEqual(len(self.spySelected), 1)
     self.assertEqual(self.scanBrowser.getCurrentScan(), '12')
     # Register a selection change since row changed
     self.scanBrowser.setCurrentScan(selectedScan + 1)
     self.assertEqual(len(spyLoaded), 2)
     self.assertEqual(len(self.spySelected), 2)
     self.assertEqual(self.scanBrowser.getCurrentScan(), '13')
예제 #11
0
 def openFile(self):
     '''
     Open a file, populate the navigator window as appropriate
     '''
     logger.debug(METHOD_ENTER_STR)
     fOptions = qtWidgets.QFileDialog.Options()
     fOptions |= qtWidgets.QFileDialog.DontUseNativeDialog
     fileName = qtWidgets.QFileDialog.getOpenFileName(
         self,
         caption="Open Spec File",
         directory=self.currentDirectory,
         options=fOptions)[0]
     specFile = None
     if fileName != "":
         try:
             specFile = SpecDataFile(fileName)
         except NotASpecDataFile as ex:
             qtWidgets.QMessageBox.warning(
                 self, "Not a Spec File",
                 "The file %s does not seem to be a spec file" % fileName)
             return
         self.currentDirectory = str(os.path.realpath(fileName))
     else:
         return
     self._dataNavigator.addSpecDataFileNode(specFile)
예제 #12
0
def is_Bluesky_specfile(source, folder=""):
    """
    Check if the specfile was created by Bluesky.

    It looks for a "Bluesky" comment in the file header.

    Parameters
    ----------
    source : string or spec2nexus.spec.SpecDataFile
        Either the spec file name or a SpecDataFile instance.
    folder : string, optional
        Folder where spec file is located.

    Returns
    -------
    y : bool
        True if spec_file was generated by Bluesky, False otherwise.

    See also
    --------
    :func:`spec2nexus.spec.SpecDataFile`
    """
    if isinstance(source, str):
        path = join(folder, source)
        source = SpecDataFile(path)

    if len(source.headers[0].comments):
        return source.headers[0].comments[0].startswith("Bluesky")
    else:
        return False
 def setUp(self):
     self.selection = IntermediateDataSelection()
     print(dir (self.selection))
     self.dataPath = os.environ.get(DATAPATH)
     specDataFileName = os.path.join(self.dataPath, FLUORESCENCE_FILE)
     specDataFile = SpecDataFile(specDataFileName)
     specNode = SpecFileDataNode(specDataFile)
예제 #14
0
    def choose_file(self):
        '''
        Opens file dialog, set file text box to the chosen path
        '''
        from spec2nexus.spec import SpecDataFile

        dirname = self.get_default_directory(self.filename.text())
        filename = getOpenFileName(self, 'Open file', dirname)
        if os.path.exists(filename):
            self.filename.setText(str(filename))
            self.spec = SpecDataFile(self.get_filename())
            self.set_default_directory(os.path.dirname(filename))
            scan_min = self.spec.getMinScanNumber()
            self.scanmin.setText(str(scan_min))
            scan_max = self.spec.getMaxScanNumber()
            self.scanmax.setText(str(scan_max))
예제 #15
0
    def __init__(self, specPath):
        super(SpecFile, self).__init__(parent=None)
        self.specFilePath = specPath
        self.specFile = SpecDataFile(specPath)
        self.scanBrowser = ScanBrowser()
        self.scanBrowser.loadScans(self.getScans(), self)

        self.selectedScans = []
예제 #16
0
 def specFileInfo(self):
     """This method sets the name, status tip for the spec file, and loads the scan to the list
     from the spec file.
     """
     self.rdOnlyFileName.setText(self.readSpec.specFileName)
     self.rdOnlyFileName.setStatusTip(self.readSpec.specFileName)
     specFile = SpecDataFile(self.readSpec.specFileName)
     self.readSpec.loadScans(specFile.scans)
예제 #17
0
 def __init__(self, specfile):
     if os.path.exists(specfile):
         from spec2nexus.spec import SpecDataFile
         self.filename = specfile
         stat = os.stat(specfile)
         self.size = stat.st_size
         self.mtime = stat.st_mtime
         self.sdf_object = SpecDataFile(specfile)
예제 #18
0
    def test_contents(self):
        spec_data = SpecDataFile(self.fname)
        self.assertTrue(isinstance(spec_data, SpecDataFile))
        out = writer.Writer(spec_data)
        scan_list = [
            1,
        ]
        out.save(self.hname, scan_list)

        # text that UXML group defined as expected
        self.assertTrue(os.path.exists(self.hname))
        with h5py.File(self.hname) as h5_file:
            self.assertTrue("S1" in h5_file)
            nxentry = h5_file["/S1"]
            self.assertTrue("UXML" in nxentry)
            uxml = nxentry["UXML"]
            self.assertTrue("NX_class" in uxml.attrs)
            self.assertEqual(uxml.attrs["NX_class"], "NXnote")

            # spot-check a couple items
            # group: attenuator_set
            self.assertTrue("attenuator_set" in uxml)
            group = uxml["attenuator_set"]
            prefix = group.attrs.get("prefix")
            description = group.attrs.get("description")
            unique_id = group.attrs.get("unique_id")
            self.assertEqual(prefix, "33idd:filter:")
            self.assertEqual(description, "33-ID-D Filters")
            self.assertEqual(unique_id, None)

            # <dataset name="corrdet_counter">corrdet</dataset>
            self.assertTrue("corrdet_counter" in group)
            ds = group["corrdet_counter"]
            # http://docs.h5py.org/en/stable/whatsnew/2.1.html?highlight=dataset#dataset-value-property-is-now-deprecated
            value = ds[()]  # ds.value deprecated in h5py
            self.assertEqual(value, [b"corrdet"])

            # <dataset name="wait_time" type="float" units="s">0.500</dataset>
            self.assertTrue("wait_time" in group)
            ds = group["wait_time"]
            attrs = dict(ds.attrs)
            self.assertEqual(ds.attrs.get("units"), "s")
            value = ds[()]  # ds.value deprecated in h5py
            self.assertEqual(value, numpy.array([.5]))

            # <hardlink name="attenuator1" target_id="33idd:filter:Fi1:"/>
            self.assertTrue("attenuator1" in group)
            attenuator1 = group["attenuator1"]
            target = attenuator1.attrs.get("target")
            self.assertNotEqual(target, attenuator1.name)
            self.assertEqual(target, uxml["attenuator1"].name)

            addr = "/S1/UXML/ad_file_info/file_format"
            self.assertTrue(addr in h5_file)
            ds = h5_file[addr]
            value = ds[()]  # ds.value deprecated in h5py
            self.assertEqual(value, [b"TIFF"])
예제 #19
0
 def testName(self):
     spec_data = SpecDataFile(self.fname)
     self.assertTrue(isinstance(spec_data, SpecDataFile))
     out = writer.Writer(spec_data)
     scan_list = [
         1,
     ]
     out.save(self.hname, scan_list)
     # TODO: make tests of other things in the Writer
     dd = out.root_attributes()
     self.assertTrue(isinstance(dd, dict))
예제 #20
0
    def loadSpec(self, specFile, directory):
        """This method loads the spec file and creates the widgets on the control QDockWidget.
        """
        self.chambers = []
        self.specFile = SpecDataFile(specFile)
        self.specHeader = SpecDataFileHeader(specFile)
        self.scans = self.specFile.scans
        self.scan = str(int(os.path.basename(directory)))
        self.specFileOpened = True

        self.maxScans = self.specFile.getMaxScanNumber()

        # Gets possible normalizer values
        for key in self.scans[self.scan].data.keys():
            if key.find("Ion_Ch_") == 0:
                self.chambers.append(key)

        self.chambers.sort()
        self.MonDialog()
        self.mainWindow.controlDockWidget.close()
        self.mainWindow.ControlDockWidget()
예제 #21
0
파일: readspec.py 프로젝트: ordirules/nexpy
 def choose_file(self):
     '''
     Opens file dialog, set file text box to the chosen path
     '''
     dirname = self.get_default_directory(self.filename.text())
     filename = getOpenFileName(self, 'Open file', dirname)
     if os.path.exists(filename):
         self.filename.setText(str(filename))
         self.spec = SpecDataFile(self.get_filename())
         self.set_default_directory(os.path.dirname(filename))
         scan_min = self.spec.getMinScanNumber()
         self.scanmin.setText(str(scan_min))
         scan_max = self.spec.getMaxScanNumber()
         self.scanmax.setText(str(scan_max))
예제 #22
0
 def choose_file(self):
     """Opens file dialog, set file text box to the chosen path."""
     from spec2nexus.spec import SpecDataFile
     dirname = self.get_default_directory(self.filename.text())
     filename = getOpenFileName(self, 'Open file', dirname)
     if os.path.exists(filename):
         self.filename.setText(str(filename))
         self.spec = SpecDataFile(self.get_filename())
         self.set_default_directory(os.path.dirname(filename))
         all_scans = self.get_scan_numbers()
         scan_min = all_scans[0]
         self.scanmin.setText(str(scan_min))
         scan_max = all_scans[-1]
         self.scanmax.setText(str(scan_max))
예제 #23
0
 def reload(self):
     logger.debug(METHOD_ENTER_STR)
     scanKeys = sorted(self._specDataFile.scans, key=int)
     logger.debug("Unload the old data")
     
     filename = self._specDataFile.fileName
     for scan in scanKeys:
         logger.debug("scan to remove %s" % self.scans[scan].__class__)
         self.removeSpecScan(scan)
     for scan in self._specDataFile.scans:
         scan = None
     self._specDataFile = None
     gc.collect
     logger.debug("Load current data in the file")
     self._specDataFile = SpecDataFile(filename)
     self.loadScans()
     gc.collect()
예제 #24
0
    def _browseForPathToReplace(self):
        if os.path.exists(str(self.projNameTxt.text())) and \
                          (str(self.scanNumsTxt.text()) != ""):
            specFile = str(self.projNameTxt.text())
            scans = self.getScanList()
            sd = SpecDataFile(specFile)
            scan = sd.scans[str(scans[0])]
            scan.interpret()
            if scan.CCD != None:
                try:
                    filePath = scan.CCD['image_dir'][0]
                    self.pathToReplaceTxt.setText(filePath)
                except:
                    qtWidgets.QMessageBox.warning(self, "File patgh not Found", \
                                              "File path not found for scan %s" \
                                              % scan)

            self.pathToReplaceTxt.editingFinished.emit()
예제 #25
0
 def testFilterByScanTypes(self):
     specFile = os.path.join(self.dataPath, FLUORESCENCE_FILE)
     spyLoaded = QSignalSpy(self.scanBrowser.scanLoaded)
     specData = SpecDataFile(specFile)
     self.scanBrowser.loadScans(specData.scans)
     self.assertEqual(len(spyLoaded), 1)
     self.assertEqual(len(self.spySelected), 0)
     self.scanBrowser.filterByScanTypes(specData.scans, SCAN_TYPES[1])
     self.assertEqual(self.scanBrowser.scanList.rowCount(),
                      NUM_SCAN_TYPES[1])
     self.scanBrowser.filterByScanTypes(specData.scans, SCAN_TYPES[2])
     self.assertEqual(self.scanBrowser.scanList.rowCount(),
                      NUM_SCAN_TYPES[2])
     self.scanBrowser.filterByScanTypes(specData.scans, SCAN_TYPES[3])
     self.assertEqual(self.scanBrowser.scanList.rowCount(),
                      NUM_SCAN_TYPES[3])
     self.scanBrowser.filterByScanTypes(specData.scans, SCAN_TYPES[4])
     self.assertEqual(self.scanBrowser.scanList.rowCount(),
                      NUM_SCAN_TYPES[4])
     self.scanBrowser.filterByScanTypes(specData.scans, SCAN_TYPES[5])
     self.assertEqual(self.scanBrowser.scanList.rowCount(),
                      NUM_SCAN_TYPES[5])
     self.scanBrowser.filterByScanTypes(specData.scans, SCAN_TYPES[6])
     self.assertEqual(self.scanBrowser.scanList.rowCount(),
                      NUM_SCAN_TYPES[6])
     self.scanBrowser.filterByScanTypes(specData.scans, SCAN_TYPES[7])
     self.assertEqual(self.scanBrowser.scanList.rowCount(),
                      NUM_SCAN_TYPES[7])
     self.scanBrowser.filterByScanTypes(specData.scans, SCAN_TYPES[8])
     self.assertEqual(self.scanBrowser.scanList.rowCount(),
                      NUM_SCAN_TYPES[8])
     self.scanBrowser.filterByScanTypes(specData.scans, SCAN_TYPES[1:])
     self.assertEqual(self.scanBrowser.scanList.rowCount(),
                      NUM_SCAN_TYPES[0])
     self.scanBrowser.filterByScanTypes(specData.scans, [])
     self.assertEqual(self.scanBrowser.scanList.rowCount(),
                      NUM_SCAN_TYPES[0])
     with self.assertRaises(ValueError):
         self.scanBrowser.filterByScanTypes(specData.scans, None)
     self.assertEqual(NUM_SCAN_TYPES[0], sum(NUM_SCAN_TYPES[1:]))
     self.assertEqual(len(spyLoaded), 11)
     self.assertEqual(len(self.spySelected), 0)
예제 #26
0
    def test_contents(self):
        spec_data = SpecDataFile(self.fname)
        self.assertTrue(isinstance(spec_data, SpecDataFile))
        out = writer.Writer(spec_data)
        scan_list = [
            1,
        ]
        out.save(self.hname, scan_list)

        # the ONLY structural difference between test_3.spec
        # and test_4.spec is the presence of this hardlink
        self.assertTrue(os.path.exists(self.hname))
        with h5py.File(self.hname) as h5_file:
            source = h5_file["/S1/UXML/ad_detector/beam_center_x"]
            link = h5_file["/S1/UXML/beam_center_x"]
            target = source.attrs.get("target")

            self.assertNotEqual(source.name, link.name)
            self.assertNotEqual(target, None)
            self.assertEqual(target, source.name)
            self.assertEqual(target, link.attrs.get("target"))
예제 #27
0
class Sector33SpecDataSource(SpecXMLDrivenDataSource):
    '''
    Class to load data from spec file and configuration xml files from 
    for the way that data is collected at sector 33.
    :members
    '''


    def __init__(self, 
                 projectDir, 
                 projectName, 
                 projectExtension,
                 instConfigFile, 
                 detConfigFile, 
                 **kwargs):
        '''
        Constructor
        :param projectDir: Directory holding the project file to open
        :param projectName: First part of file name for the project
        :param projectExt: File extension for the project file.
        :param instConfigFile: Full path to Instrument configuration file.
        :param detConfigFile: Full path to the detector configuration file
        :param kwargs: Assorted keyword arguments

        :rtype: Sector33SpecDataSource
        '''
        super(Sector33SpecDataSource, self).__init__(projectDir, 
                                                     projectName, 
                                                     projectExtension,
                                                     instConfigFile, 
                                                     detConfigFile, 
                                                     **kwargs)
        
    def _calc_eulerian_from_kappa(self, primaryAngles=None, 
                                  referenceAngles = None):
        """
        Calculate the eulerian sample angles from the kappa stage angles.
        :param primaryAngles:  list of sample axis numbers to be handled by 
        the conversion
        :param referenceAngles: list of reference angles to be used in angle 
        conversion
        """

        keta = np.deg2rad(referenceAngles[:,0])
        kappa = np.deg2rad(referenceAngles[:,1])
        kphi = np.deg2rad(referenceAngles[:,2])
        kappaParam = self.instConfig.getSampleAngleMappingParameter('kappa')
        
        try:
            if kappaParam != None:
                self.kalpha = np.deg2rad(float(kappaParam))
            else:
                self.kalpha = np.deg2rad(50.000)
            kappaInvertedParam = \
                self.instConfig.getSampleAngleMappingParameter('kappaInverted')
            if kappaInvertedParam != None:
                self.kappa_inverted = self.to_bool(kappaInvertedParam)
            else:
                self.kappa_inverted = False
        except Exception as ex:
            raise RSMap3DException("Error trying to get parameter for " + \
                                "sampleAngleMappingFunction " + \
                                "_calc_eulerian_from_kappa in inst config " + \
                                "file\n" + \
                                str(ex))
        
        _t1 = np.arctan(np.tan(kappa / 2.0) * np.cos(self.kalpha))
        if self.kappa_inverted:
            eta = np.rad2deg(keta + _t1)
            phi = np.rad2deg(kphi + _t1)
        else:
            eta = np.rad2deg(keta - _t1)
            phi = np.rad2deg(kphi - _t1)
        chi = 2.0 * np.rad2deg(np.arcsin(np.sin(kappa / 2.0) * \
                               np.sin(self.kalpha)))
        
        return eta, chi, phi

    def _calc_replace_angle_values(self , primaryAngles=None,
                                   referenceAngles=None):
        '''
        Fix a situation were some constant angle values have been 
        recorded incorrectly and need to be fixed.
        :param primaryAngles:  list of sample axis numbers to be handled by 
        the conversion
        :param referenceAngles: list of reference angles to be used in angle 
        conversion
        '''
        logger.info( "Running " + __name__)
        angles = []
        logger.debug("referenceAngles " + str(referenceAngles))
        mappingAngles = self.instConfig.getSampleAngleMappingReferenceAngles()
        
        logger.debug( "mappingAngles" + str(mappingAngles))
        for ii in range(len(referenceAngles)):
            replaceVal = \
                float(self.instConfig.getSampleAngleMappingReferenceAngleAttrib( \
                                              number= str(mappingAngles[ii]), \
                                              attribName='replaceValue'))
            logger.debug( "primary Angles" + str(referenceAngles))
            angles.append(replaceVal* np.ones(len(referenceAngles[:,ii]),))
            logger.debug("Angles" + str( angles))
        return angles
        
    def fixGeoAngles(self, scan, angles):
        '''
         Fix the angles using a user selected function.
        :param scan: scan to set the angles for
        :param angles: Array of angles to set for this scan  
        '''
        logger.debug( "starting " + __name__)
        needToCorrect = False
        refAngleNames = self.instConfig.getSampleAngleMappingReferenceAngles()
        for refAngleName in refAngleNames:
            alwaysFix = self.instConfig.getSampleAngleMappingAlwaysFix()
            if refAngleName in scan.L or alwaysFix:
                needToCorrect = True
                
        if needToCorrect:
            logger.debug( __name__ + ": Fixing angles")
            refAngles = self.getScanAngles(scan, refAngleNames)
            primaryAngles = self.instConfig.getSampleAngleMappingPrimaryAngles()
            functionName = self.instConfig.getSampleAngleMappingFunctionName()
            functionModuleName = self.instConfig.getSampleAngleMappingFunctionModule()
            logger.debug("sampleMappingFunction moduleName %s" % functionModuleName) 
           #Call a defined method to calculate angles from the reference angles.
            moduleSource = self
            if functionModuleName != None:
                functionModule = importlib.import_module(functionModuleName)
                logger.debug("dir(functionModule)" % dir(functionModule))
                moduleSource = functionModule
            method = getattr(moduleSource, functionName)
            fixedAngles = method(primaryAngles=primaryAngles, 
                                   referenceAngles=refAngles)
            logger.debug ("fixed Angles: " + str(fixedAngles))
            for i in range(len(primaryAngles)):
                logger.debug ("Fixing primaryAngles: %d " % primaryAngles[i])
                angles[:,primaryAngles[i]-1] = fixedAngles[i]
         
    def getGeoAngles(self, scan, angleNames):
        """
        This function returns all of the geometry angles for the
        for the scan as a N-by-num_geo array, where N is the number of scan
        points and num_geo is the number of geometry motors.
        """
#        scan = self.sd[scanNo]
        geoAngles = self.getScanAngles(scan, angleNames)
        if not (self.instConfig.getSampleAngleMappingFunctionName() == ""):
            tb = None
            try:
                self.fixGeoAngles(scan, geoAngles)
            except Exception as ex:
                tb = traceback.format_exc()
                raise RSMap3DException("Handling exception in getGeoAngles." + \
                                       "\n" + \
                                       str(ex) + \
                                       "\n" + \
                                       str(tb))
        logger.debug("getGeoAngles:\n" + str(geoAngles))
        return geoAngles
    
    def getUBMatrix(self, scan):
        """
        Read UB matrix from the #G3 line from the spec file. 
        """
        try:
            g3 = scan.G["G3"].strip().split()
            g3 = np.array(list(map(float, g3)))
            ub = g3.reshape(-1,3)
            logger.debug("ub " +str(ub))
            return ub
        except:
            logger.error("Unable to read UB Matrix from G3")
            logger.error( '-'*60)
            traceback.print_exc(file=sys.stdout)
            logger.error('-'*60)
            
            
    def hotpixelkill(self, areaData):
        """
        function to remove hot pixels from CCD frames
        ADD REMOVE VALUES IF NEEDED!
        :param areaData: area detector data
        """
        
        for pixel in self.getBadPixels():
            badLoc = pixel.getBadLocation()
            replaceLoc = pixel.getReplacementLocation()
            areaData[badLoc[0],badLoc[1]] = \
                areaData[replaceLoc[0],replaceLoc[1]]
        
        return areaData

    def loadSource(self, mapHKL=False):
        '''
        This method does the work of loading data from the files.  This has been
        split off from the constructor to allow this to be threaded and later 
        canceled.
        :param mapHKL: boolean to mark if the data should be mapped to HKL
        '''
        # Load up the instrument configuration file
        self.loadInstrumentXMLConfig()
        #Load up the detector configuration file
        self.loadDetectorXMLConfig()

        self.specFile = os.path.join(self.projectDir, self.projectName + \
                                     self.projectExt)
        imageDir = os.path.join(self.projectDir, \
                                IMAGE_DIR_MERGE_STR % self.projectName)
        self.imageFileTmp = os.path.join(imageDir, \
                                TIFF_FILE_MERGE_STR % 
                                (self.projectName))
        # if needed load up the bad pixel file.
        if not (self.badPixelFile is None):
            
            badPixelFile = PilatusBadPixelFile(self.badPixelFile)
            self.badPixels = badPixelFile.getBadPixels()
             
        # id needed load the flat field file
        if not (self.flatFieldFile is None):
            self.flatFieldData = np.array(Image.open(self.flatFieldFile)).T
        # Load scan information from the spec file
        try:
            self.sd = SpecDataFile(self.specFile)
            self.mapHKL = mapHKL
            maxScan = int(self.sd.getMaxScanNumber())
            logger.debug("Number of Scans" +  str(maxScan))
            if self.scans  is None:
                self.scans = range(1, maxScan+1)
            imagePath = os.path.join(self.projectDir, 
                            IMAGE_DIR_MERGE_STR % self.projectName)
            
            self.imageBounds = {}
            self.imageToBeUsed = {}
            self.availableScans = []
            self.incidentEnergy = {}
            self.ubMatrix = {}
            self.scanType = {}
            self.progress = 0
            self.progressInc = 1
            # Zero the progress bar at the beginning.
            if self.progressUpdater is not None:
                self.progressUpdater(self.progress, self.progressMax)
            for scan in self.scans:
                if (self.cancelLoad):
                    self.cancelLoad = False
                    raise LoadCanceledException(CANCEL_STR)
                
                else:
                    if (os.path.exists(os.path.join(imagePath, \
                                            SCAN_NUMBER_MERGE_STR % scan))):
                        try:
                            curScan = self.sd.scans[str(scan)]
                            self.scanType[scan] = \
                                self.sd.scans[str(scan)].scanCmd.split()[0]
                            angles = self.getGeoAngles(curScan, self.angleNames)
                            self.availableScans.append(scan)
                            if self.mapHKL==True:
                                self.ubMatrix[scan] = self.getUBMatrix(curScan)
                                if self.ubMatrix[scan] is None:
                                    raise Sector33SpecFileException("UB matrix " + \
                                                                    "not found.")
                            else:
                                self.ubMatrix[scan] = None
                            self.incidentEnergy[scan] = 12398.4 /float(curScan.G['G4'].split()[3])
                            _start_time = time.time()
                            self.imageBounds[scan] = \
                                self.findImageQs(angles, \
                                                 self.ubMatrix[scan], \
                                                 self.incidentEnergy[scan])
                            if self.progressUpdater is not None:
                                self.progressUpdater(self.progress, self.progressMax)
                            logger.info (('Elapsed time for Finding qs for scan %d: ' +
                                   '%.3f seconds') % \
                                   (scan, (time.time() - _start_time)))
                        except ScanDataMissingException:
                            logger.error( "Scan " + str(scan) + " has no data")
                    #Make sure to show 100% completion
            if self.progressUpdater is not None:
                self.progressUpdater(self.progressMax, self.progressMax)
        except IOError:
            raise IOError( "Cannot open file " + str(self.specFile))
        if len(self.getAvailableScans()) == 0:
            raise ScanDataMissingException("Could not find scan data for " + \
                                           "input file \n" + self.specFile + \
                                           "\nOne possible reason for this " + \
                                           "is that the image files are " + \
                                           "missing.  Images are assumed " + \
                                           "to be in " + \
                                           os.path.join(self.projectDir, 
                                        IMAGE_DIR_MERGE_STR % self.projectName))

        self.availableScanTypes = set(self.scanType.values())

        
    
    def to_bool(self, value):
        """
        Note this method found in answer to:
        http://stackoverflow.com/questions/715417/converting-from-a-string-to-boolean-in-python
        Converts 'something' to boolean. Raises exception if it gets a string 
        it doesn't handle.
        Case is ignored for strings. These string values are handled:
          True: 'True', "1", "TRue", "yes", "y", "t"
          False: "", "0", "faLse", "no", "n", "f"
        Non-string values are passed to bool.
        """
        if type(value) == type(''):
            if value.lower() in ("yes", "y", "true",  "t", "1"):
                return True
            if value.lower() in ("no",  "n", "false", "f", "0", ""):
                return False
            raise Exception('Invalid value for boolean conversion: ' + value)
        return bool(value)

    def rawmap(self,scans, angdelta=[0,0,0,0,0],
            adframes=None, mask = None):
        """
        read ad frames and and convert them in reciprocal space
        angular coordinates are taken from the spec file
        or read from the edf file header when no scan number is given (scannr=None)
        """
        
        if mask is None:
            mask_was_none = True
            #mask = [True] * len(self.getImageToBeUsed()[scans[0]])
        else:
            mask_was_none = False
        #sd = spec.SpecDataFile(self.specFile)
        intensity = np.array([])
        
        # fourc goniometer in fourc coordinates
        # convention for coordinate system:
        # x: upwards;
        # y: along the incident beam;
        # z: "outboard" (makes coordinate system right-handed).
        # QConversion will set up the goniometer geometry.
        # So the first argument describes the sample rotations, the second the
        # detector rotations and the third the primary beam direction.
        qconv = xu.experiment.QConversion(self.getSampleCircleDirections(), \
                                    self.getDetectorCircleDirections(), \
                                    self.getPrimaryBeamDirection())
    
        # define experimental class for angle conversion
        #
        # ipdir: inplane reference direction (ipdir points into the primary beam
        #        direction at zero angles)
        # ndir:  surface normal of your sample (ndir points in a direction
        #        perpendicular to the primary beam and the innermost detector
        #        rotation axis)
        en = self.getIncidentEnergy()
        hxrd = xu.HXRD(self.getInplaneReferenceDirection(), \
                       self.getSampleSurfaceNormalDirection(), \
                       en=en[self.getAvailableScans()[0]], \
                       qconv=qconv)

        
        # initialize area detector properties
        if (self.getDetectorPixelWidth() != None ) and \
            (self.getDistanceToDetector() != None):
            hxrd.Ang2Q.init_area(self.getDetectorPixelDirection1(), \
                self.getDetectorPixelDirection2(), \
                cch1=self.getDetectorCenterChannel()[0], \
                cch2=self.getDetectorCenterChannel()[1], \
                Nch1=self.getDetectorDimensions()[0], \
                Nch2=self.getDetectorDimensions()[1], \
                pwidth1=self.getDetectorPixelWidth()[0], \
                pwidth2=self.getDetectorPixelWidth()[1], \
                distance=self.getDistanceToDetector(), \
                Nav=self.getNumPixelsToAverage(), \
                roi=self.getDetectorROI()) 
        else:
            hxrd.Ang2Q.init_area(self.getDetectorPixelDirection1(), \
                self.getDetectorPixelDirection2(), \
                cch1=self.getDetectorCenterChannel()[0], \
                cch2=self.getDetectorCenterChannel()[1], \
                Nch1=self.getDetectorDimensions()[0], \
                Nch2=self.getDetectorDimensions()[1], \
                chpdeg1=self.getDetectorChannelsPerDegree()[0], \
                chpdeg2=self.getDetectorChannelsPerDegree()[1], \
                Nav=self.getNumPixelsToAverage(), 
                roi=self.getDetectorROI()) 
            
        angleNames = self.getAngles()
        scanAngle = {}
        for i in range(len(angleNames)):
            scanAngle[i] = np.array([])
    
        offset = 0
        imageToBeUsed = self.getImageToBeUsed()
        monitorName = self.getMonitorName()
        monitorScaleFactor = self.getMonitorScaleFactor()
        filterName = self.getFilterName()
        filterScaleFactor = self.getFilterScaleFactor()
        for scannr in scans:
            if self.haltMap:
                raise ProcessCanceledException("Process Canceled")
            scan = self.sd.scans[str(scannr)]
            angles = self.getGeoAngles(scan, angleNames)
            scanAngle1 = {}
            scanAngle2 = {}
            for i in range(len(angleNames)):
                scanAngle1[i] = angles[:,i]
                scanAngle2[i] = []
            if monitorName != None:
                monitor_data = scan.data.get(monitorName)
                if monitor_data is None:
                    raise IOError("Did not find Monitor source '" + \
                                  monitorName + \
                                  "' in the Spec file.  Make sure " + \
                                  "monitorName is correct in the " + \
                                  "instrument Config file")
            if filterName != None:
                filter_data = scan.data.get(filterName)
                if filter_data is None:
                    raise IOError("Did not find filter source '" + \
                                  filterName + \
                                  "' in the Spec file.  Make sure " + \
                                  "filterName is correct in the " + \
                                  "instrument Config file")
            # read in the image data
            arrayInitializedForScan = False
            foundIndex = 0
            
            if mask_was_none:
                mask = [True] * len(self.getImageToBeUsed()[scannr])            
            
            for ind in range(len(scan.data[list(scan.data.keys())[0]])):
                if imageToBeUsed[scannr][ind] and mask[ind]:    
                    # read tif image
                    im = Image.open(self.imageFileTmp % (scannr, scannr, ind))
                    img = np.array(im.getdata()).reshape(im.size[1],im.size[0]).T
                    img = self.hotpixelkill(img)
                    ff_data = self.getFlatFieldData()
                    if not (ff_data is None):
                        img = img * ff_data
                    # reduce data siz
                    img2 = xu.blockAverage2D(img, 
                                            self.getNumPixelsToAverage()[0], \
                                            self.getNumPixelsToAverage()[1], \
                                            roi=self.getDetectorROI())

                    # apply intensity corrections
                    if monitorName != None:
                        img2 = img2 / monitor_data[ind] * monitorScaleFactor
                    if filterName != None:
                        img2 = img2 / filter_data[ind] * filterScaleFactor

                    # initialize data array
                    if not arrayInitializedForScan:
                        imagesToProcess = [imageToBeUsed[scannr][i] and mask[i] for i in range(len(imageToBeUsed[scannr]))]
                        if not intensity.shape[0]:
                            intensity = np.zeros((np.count_nonzero(imagesToProcess),) + img2.shape)
                            arrayInitializedForScan = True
                        else: 
                            offset = intensity.shape[0]
                            intensity = np.concatenate(
                                (intensity,
                                (np.zeros((np.count_nonzero(imagesToProcess),) + img2.shape))),
                                axis=0)
                            arrayInitializedForScan = True
                    # add data to intensity array
                    intensity[foundIndex+offset,:,:] = img2
                    for i in range(len(angleNames)):
#                         logger.debug("appending angles to angle2 " + 
#                                      str(scanAngle1[i][ind]))
                        scanAngle2[i].append(scanAngle1[i][ind])
                    foundIndex += 1
            if len(scanAngle2[0]) > 0:
                for i in range(len(angleNames)):
                    scanAngle[i] = \
                        np.concatenate((scanAngle[i], np.array(scanAngle2[i])), \
                                          axis=0)
        # transform scan angles to reciprocal space coordinates for all detector pixels
        angleList = []
        for i in range(len(angleNames)):
            angleList.append(scanAngle[i])
        if self.ubMatrix[scans[0]] is None:
            qx, qy, qz = hxrd.Ang2Q.area(*angleList,  \
                            roi=self.getDetectorROI(), 
                            Nav=self.getNumPixelsToAverage())
        else:
            qx, qy, qz = hxrd.Ang2Q.area(*angleList, \
                            roi=self.getDetectorROI(), 
                            Nav=self.getNumPixelsToAverage(), \
                            UB = self.ubMatrix[scans[0]])
            

        # apply selected transform
        qxTrans, qyTrans, qzTrans = \
            self.transform.do3DTransform(qx, qy, qz)

    
        return qxTrans, qyTrans, qzTrans, intensity
예제 #28
0
    def loadSource(self, mapHKL=False):
        '''
        This method does the work of loading data from the files.  This has been
        split off from the constructor to allow this to be threaded and later 
        canceled.
        :param mapHKL: boolean to mark if the data should be mapped to HKL
        '''
        # Load up the instrument configuration file
        self.loadInstrumentXMLConfig()
        #Load up the detector configuration file
        self.loadDetectorXMLConfig()

        self.specFile = os.path.join(self.projectDir, self.projectName + \
                                     self.projectExt)
        imageDir = os.path.join(self.projectDir, \
                                IMAGE_DIR_MERGE_STR % self.projectName)
        self.imageFileTmp = os.path.join(imageDir, \
                                TIFF_FILE_MERGE_STR % 
                                (self.projectName))
        # if needed load up the bad pixel file.
        if not (self.badPixelFile is None):
            
            badPixelFile = PilatusBadPixelFile(self.badPixelFile)
            self.badPixels = badPixelFile.getBadPixels()
             
        # id needed load the flat field file
        if not (self.flatFieldFile is None):
            self.flatFieldData = np.array(Image.open(self.flatFieldFile)).T
        # Load scan information from the spec file
        try:
            self.sd = SpecDataFile(self.specFile)
            self.mapHKL = mapHKL
            maxScan = int(self.sd.getMaxScanNumber())
            logger.debug("Number of Scans" +  str(maxScan))
            if self.scans  is None:
                self.scans = range(1, maxScan+1)
            imagePath = os.path.join(self.projectDir, 
                            IMAGE_DIR_MERGE_STR % self.projectName)
            
            self.imageBounds = {}
            self.imageToBeUsed = {}
            self.availableScans = []
            self.incidentEnergy = {}
            self.ubMatrix = {}
            self.scanType = {}
            self.progress = 0
            self.progressInc = 1
            # Zero the progress bar at the beginning.
            if self.progressUpdater is not None:
                self.progressUpdater(self.progress, self.progressMax)
            for scan in self.scans:
                if (self.cancelLoad):
                    self.cancelLoad = False
                    raise LoadCanceledException(CANCEL_STR)
                
                else:
                    if (os.path.exists(os.path.join(imagePath, \
                                            SCAN_NUMBER_MERGE_STR % scan))):
                        try:
                            curScan = self.sd.scans[str(scan)]
                            self.scanType[scan] = \
                                self.sd.scans[str(scan)].scanCmd.split()[0]
                            angles = self.getGeoAngles(curScan, self.angleNames)
                            self.availableScans.append(scan)
                            if self.mapHKL==True:
                                self.ubMatrix[scan] = self.getUBMatrix(curScan)
                                if self.ubMatrix[scan] is None:
                                    raise Sector33SpecFileException("UB matrix " + \
                                                                    "not found.")
                            else:
                                self.ubMatrix[scan] = None
                            self.incidentEnergy[scan] = 12398.4 /float(curScan.G['G4'].split()[3])
                            _start_time = time.time()
                            self.imageBounds[scan] = \
                                self.findImageQs(angles, \
                                                 self.ubMatrix[scan], \
                                                 self.incidentEnergy[scan])
                            if self.progressUpdater is not None:
                                self.progressUpdater(self.progress, self.progressMax)
                            logger.info (('Elapsed time for Finding qs for scan %d: ' +
                                   '%.3f seconds') % \
                                   (scan, (time.time() - _start_time)))
                        except ScanDataMissingException:
                            logger.error( "Scan " + str(scan) + " has no data")
                    #Make sure to show 100% completion
            if self.progressUpdater is not None:
                self.progressUpdater(self.progressMax, self.progressMax)
        except IOError:
            raise IOError( "Cannot open file " + str(self.specFile))
        if len(self.getAvailableScans()) == 0:
            raise ScanDataMissingException("Could not find scan data for " + \
                                           "input file \n" + self.specFile + \
                                           "\nOne possible reason for this " + \
                                           "is that the image files are " + \
                                           "missing.  Images are assumed " + \
                                           "to be in " + \
                                           os.path.join(self.projectDir, 
                                        IMAGE_DIR_MERGE_STR % self.projectName))

        self.availableScanTypes = set(self.scanType.values())
예제 #29
0
 def openFile(self, filename):
     '''open the SPEC file and get its data'''
     from spec2nexus.spec import SpecDataFile
     if os.path.exists(filename):
         self.SPECfile = SpecDataFile(filename)
예제 #30
0
class Parser(object):
    '''parse the spec data file object'''
    def __init__(self, spec_data=None):
        ''':param obj spec_data: instance of :class:`spec2nexus.prjPySpec.SpecDataFile`'''
        self.SPECfile = spec_data
        self.progress_bar = spec_data.progress_bar
        self.update_progress = spec_data.update_progress

    def openFile(self, filename):
        '''open the SPEC file and get its data'''
        from spec2nexus.spec import SpecDataFile
        if os.path.exists(filename):
            self.SPECfile = SpecDataFile(filename)

    def toTree(self, scan_list=[]):
        '''
        convert scans from chosen SPEC file into NXroot object and structure
        
        called from nexpy.readers.readspec.ImportDialog.get_data__prjPySpec() after clicking <Ok> in dialog
        
        Each scan in the range from self.scanmin to self.scanmax (inclusive)
        will be converted to a NXentry.  Scan data will go in a NXdata where 
        the signal=1 is the last column and the corresponding axes= is the first column.
        
        :param [int] scanlist
        :raises: ValueError is Min or Max scan number are not given properly
        '''
        import spec2nexus
        from spec2nexus import utils
        # check that scan_list is valid
        if len(scan_list) == 0:
            return None

        if self.SPECfile is None:
            return None

        complete_scan_list = list(self.SPECfile.scans)
        for key in [str(s) for s in scan_list]:
            if key not in complete_scan_list:
                msg = 'scan ' + str(key) + ' was not found'
                raise ValueError(msg)

        root = NXroot()

        root.attrs['spec2nexus'] = str(spec2nexus.__version__)
        header0 = self.SPECfile.headers[0]
        root.attrs['SPEC_file'] = self.SPECfile.fileName
        root.attrs['SPEC_epoch'] = header0.epoch
        root.attrs['SPEC_date'] = utils.iso8601(header0.date)
        root.attrs['SPEC_comments'] = '\n'.join(header0.comments)
        try:
            c = header0.comments[0]
            user = c[c.find('User = '******'=')[1].strip()
            root.attrs['SPEC_user'] = user
        except:
            pass
        root.attrs['SPEC_num_headers'] = len(self.SPECfile.headers)

        self.progress_bar.setVisible(True)
        self.progress_bar.setRange(scan_list[0], scan_list[-1])
        for key in [str(s) for s in scan_list]:
            scan = self.SPECfile.getScan(key)
            scan.interpret()
            entry = NXentry()
            entry.title = str(scan)
            entry.date = utils.iso8601(scan.date)
            entry.command = scan.scanCmd
            entry.scan_number = NXfield(scan.scanNum)
            entry.comments = '\n'.join(scan.comments)
            entry.data = self.scan_NXdata(scan)  # store the scan data
            entry.positioners = self.metadata_NXlog(
                scan.positioner, 'SPEC positioners (#P & #O lines)')
            if hasattr(scan, 'metadata') and len(scan.metadata) > 0:
                entry.metadata = self.metadata_NXlog(
                    scan.metadata,
                    'SPEC metadata (UNICAT-style #H & #V lines)')

            if len(scan.G) > 0:
                entry.G = NXlog()
                desc = "SPEC geometry arrays, meanings defined by SPEC diffractometer support"
                # e.g.: SPECD/four.mac
                # http://certif.com/spec_manual/fourc_4_9.html
                entry.G.attrs['description'] = desc
                for item, value in scan.G.items():
                    entry.G[item] = NXfield(list(map(float, value.split())))
            if scan.T != '':
                entry['counting_basis'] = NXfield(
                    'SPEC scan with constant counting time')
                entry['T'] = NXfield(float(scan.T))
                entry['T'].units = 'seconds'
                entry[
                    'T'].description = 'SPEC scan with constant counting time'
            elif scan.M != '':
                entry['counting_basis'] = NXfield(
                    'SPEC scan with constant monitor count')
                entry['M'] = NXfield(float(scan.M))
                entry['M'].units = 'counts'
                entry[
                    'M'].description = 'SPEC scan with constant monitor count'
            if scan.Q != '':
                entry['Q'] = NXfield(list(map(float, scan.Q)))
                entry['Q'].description = 'hkl at start of scan'

            root['scan_' + str(key)] = entry

            self.progress_bar.setValue(int(key))
            self.update_progress()

        return root

    def scan_NXdata(self, scan):
        '''
        return the scan data in an NXdata object
        '''
        nxdata = NXdata()

        if len(scan.data) == 0:  # what if no data?
            # since no data available, provide trivial, fake data
            # this keeps the NXdata base class compliant with the NeXus standard
            nxdata.attrs['description'] = 'SPEC scan has no data'
            nxdata['noSpecData_y'] = NXfield([0, 0])  # primary Y axis
            nxdata['noSpecData_x'] = NXfield([0, 0])  # primary X axis
            nxdata.nxsignal = nxdata['noSpecData_y']
            nxdata.nxaxes = [
                nxdata['noSpecData_x'],
            ]
            return nxdata

        nxdata.attrs['description'] = 'SPEC scan data'

        scan_type = scan.scanCmd.split()[0]
        if scan_type in ('mesh', 'hklmesh'):
            # hklmesh  H 1.9 2.1 100  K 1.9 2.1 100  -800000
            self.parser_mesh(nxdata, scan)
        elif scan_type in ('hscan', 'kscan', 'lscan', 'hklscan'):
            # hklscan  1.00133 1.00133  1.00133 1.00133  2.85 3.05  200 -400000
            h_0, h_N, k_0, k_N, l_0, l_N = scan.scanCmd.split()[1:7]
            if h_0 != h_N: axis = 'H'
            elif k_0 != k_N: axis = 'K'
            elif l_0 != l_N: axis = 'L'
            else: axis = 'H'
            self.parser_1D_columns(nxdata, scan)
            nxdata.nxaxes = nxdata[axis]
        else:
            self.parser_1D_columns(nxdata, scan)

        # these locations suggested to NIAC, easier to parse than attached to dataset!
        # but these are already set by the `nxsignal` and `nxaxes` assignments
        #nxdata.attrs['signal'] = nxdata.nxsignal.nxname
        #nxdata.attrs['axes'] = ':'.join([obj.nxname for obj in nxdata.nxaxes])
        return nxdata

    def parser_1D_columns(self, nxdata, scan):
        '''generic data parser for 1-D column data'''
        from spec2nexus import utils
        for column in scan.L:
            if column in scan.data:
                clean_name = utils.sanitize_name(nxdata, column)
                nxdata[clean_name] = NXfield(scan.data[column])
                nxdata[clean_name].original_name = column

        signal = utils.sanitize_name(nxdata,
                                     scan.column_last)  # primary Y axis
        axis = utils.sanitize_name(nxdata, scan.column_first)  # primary X axis
        nxdata.nxsignal = nxdata[signal]
        nxdata.nxaxes = nxdata[axis]

        self.parser_mca_spectra(nxdata, scan, axis)

    def parser_mca_spectra(self, nxdata, scan, primary_axis_label):
        '''parse for optional MCA spectra'''
        if '_mca_' in scan.data:  # check for it
            for mca_key, mca_data in scan.data['_mca_'].items():
                key = "__" + mca_key
                nxdata[key] = NXfield(mca_data)
                nxdata[key].units = "counts"
                ch_key = key + "_channel"
                nxdata[ch_key] = NXfield(range(1, len(mca_data[0]) + 1))
                nxdata[ch_key].units = 'channel'
                axes = (primary_axis_label, ch_key)
                nxdata[key].axes = ':'.join(axes)

    def parser_mesh(self, nxdata, scan):
        '''data parser for 2-D mesh and hklmesh'''
        # 2-D parser: http://www.certif.com/spec_help/mesh.html
        #  mesh motor1 start1 end1 intervals1 motor2 start2 end2 intervals2 time
        # 2-D parser: http://www.certif.com/spec_help/hklmesh.html
        #  hklmesh Q1 start1 end1 intervals1 Q2 start2 end2 intervals2 time
        # mesh:    nexpy/examples/33id_spec.dat  scan 22  (also has MCA, thus 3-D data)
        # hklmesh: nexpy/examples/33bm_spec.dat  scan 17  (no MCA data)
        from spec2nexus import utils
        label1, start1, end1, intervals1, label2, start2, end2, intervals2, time = scan.scanCmd.split(
        )[1:]
        if label1 not in scan.data:
            label1 = scan.L[0]  # mnemonic v. name
        if label2 not in scan.data:
            label2 = scan.L[1]  # mnemonic v. name
        axis1 = scan.data.get(label1)
        axis2 = scan.data.get(label2)
        intervals1, intervals2 = int(intervals1), int(intervals2)
        start1, end1 = float(start1), float(end1)
        start2, end2 = float(start2), float(end2)
        time = float(time)
        if len(axis1) < intervals1:  # stopped scan before second row started
            self.parser_1D_columns(nxdata, scan)  # fallback support
            # TODO: what about the MCA data in this case?
        else:
            axis1 = axis1[0:intervals1 + 1]
            axis2 = [
                axis2[row] for row in range(len(axis2))
                if row % (intervals1 + 1) == 0
            ]

            column_labels = scan.L
            column_labels.remove(label1)  # special handling
            column_labels.remove(label2)  # special handling
            if scan.scanCmd.startswith('hkl'):
                # find the reciprocal space axis held constant
                label3 = [
                    key for key in ('H', 'K', 'L')
                    if key not in (label1, label2)
                ][0]
                axis3 = scan.data.get(label3)[0]
                nxdata[label3] = NXfield(axis3)
                column_labels.remove(label3)  # already handled

            nxdata[label1] = NXfield(axis1)  # 1-D array
            nxdata[label2] = NXfield(axis2)  # 1-D array

            # build 2-D data objects (do not build label1, label2, [or label3] as 2-D objects)
            data_shape = [len(axis2), len(axis1)]
            for label in column_labels:
                axis = np.array(scan.data.get(label))
                clean_name = utils.sanitize_name(nxdata, label)
                nxdata[clean_name] = NXfield(
                    utils.reshape_data(axis, data_shape))
                nxdata[clean_name].original_name = label

            signal_axis_label = utils.sanitize_name(nxdata, scan.column_last)
            nxdata.nxsignal = nxdata[signal_axis_label]
            nxdata.nxaxes = [nxdata[label2], nxdata[label1]]

        if '_mca_' in scan.data:  # 3-D array
            # TODO: ?merge with parser_mca_spectra()?
            for mca_key, mca_data in scan.data['_mca_'].items():
                key = "__" + mca_key

                spectra_lengths = list(map(len, mca_data))
                num_channels = max(spectra_lengths)
                if num_channels != min(spectra_lengths):
                    msg = 'MCA spectra have different lengths'
                    msg += ' in scan #' + str(scan.scanNum)
                    msg += ' in file ' + str(scan.specFile)
                    raise ValueError(msg)

                data_shape += [
                    num_channels,
                ]
                mca = np.array(mca_data)
                nxdata[key] = NXfield(utils.reshape_data(mca, data_shape))
                nxdata[key].units = "counts"

                try:
                    # use MCA channel numbers as known at time of scan
                    chan1 = scan.MCA['first_saved']
                    chanN = scan.MCA['last_saved']
                    channel_range = range(chan1, chanN + 1)
                except:
                    # basic indices
                    channel_range = range(1, num_channels + 1)

                ch_key = key + "_channel"
                nxdata[ch_key] = NXfield(channel_range)
                nxdata[ch_key].units = 'channel'
                axes = (label1, label2, ch_key)
                nxdata[key].axes = ':'.join(axes)

    def metadata_NXlog(self, spec_metadata, description):
        '''
        return the specific metadata in an NXlog object
        '''
        from spec2nexus import utils
        nxlog = NXlog()
        nxlog.attrs['description'] = description
        for subkey, value in spec_metadata.items():
            clean_name = utils.sanitize_name(nxlog, subkey)
            nxlog[clean_name] = NXfield(value)
            nxlog[clean_name].original_name = subkey
        return nxlog
예제 #31
0
파일: readspec.py 프로젝트: nexpy/nexpy
class Parser(object):
    '''parse the spec data file object'''
    
    def __init__(self, spec_data = None):
        ''':param obj spec_data: instance of :class:`spec2nexus.prjPySpec.SpecDataFile`'''
        self.SPECfile = spec_data
        self.progress_bar = spec_data.progress_bar
        self.update_progress = spec_data.update_progress
    
    def openFile(self, filename):
        '''open the SPEC file and get its data'''
        from spec2nexus.spec import SpecDataFile
        if os.path.exists(filename):
            self.SPECfile = SpecDataFile(filename)
    
    def toTree(self, scan_list=[]):
        '''
        convert scans from chosen SPEC file into NXroot object and structure
        
        called from nexpy.readers.readspec.ImportDialog.get_data__prjPySpec() after clicking <Ok> in dialog
        
        Each scan in the range from self.scanmin to self.scanmax (inclusive)
        will be converted to a NXentry.  Scan data will go in a NXdata where 
        the signal=1 is the last column and the corresponding axes= is the first column.
        
        :param [int] scanlist
        :raises: ValueError is Min or Max scan number are not given properly
        '''
        import spec2nexus
        from spec2nexus import utils
        # check that scan_list is valid
        if len(scan_list) == 0:
            return None
        
        if self.SPECfile is None:
            return None

        complete_scan_list = list(self.SPECfile.scans)
        for key in [str(s) for s in scan_list]:
            if key not in complete_scan_list:
                msg = 'scan ' + str(key) + ' was not found'
                raise ValueError(msg)

        root = NXroot()

        root.attrs['spec2nexus'] = str(spec2nexus.__version__)
        header0 = self.SPECfile.headers[0]
        root.attrs['SPEC_file'] = self.SPECfile.fileName
        root.attrs['SPEC_epoch'] = header0.epoch
        root.attrs['SPEC_date'] = utils.iso8601(header0.date)
        root.attrs['SPEC_comments'] = '\n'.join(header0.comments)
        try:
            c = header0.comments[0]
            user = c[c.find('User = '******'=')[1].strip()
            root.attrs['SPEC_user'] = user
        except:
            pass
        root.attrs['SPEC_num_headers'] = len(self.SPECfile.headers)

        self.progress_bar.setVisible(True)
        self.progress_bar.setRange(scan_list[0], scan_list[-1])
        for key in [str(s) for s in scan_list]:
            scan = self.SPECfile.getScan(key)
            scan.interpret()
            entry = NXentry()
            entry.title = str(scan)
            entry.date = utils.iso8601(scan.date)  
            entry.command = scan.scanCmd 
            entry.scan_number = NXfield(scan.scanNum)
            entry.comments = '\n'.join(scan.comments)
            entry.data = self.scan_NXdata(scan)            # store the scan data
            entry.positioners = self.metadata_NXlog(scan.positioner, 
                                                    'SPEC positioners (#P & #O lines)')
            if hasattr(scan, 'metadata') and len(scan.metadata) > 0:
                entry.metadata = self.metadata_NXlog(scan.metadata, 
                                                     'SPEC metadata (UNICAT-style #H & #V lines)')

            if len(scan.G) > 0:
                entry.G = NXlog()
                desc = "SPEC geometry arrays, meanings defined by SPEC diffractometer support"
                # e.g.: SPECD/four.mac
                # http://certif.com/spec_manual/fourc_4_9.html
                entry.G.attrs['description'] = desc
                for item, value in scan.G.items():
                    entry.G[item] = NXfield(list(map(float, value.split())))
            if scan.T != '':
                entry['counting_basis'] = NXfield('SPEC scan with constant counting time')
                entry['T'] = NXfield(float(scan.T))
                entry['T'].units = 'seconds'
                entry['T'].description = 'SPEC scan with constant counting time'
            elif scan.M != '':
                entry['counting_basis'] = NXfield('SPEC scan with constant monitor count')
                entry['M'] = NXfield(float(scan.M))
                entry['M'].units = 'counts'
                entry['M'].description = 'SPEC scan with constant monitor count'
            if scan.Q != '':
                entry['Q'] = NXfield(list(map(float,scan.Q)))
                entry['Q'].description = 'hkl at start of scan'

            root['scan_' + str(key)] = entry

            self.progress_bar.setValue(int(key))
            self.update_progress()

        return root
    
    def scan_NXdata(self, scan):
        '''
        return the scan data in an NXdata object
        '''
        nxdata = NXdata()

        if len(scan.data) == 0:       # what if no data?
            # since no data available, provide trivial, fake data
            # this keeps the NXdata base class compliant with the NeXus standard
            nxdata.attrs['description'] = 'SPEC scan has no data'
            nxdata['noSpecData_y'] = NXfield([0, 0])   # primary Y axis
            nxdata['noSpecData_x'] = NXfield([0, 0])   # primary X axis
            nxdata.nxsignal = nxdata['noSpecData_y']
            nxdata.nxaxes   = [nxdata['noSpecData_x'], ]
            return nxdata

        nxdata.attrs['description'] = 'SPEC scan data'
        
        scan_type = scan.scanCmd.split()[0]
        if scan_type in ('mesh', 'hklmesh'):
            # hklmesh  H 1.9 2.1 100  K 1.9 2.1 100  -800000
            self.parser_mesh(nxdata, scan)
        elif scan_type in ('hscan', 'kscan', 'lscan', 'hklscan'):
            # hklscan  1.00133 1.00133  1.00133 1.00133  2.85 3.05  200 -400000
            h_0, h_N, k_0, k_N, l_0, l_N = scan.scanCmd.split()[1:7]
            if   h_0 != h_N: axis = 'H'
            elif k_0 != k_N: axis = 'K'
            elif l_0 != l_N: axis = 'L'
            else: axis = 'H'
            self.parser_1D_columns(nxdata, scan)
            nxdata.nxaxes = nxdata[axis]
        else:
            self.parser_1D_columns(nxdata, scan)

        # these locations suggested to NIAC, easier to parse than attached to dataset!
        # but these are already set by the `nxsignal` and `nxaxes` assignments
        #nxdata.attrs['signal'] = nxdata.nxsignal.nxname         
        #nxdata.attrs['axes'] = ':'.join([obj.nxname for obj in nxdata.nxaxes])
        return nxdata
    
    def parser_1D_columns(self, nxdata, scan):
        '''generic data parser for 1-D column data'''
        from spec2nexus import utils
        for column in scan.L:
            if column in scan.data:
                clean_name = utils.sanitize_name(nxdata, column)
                nxdata[clean_name] = NXfield(scan.data[column])
                nxdata[clean_name].original_name = column

        signal = utils.sanitize_name(nxdata, scan.column_last)      # primary Y axis
        axis = utils.sanitize_name(nxdata, scan.column_first)       # primary X axis
        nxdata.nxsignal = nxdata[signal]
        nxdata.nxaxes = nxdata[axis]
        
        self.parser_mca_spectra(nxdata, scan, axis)
    
    def parser_mca_spectra(self, nxdata, scan, primary_axis_label):
        '''parse for optional MCA spectra'''
        if '_mca_' in scan.data:        # check for it
            for mca_key, mca_data in scan.data['_mca_'].items():
                key = "__" + mca_key
                nxdata[key] = NXfield(mca_data)
                nxdata[key].units = "counts"
                ch_key = key + "_channel"
                nxdata[ch_key] = NXfield(range(1, len(mca_data[0])+1))
                nxdata[ch_key].units = 'channel'
                axes = (primary_axis_label, ch_key)
                nxdata[key].axes = ':'.join( axes )
    
    def parser_mesh(self, nxdata, scan):
        '''data parser for 2-D mesh and hklmesh'''
        # 2-D parser: http://www.certif.com/spec_help/mesh.html
        #  mesh motor1 start1 end1 intervals1 motor2 start2 end2 intervals2 time
        # 2-D parser: http://www.certif.com/spec_help/hklmesh.html
        #  hklmesh Q1 start1 end1 intervals1 Q2 start2 end2 intervals2 time
        # mesh:    nexpy/examples/33id_spec.dat  scan 22  (also has MCA, thus 3-D data)
        # hklmesh: nexpy/examples/33bm_spec.dat  scan 17  (no MCA data)
        from spec2nexus import utils
        label1, start1, end1, intervals1, label2, start2, end2, intervals2, time = scan.scanCmd.split()[1:]
        if label1 not in scan.data:
            label1 = scan.L[0]      # mnemonic v. name
        if label2 not in scan.data:
            label2 = scan.L[1]      # mnemonic v. name
        axis1 = scan.data.get(label1)
        axis2 = scan.data.get(label2)
        intervals1, intervals2 = int(intervals1), int(intervals2)
        start1, end1 = float(start1), float(end1)
        start2, end2 = float(start2), float(end2)
        time = float(time)
        if len(axis1) < intervals1:     # stopped scan before second row started
            self.parser_1D_columns(nxdata, scan)        # fallback support
            # TODO: what about the MCA data in this case?
        else:
            axis1 = axis1[0:intervals1+1]
            axis2 = [axis2[row] for row in range(len(axis2)) if row % (intervals1+1) == 0]

            column_labels = scan.L
            column_labels.remove(label1)    # special handling
            column_labels.remove(label2)    # special handling
            if scan.scanCmd.startswith('hkl'):
                # find the reciprocal space axis held constant
                label3 = [key for key in ('H', 'K', 'L') if key not in (label1, label2)][0]
                axis3 = scan.data.get(label3)[0]
                nxdata[label3] = NXfield(axis3)
                column_labels.remove(label3)    # already handled

            nxdata[label1] = NXfield(axis1)    # 1-D array
            nxdata[label2] = NXfield(axis2)    # 1-D array

            # build 2-D data objects (do not build label1, label2, [or label3] as 2-D objects)
            data_shape = [len(axis2), len(axis1)]
            for label in column_labels:
                axis = np.array( scan.data.get(label) )
                clean_name = utils.sanitize_name(nxdata, label)
                nxdata[clean_name] = NXfield(utils.reshape_data(axis, data_shape))
                nxdata[clean_name].original_name = label

            signal_axis_label = utils.sanitize_name(nxdata, scan.column_last)
            nxdata.nxsignal = nxdata[signal_axis_label]
            nxdata.nxaxes = [nxdata[label2], nxdata[label1]]

        if '_mca_' in scan.data:    # 3-D array
            # TODO: ?merge with parser_mca_spectra()?
            for mca_key, mca_data in scan.data['_mca_'].items():
                key = "__" + mca_key

                spectra_lengths = list(map(len, mca_data))
                num_channels = max(spectra_lengths)
                if num_channels != min(spectra_lengths):
                    msg = 'MCA spectra have different lengths'
                    msg += ' in scan #' + str(scan.scanNum)
                    msg += ' in file ' + str(scan.specFile)
                    raise ValueError(msg)

                data_shape += [num_channels, ]
                mca = np.array(mca_data)
                nxdata[key] = NXfield(utils.reshape_data(mca, data_shape))
                nxdata[key].units = "counts"

                try:
                    # use MCA channel numbers as known at time of scan
                    chan1 = scan.MCA['first_saved']
                    chanN = scan.MCA['last_saved']
                    channel_range = range(chan1, chanN+1)
                except:
                    # basic indices
                    channel_range = range(1, num_channels+1)

                ch_key = key + "_channel"
                nxdata[ch_key] = NXfield(channel_range)
                nxdata[ch_key].units = 'channel'
                axes = (label1, label2, ch_key)
                nxdata[key].axes = ':'.join( axes )
    
    def metadata_NXlog(self, spec_metadata, description):
        '''
        return the specific metadata in an NXlog object
        '''
        from spec2nexus import utils
        nxlog = NXlog()
        nxlog.attrs['description'] = description
        for subkey, value in spec_metadata.items():
            clean_name = utils.sanitize_name(nxlog, subkey)
            nxlog[clean_name] = NXfield(value)
            nxlog[clean_name].original_name = subkey
        return nxlog
예제 #32
0
파일: readspec.py 프로젝트: nexpy/nexpy
 def openFile(self, filename):
     '''open the SPEC file and get its data'''
     from spec2nexus.spec import SpecDataFile
     if os.path.exists(filename):
         self.SPECfile = SpecDataFile(filename)
예제 #33
0
from mpl_toolkits.mplot3d import Axes3D, axes3d
import numpy as np
import matplotlib.pyplot as plt
from spec2nexus.spec import SpecDataFile
from PyQt5.QtWidgets import *
import pylab as plab
from matplotlib.backends.backend_qt5 import NavigationToolbar2QT as NavigationToolbar
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas

# Loading the data.
data = np.loadtxt("C:\\Users\\escal\\Downloads\\PVvalue.259")

# Loading Spec information for scan#: 259
# ========================================
specFile = SpecDataFile("C:\\Users\\escal\\Downloads\\pmnpt_1.spec")
l = specFile.scans["259"].data["L"]

########################################################################################################################
# Plotting the data in 3D

########################################################################################################################
fig1 = plt.figure()
ax = Axes3D(fig1)
ax.set_ylabel("Data")
ax.set_xlabel("Points")
ax.set_zlabel("L")
x = range(0, 64)  # This will have to be replaced by voltage
for i in range(0, 3721):
    z = np.full(64, l[i])
    y = data[i]
    ax.plot(x, y, zs=z)
예제 #34
0
파일: readspec.py 프로젝트: ericdill/nexpy
class ImportDialog(BaseImportDialog):
    """Dialog to import SPEC Scans"""

    def __init__(self, parent=None):

        super(ImportDialog, self).__init__(parent)

        self.accepted = False
        from nexpy.gui.consoleapp import _mainwindow

        self.default_directory = _mainwindow.default_directory
        self.import_file = None  # must set in self.get_data()
        self.spec = None

        # progress bar is updated via calls to pdate_progress()
        self.progress_bar = QtGui.QProgressBar()
        self.progress_bar.setVisible(False)

        status_layout = QtGui.QHBoxLayout()
        status_layout.addWidget(self.progress_bar)
        status_layout.addStretch()
        status_layout.addWidget(self.close_buttons())

        self.layout = QtGui.QVBoxLayout()
        self.layout.addLayout(self.filebox())
        self.layout.addLayout(self.scanbox())
        self.layout.addLayout(status_layout)
        self.setLayout(self.layout)

        self.setWindowTitle("Import " + str(filetype))

    def scanbox(self):
        """create widgets for specifying scan range to import"""
        scanminlabel = QtGui.QLabel("Min. Scan")
        self.scanmin = QtGui.QLineEdit()
        self.scanmin.setFixedWidth(100)
        self.scanmin.setAlignment(QtCore.Qt.AlignRight)
        scanmaxlabel = QtGui.QLabel("Max. Scan")
        self.scanmax = QtGui.QLineEdit()
        self.scanmax.setFixedWidth(100)
        self.scanmax.setAlignment(QtCore.Qt.AlignRight)

        scanbox = QtGui.QHBoxLayout()
        scanbox.addWidget(scanminlabel)
        scanbox.addWidget(self.scanmin)
        scanbox.addWidget(scanmaxlabel)
        scanbox.addWidget(self.scanmax)
        return scanbox

    def choose_file(self):
        """
        Opens file dialog, set file text box to the chosen path
        """
        from spec2nexus.spec import SpecDataFile

        dirname = self.get_default_directory(self.filename.text())
        filename = getOpenFileName(self, "Open file", dirname)
        if os.path.exists(filename):
            self.filename.setText(str(filename))
            self.spec = SpecDataFile(self.get_filename())
            self.set_default_directory(os.path.dirname(filename))
            scan_min = self.spec.getMinScanNumber()
            self.scanmin.setText(str(scan_min))
            scan_max = self.spec.getMaxScanNumber()
            self.scanmax.setText(str(scan_max))

    def get_data(self):
        """read the data and return either :class:`NXroot` or :class:`NXentry`"""
        self.import_file = self.get_filename()
        if not os.path.exists(self.import_file):
            return None
        if self.spec is None:
            return None
        scan_min = int(self.scanmin.text())
        scan_max = int(self.scanmax.text())
        all_scans = self.spec.getScanNumbers()
        scans = [s for s in all_scans if scan_min <= s <= scan_max]
        self.spec.progress_bar = self.progress_bar
        self.spec.update_progress = self.update_progress
        return Parser(self.spec).toTree(scans)
예제 #35
0
파일: readspec.py 프로젝트: ericdill/nexpy
    def openFile(self, filename):
        """open the SPEC file and get its data"""
        from spec2nexus.prjPySpec import SpecDataFile

        if os.path.exists(filename):
            self.SPECfile = SpecDataFile(filename)
예제 #36
0
파일: readspec.py 프로젝트: ericdill/nexpy
class Parser(object):
    """parse the spec data file object"""

    def __init__(self, spec_data=None):
        """:param obj spec_data: instance of :class:`spec2nexus.prjPySpec.SpecDataFile`"""
        self.SPECfile = spec_data
        self.progress_bar = spec_data.progress_bar
        self.update_progress = spec_data.update_progress

    def openFile(self, filename):
        """open the SPEC file and get its data"""
        from spec2nexus.prjPySpec import SpecDataFile

        if os.path.exists(filename):
            self.SPECfile = SpecDataFile(filename)

    def toTree(self, scan_list=[]):
        """
        convert scans from chosen SPEC file into NXroot object and structure
        
        called from nexpy.readers.readspec.ImportDialog.get_data__prjPySpec() after clicking <Ok> in dialog
        
        Each scan in the range from self.scanmin to self.scanmax (inclusive)
        will be converted to a NXentry.  Scan data will go in a NXdata where 
        the signal=1 is the last column and the corresponding axes= is the first column.
        
        :param [int] scanlist
        :raises: ValueError is Min or Max scan number are not given properly
        """
        import spec2nexus
        from spec2nexus import utils

        # check that scan_list is valid
        if len(scan_list) == 0:
            return None

        if self.SPECfile is None:
            return None

        complete_scan_list = self.SPECfile.scans.keys()
        for key in scan_list:
            if key not in complete_scan_list:
                msg = "scan " + str(key) + " was not found"
                raise ValueError, msg

        root = NXroot()

        root.attrs["spec2nexus"] = str(spec2nexus.__version__)
        header0 = self.SPECfile.headers[0]
        root.attrs["SPEC_file"] = self.SPECfile.fileName
        root.attrs["SPEC_epoch"] = header0.epoch
        root.attrs["SPEC_date"] = utils.iso8601(header0.date)
        root.attrs["SPEC_comments"] = "\n".join(header0.comments)
        try:
            c = header0.comments[0]
            user = c[c.find("User = "******"=")[1].strip()
            root.attrs["SPEC_user"] = user
        except:
            pass
        root.attrs["SPEC_num_headers"] = len(self.SPECfile.headers)

        self.progress_bar.setVisible(True)
        self.progress_bar.setRange(scan_list[0], scan_list[-1])
        for key in scan_list:
            scan = self.SPECfile.getScan(key)
            scan.interpret()
            entry = NXentry()
            entry.title = str(scan)
            entry.date = utils.iso8601(scan.date)
            entry.command = scan.scanCmd
            entry.scan_number = NXfield(scan.scanNum)
            entry.comments = "\n".join(scan.comments)
            entry.data = self.scan_NXdata(scan)  # store the scan data
            entry.positioners = self.metadata_NXlog(scan.positioner, "SPEC positioners (#P & #O lines)")
            if hasattr(scan, "metadata") and len(scan.metadata) > 0:
                entry.metadata = self.metadata_NXlog(scan.metadata, "SPEC metadata (UNICAT-style #H & #V lines)")

            if len(scan.G) > 0:
                entry.G = NXlog()
                desc = "SPEC geometry arrays, meanings defined by SPEC diffractometer support"
                # e.g.: SPECD/four.mac
                # http://certif.com/spec_manual/fourc_4_9.html
                entry.G.attrs["description"] = desc
                for item, value in scan.G.items():
                    entry.G[item] = NXfield(map(float, value.split()))
            if scan.T != "":
                entry["counting_basis"] = NXfield("SPEC scan with constant counting time")
                entry["T"] = NXfield(float(scan.T))
                entry["T"].units = "seconds"
                entry["T"].description = "SPEC scan with constant counting time"
            elif scan.M != "":
                entry["counting_basis"] = NXfield("SPEC scan with constant monitor count")
                entry["M"] = NXfield(float(scan.M))
                entry["M"].units = "counts"
                entry["M"].description = "SPEC scan with constant monitor count"
            if scan.Q != "":
                entry["Q"] = NXfield(map(float, scan.Q))
                entry["Q"].description = "hkl at start of scan"

            root["scan_" + str(key)] = entry

            self.progress_bar.setValue(key)
            self.update_progress()

        return root

    def scan_NXdata(self, scan):
        """
        return the scan data in an NXdata object
        """
        nxdata = NXdata()

        if len(scan.data) == 0:  # what if no data?
            # since no data available, provide trivial, fake data
            # this keeps the NXdata base class compliant with the NeXus standard
            nxdata.attrs["description"] = "SPEC scan has no data"
            nxdata["noSpecData_y"] = NXfield([0, 0])  # primary Y axis
            nxdata["noSpecData_x"] = NXfield([0, 0])  # primary X axis
            nxdata.nxsignal = nxdata["noSpecData_y"]
            nxdata.nxaxes = [nxdata["noSpecData_x"]]
            return nxdata

        nxdata.attrs["description"] = "SPEC scan data"

        scan_type = scan.scanCmd.split()[0]
        if scan_type in ("mesh", "hklmesh"):
            # hklmesh  H 1.9 2.1 100  K 1.9 2.1 100  -800000
            self.parser_mesh(nxdata, scan)
        elif scan_type in ("hscan", "kscan", "lscan", "hklscan"):
            # hklscan  1.00133 1.00133  1.00133 1.00133  2.85 3.05  200 -400000
            h_0, h_N, k_0, k_N, l_0, l_N = scan.scanCmd.split()[1:7]
            if h_0 != h_N:
                axis = "H"
            elif k_0 != k_N:
                axis = "K"
            elif l_0 != l_N:
                axis = "L"
            else:
                axis = "H"
            self.parser_1D_columns(nxdata, scan)
            nxdata.nxaxes = nxdata[axis]
        else:
            self.parser_1D_columns(nxdata, scan)

        # these locations suggested to NIAC, easier to parse than attached to dataset!
        nxdata.attrs["signal"] = nxdata.nxsignal.nxname
        nxdata.attrs["axes"] = ":".join([obj.nxname for obj in nxdata.nxaxes])
        return nxdata

    def parser_1D_columns(self, nxdata, scan):
        """generic data parser for 1-D column data"""
        from spec2nexus import utils

        for column in scan.L:
            if column in scan.data:
                clean_name = utils.sanitize_name(nxdata, column)
                nxdata[clean_name] = NXfield(scan.data[column])
                nxdata[clean_name].original_name = column

        signal = utils.sanitize_name(nxdata, scan.column_last)  # primary Y axis
        axis = utils.sanitize_name(nxdata, scan.column_first)  # primary X axis
        nxdata.nxsignal = nxdata[signal]
        nxdata.nxaxes = nxdata[axis]

        self.parser_mca_spectra(nxdata, scan, axis)

    def parser_mca_spectra(self, nxdata, scan, primary_axis_label):
        """parse for optional MCA spectra"""
        if "_mca_" in scan.data:  # check for it
            nxdata.mca__spectrum_ = NXfield(scan.data["_mca_"])
            nxdata.mca__spectrum_channel = NXfield(range(1, len(scan.data["_mca_"][0]) + 1))
            nxdata.mca__spectrum_channel.units = "channel"
            axes = (primary_axis_label, "mca__spectrum_channel")
            nxdata.mca__spectrum_.axes = ":".join(axes)

    def parser_mesh(self, nxdata, scan):
        """data parser for 2-D mesh and hklmesh"""
        # 2-D parser: http://www.certif.com/spec_help/mesh.html
        #  mesh motor1 start1 end1 intervals1 motor2 start2 end2 intervals2 time
        # 2-D parser: http://www.certif.com/spec_help/hklmesh.html
        #  hklmesh Q1 start1 end1 intervals1 Q2 start2 end2 intervals2 time
        # mesh:    nexpy/examples/33id_spec.dat  scan 22  (also has MCA, thus 3-D data)
        # hklmesh: nexpy/examples/33bm_spec.dat  scan 17  (no MCA data)
        from spec2nexus import utils

        label1, start1, end1, intervals1, label2, start2, end2, intervals2, time = scan.scanCmd.split()[1:]
        if label1 not in scan.data:
            label1 = scan.L[0]  # mnemonic v. name
        if label2 not in scan.data:
            label2 = scan.L[1]  # mnemonic v. name
        axis1 = scan.data.get(label1)
        axis2 = scan.data.get(label2)
        intervals1, intervals2 = map(int, (intervals1, intervals2))
        start1, end1, start2, end2, time = map(float, (start1, end1, start2, end2, time))
        if len(axis1) < intervals1:  # stopped scan before second row started
            self.parser_1D_columns(nxdata, scan)  # fallback support
            # TODO: what about the MCA data in this case?
        else:
            axis1 = axis1[0 : intervals1 + 1]
            axis2 = [axis2[row] for row in range(len(axis2)) if row % (intervals1 + 1) == 0]

            column_labels = scan.L
            column_labels.remove(label1)  # special handling
            column_labels.remove(label2)  # special handling
            if scan.scanCmd.startswith("hkl"):
                # find the reciprocal space axis held constant
                label3 = [key for key in ("H", "K", "L") if key not in (label1, label2)][0]
                axis3 = scan.data.get(label3)[0]
                nxdata[label3] = NXfield(axis3)
                column_labels.remove(label3)  # already handled

            nxdata[label1] = NXfield(axis1)  # 1-D array
            nxdata[label2] = NXfield(axis2)  # 1-D array

            # build 2-D data objects (do not build label1, label2, [or label3] as 2-D objects)
            data_shape = [len(axis2), len(axis1)]
            for label in column_labels:
                axis = np.array(scan.data.get(label))
                clean_name = utils.sanitize_name(nxdata, label)
                nxdata[clean_name] = NXfield(utils.reshape_data(axis, data_shape))
                nxdata[clean_name].original_name = label

            signal_axis_label = utils.sanitize_name(nxdata, scan.column_last)
            nxdata.nxsignal = nxdata[signal_axis_label]
            nxdata.nxaxes = [nxdata[label2], nxdata[label1]]

        if "_mca_" in scan.data:  # 3-D array
            # TODO: ?merge with parser_mca_spectra()?
            _num_spectra = len(scan.data["_mca_"])
            spectra_lengths = map(len, scan.data["_mca_"])
            num_channels = max(spectra_lengths)
            if num_channels != min(spectra_lengths):
                msg = "MCA spectra have different lengths"
                msg += " in scan #" + str(scan.scanNum)
                msg += " in file " + str(scan.specFile)
                raise ValueError(msg)
            data_shape += [num_channels]
            mca = np.array(scan.data["_mca_"])
            nxdata.mca__spectrum_ = NXfield(utils.reshape_data(mca, data_shape))
            try:
                # use MCA channel numbers as known at time of scan
                chan1 = scan.MCA["first_saved"]
                chanN = scan.MCA["last_saved"]
                channel_range = range(chan1, chanN + 1)
            except:
                # basic indices
                channel_range = range(1, num_channels + 1)
            nxdata.mca__spectrum_channel = NXfield(channel_range)
            nxdata.mca__spectrum_channel.units = "channel"
            axes = (label1, label2, "mca__spectrum_channel")
            nxdata.mca__spectrum_.axes = ":".join(axes)

    def metadata_NXlog(self, spec_metadata, description):
        """
        return the specific metadata in an NXlog object
        """
        from spec2nexus import utils

        nxlog = NXlog()
        nxlog.attrs["description"] = description
        for subkey, value in spec_metadata.items():
            clean_name = utils.sanitize_name(nxlog, subkey)
            nxlog[clean_name] = NXfield(value)
            nxlog[clean_name].original_name = subkey
        return nxlog