예제 #1
0
파일: utils.py 프로젝트: sjtuzhanglei/dfttk
def find_optimal_cell_shape_in_range(
    cell,
    target_size,
    target_shape,
    size_range=None,
    optimize_sc=False,
    lower_limit=-2,
    upper_limit=2,
    sc_tolerance=1e-5,
    verbose=False,
):
    """
    Returns the transformation matrix that produces a supercell
    corresponding to *target_size* unit cells with metric *cell* that
    most closely approximates the shape defined by *target_shape*.

    Parameters:
        cell: 2D array of floats
            Metric given as a (3x3 matrix) of the input structure.
        target_size: integer
            Size of desired super cell in number of unit cells.
        target_shape: str
            Desired supercell shape. Can be 'sc' for simple cubic or
            'fcc' for face-centered cubic.
        size_range: None/int/float/list/tuple
            The range of the target_size
                if None, 80%-120% target_size
                elif int/float
                    < 1    (1-size_range, 1+size_range) * target_size
                    = 1    == target_size
                    > 1    (-int(size_range), int(size_range)) + target_size
                elif list/tuple (2 elements)
                    [size_range(0), size_range(1)]
        optimize_sc: bool
            Optimize the super cell matrix (True) or not (False)
            If False, then use the closest integer transformation matrix of ideal matrix
        lower_limit: int
            Lower limit of search range.
        upper_limit: int
            Upper limit of search range.
        sc_tolerance: float
            The tolerance for the search. If the score is less than sc_tolerance, stop searching
        verbose: bool
            Set to True to obtain additional information regarding
            construction of transformation matrix.
    Return
        numpy.ndarray
            2d array of a scaling matrix, e.g. [[3,0,0],[0,3,0],[0,0,3]]

    Note: this function taken from ase(ase.build.find_optimal_cell_shape), and we made some improvements 
          (add sc_tolerance and set the ideal_matrix as the first loop, which will speed up the code for high symmetry
           add size_range, which will allow search in a range)
    """

    # Set up target metric
    if target_shape in ["sc", "simple-cubic"]:
        target_metric = np.eye(3)
    elif target_shape in ["fcc", "face-centered cubic"]:
        target_metric = 0.5 * np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]],
                                       dtype=float)
    if verbose:
        print("target metric (h_target):")
        print(target_metric)

    if size_range is None:
        min_size = int(target_size * 0.8)
        max_size = math.ceil(target_size * 1.2)
    elif isinstance(size_range, (float, int)):
        if size_range < 1:
            min_size = int(target_size * (1 - size_range))
            max_size = math.ceil(target_size * (1 + size_range))
        elif size_range == 1:
            min_size = target_size
            max_size = target_size
        else:
            min_size = target_size - int(size_range)
            max_size = target_size + math.ceil(size_range)
    elif isinstance(size_range, (list, tuple)):
        min_size = int(size_range[0])
        max_size = math.ceil(size_range[1])
    else:
        raise ValueError('Unsupported size range.')

    # Normalize cell metric to reduce computation time during looping
    norm = (target_size * np.linalg.det(cell) /
            np.linalg.det(target_metric))**(-1.0 / 3)
    norm_cell = norm * cell
    if verbose:
        print("normalization factor (Q): %g" % norm)

    # Approximate initial P matrix
    ideal_P = np.dot(target_metric, np.linalg.inv(norm_cell))
    if verbose:
        print("idealized transformation matrix:")
        print(ideal_P)
    starting_P = np.array(np.around(ideal_P, 0), dtype=int)
    if verbose:
        print("closest integer transformation matrix (P_0):")
        print(starting_P)

    if optimize_sc:
        # Prepare run.
        best_score = 1e6
        optimal_P = None
        #Set the starting_P as the first one
        dPlist = list(
            itertools.product(range(lower_limit, upper_limit + 1), repeat=9))
        dP0 = (0, 0, 0, 0, 0, 0, 0, 0, 0)
        dPlist.pop(dPlist.index(dP0))
        dPlist = [dP0] + dPlist
        for dP in dPlist:
            dP = np.array(dP, dtype=int).reshape(3, 3)
            P = starting_P + dP
            New_size = np.around(np.linalg.det(P))
            if New_size < min_size or New_size > max_size:
                continue
            norm_new = get_norm_cell(cell, New_size, target_shape=target_shape)
            score = get_deviation_from_optimal_cell_shape(
                np.dot(P, norm_new), target_shape=target_shape, norm=1.0)
            if score < best_score:
                best_score = score
                optimal_P = P
            if best_score < sc_tolerance:
                break

        if optimal_P is None:
            optimal_P = starting_P
            print(
                "Failed to find a transformation matrix, using the ideal one.")

        # Finalize.
        if verbose:
            print("smallest score (|Q P h_p - h_target|_2): %f" % best_score)
            print("optimal transformation matrix (P_opt):")
            print(optimal_P)
            print("supercell metric:")
            print(np.round(np.dot(optimal_P, cell), 4))
            print("determinant of optimal transformation matrix: %g" %
                  np.linalg.det(optimal_P))
    else:
        optimal_P = starting_P
    return optimal_P
예제 #2
0
파일: utils.py 프로젝트: sjtuzhanglei/dfttk
def supercell_scaling_by_atom_lat_vol(structure,
                                      min_obj=60,
                                      max_obj=120,
                                      scale_object='atom',
                                      optimize_sc=False,
                                      target_shape='sc',
                                      lower_search_limit=-2,
                                      upper_search_limit=2,
                                      verbose=False,
                                      sc_tolerance=1e-5):
    """
    Find a the supercell scaling matrix that gives the most cubic supercell for a
    structure, where the supercell has between the minimum and maximum nubmer of object(atom/lattice/volume).

    Parameters
    ----------
    structure : pymatgen.Structure
        Unitcell of a structure
    scale_object: str
        control the scale object, atom or lattice or volume (only first letter matters)
    min_obj/max_obj : int/float
        minimum/maximum atoms/lattice/volume(controlled by scale_object)
    target_shape : str
        Target shape of supercell. Could choose 'sc' for simple cubic or 'fcc' for face centered
        cubic. Default is 'sc'.
    lower_search_limit : int
        How far to search below the 'ideal' cubic scaling. Default is -2.
    upper_search_limit : int
        How far to search below the 'ideal' cubic scaling. Default is 2.
    verbose : bool
        Whether to print extra details on the cell shapes and scores. Useful for debugging.
    sc_tolerance: float
        The tolerance for the search. If the score is less than sc_tolerance, stop searching

    Returns
    -------
    numpy.ndarray
        2d array of a scaling matrix, e.g. [[3,0,0],[0,3,0],[0,0,3]]

    Notes
    -----
    The motiviation for this is for use in phonon calculations and defect calculations.
    It is important that defect atoms are far enough apart that they do not interact.
    Scaling unit cells that are not cubic by even dimensions might result in interacting
    defects. An example would be a tetragonal cell with 2x8x8 Ang lattice vectors being
    made into a 2x2x2 supercell. Atoms along the first dimension would not be very far
    apart.

    We are using a pure Python implementation from ASE, which is not very fast for a given
    supercell size. This allows for a variable supercell size, so it's going to be slow
    for a large range of atoms. (TODO: The performance is improved, but still can be faster)

    The search limits are passed directloy to ``find_optimal_cell_shape``.
    They define the search space for each individual supercell based on the "ideal" scaling.
    For example, a cell with 4 atoms and a target size of 110 atoms might have an ideal scaling
    of 3x3x3. The search space for a lower and upper limit of -2/+2 would be 1-5. Since the
    calculations are based on the cartesian product of 3x3 matrices, large search ranges are
    very expensive.
    """
    #from ase.build import get_deviation_from_optimal_cell_shape

    # range of supercell sizes in number of unitcells
    scale_object = scale_object.lower()
    if scale_object.startswith('a'):
        unit_obj = len(structure)
    elif scale_object.startswith('l'):
        unit_obj = structure.volume
        min_obj = min_obj**3
        max_obj = max_obj**3
    elif scale_object.startswith('v'):
        unit_obj = structure.volume
    else:
        raise ValueError(
            'Unsupported scale object, please choose atom or lattice or volume.'
        )
    size_range = [int(min_obj / unit_obj), math.ceil(max_obj / unit_obj)]

    optimal_supercell_shapes = []  # numpy arrays of optimal shapes
    optimal_supercell_scores = []  # will correspond to supercell size
    supercell_sizes_out = []

    # find the target shapes
    for sc_size in range(size_range[0], size_range[1]):
        optimal_shape = find_optimal_cell_shape_in_range(
            structure.lattice.matrix,
            sc_size,
            target_shape,
            size_range=size_range,
            upper_limit=upper_search_limit,
            lower_limit=lower_search_limit,
            verbose=True,
            sc_tolerance=sc_tolerance,
            optimize_sc=optimize_sc)
        optimal_supercell_shapes.append(optimal_shape)
        norm_cell = get_norm_cell(structure.lattice.matrix,
                                  sc_size,
                                  target_shape=target_shape)
        scores = get_deviation_from_optimal_cell_shape(
            np.dot(optimal_shape, norm_cell), target_shape)
        optimal_supercell_scores.append(scores)
        supercell_sizes_out.append(sc_size)
        if scores < sc_tolerance:
            break

    if verbose:
        for i in range(len(optimal_supercell_shapes)):
            print('{} {:0.4f} {}'.format(supercell_sizes_out[i],
                                         optimal_supercell_scores[i],
                                         optimal_supercell_shapes[i].tolist()))

    # find the most optimal cell shape along the range of sizes
    optimal_sc_shape = optimal_supercell_shapes[np.argmin(
        optimal_supercell_scores)]

    return optimal_sc_shape
예제 #3
0
def supercell_scaling_by_target_atoms(structure,
                                      min_atoms=60,
                                      max_atoms=120,
                                      target_shape='sc',
                                      lower_search_limit=-2,
                                      upper_search_limit=2,
                                      verbose=False):
    """
    Find a the supercell scaling matrix that gives the most cubic supercell for a
    structure, where the supercell has between the minimum and maximum nubmer of atoms.

    Parameters
    ----------
    structure : pymatgen.Structure
        Unitcell of a structure
    min_atoms : target number of atoms in the supercell, defaults to 5
    max_atoms : int
        Maximum number of atoms allowed in the supercell
    target_shape : str
        Target shape of supercell. Could choose 'sc' for simple cubic or 'fcc' for face centered
        cubic. Default is 'sc'.
    lower_search_limit : int
        How far to search below the 'ideal' cubic scaling. Default is -2.
    upper_search_limit : int
        How far to search below the 'ideal' cubic scaling. Default is 2.
    verbose : bool
        Whether to print extra details on the cell shapes and scores. Useful for debugging.

    Returns
    -------
    numpy.ndarray
        2d array of a scaling matrix, e.g. [[3,0,0],[0,3,0],[0,0,3]]

    Notes
    -----
    The motiviation for this is for use in phonon calculations and defect calculations.
    It is important that defect atoms are far enough apart that they do not interact.
    Scaling unit cells that are not cubic by even dimensions might result in interacting
    defects. An example would be a tetragonal cell with 2x8x8 Ang lattice vectors being
    made into a 2x2x2 supercell. Atoms along the first dimension would not be very far
    apart.

    We are using a pure Python implementation from ASE, which is not very fast for a given
    supercell size. This allows for a variable supercell size, so it's going to be slow
    for a large range of atoms.

    The search limits are passed directloy to ``find_optimal_cell_shape``.
    They define the search space for each individual supercell based on the "ideal" scaling.
    For example, a cell with 4 atoms and a target size of 110 atoms might have an ideal scaling
    of 3x3x3. The search space for a lower and upper limit of -2/+2 would be 1-5. Since the
    calculations are based on the cartesian product of 3x3 matrices, large search ranges are
    very expensive.
    """
    from ase.build import get_deviation_from_optimal_cell_shape, find_optimal_cell_shape

    # range of supercell sizes in number of unitcells
    supercell_sizes = range(min_atoms // len(structure),
                            max_atoms // len(structure) + 1)

    optimal_supercell_shapes = []  # numpy arrays of optimal shapes
    optimal_supercell_scores = []  # will correspond to supercell size

    # find the target shapes
    for sc_size in supercell_sizes:
        optimal_shape = find_optimal_cell_shape(structure.lattice.matrix,
                                                sc_size,
                                                target_shape,
                                                upper_limit=upper_search_limit,
                                                lower_limit=lower_search_limit,
                                                verbose=True)
        optimal_supercell_shapes.append(optimal_shape)
        optimal_supercell_scores.append(
            get_deviation_from_optimal_cell_shape(optimal_shape, target_shape))

    if verbose:
        for i in range(len(supercell_sizes)):
            print('{} {:0.4f} {}'.format(supercell_sizes[i],
                                         optimal_supercell_scores[i],
                                         optimal_supercell_shapes[i].tolist()))

    # find the most optimal cell shape along the range of sizes
    optimal_sc_shape = optimal_supercell_shapes[np.argmin(
        optimal_supercell_scores)]

    return optimal_sc_shape