Exemplo n.º 1
0
class CalibrateDialog(NXDialog):

    def __init__(self, parent=None):
        super().__init__(parent)

        self.plotview = None
        self.data = None
        self.counts = None
        self.points = []
        self.pattern_geometry = None
        self.cake_geometry = None
        self.polarization = None
        self.is_calibrated = False
        self.phi_max = -np.pi

        cstr = str(ALL_CALIBRANTS)
        calibrants = sorted(cstr[cstr.index(':')+2:].split(', '))
        self.parameters = GridParameters()
        self.parameters.add('calibrant', calibrants, 'Calibrant')
        self.parameters['calibrant'].value = 'CeO2'
        self.parameters.add('wavelength', 0.5, 'Wavelength (Ang)', False)
        self.parameters.add('distance', 100.0, 'Detector Distance (mm)', True)
        self.parameters.add('xc', 512, 'Beam Center - x', True)
        self.parameters.add('yc', 512, 'Beam Center - y', True)
        self.parameters.add('yaw', 0.0, 'Yaw (degrees)', True)
        self.parameters.add('pitch', 0.0, 'Pitch (degrees)', True)
        self.parameters.add('roll', 0.0, 'Roll (degrees)', True)
        self.parameters.add('search_size', 10, 'Search Size (pixels)')
        self.rings_box = self.select_box([f'Ring{i}' for i in range(1, 21)])
        self.set_layout(self.select_entry(self.choose_entry),
                        self.progress_layout(close=True))
        self.set_title('Calibrating Powder')

    def choose_file(self):
        super().choose_file()
        powder_file = self.get_filename()
        if powder_file:
            self.data = load_image(powder_file)
            self.counts = self.data.nxsignal.nxvalue
            self.plot_data()

    def choose_entry(self):
        if self.layout.count() == 2:
            self.insert_layout(
                1, self.filebox('Choose Powder Calibration File'))
            self.insert_layout(2, self.parameters.grid(header=False))
            self.insert_layout(
                3, self.action_buttons(('Select Points', self.select),
                                       ('Autogenerate Rings', self.auto),
                                       ('Clear Points', self.clear_points)))
            self.insert_layout(4, self.make_layout(self.rings_box))
            self.insert_layout(
                5, self.action_buttons(('Calibrate', self.calibrate),
                                       ('Plot Cake', self.plot_cake),
                                       ('Restore', self.restore_parameters),
                                       ('Save', self.save_parameters)))
        self.parameters['wavelength'].value = (
            self.entry['instrument/monochromator/wavelength'])
        detector = self.entry['instrument/detector']
        self.parameters['distance'].value = detector['distance']
        self.parameters['yaw'].value = detector['yaw']
        self.parameters['pitch'].value = detector['pitch']
        self.parameters['roll'].value = detector['roll']
        if 'beam_center_x' in detector:
            self.parameters['xc'].value = detector['beam_center_x']
        if 'beam_center_y' in detector:
            self.parameters['yc'].value = detector['beam_center_y']
        self.pixel_size = (
            self.entry['instrument/detector/pixel_size'].nxvalue * 1e-3)
        self.pixel_mask = self.entry['instrument/detector/pixel_mask'].nxvalue
        self.ring = self.selected_ring
        if 'calibration' in self.entry['instrument']:
            self.data = self.entry['instrument/calibration']
            self.counts = self.data.nxsignal.nxvalue
            self.plot_data()
        else:
            self.close_plots()

    @property
    def search_size(self):
        return int(self.parameters['search_size'].value)

    @property
    def selected_ring(self):
        return int(self.rings_box.currentText()[4:]) - 1

    @property
    def ring_color(self):
        colors = ['r', 'b', 'g', 'c', 'm'] * 4
        return colors[self.ring]

    def plot_data(self):
        if self.plotview is None:
            if 'Powder Calibration' in plotviews:
                self.plotview = plotviews['Powder Calibration']
            else:
                self.plotview = NXPlotView('Powder Calibration')
        self.plotview.plot(self.data, log=True)
        self.plotview.aspect = 'equal'
        self.plotview.ytab.flipped = True
        self.clear_points()

    def on_button_press(self, event):
        self.plotview.make_active()
        if event.inaxes:
            self.xp, self.yp = event.x, event.y
        else:
            self.xp, self.yp = 0, 0

    def on_button_release(self, event):
        self.ring = self.selected_ring
        if event.inaxes:
            if abs(event.x - self.xp) > 5 or abs(event.y - self.yp) > 5:
                return
            x, y = self.plotview.inverse_transform(event.xdata, event.ydata)
            for i, point in enumerate(self.points):
                circle = point[0]
                if circle.shape.contains_point(
                        self.plotview.ax.transData.transform((x, y))):
                    circle.remove()
                    for circle in point[2]:
                        circle.remove()
                    del self.points[i]
                    return
            self.add_points(x, y)

    def circle(self, idx, idy, alpha=1.0):
        return self.plotview.circle(idx, idy, self.search_size,
                                    facecolor=self.ring_color, edgecolor='k',
                                    alpha=alpha)

    def select(self):
        self.plotview.cidpress = self.plotview.mpl_connect(
            'button_press_event', self.on_button_press)
        self.plotview.cidrelease = self.plotview.mpl_connect(
            'button_release_event', self.on_button_release)

    def auto(self):
        xc, yc = self.parameters['xc'].value, self.parameters['yc'].value
        wavelength = self.parameters['wavelength'].value
        distance = self.parameters['distance'].value * 1e-3
        self.start_progress((0, self.selected_ring+1))
        for ring in range(self.selected_ring+1):
            self.update_progress(ring)
            if len([p for p in self.points if p[3] == ring]) > 0:
                continue
            self.ring = ring
            theta = 2 * np.arcsin(wavelength /
                                  (2*self.calibrant.dSpacing[ring]))
            r = distance * np.tan(theta) / self.pixel_size
            phi = self.phi_max = -np.pi
            while phi < np.pi:
                x, y = np.int(xc + r*np.cos(phi)), np.int(yc + r*np.sin(phi))
                if ((x > 0 and x < self.data.x.max()) and
                    (y > 0 and y < self.data.y.max()) and
                        not self.pixel_mask[y, x]):
                    self.add_points(x, y, phi)
                    phi = self.phi_max + 0.2
                else:
                    phi = phi + 0.2
        self.stop_progress()

    def add_points(self, x, y, phi=0.0):
        xc, yc = self.parameters['xc'].value, self.parameters['yc'].value
        idx, idy = self.find_peak(x, y)
        points = [(idy, idx)]
        circles = []
        massif = Massif(self.counts)
        extra_points = massif.find_peaks((idy, idx))
        for point in extra_points:
            points.append(point)
            circles.append(self.circle(point[1], point[0], alpha=0.3))
        phis = np.array([np.arctan2(p[0]-yc, p[1]-xc) for p in points])
        if phi < -0.5*np.pi:
            phis[np.where(phis > 0.0)] -= 2 * np.pi
        self.phi_max = max(*phis, self.phi_max)
        self.points.append([self.circle(idx, idy), points, circles, self.ring])

    def find_peak(self, x, y):
        s = self.search_size
        left = int(np.round(x - s * 0.5))
        if left < 0:
            left = 0
        top = int(np.round(y - s * 0.5))
        if top < 0:
            top = 0
        region = self.counts[top:(top+s), left:(left+s)]
        idy, idx = np.where(region == region.max())
        idx = left + idx[0]
        idy = top + idy[0]
        return idx, idy

    def clear_points(self):
        for i, point in enumerate(self.points):
            circle = point[0]
            circle.remove()
            for circle in point[2]:
                circle.remove()
        self.points = []

    @property
    def calibrant(self):
        return ALL_CALIBRANTS[self.parameters['calibrant'].value]

    @property
    def point_array(self):
        points = []
        for point in self.points:
            for p in point[1]:
                points.append((p[0], p[1], point[3]))
        return np.array(points)

    def prepare_parameters(self):
        self.parameters.set_parameters()
        self.wavelength = self.parameters['wavelength'].value * 1e-10
        self.distance = self.parameters['distance'].value * 1e-3
        self.yaw = np.radians(self.parameters['yaw'].value)
        self.pitch = np.radians(self.parameters['pitch'].value)
        self.roll = np.radians(self.parameters['roll'].value)
        self.xc = self.parameters['xc'].value
        self.yc = self.parameters['yc'].value

    def calibrate(self):
        self.prepare_parameters()
        self.orig_pixel1 = self.pixel_size
        self.orig_pixel2 = self.pixel_size
        self.pattern_geometry = GeometryRefinement(self.point_array,
                                                   dist=self.distance,
                                                   wavelength=self.wavelength,
                                                   pixel1=self.pixel_size,
                                                   pixel2=self.pixel_size,
                                                   calibrant=self.calibrant)
        self.refine()
        self.create_cake_geometry()
        self.pattern_geometry.reset()

    def refine(self):
        self.pattern_geometry.data = self.point_array

        if self.parameters['wavelength'].vary:
            self.pattern_geometry.refine2()
            fix = []
        else:
            fix = ['wavelength']
        if not self.parameters['distance'].vary:
            fix.append('dist')
        self.pattern_geometry.refine2_wavelength(fix=fix)
        self.read_parameters()
        self.is_calibrated = True
        self.create_cake_geometry()
        self.pattern_geometry.reset()

    def create_cake_geometry(self):
        self.cake_geometry = AzimuthalIntegrator()
        pyFAI_parameter = self.pattern_geometry.getPyFAI()
        pyFAI_parameter['wavelength'] = self.pattern_geometry.wavelength
        self.cake_geometry.setPyFAI(dist=pyFAI_parameter['dist'],
                                    poni1=pyFAI_parameter['poni1'],
                                    poni2=pyFAI_parameter['poni2'],
                                    rot1=pyFAI_parameter['rot1'],
                                    rot2=pyFAI_parameter['rot2'],
                                    rot3=pyFAI_parameter['rot3'],
                                    pixel1=pyFAI_parameter['pixel1'],
                                    pixel2=pyFAI_parameter['pixel2'])
        self.cake_geometry.wavelength = pyFAI_parameter['wavelength']

    def plot_cake(self):
        if 'Cake Plot' in plotviews:
            plotview = plotviews['Cake Plot']
        else:
            plotview = NXPlotView('Cake Plot')
        if not self.is_calibrated:
            raise NeXusError('No refinement performed')
        res = self.cake_geometry.integrate2d(self.counts,
                                             1024, 1024,
                                             method='csr',
                                             unit='2th_deg',
                                             correctSolidAngle=True)
        self.cake_data = NXdata(res[0],
                                (NXfield(res[2], name='azimumthal_angle'),
                                 NXfield(res[1], name='polar_angle')))
        self.cake_data['title'] = 'Cake Plot'
        plotview.plot(self.cake_data, log=True)
        wavelength = self.parameters['wavelength'].value
        polar_angles = [2 * np.degrees(np.arcsin(wavelength/(2*d)))
                        for d in self.calibrant.dSpacing]
        plotview.vlines([polar_angle for polar_angle in polar_angles
                         if polar_angle < plotview.xaxis.max],
                        linestyle=':', color='r')

    def read_parameters(self):
        pyFAI = self.pattern_geometry.getPyFAI()
        fit2d = self.pattern_geometry.getFit2D()
        self.parameters['wavelength'].value = (
            self.pattern_geometry.wavelength * 1e10)
        self.parameters['distance'].value = pyFAI['dist'] * 1e3
        self.parameters['yaw'].value = np.degrees(pyFAI['rot1'])
        self.parameters['pitch'].value = np.degrees(pyFAI['rot2'])
        self.parameters['roll'].value = np.degrees(pyFAI['rot3'])
        self.parameters['xc'].value = fit2d['centerX']
        self.parameters['yc'].value = fit2d['centerY']

    def restore_parameters(self):
        self.parameters.restore_parameters()

    def save_parameters(self):
        if not self.is_calibrated:
            raise NeXusError('No refinement performed')
        elif 'calibration' in self.entry['instrument']:
            if confirm_action(
                    "Do you want to overwrite existing calibration data?"):
                del self.entry['instrument/calibration']
            else:
                return
        self.entry['instrument/calibration'] = self.data
        if 'refinement' in self.entry['instrument/calibration']:
            if confirm_action('Overwrite previous refinement?'):
                del self.entry['instrument/calibration/refinement']
            else:
                return
        self.entry['instrument/calibration/calibrant'] = (
            self.parameters['calibrant'].value)
        process = NXprocess()
        process.program = 'pyFAI'
        process.version = pyFAI.version
        process.parameters = NXcollection()
        process.parameters['Detector'] = (
            self.entry['instrument/detector/description'])
        pyFAI_parameter = self.pattern_geometry.getPyFAI()
        process.parameters['PixelSize1'] = pyFAI_parameter['pixel1']
        process.parameters['PixelSize2'] = pyFAI_parameter['pixel2']
        process.parameters['Distance'] = pyFAI_parameter['dist']
        process.parameters['Poni1'] = pyFAI_parameter['poni1']
        process.parameters['Poni2'] = pyFAI_parameter['poni2']
        process.parameters['Rot1'] = pyFAI_parameter['rot1']
        process.parameters['Rot2'] = pyFAI_parameter['rot2']
        process.parameters['Rot3'] = pyFAI_parameter['rot3']
        process.parameters['Wavelength'] = pyFAI_parameter['wavelength']
        self.entry['instrument/calibration/refinement'] = process
        self.entry['instrument/monochromator/wavelength'] = (
            self.parameters['wavelength'].value)
        self.entry['instrument/monochromator/energy'] = (
            12.398419739640717 / self.parameters['wavelength'].value)
        detector = self.entry['instrument/detector']
        detector['distance'] = self.parameters['distance'].value
        detector['yaw'] = self.parameters['yaw'].value
        detector['pitch'] = self.parameters['pitch'].value
        detector['roll'] = self.parameters['roll'].value
        detector['beam_center_x'] = self.parameters['xc'].value
        detector['beam_center_y'] = self.parameters['yc'].value
        try:
            detector['polarization'] = self.pattern_geometry.polarization(
                factor=0.99, shape=detector['mask'].shape)
        except Exception:
            pass

    def close_plots(self):
        if 'Powder Calibration' in plotviews:
            plotviews['Powder Calibration'].close()
        if 'Cake Plot' in plotviews:
            plotviews['Cake Plot'].close()

    def closeEvent(self, event):
        self.close_plots()
        event.accept()

    def accept(self):
        super().accept()
        self.close_plots()

    def reject(self):
        super().reject()
        self.close_plots()
Exemplo n.º 2
0
class CalibrateDialog(BaseDialog):
    def __init__(self, parent=None):
        super(CalibrateDialog, self).__init__(parent)

        self.plotview = None
        self.data = None
        self.counts = None
        self.points = []
        self.pattern_geometry = None
        self.cake_geometry = None
        self.is_calibrated = False

        cstr = str(ALL_CALIBRANTS)
        calibrants = sorted(cstr[cstr.index(':') + 2:].split(', '))
        self.parameters = GridParameters()
        self.parameters.add('calibrant', calibrants, 'Calibrant')
        self.parameters['calibrant'].value = 'CeO2'
        self.parameters.add('wavelength', 0.5, 'Wavelength (Ang)', False)
        self.parameters.add('distance', 100.0, 'Detector Distance (mm)', True)
        self.parameters.add('xc', 512, 'Beam Center - x', True)
        self.parameters.add('yc', 512, 'Beam Center - y', True)
        self.parameters.add('yaw', 0.0, 'Yaw (degrees)', True)
        self.parameters.add('pitch', 0.0, 'Pitch (degrees)', True)
        self.parameters.add('roll', 0.0, 'Roll (degrees)', True)
        self.parameters.add('search_size', 10, 'Search Size (pixels)')
        rings = ['Ring1', 'Ring2', 'Ring3', 'Ring4', 'Ring5']
        self.rings_box = self.select_box(rings)
        self.set_layout(
            self.select_entry(self.choose_entry),
            self.action_buttons(('Plot Calibration', self.plot_data)),
            self.parameters.grid(header=False),
            self.make_layout(
                self.action_buttons(('Select Points', self.select)),
                self.rings_box),
            self.action_buttons(('Calibrate', self.calibrate),
                                ('Plot Cake', self.plot_cake),
                                ('Restore', self.restore_parameters),
                                ('Save', self.save_parameters)),
            self.close_buttons(close=True))
        self.set_title('Calibrating Powder')

    def choose_entry(self):
        if 'calibration' not in self.entry['instrument']:
            raise NeXusError('Please load calibration data to this entry')
        self.update_parameters()
        self.plot_data()

    def update_parameters(self):
        self.parameters['wavelength'].value = self.entry[
            'instrument/monochromator/wavelength']
        detector = self.entry['instrument/detector']
        self.parameters['distance'].value = detector['distance']
        self.parameters['yaw'].value = detector['yaw']
        self.parameters['pitch'].value = detector['pitch']
        self.parameters['roll'].value = detector['roll']
        if 'beam_center_x' in detector:
            self.parameters['xc'].value = detector['beam_center_x']
        if 'beam_center_y' in detector:
            self.parameters['yc'].value = detector['beam_center_y']
        self.data = self.entry['instrument/calibration']
        self.counts = self.data.nxsignal.nxvalue

    @property
    def search_size(self):
        return int(self.parameters['search_size'].value)

    @property
    def ring(self):
        return int(self.rings_box.currentText()[-1]) - 1

    @property
    def ring_color(self):
        colors = ['r', 'b', 'g', 'c', 'm']
        return colors[self.ring]

    def plot_data(self):
        if self.plotview is None:
            if 'Powder Calibration' in plotviews:
                self.plotview = plotviews['Powder Calibration']
            else:
                self.plotview = NXPlotView('Powder Calibration')
        self.plotview.plot(self.data, log=True)
        self.plotview.aspect = 'equal'
        self.plotview.ytab.flipped = True
        self.clear_peaks()

    def on_button_press(self, event):
        self.plotview.make_active()
        if event.inaxes:
            self.xp, self.yp = event.x, event.y
        else:
            self.xp, self.yp = 0, 0

    def on_button_release(self, event):
        if event.inaxes:
            if abs(event.x - self.xp) > 5 or abs(event.y - self.yp) > 5:
                return
            x, y = self.plotview.inverse_transform(event.xdata, event.ydata)
            for i, point in enumerate(self.points):
                circle = point[0]
                if circle.contains_point(
                        self.plotview.ax.transData.transform((x, y))):
                    circle.remove()
                    for circle in point[2]:
                        circle.remove()
                    del self.points[i]
                    return
            idx, idy = self.find_peak(x, y)
            points = [(idy, idx)]
            circles = []
            massif = Massif(self.counts)
            extra_points = massif.find_peaks((idy, idx))
            for point in extra_points:
                points.append(point)
                circles.append(self.circle(point[1], point[0], alpha=0.3))
            self.points.append(
                [self.circle(idx, idy), points, circles, self.ring])

    def circle(self, idx, idy, alpha=1.0):
        return self.plotview.circle(idx,
                                    idy,
                                    self.search_size,
                                    facecolor=self.ring_color,
                                    edgecolor='k',
                                    alpha=alpha)

    def select(self):
        self.plotview.cidpress = self.plotview.mpl_connect(
            'button_press_event', self.on_button_press)
        self.plotview.cidrelease = self.plotview.mpl_connect(
            'button_release_event', self.on_button_release)

    def find_peak(self, x, y):
        s = self.search_size
        left = int(np.round(x - s * 0.5))
        if left < 0:
            left = 0
        top = int(np.round(y - s * 0.5))
        if top < 0:
            top = 0
        region = self.counts[top:(top + s), left:(left + s)]
        idy, idx = np.where(region == region.max())
        idx = left + idx[0]
        idy = top + idy[0]
        return idx, idy

    def clear_peaks(self):
        self.points = []

    @property
    def calibrant(self):
        return ALL_CALIBRANTS[self.parameters['calibrant'].value]

    @property
    def point_array(self):
        points = []
        for point in self.points:
            for p in point[1]:
                points.append((p[0], p[1], point[3]))
        return np.array(points)

    def prepare_parameters(self):
        self.parameters.set_parameters()
        self.wavelength = self.parameters['wavelength'].value * 1e-10
        self.distance = self.parameters['distance'].value * 1e-3
        self.yaw = np.radians(self.parameters['yaw'].value)
        self.pitch = np.radians(self.parameters['pitch'].value)
        self.roll = np.radians(self.parameters['roll'].value)
        self.pixel_size = self.entry[
            'instrument/detector/pixel_size'].nxvalue * 1e-3
        self.xc = self.parameters['xc'].value
        self.yc = self.parameters['yc'].value

    def calibrate(self):
        self.prepare_parameters()
        self.orig_pixel1 = self.pixel_size
        self.orig_pixel2 = self.pixel_size
        self.pattern_geometry = GeometryRefinement(self.point_array,
                                                   dist=self.distance,
                                                   wavelength=self.wavelength,
                                                   pixel1=self.pixel_size,
                                                   pixel2=self.pixel_size,
                                                   calibrant=self.calibrant)
        self.refine()
        self.create_cake_geometry()
        self.pattern_geometry.reset()

    def refine(self):
        self.pattern_geometry.data = self.point_array

        if self.parameters['wavelength'].vary:
            self.pattern_geometry.refine2()
            fix = []
        else:
            fix = ['wavelength']
        if not self.parameters['distance'].vary:
            fix.append('dist')
        self.pattern_geometry.refine2_wavelength(fix=fix)
        self.read_parameters()
        self.is_calibrated = True
        self.create_cake_geometry()
        self.pattern_geometry.reset()

    def create_cake_geometry(self):
        self.cake_geometry = AzimuthalIntegrator()
        pyFAI_parameter = self.pattern_geometry.getPyFAI()
        pyFAI_parameter['wavelength'] = self.pattern_geometry.wavelength
        self.cake_geometry.setPyFAI(dist=pyFAI_parameter['dist'],
                                    poni1=pyFAI_parameter['poni1'],
                                    poni2=pyFAI_parameter['poni2'],
                                    rot1=pyFAI_parameter['rot1'],
                                    rot2=pyFAI_parameter['rot2'],
                                    rot3=pyFAI_parameter['rot3'],
                                    pixel1=pyFAI_parameter['pixel1'],
                                    pixel2=pyFAI_parameter['pixel2'])
        self.cake_geometry.wavelength = pyFAI_parameter['wavelength']

    def plot_cake(self):
        if 'Cake Plot' in plotviews:
            plotview = plotviews['Cake Plot']
        else:
            plotview = NXPlotView('Cake Plot')
        if not self.is_calibrated:
            raise NeXusError('No refinement performed')
        res = self.cake_geometry.integrate2d(self.counts,
                                             1024,
                                             1024,
                                             method='csr',
                                             unit='2th_deg',
                                             correctSolidAngle=True)
        self.cake_data = NXdata(res[0],
                                (NXfield(res[2], name='azimumthal_angle'),
                                 NXfield(res[1], name='polar_angle')))
        self.cake_data['title'] = self.entry['instrument/calibration/title']
        plotview.plot(self.cake_data, log=True)
        wavelength = self.parameters['wavelength'].value
        polar_angles = [
            2 * np.degrees(np.arcsin(wavelength / (2 * d)))
            for d in self.calibrant.dSpacing
        ]
        plotview.vlines([
            polar_angle
            for polar_angle in polar_angles if polar_angle < plotview.xaxis.max
        ],
                        linestyle=':',
                        color='r')

    def read_parameters(self):
        pyFAI = self.pattern_geometry.getPyFAI()
        fit2d = self.pattern_geometry.getFit2D()
        self.parameters[
            'wavelength'].value = self.pattern_geometry.wavelength * 1e10
        self.parameters['distance'].value = pyFAI['dist'] * 1e3
        self.parameters['yaw'].value = np.degrees(pyFAI['rot1'])
        self.parameters['pitch'].value = np.degrees(pyFAI['rot2'])
        self.parameters['roll'].value = np.degrees(pyFAI['rot3'])
        self.parameters['xc'].value = fit2d['centerX']
        self.parameters['yc'].value = fit2d['centerY']

    def restore_parameters(self):
        self.parameters.restore_parameters()

    def save_parameters(self):
        if not self.is_calibrated:
            raise NeXusError('No refinement performed')
        elif 'refinement' in self.entry['instrument/calibration']:
            if confirm_action('Overwrite previous refinement?'):
                del self.entry['instrument/calibration/refinement']
            else:
                return
        self.entry['instrument/calibration/calibrant'] = self.parameters[
            'calibrant'].value
        process = NXprocess()
        process.program = 'pyFAI'
        process.version = pyFAI.version
        process.parameters = NXcollection()
        process.parameters['Detector'] = self.entry[
            'instrument/detector/description']
        pyFAI_parameter = self.pattern_geometry.getPyFAI()
        process.parameters['PixelSize1'] = pyFAI_parameter['pixel1']
        process.parameters['PixelSize2'] = pyFAI_parameter['pixel2']
        process.parameters['Distance'] = pyFAI_parameter['dist']
        process.parameters['Poni1'] = pyFAI_parameter['poni1']
        process.parameters['Poni2'] = pyFAI_parameter['poni2']
        process.parameters['Rot1'] = pyFAI_parameter['rot1']
        process.parameters['Rot2'] = pyFAI_parameter['rot2']
        process.parameters['Rot3'] = pyFAI_parameter['rot3']
        process.parameters['Wavelength'] = pyFAI_parameter['wavelength']
        self.entry['instrument/calibration/refinement'] = process
        self.entry['instrument/monochromator/wavelength'] = self.parameters[
            'wavelength'].value
        self.entry[
            'instrument/monochromator/energy'] = 12.398419739640717 / self.parameters[
                'wavelength'].value
        detector = self.entry['instrument/detector']
        detector['distance'] = self.parameters['distance'].value
        detector['yaw'] = self.parameters['yaw'].value
        detector['pitch'] = self.parameters['pitch'].value
        detector['roll'] = self.parameters['roll'].value
        detector['beam_center_x'] = self.parameters['xc'].value
        detector['beam_center_y'] = self.parameters['yc'].value

    def reject(self):
        super(CalibrateDialog, self).reject()
        if 'Powder Calibration' in plotviews:
            plotviews['Powder Calibration'].close_view()
        if 'Cake Plot' in plotviews:
            plotviews['Cake Plot'].close_view()
Exemplo n.º 3
0
class CalibrateDialog(BaseDialog):

    def __init__(self, parent=None):
        super(CalibrateDialog, self).__init__(parent)

        self.plotview = None
        self.data = None
        self.counts = None
        self.points = []
        self.pattern_geometry = None
        self.cake_geometry = None
        self.is_calibrated = False    

        cstr = str(ALL_CALIBRANTS)
        calibrants = sorted(cstr[cstr.index(':')+2:].split(', '))
        self.parameters = GridParameters()
        self.parameters.add('calibrant', calibrants, 'Calibrant')
        self.parameters['calibrant'].value = 'CeO2'
        self.parameters.add('wavelength', 0.5, 'Wavelength (Ang)', False)
        self.parameters.add('distance', 100.0, 'Detector Distance (mm)', True)
        self.parameters.add('xc', 512, 'Beam Center - x', True)
        self.parameters.add('yc', 512, 'Beam Center - y', True)
        self.parameters.add('yaw', 0.0, 'Yaw (degrees)', True)
        self.parameters.add('pitch', 0.0, 'Pitch (degrees)', True)
        self.parameters.add('roll', 0.0, 'Roll (degrees)', True)
        self.parameters.add('search_size', 10, 'Search Size (pixels)')
        rings = ['Ring%s' % i for i in range(1,21)]
        self.rings_box = self.select_box(rings)
        self.set_layout(self.select_entry(self.choose_entry),
                        self.action_buttons(('Plot Calibration', self.plot_data)),
                        self.parameters.grid(header=False),
                        self.make_layout(
                            self.action_buttons(('Select Points', self.select)),
                            self.rings_box),
                        self.action_buttons(('Calibrate', self.calibrate),
                                            ('Plot Cake', self.plot_cake),
                                            ('Restore', self.restore_parameters),
                                            ('Save', self.save_parameters)), 
                        self.close_buttons(close=True))
        self.set_title('Calibrating Powder')

    def choose_entry(self):
        if 'calibration' not in self.entry['instrument']:
            raise NeXusError('Please load calibration data to this entry')
        self.update_parameters()
        self.plot_data()

    def update_parameters(self):
        self.parameters['wavelength'].value = self.entry['instrument/monochromator/wavelength']
        detector = self.entry['instrument/detector']
        self.parameters['distance'].value = detector['distance']
        self.parameters['yaw'].value = detector['yaw']
        self.parameters['pitch'].value = detector['pitch']
        self.parameters['roll'].value = detector['roll']
        if 'beam_center_x' in detector:
            self.parameters['xc'].value = detector['beam_center_x']
        if 'beam_center_y' in detector:
            self.parameters['yc'].value = detector['beam_center_y']
        self.data = self.entry['instrument/calibration']
        self.counts = self.data.nxsignal.nxvalue


    @property
    def search_size(self):
        return int(self.parameters['search_size'].value)

    @property
    def ring(self):
        return int(self.rings_box.currentText()[4:]) - 1

    @property
    def ring_color(self):
        colors = ['r', 'b', 'g', 'c', 'm'] * 4
        return colors[self.ring]

    def plot_data(self):
        if self.plotview is None:
            if 'Powder Calibration' in plotviews:
                self.plotview = plotviews['Powder Calibration']
            else:
                self.plotview = NXPlotView('Powder Calibration')
        self.plotview.plot(self.data, log=True)
        self.plotview.aspect='equal'
        self.plotview.ytab.flipped = True
        self.clear_peaks()

    def on_button_press(self, event):
        self.plotview.make_active()
        if event.inaxes:
            self.xp, self.yp = event.x, event.y
        else:
            self.xp, self.yp = 0, 0

    def on_button_release(self, event):
        if event.inaxes:
            if abs(event.x - self.xp) > 5 or abs(event.y - self.yp) > 5:
                return
            x, y = self.plotview.inverse_transform(event.xdata, event.ydata)
            for i, point in enumerate(self.points):
                circle = point[0]
                if circle.contains_point(self.plotview.ax.transData.transform((x,y))):
                    circle.remove()
                    for circle in point[2]:
                        circle.remove()
                    del self.points[i]
                    return
            idx, idy = self.find_peak(x, y)
            points = [(idy, idx)]
            circles = []
            massif = Massif(self.counts)
            extra_points = massif.find_peaks((idy, idx))
            for point in extra_points:
                points.append(point)
                circles.append(self.circle(point[1], point[0], alpha=0.3))
            self.points.append([self.circle(idx, idy), points, circles, self.ring])

    def circle(self, idx, idy, alpha=1.0):
        return self.plotview.circle(idx, idy, self.search_size,
                                    facecolor=self.ring_color, edgecolor='k',
                                    alpha=alpha)

    def select(self):
        self.plotview.cidpress = self.plotview.mpl_connect(
                                    'button_press_event', self.on_button_press)
        self.plotview.cidrelease = self.plotview.mpl_connect(
                                    'button_release_event', self.on_button_release)

    def find_peak(self, x, y):
        s = self.search_size
        left = int(np.round(x - s * 0.5))
        if left < 0:
            left = 0
        top = int(np.round(y - s * 0.5))
        if top < 0:
            top = 0
        region = self.counts[top:(top+s),left:(left+s)]
        idy, idx = np.where(region == region.max())
        idx = left + idx[0]
        idy = top + idy[0]
        return idx, idy

    def clear_peaks(self):
        self.points = []
        
    @property
    def calibrant(self):
        return ALL_CALIBRANTS[self.parameters['calibrant'].value]

    @property
    def point_array(self):
        points = []
        for point in self.points:
            for p in point[1]:
                points.append((p[0], p[1], point[3]))
        return np.array(points)

    def prepare_parameters(self):
        self.parameters.set_parameters()
        self.wavelength = self.parameters['wavelength'].value * 1e-10
        self.distance = self.parameters['distance'].value * 1e-3
        self.yaw = np.radians(self.parameters['yaw'].value)
        self.pitch = np.radians(self.parameters['pitch'].value)
        self.roll = np.radians(self.parameters['roll'].value)
        self.pixel_size = self.entry['instrument/detector/pixel_size'].nxvalue * 1e-3
        self.xc = self.parameters['xc'].value
        self.yc = self.parameters['yc'].value

    def calibrate(self):
        self.prepare_parameters()
        self.orig_pixel1 = self.pixel_size
        self.orig_pixel2 = self.pixel_size
        self.pattern_geometry = GeometryRefinement(self.point_array,
                                                   dist=self.distance,
                                                   wavelength=self.wavelength,
                                                   pixel1=self.pixel_size,
                                                   pixel2=self.pixel_size,
                                                   calibrant=self.calibrant)
        self.refine()
        self.create_cake_geometry()
        self.pattern_geometry.reset()

    def refine(self):
        self.pattern_geometry.data = self.point_array

        if self.parameters['wavelength'].vary:
            self.pattern_geometry.refine2()
            fix = []
        else:
            fix = ['wavelength']
        if not self.parameters['distance'].vary:
            fix.append('dist')
        self.pattern_geometry.refine2_wavelength(fix=fix)
        self.read_parameters()
        self.is_calibrated = True
        self.create_cake_geometry()
        self.pattern_geometry.reset()

    def create_cake_geometry(self):
        self.cake_geometry = AzimuthalIntegrator()
        pyFAI_parameter = self.pattern_geometry.getPyFAI()
        pyFAI_parameter['wavelength'] = self.pattern_geometry.wavelength
        self.cake_geometry.setPyFAI(dist=pyFAI_parameter['dist'],
                                    poni1=pyFAI_parameter['poni1'],
                                    poni2=pyFAI_parameter['poni2'],
                                    rot1=pyFAI_parameter['rot1'],
                                    rot2=pyFAI_parameter['rot2'],
                                    rot3=pyFAI_parameter['rot3'],
                                    pixel1=pyFAI_parameter['pixel1'],
                                    pixel2=pyFAI_parameter['pixel2'])
        self.cake_geometry.wavelength = pyFAI_parameter['wavelength']

    def plot_cake(self):
        if 'Cake Plot' in plotviews:
            plotview = plotviews['Cake Plot']
        else:
            plotview = NXPlotView('Cake Plot')    
        if not self.is_calibrated:
            raise NeXusError('No refinement performed')
        res = self.cake_geometry.integrate2d(self.counts, 
                                             1024, 1024,
                                             method='csr',
                                             unit='2th_deg',
                                             correctSolidAngle=True)
        self.cake_data = NXdata(res[0], (NXfield(res[2], name='azimumthal_angle'),
                                         NXfield(res[1], name='polar_angle')))
        self.cake_data['title'] = self.entry['instrument/calibration/title']
        plotview.plot(self.cake_data, log=True)
        wavelength = self.parameters['wavelength'].value
        polar_angles = [2 * np.degrees(np.arcsin(wavelength/(2*d)))
                        for d in self.calibrant.dSpacing]
        plotview.vlines([polar_angle for polar_angle in polar_angles 
                         if polar_angle < plotview.xaxis.max], 
                        linestyle=':', color='r')

    def read_parameters(self):
        pyFAI = self.pattern_geometry.getPyFAI()
        fit2d = self.pattern_geometry.getFit2D()
        self.parameters['wavelength'].value = self.pattern_geometry.wavelength * 1e10
        self.parameters['distance'].value = pyFAI['dist'] * 1e3
        self.parameters['yaw'].value = np.degrees(pyFAI['rot1'])
        self.parameters['pitch'].value = np.degrees(pyFAI['rot2'])
        self.parameters['roll'].value = np.degrees(pyFAI['rot3'])
        self.parameters['xc'].value = fit2d['centerX']
        self.parameters['yc'].value = fit2d['centerY']

    def restore_parameters(self):
        self.parameters.restore_parameters()

    def save_parameters(self):
        if not self.is_calibrated:
            raise NeXusError('No refinement performed')
        elif 'refinement' in self.entry['instrument/calibration']:
            if confirm_action('Overwrite previous refinement?'):
                del self.entry['instrument/calibration/refinement']
            else:
                return
        self.entry['instrument/calibration/calibrant'] = self.parameters['calibrant'].value
        process = NXprocess()
        process.program = 'pyFAI'
        process.version = pyFAI.version
        process.parameters = NXcollection()
        process.parameters['Detector'] = self.entry['instrument/detector/description']
        pyFAI_parameter = self.pattern_geometry.getPyFAI()
        process.parameters['PixelSize1'] =  pyFAI_parameter['pixel1']
        process.parameters['PixelSize2'] =  pyFAI_parameter['pixel2']
        process.parameters['Distance'] =  pyFAI_parameter['dist']
        process.parameters['Poni1'] =  pyFAI_parameter['poni1']
        process.parameters['Poni2'] =  pyFAI_parameter['poni2']
        process.parameters['Rot1'] =  pyFAI_parameter['rot1']
        process.parameters['Rot2'] =  pyFAI_parameter['rot2']
        process.parameters['Rot3'] =  pyFAI_parameter['rot3']
        process.parameters['Wavelength'] =  pyFAI_parameter['wavelength']
        self.entry['instrument/calibration/refinement'] = process
        self.entry['instrument/monochromator/wavelength'] = self.parameters['wavelength'].value
        self.entry['instrument/monochromator/energy'] = 12.398419739640717 / self.parameters['wavelength'].value 
        detector = self.entry['instrument/detector']
        detector['distance'] = self.parameters['distance'].value
        detector['yaw'] = self.parameters['yaw'].value
        detector['pitch'] = self.parameters['pitch'].value
        detector['roll'] = self.parameters['roll'].value
        detector['beam_center_x'] = self.parameters['xc'].value
        detector['beam_center_y'] = self.parameters['yc'].value

    def reject(self):
        super(CalibrateDialog, self).reject()
        if 'Powder Calibration' in plotviews:
            plotviews['Powder Calibration'].close_view()
        if 'Cake Plot' in plotviews:
            plotviews['Cake Plot'].close_view()