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 lengths_and_cosangles_to_conventional_basis(lengths, cosangles, lattice_system=None, orientation=1, eps=0): """ Returns the conventional cell basis given a list of lengths and cosine of angles Note: if your basis vector order does not follow the conventions for hexagonal and monoclinic cells, you get the triclinic conventional cell. Conventions: in hexagonal cell gamma=120 degrees, i.e, cosangles[2]=-1/2, in monoclinic cells beta =/= 90 degrees. """ if lattice_system is None: lattice_system = lattice_system_from_lengths_and_cosangles( lengths, cosangles) a, b, c = lengths cosalpha, cosbeta, cosgamma = cosangles #sinalpha = vectormath.sqrt(1-cosalpha**2) sinbeta = vectormath.sqrt(1 - cosbeta**2) singamma = vectormath.sqrt(1 - cosgamma**2) basis = None if lattice_system == 'cubic' or lattice_system == 'tetragonal' or lattice_system == 'orthorhombic': basis = FracVector.create([[a, 0, 0], [0, b, 0], [0, 0, c] ]) * orientation elif lattice_system == 'hexagonal': #basis = FracVector.create([[singamma*a, a*cosgamma, 0], # [0, b, 0], # [0, 0, c]])*orientation basis = FracVector.create([[a, 0, 0], [cosgamma * b, b * singamma, 0], [0, 0, c]]) * orientation elif lattice_system == 'monoclinic': basis = FracVector.create([[a, 0, 0], [0, b, 0], [c * cosbeta, 0, c * sinbeta] ]) * orientation elif lattice_system == 'rhombohedral': vc = cosalpha tx = vectormath.sqrt((1 - vc) / 2.0) ty = vectormath.sqrt((1 - vc) / 6.0) tz = vectormath.sqrt((1 + 2 * vc) / 3.0) basis = FracVector.create([[2 * a * ty, 0, a * tz], [-b * ty, b * tx, b * tz], [-c * ty, -c * tx, c * tz]]) * orientation else: angfac1 = (cosalpha - cosbeta * cosgamma) / singamma angfac2 = vectormath.sqrt(singamma**2 - cosbeta**2 - cosalpha**2 + 2 * cosalpha * cosbeta * cosgamma) / singamma basis = FracVector.create([[a, 0, 0], [b * cosgamma, b * singamma, 0], [c * cosbeta, c * angfac1, c * angfac2] ]) * orientation return basis
def simplex_le_solver(a, b, c): """ Minimizie func = a[0]*x + a[1]*y + a[2]*z + ... With constraints:: b[0,0]x + b[0,1]y + b[0,2]z + ... <= c[0] b[1,0]x + b[1,1]y + b[1,2]z + ... <= c[1] ... x,y,z, ... >= 0 Algorithm adapted from 'taw9', http://taw9.hubpages.com/hub/Simplex-Algorithm-in-Python """ Na = len(a) Nc = len(c) obj = a.get_insert(0, 1) M = [] for i in range(Nc): obj = obj.get_append(0) ident = [0] * Nc ident[i] = 1 M += [b[i].get_insert(0, 0).get_extend(ident).get_append(c[i])] M = MutableFracVector.create(M) obj = obj.get_append(0) base = list(range(Na, Na + Nc)) while not min(obj[1:-1]) >= 0: pivot_col = obj[1:-1].argmin() + 1 rhs = M[:, -1] lhs = M[:, pivot_col] nonzero = [i for i in range(len(rhs)) if lhs[i] != 0] pivot_row = min(nonzero, key=lambda i: rhs[i] / lhs[i]) M[pivot_row] = (M[pivot_row] / (M[pivot_row][pivot_col])).simplify() for i in range(Nc): if i == pivot_row: continue M[i] = (M[i] - M[i][pivot_col] * M[pivot_row]).simplify() obj = (obj - obj[pivot_col] * M[pivot_row]).simplify() base[pivot_row] = pivot_col - 1 solution = [0] * Na for j in range(Nc): if obj[base[j] + 1] == 0 and base[j] < Na: solution[base[j]] = M[j][-1].simplify() return FracVector.create(-obj[-1]), FracVector.create(solution)
def trivial_symmetry_reduce(coordgroups): """ Looks for 'trivial' ways to reduce the coordinates in the given coordgroups by a standard set of symmetry operations. This is not a symmetry finder (and it is not intended to be), but for a standard primitive cell taken from a standard conventional cell, it reverses the primitive unit cell coordgroups into the symmetry reduced coordgroups. """ # TODO: Actually implement, instead of this placeholder that just gives up and returns P 1 symops = [] symopvs = [] for symop in all_symops: symopv = FracVector.create(symop) if check_symop(coordgroups, symopv): symops += [all_symops[symop]] symopvs += [symopv] shash = symopshash(symops) if shash in symops_hash_index: hall_symbol = symops_hash_index[shash] rc_reduced_coordgroups, wyckoff_symbols, multiplicities = reduce_by_symops(coordgroups, symopvs, hall_symbol) return rc_reduced_coordgroups, hall_symbol, wyckoff_symbols, multiplicities rc_reduced_coordgroups = coordgroups hall_symbol = 'P 1' wyckoff_symbols = ['a']*sum([len(x) for x in coordgroups]) multiplicities = [1]*sum([len(x) for x in coordgroups]) return rc_reduced_coordgroups, hall_symbol, wyckoff_symbols, multiplicities
def is_point_inside_cell(cell, point): """ Checks if a given triple-vector is inside the cell given by the basis matrix in cell """ p1 = FracVector((0, 0, 0)) p2 = cell[0] p3 = cell[1] p4 = cell[0] + cell[1] p5 = cell[2] p6 = cell[0] + cell[2] p7 = cell[1] + cell[2] p8 = cell[0] + cell[1] + cell[2] tetras = [None] * 6 tetras[0] = [p1, p2, p3, p6] tetras[1] = [p2, p3, p4, p6] tetras[2] = [p3, p4, p6, p8] tetras[3] = [p3, p6, p7, p8] tetras[4] = [p3, p5, p6, p7] tetras[5] = [p1, p3, p5, p6] for tetra in tetras: if is_point_inside_tetra(tetra, point): return True return False
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 is_any_part_of_cube_inside_cell(cell, midpoint, side): """ Checks if any part of a cube is inside the cell spanned by the vectors in cell """ ps = [ midpoint + FracVector((x, y, z)) * side for x in [-1, 1] for y in [-1, 1] for z in [-1, 1] ] for p in ps: if is_point_inside_cell(cell, p): return True return False
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 __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 get_primitive_to_conventional_basis_transform(basis, eps=1e-4): """ Figures out how the 'likley' transform of a primitive cell for getting to the conventional basis This may not be foolproof, and mostly works for re-inverting cells generated by lengths_and_cosangles_to_conventional_basis. (It should only be used when getting something that isn't really the conventional cell does not equal catastrophic failure, just, e.g., a non-optimal representation.) """ half = Fraction(1, 2) lattrans = None unit = FracVector.create([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) c = basis maxele = max(c[0, 0], c[0, 1], c[0, 2], c[1, 0], c[1, 1], c[1, 2], c[2, 0], c[2, 1], c[2, 2]) maxeleneg = max(-c[0, 0], -c[0, 1], -c[0, 2], -c[1, 0], -c[1, 1], -c[1, 2], -c[2, 0], -c[2, 1], -c[2, 2]) if maxeleneg > maxele: scale = (-maxeleneg).simplify() else: scale = (maxele).simplify() rebase = basis / (2 * scale) #niggli, orientation = basis_to_niggli_and_orientation(basis) #lengths, angles = niggli_to_lengths_and_angles(niggli) #rebase = FracVector.create([basis[0]/lengths[0], basis[1]/lengths[1], basis[2]/lengths[2]]).simplify() invrebase = rebase.inv().simplify() #invrebase = FracVector.create([invrebase[0]*lengths[0], invrebase[1]*lengths[1], invrebase[2]*lengths[2]]).simplify() #print("BASIS",basis) #print("BASIS",basis.to_floats()) #print("INVREBASE:", invrebase.to_floats()) #print("TRANSFORMED", (basis*invrebase).to_floats()) for rows in invrebase: for val in rows: if val != 0 and val != 1 and val != -1: #if val != half and val != -half and val != 0 and val != 1 and val != -1: # This is not a primitive cell that can easily be turned into a cubic conventional cell #print("HUH",val) return unit #print("INVREBASE:", invrebase) return invrebase
def symopsmatrix(symop): transf, transl = symopstuple(symop, val_transform=lambda x: x) return FracVector.create(transf), FracVector.create(transl)
def angles_to_cosangles(angles): return FracVector.create_cos(angles, degrees=True)
def is_point_inside_tetra(tetra, point): """ Checks if a point is inside the tretrahedra spanned by the coordinates in tetra """ D0 = FracVector(((tetra[0][0], tetra[0][1], tetra[0][2], 1), (tetra[1][0], tetra[1][1], tetra[1][2], 1), (tetra[2][0], tetra[2][1], tetra[2][2], 1), (tetra[3][0], tetra[3][1], tetra[3][2], 1))) D1 = FracVector(((point[0], point[1], point[2], 1), (tetra[1][0], tetra[1][1], tetra[1][2], 1), (tetra[2][0], tetra[2][1], tetra[2][2], 1), (tetra[3][0], tetra[3][1], tetra[3][2], 1))) D2 = FracVector(((tetra[0][0], tetra[0][1], tetra[0][2], 1), (point[0], point[1], point[2], 1), (tetra[2][0], tetra[2][1], tetra[2][2], 1), (tetra[3][0], tetra[3][1], tetra[3][2], 1))) D3 = FracVector(((tetra[0][0], tetra[0][1], tetra[0][2], 1), (tetra[1][0], tetra[1][1], tetra[1][2], 1), (point[0], point[1], point[2], 1), (tetra[3][0], tetra[3][1], tetra[3][2], 1))) D4 = FracVector(((tetra[0][0], tetra[0][1], tetra[0][2], 1), (tetra[1][0], tetra[1][1], tetra[1][2], 1), (tetra[2][0], tetra[2][1], tetra[2][2], 1), (point[0], point[1], point[2], 1))) d0 = D0.det() if d0 < 0: s0 = -1 else: s0 = 1 d1 = D1.det() d2 = D2.det() d3 = D3.det() d4 = D4.det() if abs(d0) == 0: return False if (d1.sign() == s0 or d1 == 0) and (d2.sign() == s0 or d2 == 0) and ( d3.sign() == s0 or d3 == 0) and (d4.sign() == s0 or d4 == 0): return True return False
def hull_z(points, zs): """ points: a list of points=(x,y,..) with zs= a list of z values; a convex half-hull is constructed over negative z-values returns data on the following format.:: { 'hull_points': indices in points list for points that make up the convex hull, 'interior_points':indices for points in the interior, 'interior_zs':interior_zs 'zs_on_hull': hull z values for each point (for points on the hull, the value of the hull if this point is excluded) 'closest_points': list of best linear combination of other points for each point 'closest_weights': weights of best linear combination of other points for each point } where hull_points and interior_points are lists of the points on the hull and inside the hull. and hull_zs is a list of z-values that the hull *would have* at that point, had this point not been included. interior_zs is a list of z-values that the hull has at the interior points. """ hull_points = [] non_hull_points = [] hull_distances = [] closest_points = [] closest_weights = [] zvalues = len(zs) coeffs = len(points[0]) #print("zvalues",zvalues) #print("coeffs",coeffs) for i in range(zvalues): #print("SEND IN",[zs[j] for j in range(zvalues) if j != i]) a = FracVector.create([zs[j] for j in range(zvalues) if j != i]) b = [None] * coeffs c = [None] * coeffs includepoints = [j for j in range(len(points)) if i != j] for j in range(coeffs): b[j] = FracVector.create([points[x][j] for x in includepoints]) c[j] = points[i][j] # if i == 0: # print("A",a.to_floats()) # print("B",[x.to_floats() for x in b]) # print("C",c) solution = simplex_le_solver(a, b, c) val = solution[0] # if i == 0: # print("VAL",val) # print("SOL",solution[1]) # print("MAPPED SOL",[(solution[1][i],includepoints[i]) for i in range(len(solution[1]))]) # closest = solution[1] thisclosestpoints = [] thisclosestweights = [] for j in range(len(closest)): sol = closest[j] if sol != 0: thisclosestpoints += [includepoints[j]] thisclosestweights += [sol] #print("I AM",i,"I disolve into:",includepoints[j],"*",float(sol),"(",j,")") closest_points += [thisclosestpoints] closest_weights += [thisclosestweights] hull_distances += [zs[i] - val] if zs[i] <= val: hull_points += [i] else: non_hull_points += [i] return { 'hull_indices': hull_points, 'interior_indices': non_hull_points, 'hull_distances': hull_distances, 'competing_indices': closest_points, 'competing_weights': closest_weights, }
def standard_order_axes_transform( niggli_matrix, lattice_system, eps=0, return_identity_if_no_transform_needed=False): """ Returns the transform that re-orders the axes to standard order for each possible lattice system. Note: returns None if no transform is needed, to make it easy to skip the transform in that case. If you want the identity matrix instead, set parameter return_identity_if_no_transform_needed = True, """ # Determine lattice system even if axes are mis-ordered qrtr = Fraction(1, 4) abeq = abs(niggli_matrix[0][0] - niggli_matrix[0][1]) <= eps aceq = abs(niggli_matrix[0][0] - niggli_matrix[0][2]) <= eps bceq = abs(niggli_matrix[0][1] - niggli_matrix[0][2]) <= eps alpha90 = abs(niggli_matrix[1][0]) <= eps beta90 = abs(niggli_matrix[1][1]) <= eps gamma90 = abs(niggli_matrix[1][2]) <= eps alpha120 = (abs(niggli_matrix[1][0]**2 / (niggli_matrix[0][1] * niggli_matrix[0][2]) - qrtr) <= eps) and niggli_matrix[1][0] < 0 beta120 = (abs(niggli_matrix[1][1]**2 / (niggli_matrix[0][0] * niggli_matrix[0][2]) - qrtr) <= eps) and niggli_matrix[1][1] < 0 gamma120 = (abs(niggli_matrix[1][2]**2 / (niggli_matrix[0][0] * niggli_matrix[0][1]) - qrtr) <= eps) and niggli_matrix[1][2] < 0 equals = sum([abeq, aceq, bceq]) if equals == 1: equals = 2 orthos = sum([alpha90, beta90, gamma90]) hexangles = sum([alpha120, beta120, gamma120]) if equals == 3 and orthos == 3: lattice_system = 'cubic' elif equals == 2 and orthos == 3: lattice_system = 'tetragonal' elif equals == 0 and orthos == 3: lattice_system = 'orthorombic' elif hexangles == 1 and orthos == 2 and equals == 2: lattice_system = 'hexagonal' elif orthos == 2: lattice_system = 'monoclinic' elif equals == 3: lattice_system = 'rhombohedral' else: lattice_system = 'triclinic' if lattice_system == 'hexagonal' and not gamma120: if alpha120: return FracVector.create([[0, 0, -1], [0, -1, 0], [-1, 0, 0]]) elif beta120: return FracVector.create([[-1, 0, 0], [0, 0, -1], [0, -1, 0]]) raise Exception( "axis_standard_order_transform: unexpected cell geometry.") elif lattice_system == 'monoclinic' and not (alpha90 and gamma90 and not beta90): if (alpha90 and beta90 and not gamma90): return FracVector.create([[-1, 0, 0], [0, 0, -1], [0, -1, 0]]) elif (beta90 and gamma90 and not alpha90): return FracVector.create([[0, 0, -1], [0, -1, 0], [-1, 0, 0]]) raise Exception( "axis_standard_order_transform: unexpected cell geometry.") return None
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)
continue M[i] = (M[i] - M[i][pivot_col] * M[pivot_row]).simplify() obj = (obj - obj[pivot_col] * M[pivot_row]).simplify() base[pivot_row] = pivot_col - 1 solution = [0] * Na for j in range(Nc): if obj[base[j] + 1] == 0 and base[j] < Na: solution[base[j]] = M[j][-1].simplify() return FracVector.create(-obj[-1]), FracVector.create(solution) if __name__ == '__main__': a = FracVector.create([-3, -2]) b = FracVector.create([[2, 1], [2, 3], [3, 1]]) c = FracVector.create([18, 42, 24]) print(simplex_le_solver(a, b, c)) exit(0) from pylab import * xsample = FracVector.random((5, 1), 0, 100, 100) xsample = FracVector.create([[x[0], 1 - x[0]] for x in xsample] + [[1, 0]] + [[0, 1]]) ysample = (-1 * FracVector.random((5, ), 0, 100, 100)).get_extend([0, 0]) hull = hull_z(xsample, ysample)
def niggli_to_conventional_basis(niggli_matrix, lattice_system=None, orientation=1, eps=1e-4): """ Returns the conventional cell given a niggli_matrix Note: if your basis vector order does not follow the conventions for hexagonal and monoclinic cells, you get the triclinic conventional cell. Conventions: in hexagonal cell gamma=120 degrees., in monoclinic cells beta =/= 90 degrees. """ if lattice_system is None: lattice_system = lattice_system_from_niggli(niggli_matrix) niggli_matrix = niggli_matrix.to_floats() s11, s22, s33 = niggli_matrix[0][0], niggli_matrix[0][1], niggli_matrix[0][ 2] s23, s13, s12 = niggli_matrix[1][0] / 2.0, niggli_matrix[1][ 1] / 2.0, niggli_matrix[1][2] / 2.0 a, b, c = vectormath.sqrt(s11), vectormath.sqrt(s22), vectormath.sqrt(s33) alphar, betar, gammar = vectormath.acos(s23 / (b * c)), vectormath.acos( s13 / (c * a)), vectormath.acos(s12 / (a * b)) basis = None if lattice_system == 'cubic' or lattice_system == 'tetragonal' or lattice_system == 'orthorhombic': basis = FracVector.create([[a, 0, 0], [0, b, 0], [0, 0, c] ]) * orientation elif lattice_system == 'hexagonal': basis = FracVector.create( [[a, 0, 0], [-0.5 * b, b * vectormath.sqrt(3.0) / 2.0, 0], [0, 0, c]]) * orientation elif lattice_system == 'monoclinic': basis = FracVector.create([ [a, 0, 0], [0, b, 0], [c * vectormath.cos(gammar), 0, c * vectormath.sin(gammar)] ]) * orientation elif lattice_system == 'rhombohedral': vc = vectormath.cos(alphar) tx = vectormath.sqrt((1 - vc) / 2.0) ty = vectormath.sqrt((1 - vc) / 6.0) tz = vectormath.sqrt((1 + 2 * vc) / 3.0) basis = FracVector.create([[2 * a * ty, 0, a * tz], [-b * ty, b * tx, b * tz], [-c * ty, -c * tx, c * tz]]) * orientation else: angfac1 = (vectormath.cos(alphar) - vectormath.cos(betar) * vectormath.cos(gammar)) / vectormath.sin(gammar) angfac2 = vectormath.sqrt( vectormath.sin(gammar)**2 - vectormath.cos(betar)**2 - vectormath.cos(alphar)**2 + 2 * vectormath.cos(alphar) * vectormath.cos(betar) * vectormath.cos(gammar)) / vectormath.sin(gammar) basis = FracVector.create([ [a, 0, 0], [b * vectormath.cos(gammar), b * vectormath.sin(gammar), 0], [c * vectormath.cos(betar), c * angfac1, c * angfac2] ]) * orientation return basis
def coordgroups_cartesian_to_reduced(self, coordgroups): newcoordgroups = [] for coordgroup in coordgroups: newcoordgroups += [self.coords_cartesian_to_reduced(coordgroup)] return FracVector.stack(newcoordgroups)
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
def metric_to_niggli(cell): m = cell.noms return FracVector( ((m[0][0], m[1][1], m[2][2]), (2 * m[1][2], 2 * m[0][2], 2 * m[0][1])), cell.denom).simplify()