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: checkdatatypes.check_float_variable( 'Maximum peak fitting cost value', max_cost, (0., None)) # 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
def save_mantid_mask(mask_vec, h5_name, two_theta, note): """ 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: checkdatatypes.check_float_variable('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() return
def write_wavelength(self, wave_length): """ 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 """ checkdatatypes.check_float_variable('Wave length', wave_length, (0, 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]))
def set_single_wavelength(self, wavelength): """ If the instrument has only 1 wave length setup :param wavelength: wave length in unit A :return: """ checkdatatypes.check_float_variable('Monochromator wavelength (A)', wavelength, (1.E-5, None)) self._single_wave_length = wavelength
def set_2theta(self, two_theta): """ set 2theta value :param two_theta: :return: """ checkdatatypes.check_float_variable('Two theta', two_theta, (-180, 180)) self._2theta = two_theta return
def set_wavelength_calibration(self, wave_length_shift): """ set the wave length shift :param wave_length_shift: :return: """ checkdatatypes.check_float_variable( 'Wavelength shift from original value', wave_length_shift, (None, None)) if self._wave_length + wave_length_shift < 0.1: raise RuntimeError( 'Wavelength shift {} to {} results in an unphysical value'. format(self._wave_length, wave_length_shift))
def set_experimental_data(self, two_theta, l2, raw_count_vec): """ Set experimental data (for a sub-run) :param two_theta: detector position :param raw_count_vec: detector raw counts :return: """ checkdatatypes.check_float_variable('2-theta', two_theta, (-180, 180)) checkdatatypes.check_numpy_arrays('Detector (raw) counts', [raw_count_vec], None, False) if l2 is not None: checkdatatypes.check_float_variable('L2', l2, (1.E-2, None)) self._detector_2theta = two_theta self._detector_l2 = l2 self._detector_counts = raw_count_vec return
def rotate_project_q(self, theta, omega, chi, phi, eta): """ 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) """ checkdatatypes.check_float_variable('theta', theta, (None, None)) checkdatatypes.check_float_variable('Omega', omega, (None, None)) checkdatatypes.check_float_variable('chi', chi, (None, None)) checkdatatypes.check_float_variable('phi', phi, (None, None)) checkdatatypes.check_float_variable('eta', eta, (None, None)) 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
def set_2theta(self, two_theta, unit='degree'): """ Set 2 theta value :param two_theta: :param unit: degree or radius :return: """ checkdatatypes.check_string_variable('2theta unit', unit, ['degree', 'radius']) if unit == 'degree': two_theta_range = (-180., 180) else: two_theta_range = (-math.pi, math.pi) checkdatatypes.check_float_variable('2theta', two_theta, two_theta_range) self._two_theta = two_theta, unit return
def __init__(self, num_rows, num_columns, pixel_size_x, pixel_size_y, arm_length, calibrated): """ Initialization of instrument geometry setup for 1 angler camera :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 checkdatatypes.check_float_variable('Arm length', arm_length, (1E-5, None)) checkdatatypes.check_float_variable('Pixel size (x)', pixel_size_x, (1E-7, None)) checkdatatypes.check_float_variable('Pixel size (y)', pixel_size_y, (1E-7, None)) checkdatatypes.check_int_variable('Number of rows in detector', num_rows, (1, None)) checkdatatypes.check_int_variable('Number of columns in detector', num_columns, (1, None)) checkdatatypes.check_bool_variable( 'Flag indicating instrument setup been calibrated', calibrated) # geometry parameters for raw parameters self._arm_length = arm_length self._detector_rows = num_rows self._detector_columns = num_columns self._pixel_size_x = pixel_size_x self._pixel_size_y = pixel_size_y
def add_matched_grid(self, grid_pos_x, grid_pos_y, grid_pos_z, param_value_11, param_value_22, param_value_33): """ add an all-direction matched grid :param grid_pos_x: :param grid_pos_y: :param grid_pos_z: :param param_value_11: :param param_value_22: :param param_value_33: :return: """ checkdatatypes.check_float_variable('Parameter value for e11', param_value_11, (None, None)) checkdatatypes.check_float_variable('Parameter value for e22', param_value_22, (None, None)) if param_value_33 is not None: checkdatatypes.check_float_variable('Parameter value for e33', param_value_33, (None, None)) self.append_row([ grid_pos_x, grid_pos_y, grid_pos_z, param_value_11, param_value_22, param_value_33 ])
def rotation_z(self, value): checkdatatypes.check_float_variable('Rotation along Z direction', value, (-360, 360)) self._rotation_z = value
def center_shift_z(self, value): checkdatatypes.check_float_variable('Center shift along Z direction', value, (None, None)) self._center_shift_z = value
def build_instrument(self, two_theta, l2, instrument_calibration): """ build instrument considering calibration step 1: rotate instrument according to the calibration step 2: rotate instrument about 2theta :param two_theta :param instrument_calibration: AnglerCameraDetectorShift or None (no calibration) :return: """ # Check input checkdatatypes.check_float_variable('2theta', two_theta, (None, None)) # Check or set L2 if l2 is None: l2 = self._instrument_geom_params.arm_length else: checkdatatypes.check_float_variable('L2', l2, (1E-2, None)) # 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.AnglerCameraDetectorShift) # 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) # 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
def generate_2theta_histogram_vector(min_2theta, num_bins, max_2theta, pixel_2theta_array, mask_array): """Generate a 1-D array for histogram 2theta bins Parameters ---------- min_2theta : float or None minimum 2theta or None num_bins : int nubmer 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)] # END-IF 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) # END-IF step_2theta = (max_2theta - min_2theta) * 1. / num_bins # Check inputs checkdatatypes.check_float_variable('Minimum 2theta', min_2theta, (-180, 180)) checkdatatypes.check_float_variable('Maximum 2theta', max_2theta, (-180, 180)) checkdatatypes.check_float_variable('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