Skip to content
Snippets Groups Projects
  • Nick Draper's avatar
    ef3ccb58
    Merge pull request #23693 from mantidproject/23488_update_file_headers · ef3ccb58
    Nick Draper authored
    Update copyright headers in all files
    
    Squashed commit of the following:
    
    * First version of a script to manage copyright statements
    
    re #23468
    
    * Neatened up script, added comments
    
    re #23488
    
    * move script to tools directory
    
    re #23488
    
    * small script changes and a couple of manual file changes
    
    re #23488
    
    * Minor chnage to whitespace detection in regex
    
    re #23488
    
    * Add an excluded directory
    
    re #23488
    
    * remove a repeasted copyright statement in a file
    
    re #23488
    
    * Don't comsume the comment end if it is on the same line
    
    re #23488
    
    * fix error in new copright parsing
    
    re #23488
    
    * remove double copyrifght entry
    
    re #23488
    
    * Improve handling of old copyrights at the start of comments
    
    re #23488
    
    * remove empty comments re #23488
    
    * exclude gsoapgenerated directories
    
    re #23488
    
    * Sort out greedy line matching re #23488
    
    * improve empty comment removal
    
    re #23488
    
    * improve false positives
    
    re #23488
    
    * impressive speedup by limiting regex matching length
    
    re #23488
    
    * remove evil invisible non ascii character
    
    Also upadte the copyright at the same time
    
    re #23488
    
    * resolve multiple copyrights in a single file
    
    re #23488
    
    * resolve an issue with new statement detection
    
    re #23488
    
    * another unprintable unicode character
    
    re #23488
    
    * pep updates and cmake the new copyright fit clang format
    
    re #23488
    
    * update already done new format headers
    
    re #23488
    
    * wrong type of bracket
    
    re #23488
    
    * Update class_maker and friends
    
    re #23488
    
    * Update all copyright statements
    
    re #23488
    
    * clang format re #23488
    
    * flake8 warnings re #23488
    
    * Flake8 warnings re #23488
    
    * Exclude .cmake.in and rb.in files
    
    re #23488
    
    * replace missing line re #23488
    
    * exclude .py.in files as they are flasely recognized as C++
    
    re #23488
    
    * another setp.py.in re #23488
    
    * another .py.in correction re #23488
    
    * Hopefully the last of the .py.in files re #23488
    
    * resolve utf-8 encoding of python files and changed ABINS checksum
    
    re #23488
    
    * updates to unit tests that reference line numbers
    
    re #23488
    
    * remaining unit test files and other fixes
    
    re #23488
    ef3ccb58
    History
    Merge pull request #23693 from mantidproject/23488_update_file_headers
    Nick Draper authored
    Update copyright headers in all files
    
    Squashed commit of the following:
    
    * First version of a script to manage copyright statements
    
    re #23468
    
    * Neatened up script, added comments
    
    re #23488
    
    * move script to tools directory
    
    re #23488
    
    * small script changes and a couple of manual file changes
    
    re #23488
    
    * Minor chnage to whitespace detection in regex
    
    re #23488
    
    * Add an excluded directory
    
    re #23488
    
    * remove a repeasted copyright statement in a file
    
    re #23488
    
    * Don't comsume the comment end if it is on the same line
    
    re #23488
    
    * fix error in new copright parsing
    
    re #23488
    
    * remove double copyrifght entry
    
    re #23488
    
    * Improve handling of old copyrights at the start of comments
    
    re #23488
    
    * remove empty comments re #23488
    
    * exclude gsoapgenerated directories
    
    re #23488
    
    * Sort out greedy line matching re #23488
    
    * improve empty comment removal
    
    re #23488
    
    * improve false positives
    
    re #23488
    
    * impressive speedup by limiting regex matching length
    
    re #23488
    
    * remove evil invisible non ascii character
    
    Also upadte the copyright at the same time
    
    re #23488
    
    * resolve multiple copyrights in a single file
    
    re #23488
    
    * resolve an issue with new statement detection
    
    re #23488
    
    * another unprintable unicode character
    
    re #23488
    
    * pep updates and cmake the new copyright fit clang format
    
    re #23488
    
    * update already done new format headers
    
    re #23488
    
    * wrong type of bracket
    
    re #23488
    
    * Update class_maker and friends
    
    re #23488
    
    * Update all copyright statements
    
    re #23488
    
    * clang format re #23488
    
    * flake8 warnings re #23488
    
    * Flake8 warnings re #23488
    
    * Exclude .cmake.in and rb.in files
    
    re #23488
    
    * replace missing line re #23488
    
    * exclude .py.in files as they are flasely recognized as C++
    
    re #23488
    
    * another setp.py.in re #23488
    
    * another .py.in correction re #23488
    
    * Hopefully the last of the .py.in files re #23488
    
    * resolve utf-8 encoding of python files and changed ABINS checksum
    
    re #23488
    
    * updates to unit tests that reference line numbers
    
    re #23488
    
    * remaining unit test files and other fixes
    
    re #23488
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
tube_spec.py 13.25 KiB
# Mantid Repository : https://github.com/mantidproject/mantid
#
# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
#     NScD Oak Ridge National Laboratory, European Spallation Source
#     & Institut Laue - Langevin
# SPDX - License - Identifier: GPL - 3.0 +
#pylint: disable=invalid-name

# This class is to take a specification of a set of tubes for an instrument provided by a user
# and then provide a list of workspace index ranges corresponding to each of the specified tubes
# to be used the the tube calibration code

# Author: Karl Palmen ISIS

from __future__ import absolute_import, division, print_function


class TubeSpec:
    """
    The python class :class:`~tube_spec.TubeSpec` provides a way of specifying a set of tubes for
    calibration, so that the necessary information about detectors etc. is forethcoming. This class
    is provide by the python file tube_spec.py. The function :func:`~tube_calib.getCalibration` of
    :mod:`tube_calib` needs such an object.

    Configuration methods:

     * :meth:`~tube_spec.TubeSpec.setTubeSpecByString`
     * :meth:`~tube_spec.TubeSpec.setTubeSpecByStringArray`

    There are some functions useful for getting information about a tube specification.
    It is not necessary to call them in a calibration script, but they may be useful for checking
    the specification. These methods are:

     * :meth:`~tube_spec.TubeSpec.getNumTubes`
     * :meth:`~tube_spec.TubeSpec.getDetectorInfoFromTube`
     * :meth:`~tube_spec.TubeSpec.getTubeLength`
     * :meth:`~tube_spec.TubeSpec.getTubeName`
     * :meth:`~tube_spec.TubeSpec.getTubeByString`

    .. note::

        Tubes are currently ordered in the specification in the same order as they appear in the IDF.
        This may differ from the order they appear in the workspace indices.

    """

    def __init__(self,ws):
        """
        The constructor creates empty tube specification for specified instrument.
        :param ws: workspace containing the specified instrument with one pixel detector per spectrum.
        """
        self.ws = ws
        self.inst = ws.getInstrument()
        self.numTubes = 0
        self.componentNameArray = []
        self.componentArray = []
        self.minNumDetsInTube = 200
        self.tubes = []
        self.delimiter = '/' # delimiter between parts of string in tree

    def setTubeSpecByString(self, tubeSpecString ):
        """
        Define the sets of tube from the workspace.

        Sets tube specification by string. The string specifies a component of the instrument
        as in the instrument tree of its IDF file. This component may contain one or more tubes
        and possibly all the tunes in the instrument.
        If the tube specification is not empty this component is added to those already
        in the specification. No checking is done for repeated or overlapping components.

        :param tubeSpecString: string specifying tubes of a component of the instrument

        The **tubeSpecString** may be the full path name for the component or
        steps may be missed out provided it remains unique.
        For example panel03 of WISH can be specified by just **panel03**,
        because panel03 is unique within the instrument. Also tube012 of this panel,
        which is unique within the panel but not within the instrument
        can be specified by **panel03/tube012** but not **tube012**.
        If the specification is not unique, the first found will be used and there will
        be no error message. So if in doubt don't skip a step.
        """
        self.componentNameArray.append(tubeSpecString)
        self.numTubes = -1  # Negative value forces tubes to be searched and counted

    def setTubeSpecByStringArray( self, tubeSpecArray ):
        """
        Define the sets of tube from the workspace with an array of strings.

        Set tube specification like setTubeSpecByString, but with an array of string
        to enable multiple components to be calibrated.

        This function allows you to calibrate a set of tubes that is not defined by a single component.
        For example a set of windows. It takes an array of strings as its argument.
        Each string specifies a component such as a window or a single tube in the same manner as for
        :meth:`~tube_spec.TubeSpec.setTubeSpecByString`. The components must be disjoint.

        :param tubeSpecArray: array of strings (ex. ['door1', 'door2'])

        """
        for i in range(len(tubeSpecArray)):
            self.setTubeSpecByString(tubeSpecArray[i])

    def getInstrumentName (self):
        return self.inst.getName()

    def isTube(self, comp):
        """
        Determines whether the component is a tube.

        :param comp: the component

        :rtype: Value, true if component passes test as being a tube
        """
        # We simply assume it's a tube if it has a large number of children
        if  hasattr( comp, "nelements"):
            return comp.nelements() >= self.minNumDetsInTube
        else:
            return False

    def searchForTubes(self, comp):
        """
         Searches the component for tubes and saves them in array, appending if array is not empty.

         :param comp: the component
         """
         # Go through all descendents that are not a descendent of a tube and if it's a tube, store and count it.

        if self.isTube( comp ):
            self.tubes.append( comp )
         # If not tube, Search children, if any
        else:
            if  hasattr( comp, "nelements"):
                for i in range(comp.nelements()):
                    self.searchForTubes(comp[i])

    def getNumTubes(self):
        """
        Returns number of tubes specified. May also save info about these tubes

        :rtype: Value, number of tubes (-1 for erroneous specification)
        """
        if self.numTubes >= 0:
            return self.numTubes

        # We have a negative number set in self.numTubes, so we search for tubes
        comps = self.getComponents()
        if  comps == []:
            return self.numTubes

        for i in range( len(comps)):
            self.searchForTubes(comps[i])

        self.numTubes = len(self.tubes)
        return self.numTubes

    def getComponent ( self ):
        """
        Returns instrument component corresponding to specification

        :rtype: instrument component
        """
        if  self.componentArray != []:
            return self.componentArray[0]

        # We look for the component
        print("Looking for", self.componentNameArray[0], end="")

        comp = self.inst.getComponentByName(self.componentNameArray[0])

        if  comp :
            self.componentArray.append(comp)

        return self.componentArray[0]

    def getComponents ( self ):
        """
        Returns instrument components corresponding to specification

        :rtype: array of instrument components
        """
        if  self.componentArray != []:
            return self.componentArray

        # We look for the components
        for i in range( len(self.componentNameArray)):
            print("Looking for", self.componentNameArray[i])

            comp = self.inst.getComponentByName(self.componentNameArray[i])

        if  comp :
            self.componentArray.append(comp)
        else:
            print("Did not find", self.componentNameArray[i])
            print("Tube specification not valid")
            self.componentArray = []
            return []

        return self.componentArray

    def getDetectorInfoFromTube( self, tubeIx ):
        """
        Returns detector info for one tube.

        Returns information about detectors in the ( **tubeIx** +1)st tube in the specification,
        where **tubeIx** is the argument. Three integers are returned:

          the ID for the first detector,
          the number of detectors in the tube and
          the increment step of detector IDs in the tube (usually 1, but may be -1).

        It assumes that all the pixels along the tube have consecutive detector IDs.

        :param tubeIx:  index of Tube in specified set

        :rtype: ID of first detector, number of detectors and step between detectors +1 or -1
        """
        nTubes = self.getNumTubes()
        if nTubes < 0:
            print("Error in listing tubes")
            return 0, 0, 1
        if tubeIx < 0 or tubeIx >= nTubes:
            print("Tube index",tubeIx,"out of range 0 to",nTubes)
            return 0, 0, 1

        comp = self.tubes[tubeIx]

        if comp != 0:
            firstDet = comp[0].getID()
            numDet = comp.nelements()
            # Allow for reverse numbering of Detectors
            lastDet = comp[numDet-1].getID()
            if lastDet < firstDet:
                step = -1
                if  firstDet - lastDet + 1 != numDet:
                    print("Detector number range",firstDet-lastDet+1," not equal to number of detectors",numDet)
                    print("Detectors not numbered continuously in this tube. Calibration will fail for this tube.")
            else:
                step = 1
                if  lastDet - firstDet + 1 != numDet:
                    print("Detector number range",lastDet-firstDet+1," not equal to number of detectors",numDet)
                    print("Detectors not numbered continuously in this tube. Calibration will fail for this tube.")

        else:
            print(self.componentNameArray[0], tubeIx, "not found")
            return 0, 0, 1

        return firstDet, numDet, step

    def getTubeLength( self, tubeIx ):
        """
        Returns length of the ( **tubeIx** +1)st tube.

        :param tubeIx:  index of Tube in specified set

        :rtype: Length of tube (first pixel to last pixel) in metres. 0.0 if tube not found.

        """
        nTubes = self.getNumTubes()
        if nTubes < 0:
            print("Error in listing tubes")
            return 0.0
        if tubeIx < 0 or tubeIx >= nTubes:
            print("Tube index",tubeIx,"out of range 0 to",nTubes)
            return 0.0

        comp = self.tubes[tubeIx]

        if comp != 0:
            numDet = comp.nelements()
            return comp[0].getDistance( comp[numDet-1] )
        else:
            print(self.componentNameArray[0], tubeIx, "not found")
            return 0.0

    def getTubeName ( self, tubeIx ):
        """
        Returns name of tube.

        This function is not used in tube calibration, but may be useful as a diagnostic.
        It is used in creating the peakfile, which lists the peaks found for each tube.

        :param tubeIx:  index of Tube in specified set

        :rtype: Name of tube as in IDF or 'unknown' if not found.
        """
        nTubes = self.getNumTubes()
        if nTubes < 0:
            print("Error in listing tubes")
            return 'Unknown'
        if tubeIx < 0 or tubeIx >= nTubes:
            print("Tube index",tubeIx,"out of range 0 to",nTubes)
            return 'Unknown'

        comp = self.tubes[tubeIx]

        if comp != 0:
            return comp.getFullName()
        else:
            print(self.componentNameArray[0], tubeIx, "not found")
            return "Unknown"

    def getTubeByString(self, tubeIx):
        """
        Returns list of workspace indices of a tube set that has been specified by string
        It assumes that all the pixels along the tube have consecutive detector IDs

        :param tubeIx:  index of Tube in specified set

        :rtype: list of indices
        """
        firstDet, numDet, step = self.getDetectorInfoFromTube( tubeIx )
        wkIds = []
        skipped = []
        # print " First detector", firstDet," Last detector", firstDet+numDet-1, "Number of detectors", numDet
        # print "Histograms", self.ws.getNumberHistograms()

        # First check we have one detector per histogram/workpsaceID/spectrum
        sampleIndex = 10
        sp = self.ws.getSpectrum(sampleIndex)
        detids = sp.getDetectorIDs()
        numDetsPerWkID = len(detids)
        if  numDetsPerWkID != 1:
            print("We have",numDetsPerWkID,"detectors per workspace index. 1 is required.")
            print("cannot obtain range of workspace indices for this tube in this workspace")
            return wkIds, skipped

        # Go and get workspace Indices
        if step == -1:
            startDet = firstDet - numDet + 1
        else:
            startDet = firstDet
        if  numDet > 0:
            for i in range (0, self.ws.getNumberHistograms(), numDet):
                try:
                    deti = self.ws.getDetector(i)
                except:
                    skipped.append(i)
                    continue
                detID = deti.getID()
                if detID  >= startDet and detID < startDet+numDet:
                    iPixel = detID - firstDet
                    wkIds = range( i - iPixel, i - iPixel + step*numDet, step)

        if numDet > 0:
            return wkIds, skipped
        else:
            print("specified tube has no detectors.")
            self.numTubes = 0
        return wkIds, skipped

    def getTube(self, tubeIx):
        """
        Returns list of workspace indices of a tube

        :param tubeIx:  index of Tube in specified set

        :rtype: list of indices
        """
        nTubes = self.getNumTubes()
        if  (0 <= tubeIx) & (tubeIx < nTubes) :
            return self.getTubeByString(tubeIx)
        else:
            print("Tube", tubeIx, "out of range 0 to",self.numTubes,".")