Skip to content
Snippets Groups Projects
RefinePowderDiffProfileSeq.py 36 KiB
Newer Older
#pylint: disable=no-init,invalid-name
from mantid.api import *
import mantid.simpleapi as api
from mantid.api import *
from mantid.kernel import *


#from Calibration_ImportInformation import *


#--------------------------------------------------------------------------------
class RefinePowderDiffProfileSeq(PythonAlgorithm):
    """ Refine powder diffractometer profile by Le Bail algorithm sequentially
    """

    dataws = None
    wsindex = None
    startx = None
    endx = None
    _lastStep = None
    _projectID = None
    functionoption = None
    peaktype = None
    bkgdtype = None
    bkgdparws = None
    profilews = None
    braggpeakws = None
    paramstofit = None
    numcycles = None
    outprojectfilename = None
    inprojectfilename = None
    datawsname = None

    def category(self):
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        """ Category
        """
        return "Diffraction"
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        """ Algorithm name
        """
        return "RefinePowderDiffProfileSeq"
    def summary(self):
        return "Refine powder diffractomer profile parameters sequentially."
    def PyInit(self):
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        """ Declare properties
        """
        self.declareProperty(MatrixWorkspaceProperty("InputWorkspace", "", Direction.Input, PropertyMode.Optional),\
                "Name of data workspace containing the diffraction pattern in .prf file. ")

Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self.declareProperty("WorkspaceIndex", 0, "Spectrum (workspace index starting from 0) of the data to refine against in input workspace.")
        self.declareProperty(ITableWorkspaceProperty("SeqControlInfoWorkspace", "", Direction.InOut, PropertyMode.Optional),\
Zhou, Wenduo's avatar
Zhou, Wenduo committed
            "Name of table workspace containing sequential refinement information.")
        self.declareProperty(ITableWorkspaceProperty("InputProfileWorkspace", "", Direction.Input, PropertyMode.Optional),\
Zhou, Wenduo's avatar
Zhou, Wenduo committed
            "Name of table workspace containing starting profile parameters.")
        self.declareProperty(ITableWorkspaceProperty("InputBraggPeaksWorkspace", "", Direction.Input, PropertyMode.Optional),\
Zhou, Wenduo's avatar
Zhou, Wenduo committed
            "Name of table workspace containing a list of reflections. ")
        self.declareProperty(ITableWorkspaceProperty("InputBackgroundParameterWorkspace", "", Direction.Input,\
Zhou, Wenduo's avatar
Zhou, Wenduo committed
            PropertyMode.Optional), "Name of table workspace containing a list of reflections. ")
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self.declareProperty("StartX", -0., "Start X (TOF) to refine diffraction pattern.")
        self.declareProperty("EndX",   -0., "End X (TOF) to refine diffraction pattern.")
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        funcoptions = ["Setup", "Refine", "Save", "Load"]
        self.declareProperty("FunctionOption", "Refine", StringListValidator(funcoptions), "Options of functionality")
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        #refoptions = ["Levenberg-Marquardt", "Random Walk", "Single Peak Fit"]
        refoptions = ["Random Walk"]
        self.declareProperty("RefinementOption", "Random Walk", StringListValidator(refoptions),\
Zhou, Wenduo's avatar
Zhou, Wenduo committed
            "Options of algorithm to refine. ")
        self.declareProperty(StringArrayProperty("ParametersToRefine", values=[], direction=Direction.Input),\
Zhou, Wenduo's avatar
Zhou, Wenduo committed
            "List of parameters to refine.")
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self.declareProperty("NumRefineCycles", 1, "Number of refinement cycles.")
        peaktypes = ["", "Neutron Back-to-back exponential convoluted with pseudo-voigt",\
                "Thermal neutron Back-to-back exponential convoluted with pseudo-voigt"]
        self.declareProperty("ProfileType", "", StringListValidator(peaktypes), "Type of peak profile function.")
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        bkgdtypes = ["", "Polynomial", "Chebyshev", "FullprofPolynomial"]
        self.declareProperty("BackgroundType", "", StringListValidator(bkgdtypes), "Type of background function.")
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self.declareProperty("FromStep", -1, "If non-negative, the previous code is not set from last step, but the step specified.")
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Property for save project
        self.declareProperty(FileProperty("OutputProjectFilename","", FileAction.OptionalSave, ['.nxs']),\
                "Name of sequential project file.")
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Property for save project
        self.declareProperty(FileProperty("InputProjectFilename","", FileAction.OptionalLoad, ['.nxs']),\
                "Name of sequential project file.")
        # Project ID
        self.declareProperty("ProjectID", "", "Project ID.")
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        return
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        """
        # Process input
        self._processInputProperties()
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        seqrefine = SeqRefineProfile(self._projectID, self.log())

        # Execute
        if self.functionoption == "Setup":
            # Set up
Zhou, Wenduo's avatar
Zhou, Wenduo committed
            if seqrefine.isSetup() is True:
                raise NotImplementedError("Impossible to have it set up already.")
            seqrefine.initSetup(self.dataws, self.wsindex, self.peaktype, self.profilews, self.braggpeakws, self.bkgdtype,\
Zhou, Wenduo's avatar
Zhou, Wenduo committed
                    self.bkgdparws, self.startx, self.endx)

        elif self.functionoption == "Refine":
            # Refine
Zhou, Wenduo's avatar
Zhou, Wenduo committed
            if seqrefine.isSetup() is False:
                raise NotImplementedError("Exception because sequential refinement is not set up.")
            seqrefine.refine(self.dataws, self.wsindex, self.paramstofit,  self.numcycles, self.startx, self.endx, self._lastStep)
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        elif self.functionoption == "Save":
            # Save the current state to a project file
            seqrefine.saveProject(str(self.dataws), self.wsindex, self.outprojectfilename)
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        elif self.functionoption == "Load":
            # Set up from an exiting project file
            if seqrefine.isSetup() is True:
                raise NotImplementedError("Impossible to have it set up already.")
Zhou, Wenduo's avatar
Zhou, Wenduo committed
            seqrefine.loadProject(self.inprojectfilename)
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        else:
            # None-support
            raise NotImplementedError("Function is not supported.")
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        return


    def _processInputProperties(self):
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        """ Process input properties
        """
        # Input data workspace and related
        self.dataws = self.getProperty("InputWorkspace").value
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self.wsindex = self.getProperty("WorkspaceIndex").value
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self.startx = self.getProperty("StartX").value
        self.endx = self.getProperty("EndX").value
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self._lastStep = self.getProperty("FromStep").value

        self._projectID = self.getProperty("ProjectID").value
        if len(self._projectID) == 0:
            raise NotImplementedError("User must specify project ID.")
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self.functionoption = self.getProperty("FunctionOption").value
        if self.functionoption == "Setup":
            # Request on 'Setup'
            ptype = self.getProperty("ProfileType").value
            if ptype == "Neutron Back-to-back exponential convoluted with pseudo-voigt":
                self.peaktype = "NeutronBk2BkExpConvPVoigt"
            elif ptype == "Thermal neutron Back-to-back exponential convoluted with pseudo-voigt":
                self.peaktype = "ThermalNeutronBk2BkExpConvPVoigt"
            else:
                raise NotImplementedError("Peak profile is not supported.")
Zhou, Wenduo's avatar
Zhou, Wenduo committed
            self.bkgdtype = self.getProperty("BackgroundType").value
            self.bkgdparws = self.getProperty("InputBackgroundParameterWorkspace").value
            self.profilews = self.getProperty("InputProfileWorkspace").value
            self.braggpeakws = self.getProperty("InputBraggPeaksWorkspace").value
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        elif self.functionoption == "Refine":
            self.paramstofit = self.getProperty("ParametersToRefine").value
            self.numcycles = self.getProperty("NumRefineCycles").value
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        elif self.functionoption == "Save":
            self.outprojectfilename = self.getProperty("OutputProjectFilename").value
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        elif self.functionoption == "Load":
            self.inprojectfilename = self.getProperty("InputProjectFilename").value
        else:
            raise NotImplementedError("Unsupported function mode  %s. " % (self.functionoption))
        if self.functionoption != "Load":
            self.datawsname = str(self.dataws)
Zhou, Wenduo's avatar
Zhou, Wenduo committed
            if self.wsindex < 0 or self.wsindex >= self.dataws.getNumberHistograms():
                raise NotImplementedError("Input workspace index %d is out of range (0, %d)." %\
                        (self.wsindex, self.dataws.getNumberHistograms()))
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        return


#--------------------------------------------------------------------
#
#--------------------------------------------------------------------

class SeqRefineProfile:
    """ A class to do sequential refinement on peak profile

    Use case:
    1. Set class object such as : ID/StartDate/Directory
    2. Run main to refine some parameters
    3. Check result
    4. If no further instruction, only need to set up parameters to refine
       the input/starting values should be from the last
    """

    _datawsname = None
    _profileWS = None
    _braggpeakws = None
    _bkgdtype = None
    _bkgdparws = None
    _wsgroup = None
    datawsname = None
    wsindex = None
    _recordws = None
    _recordwsLastRowValid = None
    _lastValidStep = None
    _lastValidRowIndex = None
    _peakType = None
    _bkgdType = None
    _currstep = None

    def __init__(self, ID, glog):
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        """
        self.glog = glog
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self._ID = str(ID)
        self.glog.information("SeqRefineProfile is initialized with ID = %s" % (str(ID)))
        # Standard record table and check its existence
    # FIXME - This workspace's name should be passed from Algorithm's property!
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self._recordwsname = "Record%sTable" % (str(ID))
        self.glog.notice("Using record table %s" % (self._recordwsname))
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        if AnalysisDataService.doesExist(self._recordwsname):
            # Record workspace exists: has been set up
            self._isSetup = True
        else:
            # Record workspace does not exist: first time or need to load from file
            self._isSetup = False
        self._recordWSLastRowInvalid = False
        # Result workspace group
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self._wsgroupName = self._ID + "_Group"
        if AnalysisDataService.doesExist(self._wsgroupName):
            self._wsgroupCreated = True
        else:
            self._wsgroupCreated = False
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        return

    def initSetup(self, dataws, wsindex, peaktype, profilews, braggpeakws, bkgdtype, bkgdparws, startx, endx):
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        """ Set up the properties for LeBailFit as the first time including
        do a Le bail calculation based on the input parameters
        including profilews, braggpeakws, and etc
        """
        # Data and data range
        self._datawsname = str(dataws)
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        if startx <= 0.:
            startx = dataws.readX(wsindex)[0]
        if endx <= 0.:
            endx = dataws.readX(wsindex)[-1]
        self._peakType = peaktype
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self._profileWS = profilews
        self._braggpeakws = braggpeakws
        self._bkgdtype = bkgdtype
        self._bkgdparws = bkgdparws
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Generate record table
        self._genRecordTable()
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Check input parameters, i.e., verification/examine input parameters
        runner = RefineProfileParameters(self.glog)
        outwsname = self._datawsname+"_Init"
        runner.setInputs(self._datawsname, self._peakType, self._profileWS, self._braggpeakws, self._bkgdtype,  self._bkgdparws)
        # FIXME - Need to verify whether input and output background parameter ws name can be same
        runner.setOutputs(outwsname, self._profileWS, self._braggpeakws, self._bkgdparws)
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self._recordPreRefineInfo(runner, -1)
        runner.calculate(startx, endx)
        self._recordPostRefineInfo(runner)
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Group the newly generated workspace and do some record
        api.GroupWorkspaces(InputWorkspaces="%s, %s, %s, %s" % (outwsname, self._profileWS, self._braggpeakws, self._bkgdparws),\
                OutputWorkspace=self._wsgroupName)
        self._wsgroupCreated = True
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Repository
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Replace 'Refine' of step 0 to ID (it is always empty)
        self._recordws.setCell(0, 5, self._ID)
        # Replace 'InputProfileWorkspace' by profile type (it is alwasy same as output)
        self._recordws.setCell(0, 9, self._peakType)
        self._isSetup = True
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        return

    def loadProject(self, projectfilename):
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        """ Load the project from a saved project file
        """
        # Load workspace group
        api.LoadNexusProcessed(Filename=projectfilename, OutputWorkspace=self._wsgroupName)
        self._wsgroup = AnalysisDataService.retrieve(self._wsgroupName)
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        if self._wsgroup.__class__.__name__ != "WorkspaceGroup":
            raise NotImplementedError("Input is not a workspace group but a %s" % (self._wsgroup.__class__.__name__))
            self._wsgroupCreated = True
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Parse README
        wsnames = self._wsgroup.getNames()
        readmewsname = None
        for wsname in wsnames:
            if wsname.startswith("READ"):
                readmewsname = wsname
                break
        if readmewsname is None:
            raise NotImplementedError("No README workspace is found in loaded workspace group.")
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        readmews = AnalysisDataService.retrieve(readmewsname)
        infodict = {}
        numrows = readmews.rowCount()
        self.glog.information("Found %d rows in workspace %s" % (numrows, str(readmews)))
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        for r in xrange(numrows):
            functioncat = str(readmews.cell(r, 0)).strip()
            functiontype = str(readmews.cell(r, 1)).strip()
            infodict[functioncat] = functiontype.strip()
        self.glog.information("README keys: %s" % (infodict.keys()))
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self._peakType = infodict["Peak"]
        self.datawsname = infodict["Data"]
        self.wsindex = infodict["Spectrum"]
        if self._ID != infodict["ID"]:
            raise NotImplementedError("ID mismatch!")

        self._recordwsname = infodict["Record"]
        self._isSetup = True

Zhou, Wenduo's avatar
Zhou, Wenduo committed
        return
    def refine(self, dataws, wsindex, parametersToFit, numcycles, startx, endx, laststepindex):
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        """ Refine parameters
        """
        # Range of fit
        if startx <= 0.:
            startx = dataws.readX(wsindex)[0]
        if endx <= 0.:
            endx = dataws.readX(wsindex)[-1]
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Set up RefineProfileParameters object
        runner = RefineProfileParameters(self.glog)
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Locate refinement record table
        profilewsname, braggpeakwsname, bkgdtype, bkgdparamwsname, laststep = self._parseRecordTable(laststepindex)
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Set up runner and refine
        runner.setupMonteCarloRefine(numcycles, parametersToFit)
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        outwsname, outprofilewsname, outbraggpeakwsname = self._genOutputWorkspace(str(dataws), profilewsname, braggpeakwsname)

        # Set up input and output
        runner.setInputs(str(dataws), self._peakType, profilewsname, braggpeakwsname, bkgdtype,  bkgdparamwsname)
        # FIXME - Need to verify whether input and output background parameter ws name can be same
        runner.setOutputs(outwsname, outprofilewsname, outbraggpeakwsname, bkgdparamwsname)

Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Refine and record pre and post refinement information
        self._recordPreRefineInfo(runner, laststep)
        runner.refine(numcycles, parametersToFit, startx, endx)
        self._recordPostRefineInfo(runner)
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Group newly generated workspaces and add name to reposiotry
        if self._wsgroupCreated is True:
            api.GroupWorkspaces(InputWorkspaces="%s, %s, %s, %s" % (outwsname, outprofilewsname, outbraggpeakwsname, self._wsgroupName),\
                    OutputWorkspace=self._wsgroupName)
        else:
            wsgroup = AnalysisDataService.retrieve(self._wsgroupName)
            hasbkgd = list(wsgroup.getNames()).count(bkgdparamwsname)
                api.GroupWorkspaces(InputWorkspaces="%s, %s, %s" % (outwsname, outprofilewsname, outbraggpeakwsname),\
                        OutputWorkspace=self._wsgroupName)
            elif hasbkgd == 0:
                api.GroupWorkspaces(InputWorkspaces="%s, %s, %s, %s" % (outwsname, outprofilewsname, outbraggpeakwsname, bkgdparamwsname),\
                        OutputWorkspace=self._wsgroupName)
            else:
                raise NotImplementedError("Impossible to have 1 workspace appeared twice in a workspace group.")

Zhou, Wenduo's avatar
Zhou, Wenduo committed
        return
    def isSetup(self):
        """ Status whether refinement is set up.
        """
        return self._isSetup


    def saveProject(self, datawsname, wsindex, projectfname):
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        """ Save current to a project file
        Note: MC setup table workspace is not generated in this class.  So it won't be saved
        """
        import os

        # FIXME - Find out a good way to remove existing files/directories
        if os.path.exists(projectfname) is True:
            import shutil
                os.remove(projectfname)
            except RuntimeError:
                shutil.rmtree(projectfname)
            except IOError:
                shutil.rmtree(projectfname)
            except OSError:
                shutil.rmtree(projectfname)

Zhou, Wenduo's avatar
Zhou, Wenduo committed
        api.SaveNexusProcessed(InputWorkspace=self._wsgroupName, Filename=projectfname, Append=False)
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Add data workspace, tracking record table  to workspaces
        # api.GroupWorkspaces(InputWorkspaces="%s, %s, %s" % (datawsname, self._recordwsname, self._wsgroupName),
        #         OutputWorkspace=self._wsgroupName)
        self.glog.notice("Append record workspace %s" % (self._recordwsname))
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        api.SaveNexusProcessed(InputWorkspace=self._recordwsname, Filename=projectfname, Append=True)

        self.glog.notice("Append data workspace %s" % (datawsname))
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        api.SaveNexusProcessed(InputWorkspace=datawsname, Filename=projectfname, Append=True)
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Create a new README table workspace for some other information
        readmewsname = "READ_%s" % (self._ID)
        readmews = api.CreateEmptyTableWorkspace(OutputWorkspace=readmewsname)
        readmews.addColumn("str", "Function")
        readmews.addColumn("str", "Type")
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        readmews.addRow(["Peak", "Not Important"])
        readmews.addRow(["Background", "Not Important"])
        readmews.addRow(["ID", str(self._ID)])
        readmews.addRow(["Record", self._recordwsname])
        readmews.addRow(["Data", str(datawsname)])
        readmews.addRow(["Spectrum", str(wsindex)])

Zhou, Wenduo's avatar
Zhou, Wenduo committed
        api.SaveNexusProcessed(InputWorkspace=readmewsname, Filename=projectfname, Append=True)
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        return
    def _genRecordTable(self):
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        """ Generate record table
        """
        tablews = api.CreateEmptyTableWorkspace(OutputWorkspace=self._recordwsname)
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        tablews.addColumn("int", "Step")
        tablews.addColumn("str", "OutProfile")
        tablews.addColumn("str", "OutReflection")
        tablews.addColumn("str", "OutBackgroud")
        tablews.addColumn("str", "OutBckgroundParam")
        tablews.addColumn("str", "Refine")
        tablews.addColumn("double", "RwpOut")
        tablews.addColumn("int", "LastStep")
        tablews.addColumn("double", "RwpIn")
        tablews.addColumn("str", "InProfile")
        tablews.addColumn("str", "InReflection")
        tablews.addColumn("str", "InBackgroud")
        tablews.addColumn("str", "InBckgroundParam")
        self._recordws = tablews

Zhou, Wenduo's avatar
Zhou, Wenduo committed
        return
    def _parseRecordTable(self, laststep):
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        """ Parse record table and return the last refinement result
        Notice that 'last row' in record table might not be a valid row (incomplete).
        It might be caused by an exception raised in refinement or its setup.
        Class variable _recordWSLastRowInvalid is used to indicate this
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        """
        # Retrieve record workspace
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self._recordws = AnalysisDataService.retrieve(str(self._recordwsname))
        numrows = self._recordws.rowCount()
        if numrows == 0:
            raise NotImplementedError("Empty record table workspace. ")
        # Find last valid row
        lastvalidrow = -1
        lastrow = numrows-1
        self._recordwsLastRowValid = False
        while self._recordwsLastRowValid is False and lastrow >= 0:
Zhou, Wenduo's avatar
Zhou, Wenduo committed
            profilewsname = self._recordws.cell(lastrow, 1)
                self.glog.warning("Profile workspace name is emtpy in row %d!" % (lastrow))
                self._recordwsLastRowValid = True
                lastvalidrow = lastrow
        # ENDWHILE
        if lastvalidrow < 0:
            raise NotImplementedError("XXX")

        # Find out last step row
        lastrecordedstep = self._recordws.cell(lastvalidrow, 0)
        self.glog.notice("Last recorded valid step is %d. " % (lastrecordedstep))

        self._lastValidStep = lastrecordedstep
        self._lastValidRowIndex = lastvalidrow

        if laststep > lastrecordedstep:
            self.glog.warning("Last step %d is not recorded.  Using step %d instead. " %\
                    (laststep, lastrecordedstep))
            laststep = lastrecordedstep
        elif laststep < 0:
            self.glog.notice("Using default last valid step %d. " % (self._lastValidStep))
            laststep = self._lastValidStep

        profilewsname = ""
        while lastvalidrow >= 0:
            step = self._recordws.cell(lastvalidrow, 0)
            if step != laststep:
                lastvalidrow -= 1
            else:
Zhou, Wenduo's avatar
Zhou, Wenduo committed
                profilewsname = self._recordws.cell(lastvalidrow, 1).strip()
                reflectwsname = self._recordws.cell(lastvalidrow, 2).strip()
                bkgdtype = self._recordws.cell(lastrow, 3).strip()
                bkgdparamwsname = self._recordws.cell(lastrow, 4).strip()
                    raise NotImplementedError("Profile workspace name is emtpy in row %d.  It is not supposed to happen." %\
                            (lastvalidrow))
                break
        # ENDWHILE
        if profilewsname == "":
            raise NotImplementedError("Step %d is not found in record table.  It is impossible. " %\
        # Current step
        self._currstep = self._lastValidStep + 1
        self.glog.notice("Current step is %d" % (self._currstep))
        # Set up for other informatin
        # Peak type
        self._peakType = self._recordws.cell(0, 9).strip()
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Background type
        self._bkgdType = bkgdtype.strip()
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        return (profilewsname, reflectwsname, bkgdtype, bkgdparamwsname, laststep)

    def _recordPreRefineInfo(self, refiner, laststep):
        """ Record pre-refinement information
        """
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        rectablews = mtd[self._recordwsname]
        numrows = rectablews.rowCount()

        if self._recordWSLastRowInvalid is False:
            self._currstep = numrows
            rectablews.addRow([self._currstep, "", "", "", "", "", -1.0, laststep, -1.0, "profilews",\
                "reflectionws", "Polynomial", "BkgdParm"])
        else:
            self._currstep = numrows-1
            laststep = self._lastValidStep
        # print "*** Record workspace has %d rows. current step = %d. " % (rectablews.rowCount(), self._currstep)

        if len(refiner.paramToFit) > 0:
            rectablews.setCell(self._currstep,  5, str(refiner.paramToFit))
        rectablews.setCell(self._currstep,  9, str(refiner.inprofilewsname))
        rectablews.setCell(self._currstep, 10, str(refiner.inreflectionwsname))
        rectablews.setCell(self._currstep, 11, str(refiner.bkgdtype))
        rectablews.setCell(self._currstep, 12, str(refiner.bkgdtablewsname))
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        return

    def _recordPostRefineInfo(self, refiner):
        """ Record post-refinement information, i.e., refinement result
        """
        # Parse profile table workspace
        # print "****** outprofilews type = ", type(refiner.outprofilewsname)
        outprofilews = AnalysisDataService.retrieve(str(refiner.outprofilewsname))
        # outprofilews = api.mtd[refiner.outprofilewsname]
        # FIXME - Use Name[0], Value[1] as default
        numpars = outprofilews.rowCount()
        rwp = None
        for i in xrange(numpars):
            parname = outprofilews.cell(i, 0)
            if parname.lower() == "rwp":
                rwp = outprofilews.cell(i, 1)
                break

        # Set the record table workspace
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        rectablews = mtd[self._recordwsname]
        numrows = rectablews.rowCount()
        # currstep = numrows-1

        rectablews.setCell(self._currstep, 1, str(refiner.outprofilewsname))
        rectablews.setCell(self._currstep, 2, str(refiner.outreflectionwsname))
        rectablews.setCell(self._currstep, 3, str(refiner.bkgdtype))
        rectablews.setCell(self._currstep, 4, str(refiner.bkgdtablewsname))
            rectablews.setCell(self._currstep, 6, rwp)
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        return
    def _genOutputWorkspace(self, datawsname, profilewsname, braggpeakwsname):
        outwsname = "%s_%s_Step%d" % (datawsname, self._ID, self._currstep)

        if profilewsname.count(self._ID) > 0:
            outprofilewsname = profilewsname.split(self._ID)[0]
        else:
            outprofilewsname = profilewsname
        outprofilewsname = "%s%s_Step%d" % (outprofilewsname, self._ID, self._currstep)

        if braggpeakwsname.count(str(self._ID)) > 0:
            outbpwsname = braggpeakwsname.split(self._ID)[0]
        else:
            outbpwsname = braggpeakwsname
        outbpwsname = "%s%s_Step%d"%(outbpwsname, self._ID, self._currstep)
        return (outwsname, outprofilewsname, outbpwsname)

#--------------------------------------------------------------------

def generateMCSetupTableProf9(wsname):
    """ Generate a Le Bail fit Monte Carlo random walk setup table
    """
    tablews = api.CreateEmptyTableWorkspace(OutputWorkspace=str(wsname))
    tablews.addColumn("str", "Name")
    tablews.addColumn("double", "A0")
    tablews.addColumn("double", "A1")
    tablews.addColumn("int", "NonNegative")
    tablews.addColumn("int", "Group")
    tablews.addRow(["Dtt1"  , 5.0, 0.0, 0, group])
    tablews.addRow(["Dtt2" , 1.0, 0.0, 0, group])
    tablews.addRow(["Zero"  , 5.0, 0.0, 0, group])
    tablews.addRow(["Beta0" , 0.50, 1.0, 0, group])
    tablews.addRow(["Beta1" , 0.05, 1.0, 0, group])
    group = 2
    tablews.addRow(["Alph0" , 0.05, 1.0, 0, group])
    tablews.addRow(["Alph1" , 0.02, 1.0, 0, group])
    group = 3
    tablews.addRow(["Sig0", 2.0, 1.0, 1, group])
    tablews.addRow(["Sig1", 2.0, 1.0, 1, group])
    tablews.addRow(["Sig2", 2.0, 1.0, 1, group])
    group = 4
    tablews.addRow(["Gam0", 2.0, 1.0, 0, group])
    tablews.addRow(["Gam1", 2.0, 1.0, 0, group])
    tablews.addRow(["Gam2", 2.0, 1.0, 0, group])

    return tablews

def generateMCSetupTableProf10(wsname):
    """ Generate a Le Bail fit Monte Carlo random walk setup table
    """
    import mantid.simpleapi as api
    tablews = api.CreateEmptyTableWorkspace(OutputWorkspace=str(wsname))
    tablews.addColumn("str", "Name")
    tablews.addColumn("double", "A0")
    tablews.addColumn("double", "A1")
    tablews.addColumn("int", "NonNegative")
    tablews.addColumn("int", "Group")
    tablews.addRow(["Dtt1"  , 5.0, 0.0, 0, group])
    tablews.addRow(["Dtt1t" , 5.0, 0.0, 0, group])
    tablews.addRow(["Dtt2t" , 1.0, 0.0, 0, group])
    tablews.addRow(["Zero"  , 5.0, 0.0, 0, group])
    tablews.addRow(["Zerot" , 5.0, 0.0, 0, group])
    tablews.addRow(["Width" , 0.0, 0.1, 1, group])
    tablews.addRow(["Tcross", 0.0, 1.0, 1, group])
    tablews.addRow(["Beta0" , 0.50, 1.0, 0, group])
    tablews.addRow(["Beta1" , 0.05, 1.0, 0, group])
    tablews.addRow(["Beta0t", 0.50, 1.0, 0, group])
    tablews.addRow(["Beta1t", 0.05, 1.0, 0, group])
    group = 2
    tablews.addRow(["Alph0" , 0.05, 1.0, 0, group])
    tablews.addRow(["Alph1" , 0.02, 1.0, 0, group])
    tablews.addRow(["Alph0t", 0.10, 1.0, 0, group])
    tablews.addRow(["Alph1t", 0.05, 1.0, 0, group])
    group = 3
    tablews.addRow(["Sig0", 2.0, 1.0, 1, group])
    tablews.addRow(["Sig1", 2.0, 1.0, 1, group])
    tablews.addRow(["Sig2", 2.0, 1.0, 1, group])
    group = 4
    tablews.addRow(["Gam0", 2.0, 1.0, 0, group])
    tablews.addRow(["Gam1", 2.0, 1.0, 0, group])
    tablews.addRow(["Gam2", 2.0, 1.0, 0, group])

    return tablews

def breakParametersGroups(tablews):
    """ Break the parameter groups.  Such that each parameter/row has an individual group
    """
    numrows = tablews.rowCount()
    for ir in xrange(numrows):
        tablews.setCell(ir, 4, ir)

    return

def resetParametersGroups(tablews):
    """ Set the group number to original setup
    """
    numrows = tablews.rowCount()
    for ir in xrange(numrows):
        parname = tablews.cell(ir, 0)
        if parname in ["Dtt1", "Dtt1t", "Dtt2t", "Zero", "Zerot", "Width", "Tcross"]:
            group = 0
        elif parname in ["Beta0", "Beta1", "Beta0t", "Beta1t"]:
            group = 1
        elif parname in ["Alph0", "Alph1", "Alph0t", "Alph1t"]:
            group = 2
        elif parname in ["Sig0", "Sig1", "Sig2"]:
            group = 3
        else:
            group = 4
        tablews.setCell(ir, 4, group)
        return


class RefineProfileParameters:
    """ Class to refine profile parameters ONE step
    """

    datawsname = None
    inprofilewsname = None
    inreflectionwsname = None
    bkgdtype = None
    bkgdtablewsname = None
    outprofilewsname = None
    outreflectionwsname = None
    outbkgdtablewsname = None

    def __init__(self, glog):
        """ Initialization
        """
        self.peaktype = "NOSETUP"
        # Output
        self.outwsname = None

        self.numsteps = 0

        # Refine
        self.paramToFit = []

Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Flags
        self._inputIsSetup = False
        self._outputIsSetup = False
    def setInputs(self, datawsname, peaktype, profilewsname, braggpeakwsname, bkgdtype, bkgdparwsname):
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        """
        """
        self.datawsname = datawsname
        self.peaktype = peaktype
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self.inprofilewsname = profilewsname
        self.inreflectionwsname = braggpeakwsname
        self.bkgdtype = bkgdtype
        self.bkgdtablewsname = bkgdparwsname
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self._inputIsSetup = True
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        return

    def setOutputs(self, outwsname, profilewsname, braggpeakwsname, bkgdparwsname):
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        """ Set up the variables for output
        """
        self.outwsname = outwsname
        self.outprofilewsname = profilewsname
        self.outreflectionwsname = braggpeakwsname
        self.outbkgdtablewsname = bkgdparwsname
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self._outputIsSetup = True
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        return
    def setupMonteCarloRefine(self, numcycles, parametersToFit):
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        """ Set up refinement parameters
        """
        if numcycles <= 0:
            raise NotImplementedError("It is not allowed to set up a 0 or a negative number to MonteCarloRefine")
Zhou, Wenduo's avatar
Zhou, Wenduo committed
            self.numsteps = numcycles
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self.paramToFit = parametersToFit
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        return

    def calculate(self, startx, endx):
        """ Do Le bail calculation
        """
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        if (self._inputIsSetup and self._outputIsSetup) is False:
            raise NotImplementedError("Either input or output is not setup: inputIsStepUp = %s, outputIsSetup = %s" %\
                    (str(self._inputIsSetup), str(self._outputIsSetup)))

        self.glog.information("**** Calculate: DataWorksapce = %s" % (str(self.datawsname)))
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self.glog.information("**** Fit range: %f, %f" % (startx, endx))
        self.glog.information("**** Profile workspace = %s, Reflection workspace = %s" % (
            self.inprofilewsname, self.inreflectionwsname))
        api.LeBailFit(\
                Function                =   'Calculation',\
                InputWorkspace          =   self.datawsname,\
                OutputWorkspace         =   self.outwsname,\
                InputParameterWorkspace =   self.inprofilewsname,\
                OutputParameterWorkspace=   self.outprofilewsname,\
                InputHKLWorkspace       =   self.inreflectionwsname,\
                OutputPeaksWorkspace    =   self.outreflectionwsname,\
                FitRegion               =   '%f, %f' % (startx, endx),\
                PeakType                =   self.peaktype,\
                BackgroundType          =   self.bkgdtype,\
                UseInputPeakHeights     =   False,\
                PeakRadius              =   '8',\
                BackgroundParametersWorkspace   =   self.bkgdtablewsname\
        )

        return


    def refine(self, numsteps, parameternames, startx, endx):
        """ Main execution body (doStep4)
        """
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Check validity
        if (self._inputIsSetup and self._outputIsSetup) is False:
            raise NotImplementedError("Either input or output is not setup.")
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        self.glog.debug("[Refine] Input profile workspace = %s" % (self.inprofilewsname))
Zhou, Wenduo's avatar
Zhou, Wenduo committed
        # Update parameters' fit table
        if numsteps > 0:
            # Set up the default parameters to refine

            # Set up the parameters to refine
            # FIXME - It is found that in the 'load' mode, a ID???_Group_2 might be generated by running
            #         UpdatePeakParameterTableValue().  It is not a real new table workspace, but a link
            #         to the 'inprofilewsname'
            #         There must be something wrong in AnalysisDataService.
            api.UpdatePeakParameterTableValue(\
                    InputWorkspace  =   self.inprofilewsname,\
                    Column          =   "FitOrTie",\
                    NewStringValue  =   "tie")
            api.UpdatePeakParameterTableValue(\
                    InputWorkspace  =   self.inprofilewsname,\
                    Column          =   "FitOrTie",\
                    ParameterNames  =   parameternames,\
                    NewStringValue  =   "fit")

            if parameternames.count("Width") > 0:
                #self.cwl = 1.33
                UpdatePeakParameterTableValue(\
                        InputWorkspace  =   self.inprofilewsname,\
                        Column          =   "Min",\
                        ParameterNames  =   ["Width"],\
                        NewFloatValue   =   0.50) #cwl*0.25)
                UpdatePeakParameterTableValue(\
                        InputWorkspace  =   self.inprofilewsname,\
                        Column          =   "Max",\
                        ParameterNames  =   ["Width"],\
                        NewFloatValue   =   1.25) #cwl*4.0)
            # Generate Monte carlo table
            wsname = "MCSetupParameterTable"
            if self.peaktype == "NeutronBk2BkExpConvPVoigt":
                tablews = generateMCSetupTableProf9(wsname)
            elif self.peaktype == "ThermalNeutronBk2BkExpConvPVoigt":
                tablews = generateMCSetupTableProf10(wsname)
            else:
                raise NotImplementedError("Peak type %s is not supported to set up MC table." % (self.peaktype))
            api.LeBailFit(\
                    InputWorkspace                  = self.datawsname,\
                    OutputWorkspace                 = self.outwsname,\
                    InputParameterWorkspace         = self.inprofilewsname,\
                    OutputParameterWorkspace        = self.outprofilewsname,\
                    InputHKLWorkspace               = self.inreflectionwsname,\
                    OutputPeaksWorkspace            = self.outreflectionwsname,\
                    FitRegion                       = '%f, %f' % (startx, endx),\
                    Function                        = 'MonteCarlo',\
                    NumberMinimizeSteps             = numsteps,\
                    PeakType                        = self.peaktype,\
                    BackgroundType                  = self.bkgdtype,\
                    BackgroundParametersWorkspace   = self.bkgdtablewsname,\
                    UseInputPeakHeights             = False,\
                    PeakRadius                      ='8',\
                    Minimizer                       = 'Levenberg-Marquardt',\
                    MCSetupWorkspace                = str(wsname),\
                    Damping                         = '5.0',\
                    RandomSeed                      = 0,\
                    AnnealingTemperature            = 100.0,\
Zhou, Wenduo's avatar
Zhou, Wenduo committed
                    DrunkenWalk                     = True)
# Register algorithm with Mantid
AlgorithmFactory.subscribe(RefinePowderDiffProfileSeq)