def __init__(self, wavelength=None, energy=None, energy_eV=None, frequency=None): if ( (wavelength is not None and energy is not None) or (wavelength is not None and energy_eV is not None) or (energy is not None and energy_eV is not None) ): log_and_raise_error( logger, "Invalid arguments during initialisation of Photon instance. More than one of the arguments is not None.", ) return if wavelength is not None: self.set_wavelength(wavelength) elif energy is not None: self.set_energy(energy) elif energy_eV is not None: self.set_energy_eV(energy_eV) elif frequency is not None: self.set_frequency(frequency) else: log_and_raise_error( logger, "Photon could not be initialised." "It has to be initialized with exactly one of the following keyword arguments" "\t- wavelegth:\t photon wavelength in meters" "\t- energy:\t photon energy in Joules" "\t- energy_eV:\t photon energy in electron volts" "\t- frequency:\t photon frequency in Hertz", )
def set_model(self, model): """ Set the model Args: :model (str): Model for the spatial illumination profile *Choose one of the following options:* - ``None`` - flat illumination profile of infinite extent and the intensity at every point corresponds to the intensity of a top hat profile (see below) - ``\'top_hat\'`` - Circular top hat profile - ``\'pseudo_lorentzian\'`` - 2D radially symmetrical profile composed of two Gaussian profiles obtained by a fit to a Lorentzian profile (used instead of a real Lorentzian because this function can be normalised) - ``\'gaussian\'`` - 2D radially symmetrical Gaussian profile """ if model is None or model in [ "top_hat", "pseudo_lorentzian", "gaussian" ]: self._model = model else: log_and_raise_error( logger, "Pulse profile model %s is not implemented. Change your configuration and try again." % model) sys.exit(0)
def __init__(self, values=None, formalism=None): """ """ if values is None and formalism is None: single = True elif formalism.startswith("euler_angles_") and len(formalism) == len("euler_angles_xyz"): values = numpy.asarray(values) single = values.ndim == 1 elif formalism == "rotation_matrix": values = numpy.asarray(values) single = values.ndim == 2 elif formalism == "quaternion": values = numpy.asarray(values) single = values.ndim == 1 elif formalism in ["random","random_x","random_y","random_z"]: single = True else: log_and_raise_error(logger, "formalism=%s is not implemented" % formalism) return self._formalism = formalism self._i = 0 self._values = values # Initialise rotations if single: if values is None: # No rotation (rotation matrix = identity matrix) self._rotations = [Rotation()] else: self._rotations = [Rotation(values, formalism=formalism)] else: self._rotations = [] for i in range(len(values)): self._rotations.append(Rotation(values[i], formalism=formalism))
def rotate_vector(self, vector, order="xyz"): r""" Return the rotated copy of a given vector Args: :vector (array): 3D vector Kwargs: :order (str): Order of geometrical axes in array representation of the given vector (default ``'xyz'``) """ # Check input if vector.size != 3 or vector.ndim != 1: log_and_raise_error( logger, "Cannot rotate vector. Vector has incompatible size (%i) or number of dimensions (%i)." % (vector.size, vector.ndim)) return # Rotate if order == "xyz": return self.rotation_matrix.dot(vector) elif order == "zyx": return self.rotation_matrix.dot(vector[::-1]) else: log_and_raise_error(logger, "Corrdinates in order=%s is invalid." % order)
def set_mode(self, mode): r""" Set the mode of variation Args: :mode (str): Mode of variation *Choose one of the following options* ======================= ==================================================================== ===================================================== ``mode`` Variation model ``spread`` parameter ======================= ==================================================================== ===================================================== ``None`` No variation - ``'uniform'`` Uniform random distribution Width ``'normal'`` Normal random distribution Standard deviation ``'poisson'`` Poissonian random distribution - ``'normal_poisson'`` Normal on top of Poissonian random dist. Std. dev. of normal distribution ``'range'`` Equispaced grid around mean center position Width ======================= ==================================================================== ===================================================== """ if mode not in [ None, "normal", "poisson", "normal_poisson", "uniform", "range" ]: log_and_raise_error( logger, "Variation object cannot be configured with illegal mode %s" % mode) return self._mode = mode
def generate_qmap_3d(qn, qmax, extrinsic_rotation=None, order='xyz'): q = numpy.linspace(-qmax, qmax, qn) Qz, Qy, Qx = numpy.meshgrid(q, q, q, indexing='ij') qmap = numpy.zeros(shape=(qn, qn, qn, 3), dtype='float') if order == 'xyz': qmap[:, :, :, 0] = Qx[:, :, :] qmap[:, :, :, 1] = Qy[:, :, :] qmap[:, :, :, 2] = Qz[:, :, :] elif order == 'zyx': qmap[:, :, :, 2] = Qx[:, :, :] qmap[:, :, :, 1] = Qy[:, :, :] qmap[:, :, :, 0] = Qz[:, :, :] else: log_and_raise_error( logger, "order=\'%s\' is not a recognised argument for this function." % str(order)) return if extrinsic_rotation is not None: log_debug(logger, "Applying qmap rotation.") intrinsic_rotation = copy.deepcopy(extrinsic_rotation) intrinsic_rotation.invert() qmap = intrinsic_rotation.rotate_vectors( qmap.ravel(), order=order).reshape(qmap.shape) return qmap
def __init__(self, wavelength=None, energy=None, energy_eV=None, frequency=None): if (wavelength is not None and energy is not None) or ( wavelength is not None and energy_eV is not None) or (energy is not None and energy_eV is not None): log_and_raise_error( logger, "Invalid arguments during initialisation of Photon instance. More than one of the arguments is not None." ) return if wavelength is not None: self.set_wavelength(wavelength) elif energy is not None: self.set_energy(energy) elif energy_eV is not None: self.set_energy_eV(energy_eV) elif frequency is not None: self.set_frequency(frequency) else: log_and_raise_error( logger, "Photon could not be initialised." "It has to be initialized with exactly one of the following keyword arguments" "\t- wavelegth:\t photon wavelength in meters" "\t- energy:\t photon energy in Joules" "\t- energy_eV:\t photon energy in electron volts" "\t- frequency:\t photon frequency in Hertz")
def set_number_of_dimensions(self, number_of_dimensions): if number_of_dimensions < 1 or number_of_dimensions > 3: log_and_raise_error( logger, "Number of dimensions for variation objects can be only either 1, 2 or 3." ) return self._number_of_dimensions = number_of_dimensions
def generate_qmap(X, Y, pixel_size, detector_distance, wavelength, extrinsic_rotation=None, order="xyz"): r""" Generate scattering vector map from experimental parameters Args: :X (array): :math:`x`-coordinates of pixels in unit meter :Y (array): :math:`y`-coordinates of pixels in unit meter :pixel_size (float): Pixel size (i.e. edge length) in unit meter :detector_distance (float): Distance from interaction point to detector plane :wavelength (float): Photon wavelength in unit meter Kwargs: :extrinsic_rotation (:class:`condor.utils.rotation.Rotation`): Extrinsic rotation of the sample. If ``None`` no rotation is applied (default ``None``) :order (str): Order of scattering vector coordinates in the output array. Choose either ``'xyz'`` or ``'zyx'`` (default ``'xyz'``) """ log_debug(logger, "Allocating qmap (%i,%i,%i)" % (X.shape[0], X.shape[1], 3)) R_Ewald = 2 * numpy.pi / wavelength p_x = X * pixel_size p_y = Y * pixel_size p_z = numpy.ones_like(X) * detector_distance l = numpy.sqrt(p_x**2 + p_y**2 + p_z**2) r_x = p_x / l r_y = p_y / l r_z = p_z / l - 1. qmap = numpy.zeros(shape=(X.shape[0], X.shape[1], 3)) if order == "xyz": qmap[:, :, 0] = r_x * R_Ewald qmap[:, :, 1] = r_y * R_Ewald qmap[:, :, 2] = r_z * R_Ewald elif order == "zyx": qmap[:, :, 0] = r_z * R_Ewald qmap[:, :, 1] = r_y * R_Ewald qmap[:, :, 2] = r_x * R_Ewald else: log_and_raise_error(logger, "Indexing with order=%s is invalid." % order) if extrinsic_rotation is not None: log_debug(logger, "Applying qmap rotation.") intrinsic_rotation = copy.deepcopy(extrinsic_rotation) intrinsic_rotation.invert() qmap = intrinsic_rotation.rotate_vectors( qmap.ravel(), order=order).reshape(qmap.shape) return qmap
def rotate_vectors(self, vectors, order="xyz"): r""" Return the rotated copy of a given array of vectors Args: :vectors (array): Array of 3D vectors with shape (:math:`N`, 3) with :math:`N` denoting the number of 3D vectors Kwargs: :order (str): Order of geometrical axes in array representation of the given vector (default ``'xyz'``) """ # Check input if vectors.ndim != 2 and vectors.ndim != 1: log_and_raise_error(logger, "Cannot rotate vectors. Input must have either one or two dimensions") return n_ax = list(vectors.shape)[-1] N = vectors.size Nv = N/3 if vectors.ndim == 2 and n_ax != 3: log_and_raise_error(logger, "Cannot rotate vectors. The given array has length %i in last dimension but should be 3." % (n_ax)) return if vectors.ndim == 1 and N % 3 != 0: log_and_raise_error(logger, "Cannot rotate vectors. The given array has size %i which is not a multiple of 3." % (n_ax)) return # Rotate if order == "xyz": return numpy.array([numpy.dot(self.rotation_matrix,vectors.ravel()[i*3:(i+1)*3]) for i in numpy.arange(Nv)]) elif order == "zyx": return numpy.array([numpy.dot(self.rotation_matrix,(vectors.ravel()[i*3:(i+1)*3])[::-1])[::-1] for i in numpy.arange(Nv)]) else: log_and_raise_error(logger, "Corrdinates in order=%s is invalid." % order)
def set_with_euler_angles(self, euler_angles, rotation_axes="zxz"): r""" Set rotation with an array of three euler angles Args: :euler_angles: Array of the three euler angles representing consecutive rotations Kwargs: :rotation_axes(str): Rotation axes of the three consecutive rotations (default = \'zxz\') """ # Check input if euler_angles.size != 3: log_and_raise_error(logger, "Size of rotation variable does not match expected shape") return # Set rotation matrix self.rotation_matrix = euler_to_rotmx(euler_angles, rotation_axes)
def get_values_window(image, x_i, y_i, window_size, circle_window, i, masked_image=None, thumbnails=None): if not window_size % 2: log_and_raise_error( logger, "window_size (%i) must be an odd number. Please change your configuration and try again." % window_size) return None if (x_i-window_size/2 < 0) or \ x_i-window_size/2+window_size>=image.shape[1] or \ (y_i-window_size/2 < 0) or \ y_i-window_size/2+window_size>=image.shape[0]: log_info(logger, "Particle too close to edge - cannot be analysed.") return None values = image[y_i - (window_size - 1) / 2:y_i + (window_size - 1) / 2 + 1, x_i - (window_size - 1) / 2:x_i + (window_size - 1) / 2 + 1] if circle_window: mask = make_circle_mask(window_size) values = values[mask] if masked_image is not None: (masked_image[y_i - (window_size - 1) / 2:y_i + (window_size - 1) / 2 + 1, x_i - (window_size - 1) / 2:x_i + (window_size - 1) / 2 + 1])[mask] = values[:] if thumbnails is not None: (thumbnails[i, :, :])[mask] = values[:] else: if masked_image is not None: masked_image[y_i - (window_size - 1) / 2:y_i + (window_size - 1) / 2 + 1, x_i - (window_size - 1) / 2:x_i + (window_size - 1) / 2 + 1] = values[:, :] if thumbnail is not None: thumbnails[i, :, :] = values[:, :] return values
def generate_qmap(X,Y,pixel_size,detector_distance,wavelength,extrinsic_rotation=None, order="xyz"): r""" Generate scattering vector map from experimental parameters Args: :X (array): :math:`x`-coordinates of pixels in unit meter :Y (array): :math:`y`-coordinates of pixels in unit meter :pixel_size (float): Pixel size (i.e. edge length) in unit meter :detector_distance (float): Distance from interaction point to detector plane :wavelength (float): Photon wavelength in unit meter Kwargs: :extrinsic_rotation (:class:`condor.utils.rotation.Rotation`): Extrinsic rotation of the sample. If ``None`` no rotation is applied (default ``None``) :order (str): Order of scattering vector coordinates in the output array. Choose either ``'xyz'`` or ``'zyx'`` (default ``'xyz'``) """ log_debug(logger, "Allocating qmap (%i,%i,%i)" % (X.shape[0],X.shape[1],3)) R_Ewald = 2*numpy.pi/wavelength p_x = X*pixel_size p_y = Y*pixel_size p_z = numpy.ones_like(X)*detector_distance l = numpy.sqrt(p_x**2+p_y**2+p_z**2) r_x = p_x/l r_y = p_y/l r_z = p_z/l - 1. qmap = numpy.zeros(shape=(X.shape[0],X.shape[1],3)) if order == "xyz": qmap[:,:,0] = r_x * R_Ewald qmap[:,:,1] = r_y * R_Ewald qmap[:,:,2] = r_z * R_Ewald elif order == "zyx": qmap[:,:,0] = r_z * R_Ewald qmap[:,:,1] = r_y * R_Ewald qmap[:,:,2] = r_x * R_Ewald else: log_and_raise_error(logger, "Indexing with order=%s is invalid." % order) if extrinsic_rotation is not None: log_debug(logger, "Applying qmap rotation.") intrinsic_rotation = copy.deepcopy(extrinsic_rotation) intrinsic_rotation.invert() qmap = intrinsic_rotation.rotate_vectors(qmap.ravel(), order=order).reshape(qmap.shape) return qmap
def set_with_quaternion(self, quaternion): r""" Set rotation with a quaternion Args: :quaternion: Numpy array representing the quaternion :math:`w+ix+jy+kz`: [:math:`w`, :math:`x`, :math:`y`, :math:`z`] = [:math:`\cos(\theta/2)`, :math:`u_x \sin(\theta/2)`, :math:`u_y \sin(\theta/2)`, :math:`u_z \sin(\theta/2)`] with :math:`\theta` being the rotation angle and :math:`\vec{u}=(u_x,u_y,u_z)` the unit vector that defines the axis of rotation. """ # Check input if quaternion.size != 4: log_and_raise_error(logger, "Size of rotation variable does not match expected shape") return # Set rotation matrix self.rotation_matrix = rotmx_from_quat(quaternion)
def set_with_rotation_matrix(self, rotation_matrix): r""" Set rotation with a rotation matrix Args: :rotation_matrix: 3x3 array representing the rotation matrix """ # Check input if rotation_matrix.size != 9 or rotation_matrix.ndim != 2: log_and_raise_error(logger, "Size of rotation variable does not match expected shape") return I = rotation_matrix.dot(rotation_matrix.T) tol = 0.0001 for i in range(3): if abs(I[i,i]-1.) > tol: log_and_raise_error(logger, "Given matrix cannot be a rotation matrix because it is not unitary") # Set rotation matrix self.rotation_matrix = rotation_matrix.copy()
def set_with_euler_angles(self, euler_angles, rotation_axes="zxz"): r""" Set rotation with an array of three euler angles Args: :euler_angles: Array of the three euler angles representing consecutive rotations Kwargs: :rotation_axes(str): Rotation axes of the three consecutive rotations (default = \'zxz\') """ # Check input if euler_angles.size != 3: log_and_raise_error( logger, "Size of rotation variable does not match expected shape") return # Set rotation matrix self.rotation_matrix = euler_to_rotmx(euler_angles, rotation_axes)
def __init__(self, values=None, formalism=None): self.rotation_matrix = None if values is None and formalism is None: # No rotation (rotation matrix = identity matrix) self.rotation_matrix = numpy.ones(shape=(3,3)) elif formalism.startswith("euler_angles_") and len(formalism) == len("euler_angles_xyz"): self.set_with_euler_angles(values, rotation_axes=formalism[-3:]) elif formalism == "rotation_matrix": self.set_with_rotation_matrix(values) elif formalism == "quaternion": self.set_with_quaternion(values) elif formalism in ["random","random_x","random_y","random_z"]: if values is not None: log_warning(logger, "Specified formalism=%s but values is not None." % formalism) self._set_as_random_formalism(formalism) else: log_and_raise_error(logger, "formalism=%s is not implemented" % formalism) return
def set_with_quaternion(self, quaternion): r""" Set rotation with a quaternion Args: :quaternion: Numpy array representing the quaternion :math:`w+ix+jy+kz`: [:math:`w`, :math:`x`, :math:`y`, :math:`z`] = [:math:`\cos(\theta/2)`, :math:`u_x \sin(\theta/2)`, :math:`u_y \sin(\theta/2)`, :math:`u_z \sin(\theta/2)`] with :math:`\theta` being the rotation angle and :math:`\vec{u}=(u_x,u_y,u_z)` the unit vector that defines the axis of rotation. """ # Check input if quaternion.size != 4: log_and_raise_error( logger, "Size of rotation variable does not match expected shape") return # Set rotation matrix self.rotation_matrix = rotmx_from_quat(quaternion)
def rotate_vector(self, vector, order="xyz"): r""" Return the rotated copy of a given vector Args: :vector (array): 3D vector Kwargs: :order (str): Order of geometrical axes in array representation of the given vector (default ``'xyz'``) """ # Check input if vector.size != 3 or vector.ndim != 1: log_and_raise_error(logger, "Cannot rotate vector. Vector has incompatible size (%i) or number of dimensions (%i)." % (vector.size,vector.ndim)) return # Rotate if order == "xyz": return self.rotation_matrix.dot(vector) elif order == "zyx": return self.rotation_matrix.dot(vector[::-1]) else: log_and_raise_error(logger, "Corrdinates in order=%s is invalid." % order)
def euler_to_rotmx(euler_angles, rotation_axes="zxz"): r""" Obtain rotation matrix from three euler angles and the rotation axes Args: :euler_angles (array): Length-3 array of euler angles Kwargs: :rotation_axes (str): Rotation axes of the three consecutive Euler rotations (default ``'zxz'``) """ R = numpy.identity(3) for ang,ax in zip(euler_angles, rotation_axes): if ax == "x": R = R.dot(R_x(ang)) elif ax == "y": R = R.dot(R_y(ang)) elif ax == "z": R = R.dot(R_z(ang)) else: log_and_raise_error(logger, "%s is not a valid axis" % ax) return return R
def euler_to_rotmx(euler_angles, rotation_axes="zxz"): r""" Obtain rotation matrix from three euler angles and the rotation axes Args: :euler_angles (array): Length-3 array of euler angles Kwargs: :rotation_axes (str): Rotation axes of the three consecutive Euler rotations (default ``'zxz'``) """ R = numpy.identity(3) for ang, ax in zip(euler_angles, rotation_axes): if ax == "x": R = R.dot(R_x(ang)) elif ax == "y": R = R.dot(R_y(ang)) elif ax == "z": R = R.dot(R_z(ang)) else: log_and_raise_error(logger, "%s is not a valid axis" % ax) return return R
def rotate_vectors(self, vectors, order="xyz"): r""" Return the rotated copy of a given array of vectors Args: :vectors (array): Array of 3D vectors with shape (:math:`N`, 3) with :math:`N` denoting the number of 3D vectors Kwargs: :order (str): Order of geometrical axes in array representation of the given vector (default ``'xyz'``) """ # Check input if vectors.ndim != 2 and vectors.ndim != 1: log_and_raise_error( logger, "Cannot rotate vectors. Input must have either one or two dimensions" ) return n_ax = list(vectors.shape)[-1] N = vectors.size Nv = N / 3 if vectors.ndim == 2 and n_ax != 3: log_and_raise_error( logger, "Cannot rotate vectors. The given array has length %i in last dimension but should be 3." % (n_ax)) return if vectors.ndim == 1 and N % 3 != 0: log_and_raise_error( logger, "Cannot rotate vectors. The given array has size %i which is not a multiple of 3." % (n_ax)) return # Rotate if order == "xyz": return numpy.array([ numpy.dot(self.rotation_matrix, vectors.ravel()[i * 3:(i + 1) * 3]) for i in numpy.arange(Nv) ]) elif order == "zyx": return numpy.array([ numpy.dot(self.rotation_matrix, (vectors.ravel()[i * 3:(i + 1) * 3])[::-1])[::-1] for i in numpy.arange(Nv) ]) else: log_and_raise_error(logger, "Corrdinates in order=%s is invalid." % order)
def __init__(self, values=None, formalism=None): self.rotation_matrix = None if values is None and formalism is None: # No rotation (rotation matrix = identity matrix) self.rotation_matrix = numpy.ones(shape=(3, 3)) elif formalism.startswith("euler_angles_") and len(formalism) == len( "euler_angles_xyz"): self.set_with_euler_angles(values, rotation_axes=formalism[-3:]) elif formalism == "rotation_matrix": self.set_with_rotation_matrix(values) elif formalism == "quaternion": self.set_with_quaternion(values) elif formalism in ["random", "random_x", "random_y", "random_z"]: if values is not None: log_warning( logger, "Specified formalism=%s but values is not None." % formalism) self._set_as_random_formalism(formalism) else: log_and_raise_error(logger, "formalism=%s is not implemented" % formalism) return
def set_with_rotation_matrix(self, rotation_matrix): r""" Set rotation with a rotation matrix Args: :rotation_matrix: 3x3 array representing the rotation matrix """ # Check input if rotation_matrix.size != 9 or rotation_matrix.ndim != 2: log_and_raise_error( logger, "Size of rotation variable does not match expected shape") return I = rotation_matrix.dot(rotation_matrix.T) tol = 0.0001 for i in range(3): if abs(I[i, i] - 1.) > tol: log_and_raise_error( logger, "Given matrix cannot be a rotation matrix because it is not unitary" ) # Set rotation matrix self.rotation_matrix = rotation_matrix.copy()
def set_model(self,model): """ Set the model Args: :model (str): Model for the spatial illumination profile *Choose one of the following options:* - ``None`` - flat illuminatrion profile of infinite extent and the intensity at every point corresponds to the intensity of a top hat profile (see below) - ``\'top_hat\'`` - Circular top hat profile - ``\'pseudo_lorentzian\'`` - 2D radially symmetrical profile composed of two Gaussian profiles obtained by a fit to a Lorentzian profile (used instead of a real Lorentzian because this function can be normalised) - ``\'gaussian\'`` - 2D radially symmetrical Gaussian profile """ if model is None or model in ["top_hat","pseudo_lorentzian","gaussian"]: self._model = model else: log_and_raise_error(logger, "Pulse profile model %s is not implemented. Change your configuration and try again." % model) sys.exit(0)
def set_mode(self,mode): r""" Set the mode of variation Args: :mode (str): Mode of variation *Choose one of the following options* ======================= ==================================================================== ===================================================== ``mode`` Variation model ``spread`` parameter ======================= ==================================================================== ===================================================== ``None`` No variation - ``'uniform'`` Uniform random distribution Width ``'normal'`` Normal random distribution Standard deviation ``'poisson'`` Poissonian random distribution - ``'normal_poisson'`` Normal on top of Poissonian random dist. Std. dev. of normal distribution ``'range'`` Equispaced grid around mean center position Width ======================= ==================================================================== ===================================================== """ if mode not in [None,"normal","poisson","normal_poisson","uniform","range"]: log_and_raise_error(logger, "Variation object cannot be configured with illegal mode %s" % mode) return self._mode = mode
def __init__(self, values=None, formalism=None): """ """ if values is None and formalism is None: single = True elif formalism.startswith("euler_angles_") and len(formalism) == len( "euler_angles_xyz"): values = numpy.asarray(values) single = values.ndim == 1 elif formalism == "rotation_matrix": values = numpy.asarray(values) single = values.ndim == 2 elif formalism == "quaternion": values = numpy.asarray(values) single = values.ndim == 1 elif formalism in ["random", "random_x", "random_y", "random_z"]: single = True else: log_and_raise_error(logger, "formalism=%s is not implemented" % formalism) return self._formalism = formalism self._i = 0 self._values = values # Initialise rotations if single: if values is None: # No rotation (rotation matrix = identity matrix) self._rotations = [Rotation()] else: self._rotations = [Rotation(values, formalism=formalism)] else: self._rotations = [] for i in range(len(values)): self._rotations.append(Rotation(values[i], formalism=formalism))
def validate(self): mode = self.get_mode() spread = self.get_spread() number_of_dimensions = self.get_number_of_dimensions() if mode in ["normal","normal_poisson","uniform","range"] and spread is None: log_and_raise_error(logger, "Variation object configuration is invalid because mode \'%s\' requires \'spread\' to be not None." % mode) if mode in ["range"]: if self.n is None: log_and_raise_error(logger, "Variation object cannot be configured because mode \'%s\' requires that \'n\' is not None." % mode) if spread is not None: if number_of_dimensions != len(self._spread): log_and_raise_error(logger, "Specified number of dimensions (%i) and length of spread array (%i) do not match." % (number_of_dimensions, len(spread)))
def validate(self): mode = self.get_mode() spread = self.get_spread() number_of_dimensions = self.get_number_of_dimensions() if mode in ["normal", "normal_poisson", "uniform", "range" ] and spread is None: log_and_raise_error( logger, "Variation object configuration is invalid because mode \'%s\' requires \'spread\' to be not None." % mode) if mode in ["range"]: if self.n is None: log_and_raise_error( logger, "Variation object cannot be configured because mode \'%s\' requires that \'n\' is not None." % mode) if spread is not None: if number_of_dimensions != len(self._spread): log_and_raise_error( logger, "Specified number of dimensions (%i) and length of spread array (%i) do not match." % (number_of_dimensions, len(spread)))
def set_number_of_dimensions(self, number_of_dimensions): if number_of_dimensions < 1 or number_of_dimensions > 3: log_and_raise_error(logger, "Number of dimensions for variation objects can be only either 1, 2 or 3.") return self._number_of_dimensions = number_of_dimensions
def read_map(filename): log.log_info(logger, "Automatic scaling of EM maps may not be reliable. Please make sure to check your map after using this functionality.") # CCP4 map file format # http://www.ccp4.ac.uk/html/maplib.html with open(filename, "rb") as f: # 1024 bytes header header_buf = f.read(1024) temp_int32 = numpy.frombuffer(header_buf, dtype="int32") temp_float32 = numpy.frombuffer(header_buf, dtype="float32") #1 NC # of Columns (fastest changing in map) #2 NR # of Rows #3 NS # of Sections (slowest changing in map) NC = temp_int32[0] NR = temp_int32[1] NS = temp_int32[2] if NC != NR or NR != NS: log.log_and_raise_error(logger, "Cannot read a map with unequal dimensions") N = NC #4 MODE Data type # 0 = envelope stored as signed bytes (from # -128 lowest to 127 highest) # 1 = Image stored as Integer*2 # 2 = Image stored as Reals # 3 = Transform stored as Complex Integer*2 # 4 = Transform stored as Complex Reals # 5 == 0 # # Note: Mode 2 is the normal mode used in # the CCP4 programs. Other modes than 2 and 0 # may NOT WORK MODE = temp_int32[3] dtype = ["int8", "int16", "float32", None, "complex64", "int8"][MODE] if MODE == 3: log.log_and_raise_error(logger, "Map file data type \"MODE=%i\" is not implemented yet." % MODE) if MODE not in [0,1,2,5]: log.log_warning(logger, "Map file data type \"MODE=%i\" not supported yet and may not work reliably." % MODE) #11 X length Cell Dimensions (Angstroms) #12 Y length " #13 Z length " dX = temp_float32[10]/float(N)*1E-10 dY = temp_float32[11]/float(N)*1E-10 dZ = temp_float32[12]/float(N)*1E-10 if dX != dY or dY != dZ: log.log_and_raise_error(logger, "Cannot read a map with unequal voxel dimensions") #17 MAPC Which axis corresponds to Cols. (1,2,3 for X,Y,Z) #18 MAPR Which axis corresponds to Rows (1,2,3 for X,Y,Z) #19 MAPS Which axis corresponds to Sects. (1,2,3 for X,Y,Z) MAPC = temp_int32[16] MAPR = temp_int32[17] MAPS = temp_int32[18] #24 NSYMBT Number of bytes used for storing symmetry operators NSYMBT = temp_int32[23] if NSYMBT > 0: log.log_and_raise_error(logger, "Omitting symmetry operations in map file.") f.read(NSYMBT) # The remaining bytes are data raw_data = f.read() raw_data = numpy.frombuffer(raw_data, dtype=dtype) # Now we need to project onto the right Z-Y-X array grid S,R,C = numpy.meshgrid(numpy.arange(NS), numpy.arange(NR), numpy.arange(NC), indexing='ij') S = S.flatten() R = R.flatten() C = C.flatten() if MAPC == 1: X = C Xlen = NC elif MAPC == 2: Y = C Ylen = NC elif MAPC == 3: Z = C Zlen = NC if MAPR == 1: X = R Xlen = NR elif MAPR == 2: Y = R Ylen = NR elif MAPR == 3: Z = R Zlen = NR if MAPS == 1: X = S Xlen = NS elif MAPS == 2: Y = S Ylen = NS elif MAPS == 3: Z = S Zlen = NS i = Z*(Ylen*Xlen) + Y*(Xlen) + X i.sort() data = numpy.zeros(Zlen*Ylen*Xlen, dtype=dtype) data[:] = raw_data[i] data = data.reshape((Zlen,Ylen,Xlen)) return data, dX
def _conf_to_spsim_opts(D_source, D_particle, D_detector, ndim=2, qn=None, qmax=None): if ndim == 2: if qn is not None or qmax is not None: log_warning( logger, "As ndim=2 the passed values for qn and qmax take no effect.") if ndim == 3: if qn is None and qmax is None: log_and_raise_error( logger, "As ndim=3 both qn and qmax must be not None.") return import spsim # Create temporary file for pdb file tmpf_pdb = tempfile.NamedTemporaryFile(mode='w+b', bufsize=-1, suffix='.conf', prefix='tmp_spsim', dir=None, delete=False) tmpf_pdb_name = tmpf_pdb.name tmpf_pdb.close() # Write pdb file mol = spsim.get_molecule_from_atoms(D_particle["atomic_numbers"], D_particle["atomic_positions"]) spsim.write_pdb_from_mol(tmpf_pdb_name, mol) spsim.free_mol(mol) # Start with default spsim configuration opts = spsim.set_defaults() # Create temporary file for spsim configuration tmpf_conf = tempfile.NamedTemporaryFile(mode='w+b', bufsize=-1, suffix='.conf', prefix='tmp_spsim', dir=None, delete=False) # Write string sequence from configuration dicts s = [] s += "# THIS FILE WAS CREATED AUTOMATICALLY BY CONDOR\n" s += "# Temporary configuration file for spsim\n" s += "verbosity_level = 0;\n" s += "number_of_dimensions = %i;\n" % ndim s += "number_of_patterns = 1;\n" s += "origin_to_com = 1;\n" s += "input_type = \"pdb\";\n" #s += "pdb_filename = \"%s\";\n" % D_particle["pdb_filename"] s += "pdb_filename = \"%s\";\n" % tmpf_pdb_name if ndim == 2: D = D_detector["distance"] Lx = D_detector["pixel_size"] * D_detector["nx"] Ly = D_detector["pixel_size"] * D_detector["ny"] else: k0 = 2. * numpy.pi / D_source["wavelength"] D = qn / 2. * D_detector["pixel_size"] * k0 / qmax Lx = Ly = Lz = D_detector["pixel_size"] * qn s += "detector_distance = %.12e;\n" % D s += "detector_width = %.12e;\n" % Lx s += "detector_height = %.12e;\n" % Ly if ndim == 3: s += "detector_depth = %.12e;\n" % Lz s += "detector_pixel_width = %.12e;\n" % D_detector["pixel_size"] s += "detector_pixel_height = %.12e;\n" % D_detector["pixel_size"] if ndim == 3: s += "detector_pixel_depth = %.12e;\n" % D_detector["pixel_size"] if ndim == 2: s += "detector_center_x = %.12e;\n" % (D_detector["pixel_size"] * (D_detector["cx"] - (D_detector["nx"] - 1) / 2.)) s += "detector_center_y = %.12e;\n" % (D_detector["pixel_size"] * (D_detector["cy"] - (D_detector["ny"] - 1) / 2.)) else: s += "detector_center_x = 0;\n" s += "detector_center_y = 0;\n" s += "detector_center_z = 0;\n" s += "detector_binning = 1;\n" s += "experiment_wavelength = %.12e;\n" % D_source["wavelength"] s += "experiment_beam_intensity = %.12e;\n" % D_particle["intensity"] s += "experiment_polarization = \"ignore\";\n" # polarization correction will be done in Condor if needed (see experiment.py) #s += "use_cuda = 0;\n" intrinsic_rotation = condor.utils.rotation.Rotation( values=D_particle["extrinsic_quaternion"], formalism="quaternion") intrinsic_rotation.invert() e0, e1, e2 = intrinsic_rotation.get_as_euler_angles("zxz") if not numpy.isfinite(e0): print "ERROR: phi is not finite" if not numpy.isfinite(e1): print "ERROR: theta is not finite" if not numpy.isfinite(e2): print "ERROR: psi is not finite" s += "phi = %.12e;\n" % e0 s += "theta = %.12e;\n" % e1 s += "psi = %.12e;\n" % e2 s += "random_orientation = 0;\n" # Write string sequence to file tmpf_conf.writelines(s) # Close temporary file tmpf_conf_name = tmpf_conf.name tmpf_conf.close() # Read configuration into options struct spsim.read_options_file(tmpf_conf_name, opts) # This deletes the temporary files os.unlink(tmpf_pdb_name) os.unlink(tmpf_conf_name) return opts
def analyse_particles(image, image_raw, saturation_mask, i_labels, labels, x, y, merged, n_particles_max, full_output, **conf_analysis): if full_output: masked_image = np.zeros_like(image_raw) if conf_analysis["integration_mode"] == "windows": thumbnails = np.zeros(shape=(n_particles_max, conf_analysis["window_size"], conf_analysis["window_size"]), dtype=image.dtype) else: thumbnails = np.zeros(shape=(n_particles_max, THUMBNAILS_WINDOW_SIZE_DEFAULT, THUMBNAILS_WINDOW_SIZE_DEFAULT), dtype=image.dtype) else: masked_image = None thumbnails = None mask = None N = len(i_labels) psuccess = np.zeros(N, dtype='bool') psum = np.zeros(N) - 1 pmin = np.zeros(N) - 1 pmax = np.zeros(N) - 1 pmean = np.zeros(N) - 1 pmedian = np.zeros(N) - 1 psat = np.zeros(N) - 1 psize = np.zeros(N) - 1 pecc = np.zeros(N) - 1 pcir = np.zeros(N) - 1 for i, i_label, x_i, y_i, m in zip(range(N), i_labels, x, y, merged): if x_i < 0 or y_i < 0: continue # Analyse pixels = i_label == labels psat[i] = (pixels * saturation_mask).any() psize[i] = pixels.sum() pecc[i] = measure_eccentricity(intensity=image * pixels, mask=pixels) pcir[i] = measure_circumference(pixels) if conf_analysis["integration_mode"] == "windows": values = get_values_window( image_raw, int(round(x_i)), int(round(y_i)), window_size=conf_analysis["window_size"], circle_window=conf_analysis["circle_window"], i=i, masked_image=masked_image, thumbnails=thumbnails) elif conf_analysis["integration_mode"] == "labels": values = get_values_label(image_raw, labels, i_label, masked_image=masked_image, thumbnails=thumbnails) else: log_and_raise_error( logger, "%s is not a valid integration_mode!" % conf_analysis["integration_mode"]) if values is not None and len(values) > 0: psuccess[i] = True psum[i] = values.sum() pmin[i] = values.min() pmax[i] = values.max() pmean[i] = values.mean() pmedian[i] = np.median(values) success = True return success, psuccess, psum, pmean, pmedian, pmin, pmax, psize, psat, pecc, pcir, masked_image, thumbnails
def find_particles(image_scored, image_thresholded, min_dist, n_particles_max, peak_centering="center_of_mass"): n_lit = image_thresholded.sum() log_debug(logger, "%i pixels above threshold" % n_lit) success = False return_default = success, [], None, None, None, None, None, None, None, None if n_lit == 0: return return_default else: log_debug(logger, "Label image") labels, n_labels = scipy.ndimage.measurements.label(image_thresholded) i_labels = range(1, n_labels + 1) if n_labels > n_particles_max: log_info( logger, "%i labels - (frame overexposed? too many particles?), skipping analysis" % n_labels) return return_default V = [image_scored[i_label == labels].max() for i_label in i_labels] nx = image_thresholded.shape[1] ny = image_thresholded.shape[0] x, y = np.meshgrid(np.arange(nx), np.arange(ny)) x = x[image_thresholded] y = y[image_thresholded] l = labels[image_thresholded] v = image_scored[image_thresholded] if peak_centering == "center_of_mass": log_debug(logger, "Determine centers of mass") com = scipy.ndimage.measurements.center_of_mass( image_thresholded, labels, i_labels) X = [com_x for com_y, com_x in com] Y = [com_y for com_y, com_x in com] elif peak_centering == "center_to_max": log_debug(logger, "Determine maximum positions") X = [] Y = [] for i_label in i_labels: i_max = (v * (l == i_label)).argmax() X.append(x[i_max]) Y.append(y[i_max]) else: log_and_raise_error( logger, "%s is not a valid argument for peak_centering!" % peak_centering) dislocation = [] for i_label, xc, yc in zip(i_labels, X, Y): i_max = (v * (l == i_label)).argmax() dislocation.append(np.sqrt((x[i_max] - xc)**2 + (y[i_max] - yc)**2)) merged = list(np.zeros(len(i_labels), dtype=np.bool)) # Merge too close peaks log_debug(logger, "Merge too close points") i_labels, labels, X, Y, V, merged, dists_closest_neighbor = merge_close_points( i_labels, labels, X, Y, V, merged, min_dist) log_debug(logger, "Clean up labels") i_labels, labels = clean_up_labels(i_labels, labels) n_labels = len(i_labels) log_debug(logger, "Measure size of each peak") areas = measure_areas(i_labels, labels) success = True areas = np.asarray(areas) X = np.asarray(X) Y = np.asarray(Y) dislocation = np.asarray(dislocation) V = np.asarray(V) merged = np.asarray(merged) dists_closest_neighbor = np.asarray(dists_closest_neighbor) return success, i_labels, labels, areas, X, Y, V, merged, dists_closest_neighbor, dislocation