示例#1
0
    def __init__(self, niggli_matrix, orientation=1, basis=None):
        """
        Private constructor, as per httk coding guidelines. Use Cell.create instead.
        """
        self.niggli_matrix = niggli_matrix
        self.orientation = orientation
        if basis is None:
            basis = FracVector.use(
                niggli_to_basis(niggli_matrix, orientation=orientation))
            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()
            basis = (basis * scale.inv()).simplify()

        self._basis = 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_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]

        self.a, self.b, self.c = self.lengths
        self.alpha, self.beta, self.gamma = self.angles
示例#2
0
    def add_phase(self, symbols, counts, id, energy):
        """
        Handles energy=None, for a phase we don't know the energy of.
        """
        counts = FracVector.use(counts)
        phase = {}
        # In Python 3 symbols in an iterator, so we should convert it to
        # a list.
        symbols = list(symbols)
        for i in range(len(symbols)):
            symbol = symbols[i]
            if symbol in phase:
                phase[symbol] += counts[i]
            else:
                phase[symbol] = counts[i]
            self.seen_symbols[symbol] = True
        if energy is not None:
            self.phases += [phase]
            self.energies += [energy]
            self.ids += [id]
        else:
            self.other_phases += [phase]
            self.other_ids += [id]

        # Clear out so things get reevaluated
        self._reset()
示例#3
0
def niggli_vol_to_scale(niggli_matrix, vol):
    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 = sqrt(float(volsqr))
    return (float(vol) / det)**(1.0 / 3.0)
示例#4
0
def cubic_supercell_transformation(structure,
                                   tolerance=None,
                                   max_search_cells=1000):
    # Note: a better name for tolerance is max_extension or similar, it is not really a tolerance, it regulates the maximum number of repetitions of the primitive cell
    # in any directions to reach the soughts supercell

    if tolerance is None:
        prim_cell = structure.uc_cell.basis
        inv = prim_cell.inv().simplify()
        transformation = (inv * inv.denom).simplify()
    else:
        maxtol = max(int(FracVector.use(tolerance)), 2)
        bestlen = None
        bestortho = None
        besttrans = None
        #TODO: This loop may be possible to do with fewer iterations, since I suppose the only thing that
        #matter is the prime factors?
        for tol in range(1, maxtol):
            prim_cell = structure.uc_cell.basis
            prim_cell = structure.uc_cell.basis
            approxinv = prim_cell.inv().set_denominator(tol).simplify()
            if approxinv[0] == [0, 0, 0] or approxinv[1] == [
                    0, 0, 0
            ] or approxinv[2] == [0, 0, 0]:
                continue
            transformation = (approxinv * approxinv.denom).simplify()
            try:
                cell = Cell.create(basis=transformation * prim_cell)
            except Exception:
                continue
            ortho = (abs(cell.niggli_matrix[1][0]) +
                     abs(cell.niggli_matrix[1][1]) +
                     abs(cell.niggli_matrix[1][2])).simplify()
            equallen = abs(cell.niggli_matrix[0][0] - cell.niggli_matrix[0][1]
                           ) + abs(cell.niggli_matrix[0][0] -
                                   cell.niggli_matrix[0][2])
            if ortho == 0 and equallen == 0:
                # Already perfectly cubic, use this
                besttrans = transformation
                break
            elif bestlen is None or not (bestortho < ortho
                                         and bestlen < equallen):
                bestlen = equallen
                bestortho = ortho
                besttrans = transformation
            elif besttrans == None:
                bestlen = equallen
                bestortho = ortho
                besttrans = transformation

        transformation = besttrans

    if transformation == None:
        raise Exception(
            "Not possible to find a cubic supercell with this limitation of number of repeated cell (increase tolerance.)"
        )

    return transformation
示例#5
0
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 = sqrt(float(volsqr))
    if abs(det) < 1e-12:
        raise Exception("niggli_scale_to_vol: singular cell matrix.")
    return (((scale)**3) * det)
示例#6
0
def reduced_to_cartesian(cell, coordgroups):
    cell = FracVector.use(cell)

    newcoordgroups = []

    for coordgroup in coordgroups:
        newcoordgroup = coordgroup * cell
        newcoordgroups.append(newcoordgroup)

    return newcoordgroups
示例#7
0
def coordgroups_cartesian_to_reduced(coordgroups, basis):
    basis = FracVector.use(basis)
    cellinv = basis.inv()

    newcoordgroups = []

    for coordgroup in coordgroups:
        newcoordgroup = coordgroup * cellinv
        newcoordgroups.append(newcoordgroup)

    return newcoordgroups
示例#8
0
def cartesian_to_reduced(cell, coordgroups):
    cell = FracVector.use(cell)
    cellinv = cell.inv()

    newcoordgroups = []

    for coordgroup in coordgroups:
        newcoordgroup = coordgroup * cellinv
        newcoordgroups.append(newcoordgroup)

    return newcoordgroups
示例#9
0
def niggli_to_cell_old(niggli_matrix, orientation=1):
    cell = FracVector.use(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 = sqrt(s11), sqrt(s22), sqrt(s33)
    alpha_rad, beta_rad, gamma_rad = acos(s23 / (b * c)), acos(
        s13 / (c * a)), acos(s12 / (a * b))

    iv = 1 - cos(alpha_rad)**2 - cos(beta_rad)**2 - cos(
        gamma_rad)**2 + 2 * cos(alpha_rad) * cos(beta_rad) * cos(gamma_rad)

    # Handle that iv may be very, very slightly < 0 by the floating point accuracy limit
    if iv > 0:
        v = sqrt(iv)
    else:
        v = 0.0

    if c * v < 1e-14:
        raise Exception(
            "niggli_to_cell: Physically unreasonable cell, cell vectors degenerate or very close to degenerate."
        )

    if orientation < 0:
        cell = [[-a, 0.0, 0.0],
                [-b * cos(gamma_rad), -b * sin(gamma_rad), 0.0],
                [
                    -c * cos(beta_rad),
                    -c * (cos(alpha_rad) - cos(beta_rad) * cos(gamma_rad)) /
                    sin(gamma_rad), -c * v / sin(gamma_rad)
                ]]
    else:
        cell = [[a, 0.0, 0.0], [b * cos(gamma_rad), b * sin(gamma_rad), 0.0],
                [
                    c * cos(beta_rad),
                    c * (cos(alpha_rad) - cos(beta_rad) * cos(gamma_rad)) /
                    sin(gamma_rad), c * v / sin(gamma_rad)
                ]]

    for i in range(3):
        for j in range(3):
            cell[i][j] = round(cell[i][j], 14)

    return cell
示例#10
0
def occupations_and_coords_to_assignments_and_coordgroups(
        occupationscoords, occupations):
    if len(occupationscoords) == 0:
        return [], FracVector((), 1)

    occupationscoords = FracVector.use(occupationscoords)

    new_coordgroups = []
    new_assignments = []
    for i in range(len(occupations)):
        for j in range(len(new_assignments)):
            if occupations[i] == new_assignments[j]:
                new_coordgroups[j].append(occupationscoords[i])
                break
        else:
            new_coordgroups.append([occupationscoords[i]])
            new_assignments.append(occupations[i])
    new_coordgroups = FracVector.create(new_coordgroups)
    return new_assignments, new_coordgroups
示例#11
0
def normalized_formula_parts(assignments, ratios, counts):

    formula = {}
    alloccs = {}
    maxc = 0
    for i in range(len(counts)):
        assignment = assignments[i]
        ratio = ratios[i]
        if is_sequence(assignment):
            if len(assignment) == 1:
                assignment = assignment[0]
                ratio = ratio[0]
                occ = ratio
            else:
                assignment = tuple([(x, FracVector.use(y))
                                    for x, y in zip(assignment, ratio)])
                occ = 1
        else:
            occ = ratio
        if not assignment in formula:
            formula[assignment] = FracVector.create(0)
            alloccs[assignment] = FracVector.create(0)
        formula[assignment] += FracVector.create(occ * counts[i])
        alloccs[assignment] += FracVector.create(counts[i])
        if alloccs[assignment] > maxc:
            maxc = alloccs[assignment]

    alloccs = FracVector.create(alloccs.values())
    alloccs = (alloccs / maxc).simplify()

    for symbol in formula.keys():
        formula[symbol] = (formula[symbol] * alloccs.denom / maxc).simplify()
    #    if abs(value-int(value))<1e-6:
    #        formula[symbol] = int(value)
    #    elif int(100*(value-(int(value)))) > 1:
    #        formula[symbol] = float("%d.%.2e" % (value, 100*(value-(int(value)))))
    #    else:
    #        formula[symbol] = float("%d" % (value,))

    return formula
示例#12
0
def basis_to_niggli(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
示例#13
0
def orthogonal_supercell_transformation(structure,
                                        tolerance=None,
                                        ortho=[True, True, True]):
    # TODO: How to solve for exact orthogonal cell?
    if tolerance is None:
        prim_cell = structure.uc_cell.basis
        print("Starting cell:", prim_cell)
        inv = prim_cell.inv().simplify()
        if ortho[0]:
            row0 = (inv[0] / max(inv[0])).simplify()
        else:
            row0 = [1, 0, 0]
        if ortho[1]:
            row1 = (inv[1] / max(inv[1])).simplify()
        else:
            row1 = [0, 1, 0]
        if ortho[2]:
            row2 = (inv[2] / max(inv[2])).simplify()
        else:
            row2 = [0, 0, 1]
        transformation = FracVector.create(
            [row0 * row0.denom, row1 * row1.denom, row2 * row2.denom])
    else:
        maxtol = max(int(FracVector.use(tolerance)), 2)
        bestval = None
        besttrans = None
        for tol in range(1, maxtol):
            prim_cell = structure.uc_cell.basis
            inv = prim_cell.inv().set_denominator(tol).simplify()
            if inv[0] == [0, 0, 0] or inv[1] == [0, 0, 0
                                                 ] or inv[2] == [0, 0, 0]:
                continue
            absinv = abs(inv)
            if ortho[0]:
                row0 = (inv[0] / max(absinv[0])).simplify()
            else:
                row0 = [1, 0, 0]
            if ortho[1]:
                row1 = (inv[1] / max(absinv[1])).simplify()
            else:
                row1 = [0, 1, 0]
            if ortho[2]:
                row2 = (inv[2] / max(absinv[2])).simplify()
            else:
                row2 = [0, 0, 1]
            transformation = FracVector.create(
                [row0 * row0.denom, row1 * row1.denom, row2 * row2.denom])
            try:
                cell = Cell.create(basis=transformation * prim_cell)
            except Exception:
                continue
            maxval = (abs(cell.niggli_matrix[1][0]) +
                      abs(cell.niggli_matrix[1][1]) +
                      abs(cell.niggli_matrix[1][2])).simplify()
            if maxval == 0:
                besttrans = transformation
                break
            if bestval is None or maxval < bestval:
                bestval = maxval
                besttrans = transformation
        transformation = besttrans

    if transformation == None:
        raise Exception(
            "Not possible to find a othogonal supercell with this limitation of number of repeated cell (increase tolerance.)"
        )

    return transformation
示例#14
0
 def coords_reduced_to_cartesian(self, coords):
     coords = FracVector.use(coords)
     return coords * self.basis
示例#15
0
 def coords_cartesian_to_reduced(self, coords):
     coords = FracVector.use(coords)
     return coords * self.inv
示例#16
0
    def create(cls,
               cellshape=None,
               basis=None,
               metric=None,
               niggli_matrix=None,
               a=None,
               b=None,
               c=None,
               alpha=None,
               beta=None,
               gamma=None,
               lengths=None,
               angles=None,
               scale=None,
               scaling=None,
               volume=None,
               periodicity=None,
               nonperiodic_vecs=None,
               orientation=1):
        """
        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

        """
        if isinstance(cellshape, CellShape):
            basis = cellshape.basis
        elif cellshape is not None:
            basis = cell_to_basis(cellshape)

        if basis is not None:
            basis = FracVector.use(basis)

        if niggli_matrix is not None:
            niggli_matrix = FracVector.use(niggli_matrix)
            basis = FracVector.use(
                niggli_to_basis(niggli_matrix, orientation=orientation))

        if niggli_matrix is None and basis is not None:
            niggli_matrix, orientation = basis_to_niggli(basis)

        if niggli_matrix is None and lengths is not None and angles is not None:
            niggli_matrix = lengths_angles_to_niggli(lengths, angles)
            niggli_matrix = FracVector.use(niggli_matrix)
            if basis is None:
                basis = FracVector.use(
                    niggli_to_basis(niggli_matrix, orientation=1))

        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_angles_to_niggli([a, b, c],
                                                     [alpha, beta, gamma])
            niggli_matrix = FracVector.use(niggli_matrix)
            if basis is None:
                basis = FracVector.use(
                    niggli_to_basis(niggli_matrix, orientation=1))

        if niggli_matrix is None:
            raise Exception(
                "CellShape.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(
                "CellShape.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)
            niggli_matrix = (basis * scaling * scaling).simplify()
            if basis is not None:
                basis = (basis * scaling).simplify()

        # For the basis we use a somewhat unusual normalization where the largest one element
        # in the cell = 1, this way we avoid floating point operations for prototypes created
        # from cell vector (prototypes created from lengths and angles is another matter)

        if basis is not None:
            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()
            basis = (basis * scale.inv()).simplify()

        c = niggli_matrix
        maxele = max(c[0, 0], c[0, 1], c[0, 2])
        niggli_matrix = (niggli_matrix * maxele.inv()).simplify()

        return cls(niggli_matrix, orientation, basis)
示例#17
0
文件: sitesutils.py 项目: hpleva/httk
def coords_reduced_to_cartesian(cell, coords):
    cell = FracVector.use(cell)
    newcoords = coords * cell
    return newcoords
示例#18
0
    def insert(self, table, types, keyvals, cursor=None, updatesid=None):
        #sid=self.sids[table]
        #types=self.types[table]
        #self.store[table][sid]={}
        if cursor is None:
            cursor = self.db.cursor()
            mycursor = True
        else:
            mycursor = False

        columns = []
        columndata = []
        subinserts = []
        for column in tuple(types['keys']) + tuple(types['derived']):
            name = column[0]
            t = column[1]

            if name not in keyvals:
                val = None
            else:
                val = keyvals[name]
            if val is None:
                continue

            # Regular column, no strangeness
            if t in self.basics:
                #if issubclass(t,FracVector):
                #    val = (val*1000000000).to_int()
                if issubclass(t, FracScalar):
                    val = FracScalar.use(val)
                    val = int(val.limit_denominator(50000000) * 1000000000)
                #if isinstance(t,FracVector):
                #    print("DOES THIS HAPPEN?",t,val)
                #    val = int(val.limit_denominator(50000000))
                else:
                    try:
                        val = t(val)
                    except UnicodeEncodeError:
                        val = unicode_type(val)
                    except TypeError:
                        print("HUH", val, t)
                        raise
                columns.append(name)
                columndata.append(val)

            # List type means we need to establish a second table and store key values
            elif isinstance(t, list):
                if not isinstance(t[0], tuple):
                    t = [(name, t[0])]
                    val = [(x, ) for x in val]
                subtablename = table + "_" + name
                for idx, entry in enumerate(val):
                    subtypes = [(table + "_sid", int), (name + "_index", int)]
                    data = {name + "_index": idx}
                    for i in range(len(t)):
                        if issubclass(t[i][1], HttkObject):
                            subtypename = t[i][0] + "_" + t[i][1].types(
                            )['name'] + "_sid"
                            subtypes.append((
                                subtypename,
                                int,
                            ))

                            if entry[i].db.sid is None:
                                entryval = t[i][1].use(entry[i])
                                entryval.db.store(self)

                            data[subtypename] = entry[i].db.sid
                        else:
                            subtypename = t[i][0]
                            subtypes.append((
                                subtypename,
                                t[i][1],
                            ))
                            if isinstance(entry, tuple):
                                #print("TRYING",entry,i,subtypename)
                                data[subtypename] = entry[i]
                            elif isinstance(entry, dict):
                                data[subtypename] = entry[t[i][0]]
                            else:
                                raise Exception(
                                    "Data for multifield of wrong type, got:" +
                                    str(entry))

                    subinserts.append((subtablename, subtypes, data, ()))
                    #print("SUBINSETS",subinserts)

            # Tuple means numpy array
            elif isinstance(t, tuple):
                # Numpy array with fixed number of entries, just flatten and store as _1, _2, ... columns
                tupletype = t[0]
                #print("INSERT TUPLETYPE",tupletype,types,t)

                if t[1] >= 1:
                    #flat=val.flatten()
                    flat = tuple(flatten(val))
                    size = t[1] * t[2]
                    for i in range(size):
                        columname = name + "_" + str(i)
                        columns.append(columname)
                        if tupletype == FracVector:
                            setval = (FracVector.use(
                                flat[i]).limit_denominator(5000000) *
                                      1000000000).to_ints()
                            columndata.append(setval)
                        elif tupletype == FracScalar:
                            #print("FLATI",flat[i])
                            #columndata.append(map(lambda x:int(x*1000000000),flat[i]))
                            if flat[i] is not None:
                                setval = int(
                                    FracScalar.use(
                                        flat[i]).limit_denominator(5000000) *
                                    1000000000)
                                columndata.append(setval)
                            else:
                                columndata.append(None)
                        else:
                            columndata.append(flat[i])

                # Variable length numpy array, needs subtable
                if t[1] == 0:
                    subtablename = table + "_" + name
                    subtablecolumnname = name
                    subdimension = (tupletype, 1, t[2])

                    for idx, entry in enumerate(
                            val):  # loops over rows in 2d array
                        #data = {table+"_sid":sid,name:entry}
                        #self.insert(subtablename,data)
                        data = {name: entry, name + "_index": idx}
                        subtypes = [(subtablecolumnname, subdimension),
                                    (table + "_sid", int),
                                    (name + "_index", int)]
                        subinserts.append((subtablename, subtypes, data, ()))

            elif issubclass(t, HttkObject):
                if val.db.sid is None:
                    # This fixes an issue where sometimes a different class (e.g. a subclass) is sent in to be stored in a field.
                    val = t.use(val)
                    val.db.store(self)
                columnname = name + "_" + t.types()['name'] + "_sid"
                columns.append(columnname)
                columndata.append(val.db.sid)
            else:
                raise Exception(
                    "Dictstore.insert: unexpected class; can only handle basic types and subclasses of Storable. Offending class:"
                    + str(t))

        # TODO: this logic is not finished, more elaborate updates need handling
        if (isinstance(updatesid, int)
                and updatesid >= 0) or updatesid is None:
            if updatesid is not None:
                sid = self.db.update_row(table, table + "_id", updatesid,
                                         columns, columndata, cursor)
            else:
                sid = self.db.insert_row(table, columns, columndata, cursor)
                for subinsert in subinserts:
                    subinsert[2][table + "_sid"] = sid
                    self.insert(subinsert[0], {
                        'keys': subinsert[1],
                        'derived': subinsert[3]
                    }, subinsert[2], cursor)
        else:
            sid = -updatesid

        if mycursor:
            if not self._delay_commit:
                self.db.commit()
            cursor.close()

        return sid
示例#19
0
def transform(structure, transformation, max_search_cells=20, max_atoms=1000):

    transformation = FracVector.use(transformation).simplify()
    #if transformation.denom != 1:
    #    raise Exception("Structure.transform requires integer transformation matrix")

    old_cell = structure.uc_cell
    new_cell = Cell.create(basis=transformation * old_cell.basis)
    conversion_matrix = (old_cell.basis * new_cell.inv).simplify()

    volume_ratio = abs(
        (new_cell.basis.det() / abs(old_cell.basis.det()))).simplify()
    seek_counts = [
        int((volume_ratio * x).simplify()) for x in structure.uc_counts
    ]
    #print("HMM",(new_cell.basis.det()/old_cell.basis.det()).simplify())
    #print("SEEK_COUNTS",seek_counts, volume_ratio, structure.uc_counts, transformation)
    total_seek_counts = sum(seek_counts)
    if total_seek_counts > max_atoms:
        raise Exception("Structure.transform: more than " + str(max_atoms) +
                        " needed. Change limit with max_atoms parameter.")

    #if max_search_cells != None and maxvec[0]*maxvec[1]*maxvec[2] > max_search_cells:
    #    raise Exception("Very obtuse angles in cell, to search over all possible lattice vectors will take a very long time. To force, set max_search_cells = None when calling find_prototypeid()")

    ### Collect coordinate list of all sites inside the new cell
    coordgroups = structure.uc_reduced_coordgroups
    extendedcoordgroups = [[] for x in range(len(coordgroups))]

    if max_search_cells is not None:
        max_search = [max_search_cells, max_search_cells, max_search_cells]
    else:
        max_search = None

    for offset in breath_first_idxs(dim=3, end=max_search, negative=True):
        #print("X",offset, seek_counts)
        for idx in range(len(coordgroups)):
            coordgroup = coordgroups[idx]
            newcoordgroup = coordgroup + FracVector([offset] * len(coordgroup))
            new_reduced = newcoordgroup * conversion_matrix
            #print("NEW:",FracVector.use(new_reduced).to_floats(),)
            new_reduced = [
                x for x in new_reduced if x[0] >= 0 and x[1] >= 0 and x[2] >= 0
                and x[0] < 1 and x[1] < 1 and x[2] < 1
            ]
            extendedcoordgroups[idx] += new_reduced
            c = len(new_reduced)
            seek_counts[idx] -= c
            total_seek_counts -= c
            #print("ADD",str(c))
            if seek_counts[idx] < 0:
                #print("X",offset, seek_counts)
                raise Exception(
                    "Structure.transform safety check error, internal error: too many atoms in supercell."
                )
        if total_seek_counts == 0:
            break
    else:
        raise Exception(
            "Very obtuse angles in cell, to search over all possible lattice vectors will take a very long time. To force, set max_search_cells = None when calling find_prototypeid()"
        )

    return structure.create(uc_reduced_coordgroups=extendedcoordgroups,
                            uc_basis=new_cell.basis,
                            assignments=structure.assignments)
示例#20
0
def build_supercell_old(structure, transformation, max_search_cells=1000):
    ### New basis matrix, note: works in units of old_cell.scale to avoid floating point errors
    #print("BUILD SUPERCELL",structure.uc_sites.cell.basis.to_floats(), repetitions)

    transformation = FracVector.use(transformation).simplify()
    if transformation.denom != 1:
        raise Exception(
            "Structure.build_supercell requires integer transformation matrix")

    old_cell = structure.uc_sites.cell.get_normalized_longestvec()
    new_cell = Cell.create(basis=transformation * old_cell.basis)
    #conversion_matrix = (new_cell.inv*old_cell.basis).T().simplify()
    conversion_matrix = (old_cell.basis * new_cell.inv).T().simplify()

    volume_ratio = (new_cell.basis.det() /
                    abs(old_cell.basis.det())).simplify()

    # Generate the reduced (old cell) coordinates of each corner in the new cell
    # This determines how far we must loop the old cell to cover all these corners
    nb = new_cell.basis
    corners = FracVector.create([(0, 0, 0), nb[0], nb[1], nb[2], nb[0] + nb[1],
                                 nb[0] + nb[2], nb[1] + nb[2],
                                 nb[0] + nb[1] + nb[2]])
    reduced_corners = corners * (old_cell.basis.inv().T())

    maxvec = [
        int(reduced_corners[:, 0].max()) + 2,
        int(reduced_corners[:, 1].max()) + 2,
        int(reduced_corners[:, 2].max()) + 2
    ]
    minvec = [
        int(reduced_corners[:, 0].min()) - 2,
        int(reduced_corners[:, 1].min()) - 2,
        int(reduced_corners[:, 2].min()) - 2
    ]

    if max_search_cells is not None and maxvec[0] * maxvec[1] * maxvec[
            2] > max_search_cells:
        raise Exception(
            "Very obtuse angles in cell, to search over all possible lattice vectors will take a very long time. To force, set max_search_cells = None when calling find_prototypeid()"
        )

    ### Collect coordinate list of all sites inside the new cell
    coordgroups = structure.uc_reduced_coordgroups
    extendedcoordgroups = [[] for x in range(len(coordgroups))]
    for idx in range(len(coordgroups)):
        coordgroup = coordgroups[idx]
        for i in range(minvec[0], maxvec[0]):
            for j in range(minvec[1], maxvec[1]):
                for k in range(minvec[2], maxvec[2]):
                    newcoordgroup = coordgroup + FracVector(
                        ((i, j, k), ) * len(coordgroup))
                    new_reduced = newcoordgroup * conversion_matrix
                    new_reduced = [
                        x for x in new_reduced if x[0] >= 0 and x[1] >= 0
                        and x[2] >= 0 and x[0] < 1 and x[1] < 1 and x[2] < 1
                    ]
                    extendedcoordgroups[idx] += new_reduced

    # Safety check for avoiding bugs that change the ratio of atoms
    new_counts = [len(x) for x in extendedcoordgroups]
    for i in range(len(structure.uc_counts)):
        if volume_ratio * structure.uc_counts[i] != new_counts[i]:
            print("Volume ratio:", float(volume_ratio), volume_ratio)
            print("Extended coord groups:",
                  FracVector.create(extendedcoordgroups).to_floats())
            print("Old counts:", structure.uc_counts,
                  structure.assignments.symbols)
            print("New counts:", new_counts, structure.assignments.symbols)
            #raise Exception("Structure.build_supercell safety check failure. Volume changed by factor "+str(float(volume_ratio))+", but atoms in group "+str(i)+" changed by "+str(float(new_counts[i])/float(structure.uc_counts[i])))

    return structure.create(uc_reduced_coordgroups=extendedcoordgroups,
                            basis=new_cell.basis,
                            assignments=structure.assignments,
                            cell=structure.uc_cell)
    def show(self, showunstable=True, labelunstable=False, avoid_overlap=True):
        print(
            "Warning: graphical phase diagrams currently does not fill in *all* dividing lines."
        )

        pd = self.phasediagram
        pp = PolygonPlot(labels=pd.coord_system,
                         sides=len(pd.coord_system),
                         label_offset=0.15)

        coords, ids = pd.hull_point_coords()
        coords = FracVector.use(coords).to_floats()

        coords2, ids2 = pd.interior_point_coords()
        coords2 = FracVector.use(coords2).to_floats()
        newdata2 = pp.translate_coords(coords2)

        allcoords, allids = pd.coords()
        allcoords = FracVector.use(allcoords).to_floats()
        alldata = pp.translate_coords(allcoords)

        if showunstable and len(newdata2) > 0:
            pp.ax.scatter(newdata2[:, 0],
                          newdata2[:, 1],
                          s=50,
                          color='purple',
                          marker='o',
                          facecolor='none')

        newdata = pp.translate_coords(coords)

        pp.ax.scatter(newdata[:, 0], newdata[:, 1], s=100, marker='o')

        labelpos = []
        for i, txt in enumerate(ids):
            labelpos += [[
                newdata[i, 0], newdata[i, 1], newdata[i, 0] + 0.04,
                newdata[i, 1] + 0.01, txt, {
                    'color': 'blue'
                }
            ]]

        if showunstable and labelunstable:
            for i, txt in enumerate(ids2):
                labelpos += [[
                    newdata2[i, 0], newdata2[i, 1], newdata2[i, 0] + 0.04,
                    newdata2[i, 1] + 0.01, txt, {
                        'color': 'purple'
                    }
                ]]

        # TODO: Replace with true spring framework instead
        if avoid_overlap:
            xoffset = 0.00
            yoffset = 0.02
            overlap = True
            maxiters = 100
            while overlap and maxiters > 0:
                overlap = False
                for i in range(len(labelpos)):
                    pos1 = labelpos[i]
                    if sqrt((pos1[0] - pos1[2])**2 +
                            (pos1[1] - pos1[3])**2) < 0.02:
                        labelpos[i] = [
                            pos1[0], pos1[1], pos1[2] + xoffset *
                            (random.choice([+1, -1])), pos1[3] + yoffset *
                            (random.choice([+1, -1]))
                        ] + pos1[4:]
                        overlap = True
                        #print("OVERLAP1",i)
                    for j in range(i + 1, len(labelpos)):
                        if (i == j):
                            continue
                        pos1 = labelpos[i]
                        pos2 = labelpos[j]
                        if sqrt((pos1[0] - pos1[2])**2 +
                                (pos1[1] - pos1[3])**2) < 0.02:
                            labelpos[i] = [
                                pos1[0], pos1[1], pos1[2] + xoffset *
                                (random.choice([+1, -1])), pos1[3] + yoffset *
                                (random.choice([+1, -1]))
                            ] + pos1[4:]
                            #labelpos[j] = [pos2[0],pos2[1],pos2[2]+xoffset*(random.choice([+1, -1])),pos2[3]+yoffset*(random.choice([+1, -1]))]+pos2[4:]
                            overlap = True
                            #print("OVERLAP2",i,j,abs(sqrt(pos1[2]**2 + pos1[3]**2) - sqrt(pos2[2]**2 + pos2[3]**2)))
                            print(pos1)
                            print(pos2)

                #print(maxiters, labelpos)
                maxiters -= 1

        for i, pos in enumerate(labelpos):
            if abs(sqrt(pos[0]**2 + pos[1]**2) -
                   sqrt(pos[2]**2 + pos[3]**2)) > 0.04:
                pp.ax.annotate(pos[4],
                               xy=(pos[0], pos[1]),
                               xycoords='data',
                               xytext=(pos[2], pos[3]),
                               textcoords='data',
                               arrowprops=dict(arrowstyle="->",
                                               connectionstyle="arc3"),
                               **pos[5])
                #pp.ax.annotate(pos[2], (pos[0],pos[1]))
            else:
                pp.ax.annotate(pos[4], (pos[2], pos[3]), **pos[5])

        lines = pd.interior_competing_phase_lines()
        for line in lines:
            arrowplot(pp.ax, [alldata[line[0]][0], alldata[line[1]][0]],
                      [alldata[line[0]][1], alldata[line[1]][1]],
                      c='purple')
            #pp.ax.plot([alldata[line[0]][0],alldata[line[1]][0]],[alldata[line[0]][1],alldata[line[1]][1]],'-',color='purple')


#         lines = pd.hull_to_interior_competing_phase_lines()
#         for line in lines:
#             arrowplot(pp.ax,[alldata[line[0]][0],alldata[line[1]][0]],[alldata[line[0]][1],alldata[line[1]][1]],c='blue')
#             #pp.ax.plot([alldata[line[0]][0],alldata[line[1]][0]],[alldata[line[0]][1],alldata[line[1]][1]],'b-')

#         lines = pd.hull_competing_phase_lines()
#         for line in lines:
#             #print("LINE",line[0],"->",line[1])
#             #arrowplot(pp.ax,[newdata[line[0]][0],newdata[line[1]][0]],[newdata[line[0]][1],newdata[line[1]][1]],c='black')
#             pp.ax.plot([newdata[line[0]][0],newdata[line[1]][0]],[newdata[line[0]][1],newdata[line[1]][1]],'k-')

        lines = pd.phase_lines
        for line in lines:
            #print("LINE",line[0],"->",line[1])
            #arrowplot(pp.ax,[newdata[line[0]][0],newdata[line[1]][0]],[newdata[line[0]][1],newdata[line[1]][1]],c='black')
            pp.ax.plot([newdata[line[0]][0], newdata[line[1]][0]],
                       [newdata[line[0]][1], newdata[line[1]][1]], 'k-')

        show()