Ejemplo n.º 1
0
 def do_reduction(calibration):
     # load data
     data = ms.Load("HRP39180.RAW")
     # copy parameters from calibration to data
     ms.CopyInstrumentParameters(calibration, data)
     # Now move component on data workspace using a relative move, where that component was a detector in the calibrated workspace
     ms.MoveInstrumentComponent(data, DetectorID=1100,X=0.0,Y=0.0,Z=5.0,RelativePosition=True)
     return data.getDetector(0).getPos()
Ejemplo n.º 2
0
    def adjustComponent(self, component, pos, rot):
        wks_name = self.wks_name
        if pos:
            msa.MoveInstrumentComponent(wks_name, component, X=pos[0], Y=pos[1], Z=pos[2], RelativePosition=False)

        if rot:
            (rotw, rotx, roty, rotz) = eulerToAngleAxis(rot[0], rot[1], rot[2], self.eulerConvention)
            msa.RotateInstrumentComponent(
                wks_name, component, X=rotx, Y=roty, Z=rotz, Angle=rotw, RelativeRotation=False)
        return
Ejemplo n.º 3
0
    def _minimisation_func(self, x_0, wks_name, component, firstIndex,
                           lastIndex, difc, mask):
        """
        Basic minimization function used. Returns the chisquared difference between the expected
        difc and the new difc after the component has been moved or rotated.
        """
        xmap = self._mapOptions(x_0)

        if self._move:
            api.MoveInstrumentComponent(wks_name,
                                        component,
                                        X=xmap[0],
                                        Y=xmap[1],
                                        Z=xmap[2],
                                        RelativePosition=False)

        if self._rotate:
            (rotw, rotx, roty,
             rotz) = self._eulerToAngleAxis(xmap[3], xmap[4], xmap[5],
                                            self._eulerConvention)  # YZX
            api.RotateInstrumentComponent(wks_name,
                                          component,
                                          X=rotx,
                                          Y=roty,
                                          Z=rotz,
                                          Angle=rotw,
                                          RelativeRotation=False)

        api.CalculateDIFC(InputWorkspace=wks_name, OutputWorkspace=wks_name)

        difc_new = api.mtd[wks_name].extractY().flatten(
        )[firstIndex:lastIndex + 1]

        if self._masking:
            difc_new = np.ma.masked_array(difc_new, mask)

        return chisquare(f_obs=difc, f_exp=difc_new)[0]
Ejemplo n.º 4
0
    def PyExec(self):
        self._eulerConvention = self.getProperty('EulerConvention').value
        calWS = self.getProperty('CalibrationTable').value
        calWS = api.SortTableWorkspace(calWS, Columns='detid')
        maskWS = self.getProperty("MaskWorkspace").value

        difc = calWS.column('difc')
        if maskWS is not None:
            self._masking = True
            mask = maskWS.extractY().flatten()
            difc = np.ma.masked_array(difc, mask)

        detID = calWS.column('detid')

        if self.getProperty("Workspace").value is not None:
            wks_name = self.getProperty("Workspace").value.name()
        else:
            wks_name = "alignedWorkspace"
            api.LoadEmptyInstrument(
                Filename=self.getProperty("InstrumentFilename").value,
                OutputWorkspace=wks_name)

        # Make a dictionary of what options are being refined for sample/source. No rotation.
        for opt in self._optionsList[:3]:
            self._optionsDict[opt] = self.getProperty(opt).value
        for opt in self._optionsList[3:]:
            self._optionsDict[opt] = False

        # First fit L1 if selected for Source and/or Sample
        for component in "Source", "Sample":
            if self.getProperty("Fit" + component + "Position").value:
                self._move = True
                if component == "Sample":
                    comp = api.mtd[wks_name].getInstrument().getSample()
                else:
                    comp = api.mtd[wks_name].getInstrument().getSource()
                componentName = comp.getFullName()
                logger.notice("Working on " + componentName +
                              " Starting position is " + str(comp.getPos()))
                firstIndex = 0
                lastIndex = len(difc)
                if self._masking:
                    mask_out = mask[firstIndex:lastIndex + 1]
                else:
                    mask_out = None

                self._initialPos = [
                    comp.getPos().getX(),
                    comp.getPos().getY(),
                    comp.getPos().getZ(), 0, 0, 0
                ]

                # Set up x0 and bounds lists
                x0List = []
                boundsList = []
                for iopt, opt in enumerate(self._optionsList[:3]):
                    if self._optionsDict[opt]:
                        x0List.append(self._initialPos[iopt])
                        boundsList.append(
                            (self._initialPos[iopt] +
                             self.getProperty("Min" + opt).value,
                             self._initialPos[iopt] +
                             self.getProperty("Max" + opt).value))

                results = minimize(self._minimisation_func,
                                   x0=x0List,
                                   method='L-BFGS-B',
                                   args=(wks_name, componentName, firstIndex,
                                         lastIndex, difc[firstIndex:lastIndex +
                                                         1], mask_out),
                                   bounds=boundsList)

                # Apply the results to the output workspace
                xmap = self._mapOptions(results.x)

                # Need to grab the component again, as things have changed
                api.MoveInstrumentComponent(wks_name,
                                            componentName,
                                            X=xmap[0],
                                            Y=xmap[1],
                                            Z=xmap[2],
                                            RelativePosition=False)
                comp = api.mtd[wks_name].getInstrument().getComponentByName(
                    componentName)
                logger.notice("Finished " + componentName +
                              " Final position is " + str(comp.getPos()))
                self._move = False

        # Now fit all the components if any
        components = self.getProperty("ComponentList").value

        # Make a dictionary of what options are being refined.
        for opt in self._optionsList:
            self._optionsDict[opt] = self.getProperty(opt).value

        self._move = (self._optionsDict["Xposition"]
                      or self._optionsDict["Yposition"]
                      or self._optionsDict["Zposition"])

        self._rotate = (self._optionsDict["AlphaRotation"]
                        or self._optionsDict["BetaRotation"]
                        or self._optionsDict["GammaRotation"])

        prog = Progress(self, start=0, end=1, nreports=len(components))
        for component in components:
            comp = api.mtd[wks_name].getInstrument().getComponentByName(
                component)
            firstDetID = self._getFirstDetID(comp)
            firstIndex = detID.index(firstDetID)
            lastDetID = self._getLastDetID(comp)
            lastIndex = detID.index(lastDetID)
            if lastDetID - firstDetID != lastIndex - firstIndex:
                raise RuntimeError(
                    "Calibration detid doesn't match instrument")

            eulerAngles = comp.getRotation().getEulerAngles(
                self._eulerConvention)

            logger.notice("Working on " + comp.getFullName() +
                          " Starting position is " + str(comp.getPos()) +
                          " Starting rotation is " + str(eulerAngles))

            x0List = []
            self._initialPos = [
                comp.getPos().getX(),
                comp.getPos().getY(),
                comp.getPos().getZ(), eulerAngles[0], eulerAngles[1],
                eulerAngles[2]
            ]

            boundsList = []

            if self._masking:
                mask_out = mask[firstIndex:lastIndex + 1]
                if mask_out.sum() == mask_out.size:
                    self.log().warning(
                        "All pixels in '%s' are masked. Skipping calibration."
                        % component)
                    continue
            else:
                mask_out = None

            for iopt, opt in enumerate(self._optionsList):
                if self._optionsDict[opt]:
                    x0List.append(self._initialPos[iopt])
                    boundsList.append((self._initialPos[iopt] +
                                       self.getProperty("Min" + opt).value,
                                       self._initialPos[iopt] +
                                       self.getProperty("Max" + opt).value))

            results = minimize(self._minimisation_func,
                               x0=x0List,
                               method='L-BFGS-B',
                               args=(wks_name, component, firstIndex,
                                     lastIndex, difc[firstIndex:lastIndex + 1],
                                     mask_out),
                               bounds=boundsList)

            # Apply the results to the output workspace
            xmap = self._mapOptions(results.x)

            if self._move:
                api.MoveInstrumentComponent(wks_name,
                                            component,
                                            X=xmap[0],
                                            Y=xmap[1],
                                            Z=xmap[2],
                                            RelativePosition=False)

            if self._rotate:
                (rotw, rotx, roty,
                 rotz) = self._eulerToAngleAxis(xmap[3], xmap[4], xmap[5],
                                                self._eulerConvention)
                api.RotateInstrumentComponent(wks_name,
                                              component,
                                              X=rotx,
                                              Y=roty,
                                              Z=rotz,
                                              Angle=rotw,
                                              RelativeRotation=False)

            # Need to grab the component again, as things have changed
            comp = api.mtd[wks_name].getInstrument().getComponentByName(
                component)
            logger.notice(
                "Finshed " + comp.getFullName() + " Final position is " +
                str(comp.getPos()) + " Final rotation is " +
                str(comp.getRotation().getEulerAngles(self._eulerConvention)))

            prog.report()
        logger.notice("Results applied to workspace " + wks_name)
Ejemplo n.º 5
0
    def _minimisation_func(self, x_0, wks_name, component, firstIndex,
                           lastIndex):
        """
        Basic minimization function used. Returns the sum of the absolute values for the fractional peak
        deviations:

        .. math::

            \\sum_i^{N_d}\\sum_j^{N_p} (1 - m_{i,j}) \\frac{|d_{i,j} - d_j^*|}{d_j^*}

        where :math:`N_d` is the number of detectors in the bank, :math:`N_p` is the number of reference peaks, and
        :math:`m_{i,j}` is the mask for peak :math:`j` and detector :math:`i`. The mask evaluates to 1 if the
        detector is defective or the peak is missing in the detector, otherwise the mask evaluates to zero.

        There's an implicit one-to-correspondence between array index of ``difc`` and workspace index of ``wks_name``,
        that is, between row index of the input TOFS table and workspace index of ``wks_name``.

        @param x_0 :: list of length 3 (new XYZ coordinates of the component) or length 6 (XYZ and rotation coords)
        @param wks_name :: name of a workspace with an embedded instrument. The instrument will be adjusted according to
            the new coordinates ``x_0`` for instrument component ``component``. It's pixel spectra will contain the new DIFC
        @param component :: name of the instrument component to be optimized
        @param firstIndex :: workspace index of first index of ``difc`` array to be considered when comparing old
            and new DIFC values. When fitting the source or sample, this is the first spectrum index.
        @param lastIndex ::  workspace index of last index of ``difc`` array to be considered when comparing old
            and new DIFC values. When fitting the source or sample, this is the last row number of the input
            TOFS table.

        @return Chi-square value between old and new DIFC values for the unmasked spectra
        """
        xmap = self._mapOptions(
            x_0)  # pad null rotations when x_0 contains only translations

        if self._move:
            api.MoveInstrumentComponent(wks_name,
                                        component,
                                        X=xmap[0],
                                        Y=xmap[1],
                                        Z=xmap[2],
                                        RelativePosition=False,
                                        EnableLogging=False)

        if self._rotate:
            (rotw, rotx, roty,
             rotz) = self._eulerToAngleAxis(xmap[3], xmap[4], xmap[5],
                                            self._eulerConvention)  # YZX
            api.RotateInstrumentComponent(wks_name,
                                          component,
                                          X=rotx,
                                          Y=roty,
                                          Z=rotz,
                                          Angle=rotw,
                                          RelativeRotation=False,
                                          EnableLogging=False)

        api.CalculateDIFC(InputWorkspace=wks_name,
                          OutputWorkspace=wks_name,
                          EnableLogging=False)
        difc = api.mtd[wks_name].extractY().flatten()[firstIndex:lastIndex + 1]
        peaks_d = self.peaks_tof[
            firstIndex:lastIndex +
            1] / difc[:, np.newaxis]  # peak centers in d-spacing units

        # calculate the fractional peak center deviations, then sum their absolute values
        return np.sum(np.abs((peaks_d - self.peaks_ref) / self.peaks_ref))
Ejemplo n.º 6
0
    def PyExec(self):
        table_tof = self.getProperty('PeakCentersTofTable').value
        self.peaks_tof = self._extract_tofs(table_tof)
        detector_count, peak_count = self.peaks_tof.shape
        table_tof = api.SortTableWorkspace(table_tof, Columns='detid')
        detID = table_tof.column('detid')
        peaks_ref = np.sort(self.getProperty(
            'PeakPositions').value)  # sort by increasing value
        self.peaks_ref = peaks_ref[np.newaxis, :]  # shape = (1, peak_count)

        # Process input mask
        maskWS = self.getProperty("MaskWorkspace").value
        if maskWS is not None:
            mask = maskWS.extractY().flatten()  # shape=(detector_count,)
            peaks_mask = np.tile(
                mask[:, np.newaxis],
                peak_count)  # shape=(detector_count, peak_count)
        else:
            peaks_mask = np.zeros(
                (detector_count, peak_count))  # no detectors are masked
        peaks_mask[np.isnan(self.peaks_tof)] = True
        # mask the defective detectors and missing peaks
        self.peaks_tof = np.ma.masked_array(self.peaks_tof, peaks_mask)

        input_workspace = self.getProperty('InputWorkspace').value

        # Table containing the optimized absolute locations and orientations for each component
        adjustments_table_name = self.getProperty('AdjustmentsTable').value
        if len(adjustments_table_name) > 0:
            adjustments_table = self._initialize_adjustments_table(
                adjustments_table_name)
            saving_adjustments = True
        else:
            saving_adjustments = False

        # Table containing the relative changes in position and euler angles for each bank component
        displacements_table_name = self.getProperty('DisplacementsTable').value
        if len(displacements_table_name) > 0:
            displacements_table = self._initialize_displacements_table(
                displacements_table_name)
            saving_displacements = True
        else:
            saving_displacements = False

        self._eulerConvention = self.getProperty('EulerConvention').value

        output_workspace = self.getPropertyValue("OutputWorkspace")
        wks_name = '__alignedworkspace'  # workspace whose counts will be DIFC values
        if bool(input_workspace) is True:
            api.CloneWorkspace(InputWorkspace=input_workspace,
                               OutputWorkspace=wks_name)
            if output_workspace != str(input_workspace):
                api.CloneWorkspace(InputWorkspace=input_workspace,
                                   OutputWorkspace=output_workspace)
        else:
            api.LoadEmptyInstrument(
                Filename=self.getProperty("InstrumentFilename").value,
                OutputWorkspace=wks_name)

        # Make a dictionary of what options are being refined for sample/source. No rotation.
        for translation_option in self._optionsList[:3]:
            self._optionsDict[translation_option] = self.getProperty(
                translation_option).value
        for rotation_option in self._optionsList[3:]:
            self._optionsDict[rotation_option] = False

        # First fit L1 if selected for Source and/or Sample
        sample_position_begin = api.mtd[wks_name].getInstrument().getSample(
        ).getPos()
        for component in "Source", "Sample":  # fit first the source position, then the sample position
            if self.getProperty("Fit" + component + "Position").value:
                self._move = True
                if component == "Sample":
                    comp = api.mtd[wks_name].getInstrument().getSample()
                else:
                    comp = api.mtd[wks_name].getInstrument().getSource()
                componentName = comp.getFullName()
                logger.notice("Working on " + componentName +
                              " Starting position is " + str(comp.getPos()))
                firstIndex = 0
                lastIndex = detector_count - 1

                self._initialPos = [
                    comp.getPos().getX(),
                    comp.getPos().getY(),
                    comp.getPos().getZ(), 0, 0, 0
                ]  # no rotation

                # Set up x0 and bounds lists
                x0List = []  # initial X, Y, Z coordinates
                boundsList = []  # [(minX, maxX), (minZ, maxZ), (minZ, maxZ)]
                for iopt, translation_option in enumerate(
                        self._optionsList[:3]):  # iterate over X, Y, and Z
                    if self._optionsDict[translation_option]:
                        x0List.append(self._initialPos[iopt])
                        # default range for X is (x0 - 0.1m, x0 + 0.1m), same for Y and Z
                        boundsList.append((
                            self._initialPos[iopt] +
                            self.getProperty("Min" + translation_option).value,
                            self._initialPos[iopt] +
                            self.getProperty("Max" +
                                             translation_option).value))

                # scipy.opimize.minimize with the L-BFGS-B algorithm
                results: OptimizeResult = minimize(
                    self._minimisation_func,
                    x0=x0List,
                    method='L-BFGS-B',
                    args=(wks_name, componentName, firstIndex, lastIndex),
                    bounds=boundsList)

                # Apply the results to the output workspace
                xmap = self._mapOptions(results.x)

                # Save translation and rotations, if requested
                if saving_adjustments:
                    instrument = api.mtd[wks_name].getInstrument()
                    name_finder = {
                        'Source': instrument.getSource().getName(),
                        'Sample': instrument.getSample().getName()
                    }
                    component_adjustments = [
                        name_finder[component]
                    ] + xmap[:3] + [0.0] * 4  # no rotations
                    adjustments_table.addRow(component_adjustments)

                # Need to grab the component again, as things have changed
                kwargs = dict(X=xmap[0],
                              Y=xmap[1],
                              Z=xmap[2],
                              RelativePosition=False,
                              EnableLogging=False)
                api.MoveInstrumentComponent(wks_name, componentName,
                                            **kwargs)  # adjust workspace
                api.MoveInstrumentComponent(output_workspace, componentName,
                                            **kwargs)  # adjust workspace
                comp = api.mtd[wks_name].getInstrument().getComponentByName(
                    componentName)
                logger.notice("Finished " + componentName +
                              " Final position is " + str(comp.getPos()))
                self._move = False
        sample_position_end = api.mtd[wks_name].getInstrument().getSample(
        ).getPos()

        # Now fit all the remaining components, if any
        components = self.getProperty("ComponentList").value

        # Make a dictionary of what translational and rotational options are being refined.
        for opt in self._optionsList:
            self._optionsDict[opt] = self.getProperty(opt).value

        self._move = any([
            self._optionsDict[t]
            for t in ('Xposition', 'Yposition', 'Zposition')
        ])
        self._rotate = any([
            self._optionsDict[r]
            for r in ('AlphaRotation', 'BetaRotation', 'GammaRotation')
        ])

        prog = Progress(self, start=0, end=1, nreports=len(components))
        for component in components:
            comp = api.mtd[wks_name].getInstrument().getComponentByName(
                component)
            firstDetID = self._getFirstDetID(comp)
            firstIndex = detID.index(
                firstDetID)  # a row index in the input TOFS table
            lastDetID = self._getLastDetID(comp)
            lastIndex = detID.index(
                lastDetID)  # a row index in the input TOFS table
            if lastDetID - firstDetID != lastIndex - firstIndex:
                raise RuntimeError("TOFS detid doesn't match instrument")

            eulerAngles: List[float] = comp.getRotation().getEulerAngles(
                self._eulerConvention)

            logger.notice("Working on " + comp.getFullName() +
                          " Starting position is " + str(comp.getPos()) +
                          " Starting rotation is " + str(eulerAngles))

            x0List = []
            self._initialPos = [
                comp.getPos().getX(),
                comp.getPos().getY(),
                comp.getPos().getZ(), eulerAngles[0], eulerAngles[1],
                eulerAngles[2]
            ]

            # Distance between the original position of the sample and the original position of the component
            comp_sample_distance_begin = (comp.getPos() -
                                          sample_position_begin).norm()

            boundsList = []

            if np.all(peaks_mask[firstIndex:lastIndex + 1].astype(bool)):
                self.log().warning(
                    "All pixels in '%s' are masked. Skipping calibration." %
                    component)
                continue

            for iopt, opt in enumerate(self._optionsList):
                if self._optionsDict[opt]:
                    x0List.append(self._initialPos[iopt])
                    boundsList.append((self._initialPos[iopt] +
                                       self.getProperty("Min" + opt).value,
                                       self._initialPos[iopt] +
                                       self.getProperty("Max" + opt).value))

            minimizer_selection = self.getProperty('Minimizer').value
            if minimizer_selection == 'L-BFGS-B':
                # scipy.opimize.minimize with the L-BFGS-B algorithm
                results: OptimizeResult = minimize(self._minimisation_func,
                                                   x0=x0List,
                                                   method='L-BFGS-B',
                                                   args=(wks_name, component,
                                                         firstIndex,
                                                         lastIndex),
                                                   bounds=boundsList)
            elif minimizer_selection == 'differential_evolution':
                results: OptimizeResult = differential_evolution(
                    self._minimisation_func,
                    bounds=boundsList,
                    args=(wks_name, component, firstIndex, lastIndex),
                    maxiter=self.getProperty('MaxIterations').value)
            # Apply the results to the output workspace
            xmap = self._mapOptions(results.x)

            comp = api.mtd[wks_name].getInstrument().getComponentByName(
                component)  # adjusted component
            # Distance between the adjusted position of the sample and the adjusted position of the component
            comp_sample_distance_end = (comp.getPos() -
                                        sample_position_end).norm()

            component_adjustments = [
                0.
            ] * 7  # 3 for translation, 3 for rotation axis, 1 for rotation angle
            component_displacements = [
                0.
            ] * 7  # 1 for distnace, 3 for translation, 3 for Euler angles
            component_displacements[0] = 1000 * (
                comp_sample_distance_end - comp_sample_distance_begin
            )  # in mili-meters

            if self._move:
                kwargs = dict(X=xmap[0],
                              Y=xmap[1],
                              Z=xmap[2],
                              RelativePosition=False,
                              EnableLogging=False)
                api.MoveInstrumentComponent(wks_name, component,
                                            **kwargs)  # adjust workspace
                api.MoveInstrumentComponent(output_workspace, component,
                                            **kwargs)  # adjust workspace
                component_adjustments[:3] = xmap[:3]
                for i in range(3):
                    component_displacements[i + 1] = 1000 * (
                        xmap[i] - self._initialPos[i])  # in mili-meters

            if self._rotate:
                (rotw, rotx, roty,
                 rotz) = self._eulerToAngleAxis(xmap[3], xmap[4], xmap[5],
                                                self._eulerConvention)
                kwargs = dict(X=rotx,
                              Y=roty,
                              Z=rotz,
                              Angle=rotw,
                              RelativeRotation=False,
                              EnableLogging=False)
                api.RotateInstrumentComponent(wks_name, component,
                                              **kwargs)  # adjust workspace
                api.RotateInstrumentComponent(output_workspace, component,
                                              **kwargs)  # adjust workspace
                component_adjustments[3:] = [rotx, roty, rotz, rotw]
                for i in range(3, 6):
                    component_displacements[
                        i + 1] = xmap[i] - self._initialPos[i]  # in degrees

            if saving_adjustments and (self._move or self._rotate):
                adjustments_table.addRow([component] + component_adjustments)

            if saving_displacements and (self._move or self._rotate):
                displacements_table.addRow([component] +
                                           component_displacements)

            # Need to grab the component object again, as things have changed
            logger.notice(
                "Finished " + comp.getFullName() + " Final position is " +
                str(comp.getPos()) + " Final rotation is " +
                str(comp.getRotation().getEulerAngles(self._eulerConvention)))

            prog.report()
        api.DeleteWorkspace(wks_name)
        self.setProperty("OutputWorkspace", output_workspace)
        logger.notice("Results applied to workspace " + wks_name)