예제 #1
0
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
예제 #2
0
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)
예제 #3
0
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
예제 #4
0
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
예제 #5
0
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
예제 #6
0
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
예제 #7
0
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,
    }
예제 #8
0
                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)
예제 #9
0
def symopsmatrix(symop):
    transf, transl = symopstuple(symop, val_transform=lambda x: x)
    return FracVector.create(transf), FracVector.create(transl)
예제 #10
0
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
예제 #11
0
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