def calculate_pixel_angles_using_vectors(self, center, base, up): """Calculate the pixels using specified vectors: Parameters center: position of center in mm base: horizontal direction up: vertical direction """ #Normalize base /= vector_length(base) up /= vector_length(up) #Save the vectors self.base_point = column(center) self.horizontal = column(base) self.vertical = column(up) #Calculate the normal vector self.normal = np.cross(self.vertical.flatten(), self.horizontal.flatten()) self.normal /= vector_length(self.normal) self.normal = column(self.normal) assert np.allclose(vector_length(self.normal), 1.0), "Normal vector is normalized to length 1.0" #Now let's make the pixels x = np.linspace(-self.width / 2, self.width / 2, self.xpixels) y = np.linspace(-self.height / 2, self.height / 2, self.ypixels) #Starting x, y, z position (px, py) = np.meshgrid(x, y) #XY is the horizontal, vertical position px = px.flatten() py = py.flatten() #Multiply by the base vectors and add the center pixels = px * self.horizontal + py * self.vertical + self.base_point #Save em - for plotting, mostly. Indices go Z, Y, X self.pixels = np.reshape(pixels, (3, self.ypixels, self.xpixels)) #Save the corners self.corners = list() self.corners.append(self.pixels[:, 0, 0]) #One corner self.corners.append(self.pixels[:, 0, -1]) #The end in X self.corners.append(self.pixels[:, -1, -1]) #Max x and Y self.corners.append(self.pixels[:, -1, 0]) #The end in Y #Now, we calculate the azimuth and elevation angle of each pixel. x = pixels[0, :] y = pixels[1, :] z = pixels[2, :] self.azimuthal_angle = np.reshape(np.arctan2(x, z), (self.ypixels, self.xpixels)) self.elevation_angle = np.reshape(np.arctan(y / np.sqrt(x**2 + z**2)), (self.ypixels, self.xpixels))
def calculate_pixel_angles_using_vectors(self, center, base, up): """Calculate the pixels using specified vectors: Parameters center: position of center in mm base: horizontal direction up: vertical direction """ #Normalize base /= vector_length(base) up /= vector_length(up) #Save the vectors self.base_point = column(center) self.horizontal = column(base) self.vertical = column(up) #Calculate the normal vector self.normal = np.cross(self.vertical.flatten(), self.horizontal.flatten()) self.normal /= vector_length(self.normal) self.normal = column(self.normal) assert np.allclose(vector_length(self.normal), 1.0), "Normal vector is normalized to length 1.0" #Now let's make the pixels x = np.linspace(-self.width/2, self.width/2, self.xpixels) y = np.linspace(-self.height/2, self.height/2, self.ypixels) #Starting x, y, z position (px, py) = np.meshgrid(x,y) #XY is the horizontal, vertical position px = px.flatten() py = py.flatten() #Multiply by the base vectors and add the center pixels = px*self.horizontal + py*self.vertical + self.base_point #Save em - for plotting, mostly. Indices go Z, Y, X self.pixels = np.reshape(pixels, (3, self.ypixels, self.xpixels) ) #Save the corners self.corners = list() self.corners.append(self.pixels[:, 0, 0]) #One corner self.corners.append(self.pixels[:, 0, -1]) #The end in X self.corners.append(self.pixels[:, -1, -1]) #Max x and Y self.corners.append(self.pixels[:, -1, 0]) #The end in Y #Now, we calculate the azimuth and elevation angle of each pixel. x=pixels[0,:] y=pixels[1,:] z=pixels[2,:] self.azimuthal_angle = np.reshape( np.arctan2(x, z), (self.ypixels, self.xpixels) ) self.elevation_angle = np.reshape( np.arctan(y / np.sqrt(x**2 + z**2)), (self.ypixels, self.xpixels) )
def get_pixel_direction(self, horizontal, vertical): """Return a 3x1 column vector giving the direction of a pixel on the face of the detector. Parameters: horizontal, vertical: position in mm on the face of the detector. 0,0 = center of the detector Returns: 3x1 column vector of the beam direction, normalized. """ # Vertical position, relative to 0,0 y = vertical + self.origin[1] + self.height/2. # Position in the XZ plane angle = horizontal / self.radius + self.angle_start + self.width/2. x = np.cos(angle) * self.radius z = np.sin(angle) * self.radius pixel = column([x, y, z]) #Normalize pixel = pixel / vector_length(pixel) return pixel
def get_pixel_direction(self, horizontal, vertical): """Return a 3x1 column vector giving the direction of a pixel on the face of the detector. Parameters: horizontal, vertical: position in mm on the face of the detector. 0,0 = center of the detector Returns: 3x1 column vector of the beam direction, normalized. """ # Vertical position, relative to 0,0 y = vertical + self.origin[1] + self.height / 2. # Position in the XZ plane angle = horizontal / self.radius + self.angle_start + self.width / 2. x = np.cos(angle) * self.radius z = np.sin(angle) * self.radius pixel = column([x, y, z]) #Normalize pixel = pixel / vector_length(pixel) return pixel
def get_pixel_direction(self, horizontal, vertical): """Return a 3x1 column vector giving the direction of a pixel on the face of the detector. Parameters: horizontal, vertical: position in mm on the face of the detector. 0,0 = center of the detector. Returns: 3x1 column vector of the beam direction, normalized. """ #Use the vectors to build the direction pixel = horizontal * self.horizontal + vertical * self.vertical + self.base_point # #Make a column array of the pixel position, with the z given by the # # detector to sample distance # pixel = np.array([ [horizontal], [vertical], [self.distance] ]) # #Perform the appropriate rotation, calculated before # pixel = np.dot(self.pixel_rotation_matrix , pixel) #Normalize pixel = pixel / vector_length(pixel) return pixel
def calculate_pixel_angles(self): """Given the center angle and other geometry of the detector, calculate the azimuth and elevation angle of every pixel.""" x = np.linspace(-self.width/2, self.width/2, self.xpixels) y = np.linspace(-self.height/2, self.height/2, self.ypixels) #Starting x, y, z position (px, py) = np.meshgrid(x,y) #Start with a Z equal to the given distance pz = np.zeros( px.shape ) + self.distance #Reshape into a nx3 buncha columns, where the columns are the pixels XYZ positions num = self.xpixels * self.ypixels pixels = np.array( [ px.flatten(), py.flatten(), pz.flatten()] ) #Ok, first rotate the detector around its center by angle. #Since this is rotation around z axis, it is a chi angle rot = rotation_matrix(0, self.rotation, 0) #Now do the elevation rotation, by rotating around the x axis. # Elevation is positive above the horizontal plane - means the x_rotation angle has to be negative rot = np.dot(x_rotation_matrix(-self.elevation_center), rot) #Finally add an azimuthal rotation (around the y axis, or phi) rot = np.dot(rotation_matrix(self.azimuth_center, 0, 0), rot) #Save it for later self.pixel_rotation_matrix = rot #This applies to the pixels pixels = np.dot(rot, pixels) #Save em - for plotting, mostly. Indices go Z, Y, X self.pixels = np.reshape(pixels, (3, self.ypixels, self.xpixels) ) #Save the corners self.corners = list() self.corners.append(self.pixels[:, 0, 0]) #One corner self.corners.append(self.pixels[:, 0, -1]) #The end in X self.corners.append(self.pixels[:, -1, -1]) #Max x and Y self.corners.append(self.pixels[:, -1, 0]) #The end in Y #This is the base point - the center of the detector base_pixel = column([0, 0, self.distance]) #Do the same rotation as the rest of the pixels base_pixel = np.dot(rot, base_pixel) self.base_point = column(base_pixel) #Horizontal and vertical vector self.horizontal = column(self.corners[1] - self.corners[0]) self.vertical = column(self.corners[3] - self.corners[0]) #Normalize them self.vertical /= vector_length(self.vertical) self.horizontal /= vector_length(self.horizontal) #Normal vector: take a vector pointing in positive Z, and the do the same rotation as all the pixels. self.normal = column(np.cross(self.horizontal.flatten(), self.vertical.flatten() )) assert np.allclose(vector_length(self.normal), 1.0), "normal vector is normalized." #Another way to calculate the normal. Compare it other_normal = np.dot(rot, column([0,0,1])).flatten() assert np.allclose(self.normal.flatten(), other_normal), "Match between two methods of calculating normals. %s vs %s" % (self.normal.flatten(), other_normal) #Now, we calculate the azimuth and elevation angle of each pixel. x=pixels[0,:] y=pixels[1,:] z=pixels[2,:] self.azimuthal_angle = np.reshape( np.arctan2(x, z), (self.ypixels, self.xpixels) ) self.elevation_angle = np.reshape( np.arctan(y / np.sqrt(x**2 + z**2)), (self.ypixels, self.xpixels) )
def get_sample_rotation_matrix_to_get_beam(beam_wanted, hkl, ub_matrix, starting_rot_matrix=None): """Find the sample rotation matrix that will result in a scattered beam going in the desired direction. Parameters: beam_wanted: vector containing a NORMALIZED vector of the DESIRED scattered beam direction. hkl: vector containing the h,k,l reflection of interest. ub_matrix: for the current sample and mounting. B converts hkl to q-vector, and U orients. starting_rot_matrix: optional: rotation matrix that is close to the result we are looking for. e.g. we want the beam to move a few degrees away from this starting sample orientation. Returns: rot_matrix: the 3x3 rotation matrix for the sample orientation that satisfies the desired beam direction; or None if it cannot be done. wavelength: wavelength at which it would be measured. Algorithm --------- - First, we find the _direction_ of q, ignoring the wavelength kf - ki = delta_q kf = two_pi_over_wl * (beam_wanted) ki = two_pi_over_wl (in z only) # so q/two_pi_over_wl = (kf - ki)/two_pi_over_wl = beam_x in x; beam_y in y; beam_z-1 in z """ # a means two_pi_over_wl #First, we find the _direction_ of q, ignoring the wavelength q_over_a = beam_wanted.ravel() #Subtract 1 from Z to account for ki, incident beam q_over_a[2] -= 1 #Find the q vector from HKL before sample orientation q0 = np.dot(ub_matrix, hkl) if not starting_rot_matrix is None: #Also rotate using the given matrix, to find a starting point q0 = np.dot(starting_rot_matrix, q0) #Flatten into vector q0 = q0.flatten() #Since the rotation does not change length, norm(q0) = norm(q_over_a), # meaning that a = norm(q)/ norm(q_over_a) a = vector_length(q0) / vector_length(q_over_a) #And a's definition is such that: wavelength = 2*np.pi/a #So lets get q directly. q_rotated = q_over_a * a #Find the rotation matrix that satisfies q_over_a = R_over_a . q0 #The cross product of q0 X q_over_a gives a rotation axis to use rotation_axis = np.cross(q0, q_over_a) #Normalize rotation_axis = rotation_axis / vector_length(rotation_axis) #Now we find the rotation angle about that axis that puts q0 on q_over_a angle = np.arccos( np.dot(q0, q_over_a) / (vector_length(q0)*vector_length(q_over_a))) #From http://www.euclideanspace.com/maths/algebra/matrix/orthogonal/rotation/index.htm # we make a rotation matrix (x,y,z) = rotation_axis #This is a normalized rotation matrix R = np.array([[1 + (1-cos(angle))*(x*x-1), -z*sin(angle)+(1-cos(angle))*x*y, y*sin(angle)+(1-cos(angle))*x*z], [z*sin(angle)+(1-cos(angle))*x*y, 1 + (1-cos(angle))*(y*y-1), -x*sin(angle)+(1-cos(angle))*y*z], [-y*sin(angle)+(1-cos(angle))*x*z, x*sin(angle)+(1-cos(angle))*y*z, 1 + (1-cos(angle))*(z*z-1)]]) if not starting_rot_matrix is None: #The final rotation we want is sstarting_rot_matrix 1st; R second. # So this is the resulting matrix R = np.dot(R, starting_rot_matrix) # print "q0", q0.ravel() # print "two pi over wl (a)", a # print "q_over_a", q_over_a.ravel() # print "rotation_axis", rotation_axis # print "angle", angle # print "get_sample_rotation_matrix_to_get_beam: angle was", np.rad2deg(angle) return (R, wavelength)
def check_matrix_lengths(self, M, lat): """Check if the lengths of the columns of M match the values in tuple lat""" lengths = [(vector_length(M[:,x])) for x in xrange(3)] #print "lengths", lengths return np.allclose(lengths, lat)
def get_d_spacing(self): """Return the d-spacing corresponding to this hkl""" return 1 / (vector_length(self.q_vector) / (2*np.pi))
def get_q_norm(self): """Return the norm of the q-vector corresponding to this hkl""" return vector_length(self.q_vector)
def calculate_pixel_angles(self): """Given the center angle and other geometry of the detector, calculate the azimuth and elevation angle of every pixel.""" x = np.linspace(-self.width / 2, self.width / 2, self.xpixels) y = np.linspace(-self.height / 2, self.height / 2, self.ypixels) #Starting x, y, z position (px, py) = np.meshgrid(x, y) #Start with a Z equal to the given distance pz = np.zeros(px.shape) + self.distance #Reshape into a nx3 buncha columns, where the columns are the pixels XYZ positions num = self.xpixels * self.ypixels pixels = np.array([px.flatten(), py.flatten(), pz.flatten()]) #Ok, first rotate the detector around its center by angle. #Since this is rotation around z axis, it is a chi angle rot = rotation_matrix(0, self.rotation, 0) #Now do the elevation rotation, by rotating around the x axis. # Elevation is positive above the horizontal plane - means the x_rotation angle has to be negative rot = np.dot(x_rotation_matrix(-self.elevation_center), rot) #Finally add an azimuthal rotation (around the y axis, or phi) rot = np.dot(rotation_matrix(self.azimuth_center, 0, 0), rot) #Save it for later self.pixel_rotation_matrix = rot #This applies to the pixels pixels = np.dot(rot, pixels) #Save em - for plotting, mostly. Indices go Z, Y, X self.pixels = np.reshape(pixels, (3, self.ypixels, self.xpixels)) #Save the corners self.corners = list() self.corners.append(self.pixels[:, 0, 0]) #One corner self.corners.append(self.pixels[:, 0, -1]) #The end in X self.corners.append(self.pixels[:, -1, -1]) #Max x and Y self.corners.append(self.pixels[:, -1, 0]) #The end in Y #This is the base point - the center of the detector base_pixel = column([0, 0, self.distance]) #Do the same rotation as the rest of the pixels base_pixel = np.dot(rot, base_pixel) self.base_point = column(base_pixel) #Horizontal and vertical vector self.horizontal = column(self.corners[1] - self.corners[0]) self.vertical = column(self.corners[3] - self.corners[0]) #Normalize them self.vertical /= vector_length(self.vertical) self.horizontal /= vector_length(self.horizontal) #Normal vector: take a vector pointing in positive Z, and the do the same rotation as all the pixels. self.normal = column( np.cross(self.horizontal.flatten(), self.vertical.flatten())) assert np.allclose(vector_length(self.normal), 1.0), "normal vector is normalized." #Another way to calculate the normal. Compare it other_normal = np.dot(rot, column([0, 0, 1])).flatten() assert np.allclose( self.normal.flatten(), other_normal ), "Match between two methods of calculating normals. %s vs %s" % ( self.normal.flatten(), other_normal) #Now, we calculate the azimuth and elevation angle of each pixel. x = pixels[0, :] y = pixels[1, :] z = pixels[2, :] self.azimuthal_angle = np.reshape(np.arctan2(x, z), (self.ypixels, self.xpixels)) self.elevation_angle = np.reshape(np.arctan(y / np.sqrt(x**2 + z**2)), (self.ypixels, self.xpixels))