def cell_to_basis(cell): if cell is None: raise Exception("cell_to_basis: bad cell specification.") try: return cell.basis except AttributeError: pass if isinstance(cell, dict): if 'niggli_matrix' in cell: niggli_matrix = FracVector.use(cell['niggli_matrix']) if 'orientation' in cell: orientation = cell['orientation'] else: orientation = 1 basis = FracVector.use( niggli_to_basis(niggli_matrix, orientation=orientation)) elif 'a' in cell: lengths = FracVector.create((cell['a'], cell['b'], cell['c'])) cosangles = FracVector.create_cos( (cell['a'], cell['b'], cell['c']), degrees=True) basis = lengths_and_cosangles_to_conventional_basis( lengths, cosangles) #niggli_matrix = lengths_cosangles_to_niggli(lengths,cosangles) #basis = FracVector.use(niggli_to_basis(niggli_matrix, orientation=1)) else: basis = FracVector.use(cell) return basis
def niggli_to_metric(niggli): niggli = FracVector.use(niggli) m = niggli.noms # Since the niggli matrix contains 2*the product of the off diagonal elements, we increase the denominator by cell.denom*2 return FracVector( ((2 * m[0][0], m[1][2], m[1][1]), (m[1][2], 2 * m[0][1], m[1][0]), (m[1][1], m[1][0], 2 * m[0][2])), niggli.denom * 2).simplify()
def niggli_scale_to_vol(niggli_matrix, scale): niggli_matrix = FracVector.use(niggli_matrix) metric = niggli_to_metric(niggli_matrix) volsqr = metric.det() if volsqr == 0: raise Exception("niggli_vol_to_scale: singular cell matrix.") det = vectormath.sqrt(float(volsqr)) if abs(det) < 1e-12: raise Exception("niggli_scale_to_vol: singular cell matrix.") return (((scale)**3) * det)
def __init__(self, basis, lattice_system, orientation=1): """ Private constructor, as per httk coding guidelines. Use Cell.create instead. """ self.basis = basis self.orientation = orientation self.lattice_system = lattice_system self.niggli_matrix, self.orientation = basis_to_niggli_and_orientation(basis) self.det = basis.det() self.inv = basis.inv() self._volume = abs(self.det) self.metric = niggli_to_metric(self.niggli_matrix) self.lengths, self.angles = niggli_to_lengths_and_angles(self.niggli_matrix) self.lengths = [FracVector.use(x).simplify() for x in self.lengths] self.angles = [FracVector.use(x).simplify() for x in self.angles] _dummy, self.cosangles, self.sinangles = niggli_to_lengths_and_trigangles(self.niggli_matrix) self.a, self.b, self.c = self.lengths self.alpha, self.beta, self.gamma = self.angles self.cosalpha, self.cosbeta, self.cosgamma = self.cosangles self.sinalpha, self.sinbeta, self.singamma = self.sinangles
def niggli_to_lengths_and_trigangles(niggli_matrix): niggli_matrix = FracVector.use(niggli_matrix) s11, s22, s33 = niggli_matrix[0][0], niggli_matrix[0][1], niggli_matrix[0][ 2] s23, s13, s12 = niggli_matrix[1][0] / 2, niggli_matrix[1][ 1] / 2, niggli_matrix[1][2] / 2 a, b, c = vectormath.sqrt(s11), vectormath.sqrt(s22), vectormath.sqrt(s33) cosalpha, cosbeta, cosgamma = s23 / (b * c), s13 / (c * a), s12 / (a * b) sinalpha, sinbeta, singamma = vectormath.sqrt( 1 - cosalpha), vectormath.sqrt(1 - cosbeta), vectormath.sqrt(1 - cosgamma) return [a, b, c], [cosalpha, cosbeta, cosgamma], [sinalpha, sinbeta, singamma]
def scaling_to_volume(basis, scaling): volume = None if isinstance(scaling, dict): if 'volume' in scaling: volume = scaling['volume'] elif 'scale' in scaling: scale = scaling['scale'] elif scaling > 0: scale = scaling else: volume = -scaling if volume is None: scale = FracVector.use(scale) volume = scale_to_vol(basis, scale) return volume
def niggli_to_lengths_and_angles(niggli_matrix): niggli_matrix = FracVector.use(niggli_matrix) pi = niggli_matrix.pi() s11, s22, s33 = niggli_matrix[0][0], niggli_matrix[0][1], niggli_matrix[0][ 2] s23, s13, s12 = niggli_matrix[1][0] / 2, niggli_matrix[1][ 1] / 2, niggli_matrix[1][2] / 2 a, b, c = vectormath.sqrt(s11), vectormath.sqrt(s22), vectormath.sqrt(s33) alpha, beta, gamma = vectormath.acos( s23 / (b * c)) * 180 / pi, vectormath.acos( s13 / (c * a)) * 180 / pi, vectormath.acos(s12 / (a * b)) * 180 / pi return [a, b, c], [alpha, beta, gamma]
def basis_to_niggli_and_orientation(basis): basis = FracVector.use(basis) A = basis.noms det = basis.det() if det == 0: raise Exception("basis_to_niggli: singular cell matrix.") if det > 0: orientation = 1 else: orientation = -1 s11 = A[0][0] * A[0][0] + A[0][1] * A[0][1] + A[0][2] * A[0][2] s22 = A[1][0] * A[1][0] + A[1][1] * A[1][1] + A[1][2] * A[1][2] s33 = A[2][0] * A[2][0] + A[2][1] * A[2][1] + A[2][2] * A[2][2] s23 = A[1][0] * A[2][0] + A[1][1] * A[2][1] + A[1][2] * A[2][2] s13 = A[0][0] * A[2][0] + A[0][1] * A[2][1] + A[0][2] * A[2][2] s12 = A[0][0] * A[1][0] + A[0][1] * A[1][1] + A[0][2] * A[1][2] new = FracVector.create(((s11, s22, s33), (2 * s23, 2 * s13, 2 * s12)), denom=basis.denom**2).simplify() return new, orientation
def create(cls, cell=None, basis=None, metric=None, niggli_matrix=None, a=None, b=None, c=None, alpha=None, beta=None, gamma=None, lengths=None, angles=None, cosangles=None, scale=None, scaling=None, volume=None, periodicity=None, nonperiodic_vecs=None, orientation=1, hall=None, lattice_system=None, eps=0): """ Create a new cell object, cell: any one of the following: - a 3x3 array with (in rows) the three basis vectors of the cell (a non-periodic system should conventionally use an identity matrix) - a dict with a single key 'niggli_matrix' with a 3x2 array with the Niggli Matrix representation of the cell - a dict with 6 keys, 'a', 'b', 'c', 'alpha', 'beta', 'gamma' giving the cell parameters as floats scaling: free form input parsed for a scale. positive value = multiply basis vectors by this value negative value = rescale basis vectors so that cell volume becomes abs(value). scale: set to non-None to multiply all cell vectors with this factor volume: set to non-None if the basis vectors only give directions, and the volume of the cell should be this value (overrides scale) periodicity: free form input parsed for periodicity sequence: True/False for each basis vector being periodic integer: number of non-periodic basis vectors hall: giving the hall symbol makes it possible to determine the lattice system without numerical inaccuracy lattice_system: any one of: 'cubic', 'hexagonal', 'tetragonal', 'orthorhombic', 'trigonal', 'triclinic', 'monoclinic', 'unknown' """ #print("Create cell:",cell,basis,angles, lengths,cosangles,a,b,c,alpha,beta,gamma) if cell is not None: return Cell.use(cell) if basis is not None: basis = FracVector.use(basis) if angles is None and not (alpha is None or beta is None or gamma is None): angles = [alpha, beta, gamma] if lengths is None and not (a is None or b is None or c is None): lengths = [a, b, c] if cosangles is None and angles is not None: cosangles = angles_to_cosangles(angles) if basis is None and lengths is not None and cosangles is not None: if hall is not None: lattice_system = lattice_system_from_hall(hall) else: lattice_system = lattice_system_from_lengths_and_cosangles(lengths, cosangles) basis = lengths_and_cosangles_to_conventional_basis(lengths, cosangles, lattice_system, orientation=orientation, eps=eps) if niggli_matrix is not None: niggli_matrix = FracVector.use(niggli_matrix) if niggli_matrix is None and basis is not None: niggli_matrix, orientation = basis_to_niggli_and_orientation(basis) #if niggli_matrix is None and lengths is not None and cosangles is not None: # niggli_matrix = lengths_and_cosangles_to_niggli(lengths, cosangles) # niggli_matrix = FracVector.use(niggli_matrix) #if niggli_matrix is None and lengths is not None and angles is not None: # niggli_matrix = lengths_and_angles_to_niggli(lengths, angles) # niggli_matrix = FracVector.use(niggli_matrix) #if niggli_matrix is None and not (a is None or b is None or c is None or alpha is None or beta is None or gamma is None): # niggli_matrix = lengths_and_angles_to_niggli([a, b, c], [alpha, beta, gamma]) # niggli_matrix = FracVector.use(niggli_matrix) if basis is None: raise Exception("cell.create: Not enough information to specify a cell given.") if scaling is None and scale is not None: scaling = scale if scaling is not None and volume is not None: raise Exception("Cell.create: cannot specify both scaling and volume!") if volume is not None: scaling = vol_to_scale(basis, volume) if scaling is not None: scaling = FracVector.use(scaling) basis = (basis*scaling).simplify() # Determination of the lattice system can only be made approximately, so it is recommended # that it is given to the constructor if possible if (lattice_system is None or lattice_system == 'unknown') and hall is not None: lattice_system = lattice_system_from_hall(hall) if lattice_system is None or lattice_system == 'unknown': lattice_system = lattice_system_from_niggli(niggli_matrix) if basis is None: basis = FracVector.use(niggli_to_conventional_basis(niggli_matrix, lattice_system, orientation=orientation)) return cls(basis, lattice_system, orientation)
def coords_cartesian_to_reduced(self, coords): coords = FracVector.use(coords) return coords*self.inv
def coords_reduced_to_cartesian(self, coords): coords = FracVector.use(coords) return coords*self.basis