示例#1
0
def save_mantid_mask(mask_vec,
                     h5_name: str,
                     two_theta: float,
                     note: Optional[str] = None) -> None:
    """
    Save a mask vector to
    :param mask_vec:
    :param h5_name:
    :param two_theta:
    :param note:
    :return:
    """
    checkdatatypes.check_numpy_arrays('Mask vector', [mask_vec],
                                      dimension=1,
                                      check_same_shape=False)
    checkdatatypes.check_file_name(h5_name, False, True, False,
                                   'PyRS masking file to export to')
    if two_theta is not None:
        two_theta = to_float('2-theta', two_theta, -360., 360)
    if note is not None:
        checkdatatypes.check_string_variable('Mask note', note, None)

    # create file
    mask_file = h5py.File(h5_name, 'w')
    # add data set
    mask_data_set = mask_file.create_dataset('mask', data=mask_vec)
    # add attributes
    if two_theta:
        mask_data_set.attrs['2theta'] = two_theta  # '{}'.format(two_theta)
    if note:
        mask_data_set.attrs['note'] = note
    # close file
    mask_file.close()
示例#2
0
    def write_wavelength(self, wave_length: float):
        """ Set the calibrated wave length
        Location:
          .../instrument/monochromator setting/ ... .../
        Note:
        - same wave length to all sub runs
        - only calibrated wave length in project file
        - raw wave length comes from a table with setting
        :param wave_length: wave length in A
        :return: None
        """
        wave_length = to_float('Wave length',
                               wave_length,
                               min_value=0,
                               max_value=1000)

        # Create 'monochromator setting' node if it does not exist
        if HidraConstants.MONO not in list(
                self._project_h5[HidraConstants.INSTRUMENT].keys()):
            self._project_h5[HidraConstants.INSTRUMENT].create_group(
                HidraConstants.MONO)

        # Get node and write value
        wl_entry = self._project_h5[HidraConstants.INSTRUMENT][
            HidraConstants.MONO]
        # delete the dataset if it does exist to replace
        if HidraConstants.WAVELENGTH in list(wl_entry.keys()):
            del wl_entry[HidraConstants.WAVELENGTH]
        wl_entry.create_dataset(HidraConstants.WAVELENGTH,
                                data=numpy.array([wave_length]))
示例#3
0
    def get_pole_figure_vectors(self, det_id, max_cost, min_cost=1.):
        """ return Pole figure in a numpy 2D array
        :param det_id:
        :param max_cost:
        :param min_cost:
        :return: 2-tuple: (1) an integer list (2) numpy array with shape (n, 3).  n is the number of data points
        """
        # check input
        if max_cost is None:
            max_cost = 1.E20
        else:
            max_cost = to_float('Maximum peak fitting cost value',
                                max_cost,
                                min_value=0.)

        # get raw parameters' fitted value
        log_index_vec, pole_figure_vec = self._pole_figure_dict[det_id]

        # get costs and filter out isnan()
        cost_vec = self.get_peak_fit_parameter_vec('cost', det_id)

        taken_index_list = list()
        for idx in range(len(cost_vec)):
            if min_cost < cost_vec[idx] < max_cost:
                taken_index_list.append(idx)
        # END-IF

        selected_log_index_vec = np.take(log_index_vec,
                                         taken_index_list,
                                         axis=0)
        selected_pole_figure_vec = np.take(pole_figure_vec,
                                           taken_index_list,
                                           axis=0)

        return selected_log_index_vec, selected_pole_figure_vec
示例#4
0
    def set_experimental_data(self, two_theta: float, l2: Optional[float],
                              raw_count_vec):
        """ Set experimental data (for a sub-run)
        :param two_theta: detector position
        :param l2: detector distance from center of rotation
        :param raw_count_vec: detector raw counts
        :return:
        """
        self._detector_2theta = to_float('2-theta', two_theta, -180, 180)

        if l2 is not None:
            l2 = to_float('L2', l2, 1.E-2)
        self._detector_l2 = l2

        checkdatatypes.check_numpy_arrays('Detector (raw) counts',
                                          [raw_count_vec], None, False)
        self._detector_counts = raw_count_vec
示例#5
0
    def rotate_project_q(self, theta: float, omega: float, chi: float,
                         phi: float, eta: float) -> Tuple[float, float]:
        """
        Projection of angular dependent data onto pole sphere. Analytical solution taken from
        Chapter 8.3 in Bob He Two-Dimensional X-ray Diffraction

        _______________________
        :param two_theta:
        :param omega:
        :param chi:
        :param phi:
        :return: 2-tuple as the projection (alpha, beta)
        """
        theta = to_float('theta', theta)
        omega = to_float('Omega', omega)
        chi = to_float('chi', chi)
        phi = to_float('phi', phi)
        eta = to_float('eta', eta)

        sp = np.sin(np.deg2rad(phi))
        sw = np.sin(np.deg2rad(omega))
        sc = np.sin(np.deg2rad(chi))
        sg = np.sin(np.deg2rad(eta))
        st = np.sin(np.deg2rad(theta))

        cp = np.cos(np.deg2rad(phi))
        cw = np.cos(np.deg2rad(omega))
        cc = np.cos(np.deg2rad(chi))
        cg = np.cos(np.deg2rad(eta))
        ct = np.cos(np.deg2rad(theta))

        h1 = st * (sp * sc * sw + cp * cw) + ct * cg * sp * cc - ct * sg * (
            sp * sc * cw - cp * sw)
        h2 = -st * (cp * sc * sw - sp * cw) - ct * cg * cp * cc + ct * sg * (
            cp * sc * cw + sp * sw)
        # h3 = st*cc*sw - ct*sg*cc*cw - ct*cg*sc
        h_length = np.sqrt(np.square(h1) + np.square(h2))

        alpha = np.arccos(h_length)
        beta = np.rad2deg(np.arccos(h1 / h_length))
        if h2 < 0:
            beta *= -1.

        return alpha, beta
示例#6
0
 def __init__(self, num_rows: int, num_columns: int,
              pixel_size_x: float, pixel_size_y: float,
              arm_length: float, calibrated: bool) -> None:
     """
     Initialization of instrument geometry setup for 1 denex detector
     :param num_rows: number of rows of pixels in detector (number of pixels per column)
     :param num_columns: number of columns of pixels in detector (number of pixels per row)
     :param pixel_size_x: pixel size at X direction (along a row)
     :param pixel_size_y: pixel size at Y direction (along a column)
     :param arm_length: arm length
     :param calibrated: flag whether these values are calibrated
     """
     # check inputs
     self._arm_length = to_float('Arm length', arm_length, min_value=1E-5)
     self._pixel_size_x = to_float('Pixel size (x)', pixel_size_x, min_value=1E-7)
     self._pixel_size_y = to_float('Pixel size (y)', pixel_size_y, min_value=1E-7)
     self._detector_rows = to_int('Number of rows in detector', num_rows, min_value=1)
     self._detector_columns = to_int('Number of columns in detector', num_columns, min_value=1)
     # TODO this isn't used - BUG?
     checkdatatypes.check_bool_variable('Flag indicating instrument setup been calibrated', calibrated)
示例#7
0
 def two_theta_0(self, value: float) -> None:
     self._two_theta_0 = to_float('offset in two_theta arm for ideal 0', value, -360, 360)
示例#8
0
 def rotation_z(self, value: float) -> None:
     self._rotation_z = to_float('Rotation along Z direction', value, -360, 360)
示例#9
0
 def center_shift_z(self, value: float) -> None:
     self._center_shift_z = to_float('Center shift along Z direction', value)
示例#10
0
    def build_instrument(self,
                         two_theta: float,
                         l2: Optional[float] = None,
                         instrument_calibration=None):
        """
        build instrument considering calibration
        step 1: rotate instrument according to the calibration
        step 2: rotate instrument about 2theta
        :param two_theta
        :param l2
        :param instrument_calibration: DENEXDetectorShift or None (no calibration)
        :return:
        """
        # Check input
        two_theta = to_float('2theta', two_theta)
        # Check or set L2
        if l2 is None:
            l2 = self._instrument_geom_params.arm_length
        else:
            l2 = to_float('L2', l2, 1E-2)

        # print('[DB...L101] Build instrument: 2theta = {}, arm = {} (diff to default = {})'
        #       ''.format(two_theta, l2, l2 - self._instrument_geom_params.arm_length))

        # make a copy from raw (constant position)
        self._pixel_matrix = self._raw_pixel_matrix.copy()

        # Check and set instrument calibration
        if instrument_calibration is not None:
            # check type
            checkdatatypes.check_type('Instrument calibration',
                                      instrument_calibration,
                                      instrument_geometry.DENEXDetectorShift)

            # shift center
            self._pixel_matrix[:, :,
                               0] += instrument_calibration.center_shift_x
            self._pixel_matrix[:, :,
                               1] += instrument_calibration.center_shift_y

            # rotation around instrument center
            # get rotation matrix at origin (for flip, spin and vertical): all data from calibration value
            rot_x_flip = instrument_calibration.rotation_x * np.pi / 180.
            rot_y_flip = instrument_calibration.rotation_y * np.pi / 180.
            rot_z_spin = instrument_calibration.rotation_z * np.pi / 180.
            calib_matrix = self.generate_rotation_matrix(
                rot_x_flip, rot_y_flip, rot_z_spin)
            # print ('[DB...BAT] Calibration rotation matrix:\n{}'.format(calib_matrix))
            # and rotate at origin
            self._pixel_matrix = self._rotate_detector(self._pixel_matrix,
                                                       calib_matrix)

            # shift two_theta by offset
            two_theta += instrument_calibration.two_theta_0
        # END-IF-ELSE

        # push to +Z at length of detector arm
        arm_l2 = l2
        if instrument_calibration is not None:
            # Apply the shift on Z (arm length)
            arm_l2 += instrument_calibration.center_shift_z
        # END-IF
        self._pixel_matrix[:, :, 2] += arm_l2

        # rotate detector (2theta) if it is not zero
        self.rotate_detector_2theta(two_theta)

        return self._pixel_matrix
示例#11
0
    def generate_2theta_histogram_vector(min_2theta: Optional[float],
                                         num_bins: int,
                                         max_2theta: Optional[float],
                                         pixel_2theta_array,
                                         mask_array,
                                         step_2theta=None):
        """Generate a 1-D array for histogram 2theta bins

        Parameters
        ----------
        min_2theta : float or None
            minimum 2theta or None
        num_bins : int
            number of bins
        max_2theta : float  or None
             maximum 2theta and must be integer
        pixel_2theta_array : numpy.ndarray
            2theta of each detector pixel
        mask_array : numpy.ndarray or None
            array of mask

        Returns
        -------
        numpy.ndarray
            2theta values serving as bin boundaries, such its size is 1 larger than num_bins

        """

        # If default value is required: set the default
        if min_2theta is None or max_2theta is None:
            # check inputs
            if mask_array is None:
                checkdatatypes.check_numpy_arrays('Pixel 2theta angles',
                                                  [pixel_2theta_array], 1,
                                                  False)
            else:
                checkdatatypes.check_numpy_arrays(
                    'Pixel 2theta position and mask array',
                    [pixel_2theta_array, mask_array], 1, True)
                # mask
                pixel_2theta_array = pixel_2theta_array[np.where(
                    mask_array == 1)]

            if min_2theta is None:
                # lower boundary of 2theta for bins is the minimum 2theta angle of all the pixels
                min_2theta = np.min(pixel_2theta_array)
            if max_2theta is None:
                # upper boundary of 2theta for bins is the maximum 2theta angle of all the pixels
                max_2theta = np.max(pixel_2theta_array)

        if step_2theta is None:
            step_2theta = (max_2theta - min_2theta) * 1. / num_bins
        else:
            num_bins = np.ceil((max_2theta - min_2theta) / step_2theta) + 1

        # Check inputs
        min_2theta = to_float('Minimum 2theta', min_2theta, 20, 140)
        max_2theta = to_float('Maximum 2theta', max_2theta, 21, 180)
        step_2theta = to_float('2theta bin size', step_2theta, 0, 180)
        if min_2theta >= max_2theta:
            raise RuntimeError(
                '2theta range ({}, {}) is invalid for generating histogram'
                ''.format(min_2theta, max_2theta))

        # Create 2theta: these are bin edges from (min - 1/2) to (max + 1/2) with num_bins bins
        # and (num_bins + 1) data points
        vec_2theta = np.arange(num_bins + 1).astype(float) * step_2theta + (
            min_2theta - step_2theta)
        if vec_2theta.shape[0] != num_bins + 1:
            raise RuntimeError(
                'Expected = {} vs {}\n2theta min max  = {}, {}\n2thetas: {}'
                ''.format(num_bins, vec_2theta.shape, min_2theta, max_2theta,
                          vec_2theta))

        # Sanity check
        assert vec_2theta.shape[0] == num_bins + 1, '2theta bins (boundary)\'size ({}) shall be exactly ' \
                                                    '1 larger than specified num_bins ({})' \
                                                    ''.format(vec_2theta.shape, num_bins)

        return vec_2theta
示例#12
0
def test_convert_to_float():
    GOOD = 'good'
    BAD = 'bad'

    # simple checks
    assert to_float(GOOD, 42.) == 42.
    assert to_float(GOOD, 42) == 42.
    assert to_float(GOOD, '42') == 42.
    assert to_float(GOOD, 42., 41, 43) == 42.
    assert to_float(BAD, 42., 42, 43, min_inclusive=True, max_inclusive=False) == 42.
    assert to_float(BAD, 42., 41, 42, min_inclusive=False, max_inclusive=True) == 42.

    # check invalid range
    with pytest.raises(ValueError) as err:
        to_float(BAD, 42., 43, 41)
        assert BAD in str(err.value)

    # check value outside of range
    with pytest.raises(ValueError) as err:
        assert not to_float(BAD, 42., 42, 43, min_inclusive=False, max_inclusive=True)
        assert BAD in str(err.value)
    with pytest.raises(ValueError) as err:
        assert not to_float(BAD, 42., 41, 42, min_inclusive=True, max_inclusive=False)
        assert BAD in str(err.value)

    # check with non-convertable values
    for value in [None, 'strings cannot be converted', (1, 2)]:
        with pytest.raises(TypeError) as err:
            assert not to_float(BAD, value)
            assert BAD in str(err.value)