def __init__(self,
                 group_uuid,
                 structure,
                 sc_size,
                 process_label,
                 energy_threshold=0.01,
                 symprec=1e-3,
                 if_with_energy=True):
        """
        Parameters:
        -----------
        uuid : int 
               A uuid of a given group to generate the cluster
        structure : Structure 
                    A structure object to generate the symmetry operations
        sc_size : int 
                  A list of supercell size to generate symmetry operations
        process_label : str
                        workchain name to query calculation from
        energy_threshold : float 
                     Tolerance of energy difference threshold. Default = 0.01
        symprec : float 
                  Tolerance in atomic distance to test if atoms are symmetrically similar. 
                  Default = 0.1 (if for positions obtain from electronic structure)        
        if_with_energy : bool
                      If False, to not query energy . Default=True                      
        """

        self.group_uuid = group_uuid
        self.structure = structure
        self.process_label = process_label
        self.energy_threshold = energy_threshold
        self.symprec = symprec
        self.if_with_energy = if_with_energy
        #self.frac_coords = frac_coords

        if sc_size is None or sc_size == "":
            self.sc_size = "1 1 1"
        else:
            self.sc_size = sc_size

        self.structure_sc = structure.copy()
        self.structure_sc.make_supercell(
            [int(x) for x in self.sc_size.split()])

        # Query nodes, energy, positions
        QG = QueryCalculationsFromGroup(self.group_uuid, self.process_label)

        self.uuid = QG.get_relaxed_nodes()
        self.positions = QG.get_positions()
        if self.if_with_energy:
            self.energies = QG.get_energies()
        else:
            self.energies = np.zeros([len(self.uuid)])
        """#To find the space group symmetry operations of the structure 
        """
        SA = SpacegroupAnalyzer(self.structure_sc)
        self.SG = SpacegroupOperations(
            SA.get_space_group_number(), SA.get_space_group_symbol(),
            SA.get_symmetry_operations(cartesian=False))
Example #2
0
    def symm_reduce(self, coords_set, threshold=1e-6):
        """
        Reduces the set of adsorbate sites by finding removing
        symmetrically equivalent duplicates

        Args:
            coords_set: coordinate set in cartesian coordinates
            threshold: tolerance for distance equivalence, used
                as input to in_coord_list_pbc for dupl. checking
        """
        surf_sg = SpacegroupAnalyzer(self.slab, 0.1)
        symm_ops = surf_sg.get_symmetry_operations()
        unique_coords = []
        # Convert to fractional
        coords_set = [
            self.slab.lattice.get_fractional_coords(coords)
            for coords in coords_set
        ]
        for coords in coords_set:
            incoord = False
            for op in symm_ops:
                if in_coord_list_pbc(unique_coords,
                                     op.operate(coords),
                                     atol=threshold):
                    incoord = True
                    break
            if not incoord:
                unique_coords += [coords]
        # convert back to cartesian
        return [
            self.slab.lattice.get_cartesian_coords(coords)
            for coords in unique_coords
        ]
Example #3
0
def print_info(struc):
    """
	Read a Structure object and print structure information.

	Args: 
	    struc : pymatgen Structure object.

	Returns:
		None.
	"""

    print(struc)

    print('\n')

    sga = SpacegroupAnalyzer(struc)

    print('The space group: {} {}\n'.format(sga.get_space_group_number(),
                                            sga.get_space_group_symbol()))

    equi_sites = sga.get_symmetrized_structure().equivalent_sites
    for sites in equi_sites:
        print('the symmtry equivalent sites are \n{}\n'.format(sites))

    sym_operations = sga.get_symmetry_operations()
    print('there are {} symmetry operations: \n {} \n'.format(
        len(sym_operations), sym_operations))
Example #4
0
    def set_miller_family(self):
        """
        get all miller indices for the given maximum index
        get the list of indices that correspond to the given family
        of indices
        """
        recp_structure = Structure(self.recp_lattice, ["H"], [[0, 0, 0]])
        analyzer = SpacegroupAnalyzer(recp_structure, symprec=0.001)
        symm_ops = analyzer.get_symmetry_operations()

        max_index = max(max(m) for m in self.hkl_family)
        r = list(range(-max_index, max_index + 1))
        r.reverse()
        miller_indices = []
        self.all_equiv_millers = []
        self.all_surface_energies = []
        for miller in itertools.product(r, r, r):
            if any([i != 0 for i in miller]):
                d = abs(reduce(gcd, miller))
                miller_index = tuple([int(i / d) for i in miller])
                for op in symm_ops:
                    for i, u_miller in enumerate(self.hkl_family):
                        if in_coord_list(u_miller, op.operate(miller_index)):
                            self.all_equiv_millers.append(miller_index)
                            self.all_surface_energies.append(
                                self.surface_energies[i])
Example #5
0
def get_reciprocal_point_group_operations(
    structure: Structure, symprec: float = _SYMPREC, time_reversal_symmetry: bool = True
):
    from pymatgen.symmetry.analyzer import SpacegroupAnalyzer

    frac_real_to_frac_recip = np.dot(
        np.linalg.inv(structure.lattice.reciprocal_lattice.matrix.T),
        structure.lattice.matrix.T,
    )
    frac_recip_to_frac_real = np.linalg.inv(frac_real_to_frac_recip)

    sga = SpacegroupAnalyzer(structure, symprec=symprec)
    isomorphic_ops = [op.rotation_matrix for op in sga.get_symmetry_operations()]

    parity = -np.eye(3)

    if time_reversal_symmetry:
        reciprocal_ops = [-np.eye(3)]
    else:
        reciprocal_ops = [np.eye(3)]

    for op in isomorphic_ops:

        # convert to reciprocal primitive basis
        op = np.around(
            np.dot(frac_real_to_frac_recip, np.dot(op, frac_recip_to_frac_real)),
            decimals=2,
        )

        reciprocal_ops.append(op)
        if time_reversal_symmetry:
            reciprocal_ops.append(np.dot(parity, op))

    return np.unique(reciprocal_ops, axis=0)
def is_structure_invertible(structure):
    '''
    This function figures out whether or not an `pymatgen.Structure` object has
    symmetricity.  In this function, the affine matrix is a rotation matrix
    that is multiplied with the XYZ positions of the crystal. If the z,z
    component of that is negative, it means symmetry operation exist, it could
    be a mirror operation, or one that involves multiple rotations/etc.
    Regardless, it means that the top becomes the bottom and vice-versa, and
    the structure is the symmetric.  i.e. structure_XYZ = structure_XYZ*M.
    Arg:
        structure   A `pymatgen.Structure` object.
    Returns
        A boolean indicating whether or not your `ase.Atoms` object is
        symmetric in z-direction (i.e. symmetric with respect to x-y plane).
    '''
    # If any of the operations involve a transformation in the z-direction,
    # then the structure is invertible.
    sga = SpacegroupAnalyzer(structure, symprec=0.1)
    for operation in sga.get_symmetry_operations():
        xform_matrix = operation.affine_matrix
        z_xform = xform_matrix[2, 2]
        if z_xform == -1:
            return True

    return False
Example #7
0
def create_saturated_interstitial_structure(interstitial_def, dist_tol=0.1):
    """
    this takes a Interstitial defect object and generates the
    sublattice for it based on the structure's space group.
    Useful for understanding multiplicity of an interstitial
    defect in thermodynamic analysis.

    NOTE: if large relaxation happens to interstitial or
        defect involves a complex then there may be additional
        degrees of freedom that need to be considered for
        the multiplicity.

    Args:
        dist_tol: changing distance tolerance of saturated structure,
                allowing for possibly overlapping sites
                but ensuring space group is maintained

    Returns:
        Structure object decorated with interstitial site equivalents
    """
    sga = SpacegroupAnalyzer(interstitial_def.bulk_structure.copy())
    sg_ops = sga.get_symmetry_operations(cartesian=True)

    # copy bulk structure to make saturated interstitial structure out of
    # artificially lower distance_tolerance to allow for distinct interstitials
    # with lower symmetry to be replicated - This is OK because one would never
    # actually use this structure for a practical calcualtion...
    saturated_defect_struct = interstitial_def.bulk_structure.copy()
    saturated_defect_struct.DISTANCE_TOLERANCE = dist_tol

    for sgo in sg_ops:
        new_interstit_coords = sgo.operate(interstitial_def.site.coords[:])
        poss_new_site = PeriodicSite(
            interstitial_def.site.specie,
            new_interstit_coords,
            saturated_defect_struct.lattice,
            to_unit_cell=True,
            coords_are_cartesian=True,
        )
        try:
            # will raise value error if site already exists in structure
            saturated_defect_struct.append(
                poss_new_site.specie,
                poss_new_site.coords,
                coords_are_cartesian=True,
                validate_proximity=True,
            )
        except ValueError:
            pass

    # do final space group analysis to make sure symmetry not lowered by saturating defect structure
    saturated_sga = SpacegroupAnalyzer(saturated_defect_struct)
    if saturated_sga.get_space_group_number() != sga.get_space_group_number():
        raise ValueError(
            "Warning! Interstitial sublattice generation "
            "has changed space group symmetry. Recommend "
            "reducing dist_tol and trying again..."
        )

    return saturated_defect_struct
Example #8
0
def unique_symmetry_operations_as_vectors_from_structure( structure, subset = None ):
    """
    Uses pymatgen symmetry analysis to find the minimum complete set of symmetry operations for the space group of a structure.

    Args:
        structure (pymatgen Structure): structure to be analysed.
        subset    (Optional [list]):    list of atom indices to be used for generating the symmetry operations

    Returns:
        a list of lists, containing the symmetry operations as vector mappings.
    """
    symmetry_analyzer = SpacegroupAnalyzer( structure )

    print( "The spacegroup for structure is {}".format(symmetry_analyzer.get_spacegroup_symbol()) )

    symmetry_operations = symmetry_analyzer.get_symmetry_operations()

    mappings = []
    if subset:
        species_subset = [ spec for i,spec in enumerate(structure.species) if i in subset]
        frac_coords_subset = [ coord for i, coord in enumerate( structure.frac_coords ) if i in subset ]
        mapping_structure = Structure( structure.lattice, species_subset, frac_coords_subset ) 
    else:
        mapping_structure = structure

    for symmop in symmetry_operations:
        new_structure = Structure( mapping_structure.lattice, mapping_structure.species, symmop.operate_multi( mapping_structure.frac_coords ) )
        new_mapping = [ x+1 for x in list( coord_list_mapping_pbc( new_structure.frac_coords, mapping_structure.frac_coords ) ) ]
        if new_mapping not in mappings:
            mappings.append( new_mapping )

    return mappings
Example #9
0
    def symm_reduce(self, coords_set, threshold=1e-6):
        """
        Reduces the set of adsorbate sites by finding removing
        symmetrically equivalent duplicates

        Args:
            coords_set: coordinate set in cartesian coordinates
            threshold: tolerance for distance equivalence, used
                as input to in_coord_list_pbc for dupl. checking
        """
        surf_sg = SpacegroupAnalyzer(self.slab, 0.1)
        symm_ops = surf_sg.get_symmetry_operations()
        unique_coords = []
        # Convert to fractional
        coords_set = [self.slab.lattice.get_fractional_coords(coords)
                      for coords in coords_set]
        for coords in coords_set:
            incoord = False
            for op in symm_ops:
                if in_coord_list_pbc(unique_coords, op.operate(coords),
                                     atol=threshold):
                    incoord = True
                    break
            if not incoord:
                unique_coords += [coords]
        # convert back to cartesian
        return [self.slab.lattice.get_cartesian_coords(coords)
                for coords in unique_coords]
Example #10
0
def symmetry_reduce(tensors, structure, tol=1e-8, **kwargs):
    """
    Function that converts a list of tensors corresponding to a structure
    and returns a dictionary consisting of unique tensor keys with symmop
    values corresponding to transformations that will result in derivative
    tensors from the original list

    Args:
        tensors (list of tensors): list of Tensor objects to test for
            symmetrically-equivalent duplicates
        structure (Structure): structure from which to get symmetry
        tol (float): tolerance for tensor equivalence
        kwargs: keyword arguments for the SpacegroupAnalyzer

    returns:
        dictionary consisting of unique tensors with symmetry operations
        corresponding to those which will reconstruct the remaining
        tensors as values
    """
    sga = SpacegroupAnalyzer(structure, **kwargs)
    symmops = sga.get_symmetry_operations(cartesian=True)
    unique_tdict = {}
    for tensor in tensors:
        is_unique = True
        for unique_tensor, symmop in itertools.product(unique_tdict, symmops):
            if (np.abs(unique_tensor.transform(symmop) - tensor) < tol).all():
                unique_tdict[unique_tensor].append(symmop)
                is_unique = False
                break
        if is_unique:
            unique_tdict[tensor] = []
    return unique_tdict
Example #11
0
def symmetry_reduce(tensors, structure, tol=1e-8, **kwargs):
    """
    Function that converts a list of tensors corresponding to a structure
    and returns a dictionary consisting of unique tensor keys with symmop
    values corresponding to transformations that will result in derivative
    tensors from the original list

    Args:
        tensors (list of tensors): list of Tensor objects to test for
            symmetrically-equivalent duplicates
        structure (Structure): structure from which to get symmetry
        tol (float): tolerance for tensor equivalence
        kwargs: keyword arguments for the SpacegroupAnalyzer

    returns:
        dictionary consisting of unique tensors with symmetry operations
        corresponding to those which will reconstruct the remaining
        tensors as values
    """
    sga = SpacegroupAnalyzer(structure, **kwargs)
    symmops = sga.get_symmetry_operations(cartesian=True)
    unique_mapping = TensorMapping([tensors[0]], [[]], tol=tol)
    for tensor in tensors[1:]:
        is_unique = True
        for unique_tensor, symmop in itertools.product(unique_mapping,
                                                       symmops):
            if np.allclose(unique_tensor.transform(symmop), tensor, atol=tol):
                unique_mapping[unique_tensor].append(symmop)
                is_unique = False
                break
        if is_unique:
            unique_mapping[tensor] = []
    return unique_mapping
Example #12
0
    def set_miller_family(self):
        """
        get all miller indices for the given maximum index
        get the list of indices that correspond to the given family
        of indices
        """
        recp_structure = Structure(self.recp_lattice, ["H"], [[0, 0, 0]])
        analyzer = SpacegroupAnalyzer(recp_structure, symprec=0.001)
        symm_ops = analyzer.get_symmetry_operations()

        max_index = max(max(m) for m in self.hkl_family)
        r = list(range(-max_index, max_index + 1))
        r.reverse()
        miller_indices = []
        self.all_equiv_millers = []
        self.all_surface_energies = []
        for miller in itertools.product(r, r, r):
            if any([i != 0 for i in miller]):
                d = abs(reduce(gcd, miller))
                miller_index = tuple([int(i / d) for i in miller])
                for op in symm_ops:
                    for i, u_miller in enumerate(self.hkl_family):
                        if in_coord_list(u_miller, op.operate(miller_index)):
                            self.all_equiv_millers.append(miller_index)
                            self.all_surface_energies.append(self.surface_energies[i])
Example #13
0
    def __init__(self,
                 rlxd_str,
                 nd=0.01,
                 ns=0.08,
                 num_norm=4,
                 num_shear=4,
                 symmetry=False):
        """
        constructs the deformed geometries of a structure.  Generates
        m + n deformed structures according to the supplied parameters.

        Args:
            rlxd_str (structure): structure to undergo deformation, if
                fitting elastic tensor is desired, should be a geometry
                optimized structure
            nd (float): maximum perturbation applied to normal deformation
            ns (float): maximum perturbation applied to shear deformation
            m (int): number of deformation structures to generate for
                normal deformation, must be even
            n (int): number of deformation structures to generate for
                shear deformation, must be even
        """

        if num_norm % 2 != 0:
            raise ValueError("Number of normal deformations (num_norm)"
                             " must be even.")
        if num_shear % 2 != 0:
            raise ValueError("Number of shear deformations (num_shear)"
                             " must be even.")

        norm_deformations = np.linspace(-nd, nd, num=num_norm + 1)
        norm_deformations = norm_deformations[norm_deformations.nonzero()]
        shear_deformations = np.linspace(-ns, ns, num=num_shear + 1)
        shear_deformations = shear_deformations[shear_deformations.nonzero()]

        self.undeformed_structure = rlxd_str
        self.deformations = []
        self.def_structs = []

        # Generate deformations
        for ind in [(0, 0), (1, 1), (2, 2)]:
            for amount in norm_deformations:
                defo = Deformation.from_index_amount(ind, amount)
                self.deformations.append(defo)

        for ind in [(0, 1), (0, 2), (1, 2)]:
            for amount in shear_deformations:
                defo = Deformation.from_index_amount(ind, amount)
                self.deformations.append(defo)

        # Perform symmetry reduction if specified
        if symmetry:
            sga = SpacegroupAnalyzer(self.undeformed_structure, symprec=0.1)
            symm_ops = sga.get_symmetry_operations(cartesian=True)
            self.deformations = symm_reduce(symm_ops, self.deformations)

        self.def_structs = [
            defo.apply_to_structure(rlxd_str) for defo in self.deformations
        ]
Example #14
0
def create_saturated_interstitial_structure( interstitial_def, dist_tol=0.1):
    """
    this takes a Interstitial defect object and generates the
    sublattice for it based on the structure's space group.
    Useful for understanding multiplicity of an interstitial
    defect in thermodynamic analysis.

    NOTE: if large relaxation happens to interstitial or
        defect involves a complex then there may be additional
        degrees of freedom that need to be considered for
        the multiplicity.

    Args:
        dist_tol: changing distance tolerance of saturated structure,
                allowing for possibly overlapping sites
                but ensuring space group is maintained

    Returns:
        Structure object decorated with interstitial site equivalents
    """
    sga = SpacegroupAnalyzer( interstitial_def.bulk_structure.copy())
    sg_ops = sga.get_symmetry_operations( cartesian=True)

    # copy bulk structure to make saturated interstitial structure out of
    # artificially lower distance_tolerance to allow for distinct interstitials
    # with lower symmetry to be replicated - This is OK because one would never
    # actually use this structure for a practical calcualtion...
    saturated_defect_struct = interstitial_def.bulk_structure.copy()
    saturated_defect_struct.DISTANCE_TOLERANCE = dist_tol

    for sgo in sg_ops:
        new_interstit_coords = sgo.operate( interstitial_def.site.coords[:])
        poss_new_site = PeriodicSite(
                interstitial_def.site.specie,
                new_interstit_coords,
                saturated_defect_struct.lattice,
                to_unit_cell=True,
                coords_are_cartesian=True)
        try:
            #will raise value error if site already exists in structure
            saturated_defect_struct.append(
                        poss_new_site.specie, poss_new_site.coords,
                        coords_are_cartesian=True, validate_proximity=True)
        except ValueError:
            pass

    # do final space group analysis to make sure symmetry not lowered by saturating defect structure
    saturated_sga = SpacegroupAnalyzer( saturated_defect_struct)
    if saturated_sga.get_space_group_number() != sga.get_space_group_number():
        raise ValueError("Warning! Interstitial sublattice generation "
                         "has changed space group symmetry. Recommend "
                         "reducing dist_tol and trying again...")

    return saturated_defect_struct
Example #15
0
    def __init__(self, rlxd_str, nd=0.01, ns=0.08,
                 num_norm=4, num_shear=4, symmetry=False):
        """
        constructs the deformed geometries of a structure.  Generates
        m + n deformed structures according to the supplied parameters.

        Args:
            rlxd_str (structure): structure to undergo deformation, if
                fitting elastic tensor is desired, should be a geometry
                optimized structure
            nd (float): maximum perturbation applied to normal deformation
            ns (float): maximum perturbation applied to shear deformation
            m (int): number of deformation structures to generate for
                normal deformation, must be even
            n (int): number of deformation structures to generate for
                shear deformation, must be even
        """

        if num_norm % 2 != 0:
            raise ValueError("Number of normal deformations (num_norm)"
                             " must be even.")
        if num_shear % 2 != 0:
            raise ValueError("Number of shear deformations (num_shear)"
                             " must be even.")

        norm_deformations = np.linspace(-nd, nd, num=num_norm + 1)
        norm_deformations = norm_deformations[norm_deformations.nonzero()]
        shear_deformations = np.linspace(-ns, ns, num=num_shear + 1)
        shear_deformations = shear_deformations[shear_deformations.nonzero()]

        self.undeformed_structure = rlxd_str
        self.deformations = []
        self.def_structs = []

        # Generate deformations
        for ind in [(0, 0), (1, 1), (2, 2)]:
            for amount in norm_deformations:
                defo = Deformation.from_index_amount(ind, amount)
                self.deformations.append(defo)

        for ind in [(0, 1), (0, 2), (1, 2)]:
            for amount in shear_deformations:
                defo = Deformation.from_index_amount(ind, amount)
                self.deformations.append(defo)

        # Perform symmetry reduction if specified
        if symmetry:
            sga = SpacegroupAnalyzer(self.undeformed_structure, tol=0.1)
            symm_ops = sga.get_symmetry_operations(cartesian=True)
            self.deformations = symm_reduce(symm_ops, self.deformations)

        self.def_structs = [defo.apply_to_structure(rlxd_str)
                            for defo in self.deformations]
Example #16
0
def combo_symm_reduce(slab, coord_combos):
    surf_sg = SpacegroupAnalyzer(slab, 0.1)
    symm_ops = surf_sg.get_symmetry_operations()
    #get symmetry operations for the spacegroup of our slab
    test = []
    for coord in list(coord_combos[0].values())[0]:
        test.append(np.array([1,1,1]))
    unique_combos = [{'test_case':test}]
    #create a generic first combination of coordinates based on the number of 
    #coordinates in the combination submitted. necessary to initialize the search, 
    #but this set should never be matched and is removed at the end of the cycle
    u_combos_lst = []
    duplicate_lst = []
    for combo in coord_combos:
        coords = list(combo.values())[0]
        coords = [slab.lattice.get_fractional_coords(coord) for coord in coords]
        #converts coordinates to fractional form
        for u_combo in unique_combos:
            match = False
            u_coords = list(u_combo.values())[0]
            u_coords = [slab.lattice.get_fractional_coords(u_coord) for u_coord in u_coords]
            #converts unique coordinates in memory to fractional form
            for op in symm_ops:
                coord_match = []
                #iterates through symmetry operations
                for coord in coords:
                    incoord = False
                    if mg.util.coord.in_coord_list_pbc(u_coords, op.operate(coord), atol=1e-6):
                            #tests if the coordinate is in the combination of unique coordinates
                            incoord = True
                    coord_match.append(incoord)
                if all(coord_match):
                    #if all of the coordinates in a combination are already in the memory, 
                    #breaks the loop so that the repeated combination is not 
                    #added to the set of the unique coordinate combinations
                    #print('suggests %s is symmetrically equivalent to %s' %(list(combo.keys())[0], list(u_combo.keys()))[0])
#                    u_combos_lst.append(list(u_combo.keys())[0])
#                    duplicate_lst.append(list(combo.keys())[0])
                    match = True
                    break
            if match:
                break
        if not match:
            unique_combos.append(combo)
    unique_combos.pop(0)
#    combo_pairs = {'combo_pairs1':u_combos_lst, 'combo_pairs2':duplicate_lst}
#    pair_df = pd.DataFrame.from_dict(combo_pairs)
#    pair_df.to_csv(path_or_buf=os.getcwd()+'\\75ML_eq_pairs.csv')
    
                
    return unique_combos #, pair_df
Example #17
0
    def fit_to_structure(self, structure, symprec=0.1):
        """
        Returns a tensor that is invariant with respect to symmetry
        operations corresponding to a structure

        Args:
            structure (Structure): structure from which to generate
                symmetry operations
            symprec (float): symmetry tolerance for the Spacegroup Analyzer
                used to generate the symmetry operations
        """
        sga = SpacegroupAnalyzer(structure, symprec)
        symm_ops = sga.get_symmetry_operations(cartesian=True)
        return sum([self.transform(symm_op) for symm_op in symm_ops]) / len(symm_ops)
Example #18
0
    def fit_to_structure(self, structure, symprec=0.1):
        """
        Returns a tensor that is invariant with respect to symmetry
        operations corresponding to a structure

        Args: 
            structure (Structure): structure from which to generate 
                symmetry operations
            symprec (float): symmetry tolerance for the Spacegroup Analyzer
                used to generate the symmetry operations
        """
        sga = SpacegroupAnalyzer(structure, symprec)
        symm_ops = sga.get_symmetry_operations(cartesian=True)
        return sum([self.transform(symm_op) for symm_op in symm_ops]) / len(symm_ops)
Example #19
0
def get_symmops(structure, symprec=defaults["symprec"]):
    """Returns fractional ops as the cartesian ones from pmg have issues."""
    from pymatgen.symmetry.analyzer import SpacegroupAnalyzer

    sga = SpacegroupAnalyzer(structure, symprec=symprec)

    # fractional rotation matrices from spglib need to be transposed so that the
    # operation is R.M
    return [
        SymmOp.from_rotation_and_translation(
            rotation_matrix=o.rotation_matrix.T,
            translation_vec=o.translation_vector)
        for o in sga.get_symmetry_operations(cartesian=False)
    ]
Example #20
0
def unique_symmetry_operations_as_vectors_from_structure( structure, verbose=False, subset=None, atol=1e-5 ):
    """
    Uses `pymatgen`_ symmetry analysis to find the minimum complete set of symmetry operations for the space group of a structure.

    Args:
        structure (pymatgen ``Structure``): structure to be analysed.
        subset    (Optional [list]):        list of atom indices to be used for generating the symmetry operations.
        atol      (Optional [float]):       tolerance factor for the ``pymatgen`` `coordinate mapping`_ under each symmetry operation.

    Returns:
        (list[list]): a list of lists, containing the symmetry operations as vector mappings.

    .. _pymatgen:
        http://pymatgen.org
    .. _coordinate mapping:
        http://pymatgen.org/pymatgen.util.coord_utils.html#pymatgen.util.coord_utils.coord_list_mapping_pbc

    """
    if isinstance( structure, Structure ):
        instantiate_structure = partial( Structure, lattice=structure.lattice, coords_are_cartesian=True )
        coord_mapping = structure_cartesian_coordinates_mapping
        mapping_list = structure_mapping_list
        symmetry_analyzer = SpacegroupAnalyzer( structure )
        if verbose:
            print( "The space group for this structure is {}".format( symmetry_analyzer.get_space_group_symbol()) )
    elif isinstance( structure, Molecule ):
        instantiate_structure = Molecule
        coord_mapping = molecule_cartesian_coordinates_mapping
        mapping_list = molecule_mapping_list
        symmetry_analyzer = PointGroupAnalyzer( structure, tolerance=atol )
        if verbose:
            print( "The point group for this structure is {}".format( symmetry_analyzer.get_pointgroup()) )
    else:
        raise ValueError( 'structure argument should be a Structure or Molecule object' )
    symmetry_operations = symmetry_analyzer.get_symmetry_operations()
    mappings = []
    if subset:
        species_subset = [ spec for i,spec in enumerate( structure.species ) if i in subset ]
        cart_coords_subset = [ coord for i, coord in enumerate( structure.cart_coords ) if i in subset ]
        mapping_structure = instantiate_structure( species=species_subset, coords=cart_coords_subset )
    else:
        mapping_structure = structure
    for symmop in symmetry_operations:
        cart_coords = coord_mapping( mapping_structure, symmop )
        new_structure = instantiate_structure( species=mapping_structure.species, coords=cart_coords )
        new_mapping = [ x+1 for x in list( mapping_list( new_structure, mapping_structure, atol ) ) ]
        if new_mapping not in mappings:
            mappings.append( new_mapping )
    return mappings
Example #21
0
 def run_task(self, fw_spec):
     self.user_incar_settings.update({"NPAR": 2})
     # Get kpoint density per vol
     vol = Poscar.from_file("POSCAR").structure.volume
     kppra_vol = self.kpoints_density / vol
     new_set = MPStaticSet.from_prev_calc(
         os.getcwd(),
         user_incar_settings=self.user_incar_settings, 
         reciprocal_density=kppra_vol)
     new_set.write_input('.')
     structure = new_set.structure
     sga = SpacegroupAnalyzer(structure, 0.1)
     return FWAction(stored_data={
         'refined_structure': sga.get_refined_structure().as_dict(),
         'conventional_standard_structure': sga.get_conventional_standard_structure().as_dict(),
         'symmetry_dataset': sga.get_symmetry_dataset(),
         'symmetry_operations': [x.as_dict() for x in sga.get_symmetry_operations()]})
Example #22
0
    def get_syms(self):
        """ Gets symmetries with Pymatgen"""
        from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
        # Gets symmetries with pymatgen:
        symprec = 0.1  # symmetry tolerance for the Spacegroup Analyzer
        #used to generate the symmetry operations
        sga = SpacegroupAnalyzer(self.structure, symprec)
        # ([SymmOp]): List of symmetry operations.
        SymmOp = sga.get_symmetry_operations()
        nsym = len(SymmOp)

        # Symmetries directory:
        # dirname=path.dirname(path.abspath(__file__))
        dirname = path.realpath(curdir)
        newdir = "symmetries"
        SYMdir = path.join(dirname, newdir)
        if not path.exists(SYMdir):
            mkdir(SYMdir)

        # symmetries/sym.d file:
        #self.symd_fname=SYMdir+"/sym.d"
        f = open(self.symd_fname, "w")
        f.write("%i\n" % (nsym))
        for isym in range(0, nsym):
            symrel = np_array(SymmOp[isym].rotation_matrix)  #rotations
            # Transpose all symmetry matrices
            symrel = np_linalg.inv(symrel.transpose())
            symrel = np_array(symrel, np_int)
            #translation=SymmOp[isym].translation_vector
            f.write(" ".join(map(str, symrel[0][:])) + " ")
            f.write(" ".join(map(str, symrel[1][:])) + " ")
            f.write(" ".join(map(str, symrel[2][:])) + "\n")
        f.close()
        # Get lattice parameters
        lattice = self.structure.lattice
        rprim = lattice
        ang2bohr = 1.88972613
        acell = np_ones(3) * ang2bohr
        # Write pvectors file
        # symmetries/pvectors file:
        #        self.pvectors_fname=SYMdir+"/pvectors"
        f = open(self.pvectors_fname, "w")
        f.write(str(lattice) + "\n")
        f.write(" ".join(map(str, acell[:])) + "\n")
        f.close()
def sg_pg_compare(mpid='mp-989535'):
    stru = m.get_structure_by_material_id(mpid)
    spa = SpacegroupAnalyzer(stru)
    pg_name = spa.get_symmetry_dataset()['pointgroup']
    pg_ops = PointGroup(pg_name).symmetry_ops
    sp_ops = []
    sp_mats = []
    pg_mats = []
    for op in pg_ops:
        rotation = op.rotation_matrix
        pg_mats.append(jsanitize(rotation))
    for op in spa.get_symmetry_operations():
        rotation = op.rotation_matrix
        sp_mats.append(jsanitize(rotation))
        sp_ops.append(SymmOp.from_rotation_and_translation(
            rotation, (0, 0, 0)))
    sp_ops = set(sp_ops)
    #pg_mats.sort();sp_mats.sort()
    return pg_mats, sp_mats
Example #24
0
def is_structure_invertible(structure):
    '''
    This function figures out whether or not an `pymatgen.Structure` object is
    invertible in the z-direction (i.e., is it symmetric about the x-y axis?).

    Arg:
        structure   A `pymatgen.Structure` object.
    Returns
        A boolean indicating whether or not your `ase.Atoms` object is
        invertible.
    '''
    # If any of the operations involve a transformation in the z-direction,
    # then the structure is invertible.
    sga = SpacegroupAnalyzer(structure, symprec=0.1)
    for operation in sga.get_symmetry_operations():
        xform_matrix = operation.affine_matrix
        z_xform = xform_matrix[2, 2]
        if z_xform == -1:
            return True

    return False
Example #25
0
def get_recp_symmetry_operation(structure, symprec=0.001):
    """
    Find the symmetric operations of the reciprocal lattice,
    to be used for hkl transformations
    Args:
        structure (Structure): conventional unit cell
        symprec: default is 0.001

    """
    recp_lattice = structure.lattice.reciprocal_lattice_crystallographic
    # get symmetry operations from input conventional unit cell
    # Need to make sure recp lattice is big enough, otherwise symmetry
    # determination will fail. We set the overall volume to 1.
    recp_lattice = recp_lattice.scale(1)
    recp = Structure(recp_lattice, ["H"], [[0, 0, 0]])
    # Creates a function that uses the symmetry operations in the
    # structure to find Miller indices that might give repetitive slabs
    analyzer = SpacegroupAnalyzer(recp, symprec=symprec)
    recp_symmops = analyzer.get_symmetry_operations()

    return recp_symmops
Example #26
0
def get_symmops(structure, symprec):
    """
    Helper function to get the symmetry operations of the structure
    in the reciprocal lattice fractional coordinates.

    Args:
            structure (pymatgen.core.structure.Structure)
            symprec (number): symmetry precision for pymatgen SpacegroupAnalyzer
    """

    sga = SpacegroupAnalyzer(structure, symprec * max(structure.lattice.abc))
    symmops = sga.get_symmetry_operations(cartesian=True)
    lattice = structure.lattice.matrix
    invlattice = structure.lattice.inv_matrix
    newops = []
    for op in symmops:
        newrot = np.dot(lattice, op.rotation_matrix)
        newrot = np.dot(newrot, invlattice)
        newtrans = np.dot(op.translation_vector, invlattice)
        newops.append(SymmOp.from_rotation_and_translation(newrot, newtrans))
    return newops
Example #27
0
def get_recp_symmetry_operation(structure, symprec=0.001):
    """
    Find the symmetric operations of the reciprocal lattice,
    to be used for hkl transformations
    Args:
        structure (Structure): conventional unit cell
        symprec: default is 0.001

    """
    recp_lattice = structure.lattice.reciprocal_lattice_crystallographic
    # get symmetry operations from input conventional unit cell
    # Need to make sure recp lattice is big enough, otherwise symmetry
    # determination will fail. We set the overall volume to 1.
    recp_lattice = recp_lattice.scale(1)
    recp = Structure(recp_lattice, ["H"], [[0, 0, 0]])
    # Creates a function that uses the symmetry operations in the
    # structure to find Miller indices that might give repetitive slabs
    analyzer = SpacegroupAnalyzer(recp, symprec=symprec)
    recp_symmops = analyzer.get_symmetry_operations()

    return recp_symmops
Example #28
0
    def symmeterize(self, structure, symprec=0.1, tol=1e-3):
        """
        Averages the piezo tensor based on the symmetries of the structure

        Args:
            structure (Structure): structure to check against
            symprec (float): Tolerance for symmetry finding, default good for
                             as-made structures. Set to 0.1 for MP structures
            tol (float): tolerance for zero'ing out indicies. The average procedure produces very small numbers rather then 0. This tolerance is used to zero out those values to make the tensor less messy.
        """

        sg = SpacegroupAnalyzer(structure, symprec)
        new_pt = PiezoTensor(self)

        for symm in sg.get_symmetry_operations(cartesian=True):
            new_pt = (new_pt + new_pt.transform(symm)) / 2

        low_values_indices = np.abs(new_pt) < tol
        new_pt [low_values_indices] = 0

        return new_pt
Example #29
0
    def symmeterize(self, structure, symprec=0.1, tol=1e-3):
        """
        Averages the piezo tensor based on the symmetries of the structure

        Args:
            structure (Structure): structure to check against
            symprec (float): Tolerance for symmetry finding, default good for
                             as-made structures. Set to 0.1 for MP structures
            tol (float): tolerance for zero'ing out indicies. The average procedure produces very small numbers rather then 0. This tolerance is used to zero out those values to make the tensor less messy.
        """

        sg = SpacegroupAnalyzer(structure, symprec)
        new_pt = PiezoTensor(self)

        for symm in sg.get_symmetry_operations(cartesian=True):
            new_pt = (new_pt + new_pt.transform(symm)) / 2

        low_values_indices = np.abs(new_pt) < tol
        new_pt[low_values_indices] = 0

        return new_pt
Example #30
0
def get_symmetrically_distinct_miller_indices(structure, max_index):
    """
    Returns all symmetrically distinct indices below a certain max-index for
    a given structure. Analysis is based on the symmetry of the reciprocal
    lattice of the structure.

    Args:
        structure (Structure): input structure.
        max_index (int): The maximum index. For example, a max_index of 1
            means that (100), (110), and (111) are returned for the cubic
            structure. All other indices are equivalent to one of these.
    """

    recp_lattice = structure.lattice.reciprocal_lattice_crystallographic
    # Need to make sure recp lattice is big enough, otherwise symmetry
    # determination will fail. We set the overall volume to 1.
    recp_lattice = recp_lattice.scale(1)
    recp = Structure(recp_lattice, ["H"], [[0, 0, 0]])

    # Creates a function that uses the symmetry operations in the
    # structure to find Miller indices that might give repetitive slabs
    analyzer = SpacegroupAnalyzer(recp, symprec=0.001)
    symm_ops = analyzer.get_symmetry_operations()
    unique_millers = []

    def is_already_analyzed(miller_index):
        for op in symm_ops:
            if in_coord_list(unique_millers, op.operate(miller_index)):
                return True
        return False

    r = list(range(-max_index, max_index + 1))
    r.reverse()
    for miller in itertools.product(r, r, r):
        if any([i != 0 for i in miller]):
            d = abs(reduce(gcd, miller))
            miller = tuple([int(i / d) for i in miller])
            if not is_already_analyzed(miller):
                unique_millers.append(miller)
    return unique_millers
Example #31
0
def get_symmetrically_distinct_miller_indices(structure, max_index):
    """
    Returns all symmetrically distinct indices below a certain max-index for
    a given structure. Analysis is based on the symmetry of the reciprocal
    lattice of the structure.

    Args:
        structure (Structure): input structure.
        max_index (int): The maximum index. For example, a max_index of 1
            means that (100), (110), and (111) are returned for the cubic
            structure. All other indices are equivalent to one of these.
    """

    recp_lattice = structure.lattice.reciprocal_lattice_crystallographic
    # Need to make sure recp lattice is big enough, otherwise symmetry
    # determination will fail. We set the overall volume to 1.
    recp_lattice = recp_lattice.scale(1)
    recp = Structure(recp_lattice, ["H"], [[0, 0, 0]])

    # Creates a function that uses the symmetry operations in the
    # structure to find Miller indices that might give repetitive slabs
    analyzer = SpacegroupAnalyzer(recp, symprec=0.001)
    symm_ops = analyzer.get_symmetry_operations()
    unique_millers = []

    def is_already_analyzed(miller_index):
        for op in symm_ops:
            if in_coord_list(unique_millers, op.operate(miller_index)):
                return True
        return False

    r = list(range(-max_index, max_index + 1))
    r.reverse()
    for miller in itertools.product(r, r, r):
        if any([i != 0 for i in miller]):
            d = abs(reduce(gcd, miller))
            miller = tuple([int(i / d) for i in miller])
            if not is_already_analyzed(miller):
                unique_millers.append(miller)
    return unique_millers
Example #32
0
def test_is_structure_invertible():
    '''
    Currently, we test this function only on slabs, because that's what we use
    it on mainly. You can test other things if you want.
    '''
    # Get all of our test slabs and convert them to `pymatgen.Structure` ojbects
    slabs_folder = TEST_CASE_LOCATION + 'slabs/'
    for file_name in os.listdir(slabs_folder):
        atoms = ase.io.read(slabs_folder + file_name)
        structure = AseAtomsAdaptor.get_structure(atoms)

        # Check for invertibility manually
        expected_invertibility = False
        sga = SpacegroupAnalyzer(structure, symprec=0.1)
        for operation in sga.get_symmetry_operations():
            xform_matrix = operation.affine_matrix
            z_xform = xform_matrix[2, 2]
            if z_xform == -1:
                expected_invertibility = True

        invertibility = is_structure_invertible(structure)
        assert invertibility == expected_invertibility
Example #33
0
    def _get_reciprocal_point_group_nonmagnetic(struct):
        """
        Obtain the symmetry ops. in the reciprocal point group of an input structure.  

        Returns:
            recip_point_group (list): List of symmetry operations as numpy arrays in the 
            fractional reciprocal space basis. 

        """
        R = -1 * np.eye(3)
        sga = SpacegroupAnalyzer(struct)
        ops = sga.get_symmetry_operations()
        isomorphic_point_group = [op.rotation_matrix for op in ops]

        V = struct.lattice.matrix.T  # fractional real space to cartesian real space
        # fractional reciprocal space to cartesian reciprocal space
        W = struct.lattice.reciprocal_lattice.matrix.T
        # fractional real space to fractional reciprocal space
        A = np.dot(np.linalg.inv(W), V)

        Ainv = np.linalg.inv(A)
        # convert to reciprocal primitive basis
        recip_point_group = [np.around(np.dot(A, np.dot(R, Ainv)), decimals=2)]
        for op in isomorphic_point_group:
            op = np.around(np.dot(A, np.dot(op, Ainv)), decimals=2)
            new = True
            new_coset = True
            for thing in recip_point_group:
                if (thing == op).all():
                    new = False
                if (thing == np.dot(R, op)).all():
                    new_coset = False

            if new:
                recip_point_group.append(op)
            if new_coset:
                recip_point_group.append(np.dot(R, op))

        return recip_point_group
Example #34
0
def num_equivalent_clusters(structure: Structure,
                            inserted_atom_coords: Optional[list],
                            removed_atom_indices: Optional[list],
                            symprec: float = SYMMETRY_TOLERANCE,
                            angle_tolerance: float = ANGLE_TOL
                            ) -> Tuple[int, str]:
    """Calculate number of equivalent clusters in the structure.

    Args:
        structure (Structure):
            Supercell is assumed to big enough.
        inserted_atom_coords (list):
        removed_atom_indices (list):
            Needs to begin from 0.
        symprec (float):
        angle_tolerance (float):
            Angle tolerance in degree used for identifying the space group.

    Returns:
        Tuple of (num_equivalent_clusters (int), point_group (str))
    """
    inserted_atom_coords = inserted_atom_coords or []
    removed_atom_indices = removed_atom_indices or []

    sga = SpacegroupAnalyzer(structure, symprec, angle_tolerance)
    num_symmop = len(sga.get_symmetry_operations())

    structure_with_cluster = structure.copy()
    for i in inserted_atom_coords:
        structure_with_cluster.append(DummySpecie(), i)
    structure_with_cluster.remove_sites(removed_atom_indices)

    sga_with_cluster = \
        SpacegroupAnalyzer(structure_with_cluster, symprec, angle_tolerance)
    sym_dataset = sga_with_cluster.get_symmetry_dataset()
    point_group = sym_dataset["pointgroup"]

    return int(num_symmop / num_symmetry_operation(point_group)), point_group
Example #35
0
    def is_valid(self, structure, symprec=0.1, tol=1e-3):
        """
        Checks the piezo tensor against the symmetries of the structure and
        determine if the piezoelectric tensor is valid for that structure

        Args:
            structure (Structure): structure to check against
            symprec (float): Tolerance for symmetry finding, default good for
                             as-made structures. Set to 0.1 for MP structures
            tol (float): tolerance for equality
        """

        sg = SpacegroupAnalyzer(structure, symprec)

        # Check every symmetry operation in the space group
        for symm in sg.get_symmetry_operations(cartesian=True):
            # does it produce the same tensor?
            diff = self.transform(symm) - self
            if not (np.abs(diff) < tol).all():
                print(diff)
                return False

        return True
Example #36
0
    def get_recp_symmetry_operation(self, symprec: float = 0.01) -> List:
        """
        Find the symmetric operations of the reciprocal lattice,
        to be used for hkl transformations
        Args:
            symprec: default is 0.001
        """
        recp_lattice = self.reciprocal_lattice_crystallographic
        # get symmetry operations from input conventional unit cell
        # Need to make sure recp lattice is big enough, otherwise symmetry
        # determination will fail. We set the overall volume to 1.
        recp_lattice = recp_lattice.scale(1)
        # need a localized import of structure to build a
        # pseudo empty lattice for SpacegroupAnalyzer
        from pymatgen import Structure
        from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
        recp = Structure(recp_lattice, ["H"], [[0, 0, 0]])
        # Creates a function that uses the symmetry operations in the
        # structure to find Miller indices that might give repetitive slabs
        analyzer = SpacegroupAnalyzer(recp, symprec=symprec)
        recp_symmops = analyzer.get_symmetry_operations()

        return recp_symmops
Example #37
0
    def is_valid(self, structure, symprec=0.1, tol=1e-3):
        """
        Checks the piezo tensor against the symmetries of the structure and
        determine if the piezoelectric tensor is valid for that structure

        Args:
            structure (Structure): structure to check against
            symprec (float): Tolerance for symmetry finding, default good for
                             as-made structures. Set to 0.1 for MP structures
            tol (float): tolerance for equality
        """

        sg = SpacegroupAnalyzer(structure, symprec)

        # Check every symmetry operation in the space group
        for symm in sg.get_symmetry_operations(cartesian=True):
            # does it produce the same tensor?
            diff = self.transform(symm) - self
            if not (np.abs(diff) < tol).all():
                print(diff)
                return False

        return True
Example #38
0
def get_distinct_rotations(structure, symprec=0.1, atol=1e-6):
    """
    Get distinct rotations from structure spacegroup operations

    Args:
        structure (Structure): structure object to analyze and
            get corresponding rotations for
        symprec (float): symprec for SpacegroupAnalyzer
        atol (float): absolute tolerance for relative indices
    """
    sga = SpacegroupAnalyzer(structure, symprec)
    symmops = sga.get_symmetry_operations(cartesian=True)
    rotations = [s.rotation_matrix for s in symmops]
    if len(rotations) == 1:
        return rotations
    unique_rotations = [np.array(rotations[0])]
    for rotation in rotations[1:]:
        if not any([
                np.allclose(urot, rotation, atol=atol)
                for urot in unique_rotations
        ]):
            unique_rotations.append(rotation)
    return unique_rotations
Example #39
0
    def generate_doc(self, dir_name, vasprun_files, outcar_files):
        """
        Adapted from matgendb.creator.generate_doc
        """
        try:
            # basic properties, incl. calcs_reversed and run_stats
            fullpath = os.path.abspath(dir_name)
            d = {k: v for k, v in self.additional_fields.items()}
            d["schema"] = {"code": "atomate", "version": VaspDrone.__version__}
            d["dir_name"] = fullpath
            d["calcs_reversed"] = [self.process_vasprun(dir_name, taskname, filename)
                                   for taskname, filename in vasprun_files.items()]
            outcar_data = [Outcar(os.path.join(dir_name, filename)).as_dict()
                           for taskname, filename in outcar_files.items()]
            run_stats = {}
            for i, d_calc in enumerate(d["calcs_reversed"]):
                run_stats[d_calc["task"]["name"]] = outcar_data[i].pop("run_stats")
                if d_calc.get("output"):
                    d_calc["output"].update({"outcar": outcar_data[i]})
                else:
                    d_calc["output"] = {"outcar": outcar_data[i]}
            try:
                overall_run_stats = {}
                for key in ["Total CPU time used (sec)", "User time (sec)", "System time (sec)",
                            "Elapsed time (sec)"]:
                    overall_run_stats[key] = sum([v[key] for v in run_stats.values()])
                run_stats["overall"] = overall_run_stats
            except:
                logger.error("Bad run stats for {}.".format(fullpath))
            d["run_stats"] = run_stats

            # reverse the calculations data order so newest calc is first
            d["calcs_reversed"].reverse()

            # set root formula/composition keys based on initial and final calcs
            d_calc_init = d["calcs_reversed"][-1]
            d_calc_final = d["calcs_reversed"][0]
            d["chemsys"] = "-".join(sorted(d_calc_final["elements"]))
            comp = Composition(d_calc_final["composition_unit_cell"])
            d["formula_anonymous"] = comp.anonymized_formula
            d["formula_reduced_abc"] = comp.reduced_composition.alphabetical_formula
            for root_key in ["completed_at", "nsites", "composition_unit_cell",
                             "composition_reduced", "formula_pretty", "elements", "nelements"]:
                d[root_key] = d_calc_final[root_key]

            # store the input key based on initial calc
            # store any overrides to the exchange correlation functional
            xc = d_calc_init["input"]["incar"].get("GGA")
            if xc:
                xc = xc.upper()
            p = d_calc_init["input"]["potcar_type"][0].split("_")
            pot_type = p[0]
            functional = "lda" if len(pot_type) == 1 else "_".join(p[1:])
            d["input"] = {"structure": d_calc_init["input"]["structure"],
                          "is_hubbard": d_calc_init.pop("is_hubbard"),
                          "hubbards": d_calc_init.pop("hubbards"),
                          "is_lasph": d_calc_init["input"]["incar"].get("LASPH", False),
                          "potcar_spec": d_calc_init["input"].get("potcar_spec"),
                          "xc_override": xc,
                          "pseudo_potential": {"functional": functional.lower(),
                                               "pot_type": pot_type.lower(),
                                               "labels": d_calc_init["input"]["potcar"]},
                          "parameters": d_calc_init["input"]["parameters"],
                          "incar": d_calc_init["input"]["incar"]
                          }

            # store the output key based on final calc
            d["output"] = {
                "structure": d_calc_final["output"]["structure"],
                "density": d_calc_final.pop("density"),
                "energy": d_calc_final["output"]["energy"],
                "energy_per_atom": d_calc_final["output"]["energy_per_atom"],
                "forces": d_calc_final["output"]["ionic_steps"][-1].get("forces"),
                "stress": d_calc_final["output"]["ionic_steps"][-1].get("stress")}

            # patch calculated magnetic moments into final structure
            if len(d_calc_final["output"]["outcar"]["magnetization"]) != 0:
                magmoms = [m["tot"] for m in d_calc_final["output"]["outcar"]["magnetization"]]
                s = Structure.from_dict(d["output"]["structure"])
                s.add_site_property('magmom', magmoms)
                d["output"]["structure"] = s.as_dict()

            calc = d["calcs_reversed"][0]

            try:
                d["output"].update({"bandgap": calc["output"]["bandgap"],
                                    "cbm": calc["output"]["cbm"],
                                    "vbm": calc["output"]["vbm"],
                                    "is_gap_direct": calc["output"]["is_gap_direct"],
                                    "is_metal": calc["output"]["is_metal"]})
                if not calc["output"]["is_gap_direct"]:
                    d["output"]["direct_gap"] = calc["output"]["direct_gap"]
                if "transition" in calc["output"]:
                    d["output"]["transition"] = calc["output"]["transition"]

            except Exception:
                if self.bandstructure_mode is True:
                    import traceback
                    logger.error(traceback.format_exc())
                    logger.error("Error in " + os.path.abspath(dir_name) + ".\n" + traceback.format_exc())
                    raise

            sg = SpacegroupAnalyzer(Structure.from_dict(d_calc_final["output"]["structure"]), 0.1)
            if not sg.get_symmetry_dataset():
                sg = SpacegroupAnalyzer(Structure.from_dict(d_calc_final["output"]["structure"]),
                                        1e-3, 1)
            d["output"]["spacegroup"] = {
                "source": "spglib",
                "symbol": sg.get_space_group_symbol(),
                "number": sg.get_space_group_number(),
                "point_group": sg.get_point_group_symbol(),
                "crystal_system": sg.get_crystal_system(),
                "hall": sg.get_hall()}
            if d["input"]["parameters"].get("LEPSILON"):
                for k in ['epsilon_static', 'epsilon_static_wolfe', 'epsilon_ionic']:
                    d["output"][k] = d_calc_final["output"][k]
                if SymmOp.inversion() not in sg.get_symmetry_operations():
                    for k in ["piezo_ionic_tensor", "piezo_tensor"]:
                        d["output"][k] = d_calc_final["output"]["outcar"][k]

            d["state"] = "successful" if d_calc["has_vasp_completed"] else "unsuccessful"

            self.set_analysis(d)

            d["last_updated"] = datetime.datetime.today()
            return d

        except Exception:
            import traceback
            logger.error(traceback.format_exc())
            logger.error("Error in " + os.path.abspath(dir_name) + ".\n" + traceback.format_exc())
            raise
Example #40
0
    def __init__(self, struct, symprec=None):
        format_str = "{:.8f}"

        block = OrderedDict()
        loops = []
        spacegroup = ("P 1", 1)
        if symprec is not None:
            sf = SpacegroupAnalyzer(struct, symprec)
            spacegroup = (sf.get_space_group_symbol(),
                          sf.get_space_group_number())
            # Needs the refined struture when using symprec. This converts
            # primitive to conventional structures, the standard for CIF.
            struct = sf.get_refined_structure()

        latt = struct.lattice
        comp = struct.composition
        no_oxi_comp = comp.element_composition
        block["_symmetry_space_group_name_H-M"] = spacegroup[0]
        for cell_attr in ['a', 'b', 'c']:
            block["_cell_length_" + cell_attr] = format_str.format(
                getattr(latt, cell_attr))
        for cell_attr in ['alpha', 'beta', 'gamma']:
            block["_cell_angle_" + cell_attr] = format_str.format(
                getattr(latt, cell_attr))
        block["_symmetry_Int_Tables_number"] = spacegroup[1]
        block["_chemical_formula_structural"] = no_oxi_comp.reduced_formula
        block["_chemical_formula_sum"] = no_oxi_comp.formula
        block["_cell_volume"] = latt.volume.__str__()

        reduced_comp, fu = no_oxi_comp.get_reduced_composition_and_factor()
        block["_cell_formula_units_Z"] = str(int(fu))

        if symprec is None:
            block["_symmetry_equiv_pos_site_id"] = ["1"]
            block["_symmetry_equiv_pos_as_xyz"] = ["x, y, z"]
        else:
            sf = SpacegroupAnalyzer(struct, symprec)

            def round_symm_trans(i):

                for t in TRANSLATIONS.values():
                    if abs(i - t) < symprec:
                        return t
                if abs(i - round(i)) < symprec:
                    return 0
                raise ValueError("Invalid translation!")

            symmops = []
            for op in sf.get_symmetry_operations():
                v = op.translation_vector
                v = [round_symm_trans(i) for i in v]
                symmops.append(SymmOp.from_rotation_and_translation(
                    op.rotation_matrix, v))

            ops = [op.as_xyz_string() for op in symmops]
            block["_symmetry_equiv_pos_site_id"] = \
                ["%d" % i for i in range(1, len(ops) + 1)]
            block["_symmetry_equiv_pos_as_xyz"] = ops

        loops.append(["_symmetry_equiv_pos_site_id",
                      "_symmetry_equiv_pos_as_xyz"])

        contains_oxidation = True
        try:
            symbol_to_oxinum = OrderedDict([
                (el.__str__(), float(el.oxi_state))
                for el in sorted(comp.elements)])
        except AttributeError:
            symbol_to_oxinum = OrderedDict([(el.symbol, 0) for el in
                                            sorted(comp.elements)])
            contains_oxidation = False
        if contains_oxidation:
            block["_atom_type_symbol"] = symbol_to_oxinum.keys()
            block["_atom_type_oxidation_number"] = symbol_to_oxinum.values()
            loops.append(["_atom_type_symbol", "_atom_type_oxidation_number"])

        atom_site_type_symbol = []
        atom_site_symmetry_multiplicity = []
        atom_site_fract_x = []
        atom_site_fract_y = []
        atom_site_fract_z = []
        atom_site_label = []
        atom_site_occupancy = []
        count = 1
        if symprec is None:
            for site in struct:
                for sp, occu in site.species_and_occu.items():
                    atom_site_type_symbol.append(sp.__str__())
                    atom_site_symmetry_multiplicity.append("1")
                    atom_site_fract_x.append("{0:f}".format(site.a))
                    atom_site_fract_y.append("{0:f}".format(site.b))
                    atom_site_fract_z.append("{0:f}".format(site.c))
                    atom_site_label.append("{}{}".format(sp.symbol, count))
                    atom_site_occupancy.append(occu.__str__())
                    count += 1
        else:
            # The following just presents a deterministic ordering.
            unique_sites = [
                (sorted(sites, key=lambda s: tuple([abs(x) for x in
                                                   s.frac_coords]))[0],
                 len(sites))
                for sites in sf.get_symmetrized_structure().equivalent_sites
            ]
            for site, mult in sorted(
                    unique_sites,
                    key=lambda t: (t[0].species_and_occu.average_electroneg,
                                   -t[1], t[0].a, t[0].b, t[0].c)):
                for sp, occu in site.species_and_occu.items():
                    atom_site_type_symbol.append(sp.__str__())
                    atom_site_symmetry_multiplicity.append("%d" % mult)
                    atom_site_fract_x.append("{0:f}".format(site.a))
                    atom_site_fract_y.append("{0:f}".format(site.b))
                    atom_site_fract_z.append("{0:f}".format(site.c))
                    atom_site_label.append("{}{}".format(sp.symbol, count))
                    atom_site_occupancy.append(occu.__str__())
                    count += 1

        block["_atom_site_type_symbol"] = atom_site_type_symbol
        block["_atom_site_label"] = atom_site_label
        block["_atom_site_symmetry_multiplicity"] = \
            atom_site_symmetry_multiplicity
        block["_atom_site_fract_x"] = atom_site_fract_x
        block["_atom_site_fract_y"] = atom_site_fract_y
        block["_atom_site_fract_z"] = atom_site_fract_z
        block["_atom_site_occupancy"] = atom_site_occupancy
        loops.append(["_atom_site_type_symbol",
                      "_atom_site_label",
                      "_atom_site_symmetry_multiplicity",
                      "_atom_site_fract_x",
                      "_atom_site_fract_y",
                      "_atom_site_fract_z",
                      "_atom_site_occupancy"])
        d = OrderedDict()
        d[comp.reduced_formula] = CifBlock(block, loops, comp.reduced_formula)
        self._cf = CifFile(d)
Example #41
0
    def __init__(self, struct, symprec=None):
        format_str = "{:.8f}"

        block = OrderedDict()
        loops = []
        spacegroup = ("P 1", 1)
        if symprec is not None:
            sf = SpacegroupAnalyzer(struct, symprec)
            spacegroup = (sf.get_spacegroup_symbol(),
                          sf.get_spacegroup_number())
            # Needs the refined struture when using symprec. This converts
            # primitive to conventional structures, the standard for CIF.
            struct = sf.get_refined_structure()

        latt = struct.lattice
        comp = struct.composition
        no_oxi_comp = comp.element_composition
        block["_symmetry_space_group_name_H-M"] = spacegroup[0]
        for cell_attr in ['a', 'b', 'c']:
            block["_cell_length_" + cell_attr] = format_str.format(
                getattr(latt, cell_attr))
        for cell_attr in ['alpha', 'beta', 'gamma']:
            block["_cell_angle_" + cell_attr] = format_str.format(
                getattr(latt, cell_attr))
        block["_symmetry_Int_Tables_number"] = spacegroup[1]
        block["_chemical_formula_structural"] = no_oxi_comp.reduced_formula
        block["_chemical_formula_sum"] = no_oxi_comp.formula
        block["_cell_volume"] = latt.volume.__str__()

        reduced_comp, fu = no_oxi_comp.get_reduced_composition_and_factor()
        block["_cell_formula_units_Z"] = str(int(fu))

        if symprec is None:
            block["_symmetry_equiv_pos_site_id"] = ["1"]
            block["_symmetry_equiv_pos_as_xyz"] = ["x, y, z"]
        else:
            sf = SpacegroupAnalyzer(struct, symprec)

            def round_symm_trans(i):

                for t in TRANSLATIONS.values():
                    if abs(i - t) < symprec:
                        return t
                if abs(i - round(i)) < symprec:
                    return 0
                raise ValueError("Invalid translation!")

            symmops = []
            for op in sf.get_symmetry_operations():
                v = op.translation_vector
                v = [round_symm_trans(i) for i in v]
                symmops.append(
                    SymmOp.from_rotation_and_translation(
                        op.rotation_matrix, v))

            ops = [op.as_xyz_string() for op in symmops]
            block["_symmetry_equiv_pos_site_id"] = \
                ["%d" % i for i in range(1, len(ops) + 1)]
            block["_symmetry_equiv_pos_as_xyz"] = ops

        loops.append(
            ["_symmetry_equiv_pos_site_id", "_symmetry_equiv_pos_as_xyz"])

        contains_oxidation = True
        try:
            symbol_to_oxinum = OrderedDict([(el.__str__(), float(el.oxi_state))
                                            for el in sorted(comp.elements)])
        except AttributeError:
            symbol_to_oxinum = OrderedDict([(el.symbol, 0)
                                            for el in sorted(comp.elements)])
            contains_oxidation = False
        if contains_oxidation:
            block["_atom_type_symbol"] = symbol_to_oxinum.keys()
            block["_atom_type_oxidation_number"] = symbol_to_oxinum.values()
            loops.append(["_atom_type_symbol", "_atom_type_oxidation_number"])

        atom_site_type_symbol = []
        atom_site_symmetry_multiplicity = []
        atom_site_fract_x = []
        atom_site_fract_y = []
        atom_site_fract_z = []
        atom_site_label = []
        atom_site_occupancy = []
        count = 1
        if symprec is None:
            for site in struct:
                for sp, occu in site.species_and_occu.items():
                    atom_site_type_symbol.append(sp.__str__())
                    atom_site_symmetry_multiplicity.append("1")
                    atom_site_fract_x.append("{0:f}".format(site.a))
                    atom_site_fract_y.append("{0:f}".format(site.b))
                    atom_site_fract_z.append("{0:f}".format(site.c))
                    atom_site_label.append("{}{}".format(sp.symbol, count))
                    atom_site_occupancy.append(occu.__str__())
                    count += 1
        else:
            # The following just presents a deterministic ordering.
            unique_sites = [
                (sorted(sites,
                        key=lambda s: tuple([abs(x)
                                             for x in s.frac_coords]))[0],
                 len(sites))
                for sites in sf.get_symmetrized_structure().equivalent_sites
            ]
            for site, mult in sorted(unique_sites,
                                     key=lambda t:
                                     (t[0].species_and_occu.average_electroneg,
                                      -t[1], t[0].a, t[0].b, t[0].c)):
                for sp, occu in site.species_and_occu.items():
                    atom_site_type_symbol.append(sp.__str__())
                    atom_site_symmetry_multiplicity.append("%d" % mult)
                    atom_site_fract_x.append("{0:f}".format(site.a))
                    atom_site_fract_y.append("{0:f}".format(site.b))
                    atom_site_fract_z.append("{0:f}".format(site.c))
                    atom_site_label.append("{}{}".format(sp.symbol, count))
                    atom_site_occupancy.append(occu.__str__())
                    count += 1

        block["_atom_site_type_symbol"] = atom_site_type_symbol
        block["_atom_site_label"] = atom_site_label
        block["_atom_site_symmetry_multiplicity"] = \
            atom_site_symmetry_multiplicity
        block["_atom_site_fract_x"] = atom_site_fract_x
        block["_atom_site_fract_y"] = atom_site_fract_y
        block["_atom_site_fract_z"] = atom_site_fract_z
        block["_atom_site_occupancy"] = atom_site_occupancy
        loops.append([
            "_atom_site_type_symbol", "_atom_site_label",
            "_atom_site_symmetry_multiplicity", "_atom_site_fract_x",
            "_atom_site_fract_y", "_atom_site_fract_z", "_atom_site_occupancy"
        ])
        d = OrderedDict()
        d[comp.reduced_formula] = CifBlock(block, loops, comp.reduced_formula)
        self._cf = CifFile(d)
Example #42
0
def process_elastic_calcs(opt_doc, defo_docs, add_derived=True, tol=0.002):
    """
    Generates the list of calcs from deformation docs, along with 'derived
    stresses', i. e. stresses derived from symmop transformations of existing
    calcs from transformed strains resulting in an independent strain
    not in the input list

    Args:
        opt_doc (dict): document for optimization task
        defo_docs ([dict]) list of documents for deformation tasks
        add_derived (bool): flag for whether or not to add derived
            stress-strain pairs based on symmetry
        tol (float): tolerance for assigning equivalent stresses/strains

    Returns ([dict], [dict]):
        Two lists of summary documents corresponding to strains
        and stresses, one explicit and one derived
    """
    structure = Structure.from_dict(opt_doc['output']['structure'])
    input_structure = Structure.from_dict(opt_doc['input']['structure'])

    # Process explicit calcs, store in dict keyed by strain
    explicit_calcs = TensorMapping()
    for doc in defo_docs:
        calc = {
            "type": "explicit",
            "input": doc["input"],
            "output": doc["output"],
            "task_id": doc["task_id"],
            "completed_at": doc["completed_at"]
        }
        deformed_structure = Structure.from_dict(doc['output']['structure'])
        defo = Deformation(calculate_deformation(structure,
                                                 deformed_structure))
        # Warning if deformation is not equivalent to stored deformation
        stored_defo = doc['transmuter']['transformation_params'][0]\
            ['deformation']
        if not np.allclose(defo, stored_defo, atol=1e-5):
            wmsg = "Inequivalent stored and calc. deformations."
            logger.debug(wmsg)
            calc["warnings"] = wmsg
        cauchy_stress = -0.1 * Stress(doc['output']['stress'])
        pk_stress = cauchy_stress.piola_kirchoff_2(defo)
        strain = defo.green_lagrange_strain
        calc.update({
            "deformation": defo,
            "cauchy_stress": cauchy_stress,
            "strain": strain,
            "pk_stress": pk_stress
        })
        if strain in explicit_calcs:
            existing_value = explicit_calcs[strain]
            if doc['completed_at'] > existing_value['completed_at']:
                explicit_calcs[strain] = calc
        else:
            explicit_calcs[strain] = calc

    if not add_derived:
        return explicit_calcs.values(), None

    # Determine all of the implicit calculations to include
    sga = SpacegroupAnalyzer(structure, symprec=0.1)
    symmops = sga.get_symmetry_operations(cartesian=True)
    derived_calcs_by_strain = TensorMapping(tol=0.002)
    for strain, calc in explicit_calcs.items():
        # Generate all transformed strains
        task_id = calc['task_id']
        tstrains = [(symmop, strain.transform(symmop)) for symmop in symmops]
        # Filter strains by those which are independent and new
        # For second order
        if len(explicit_calcs) < 30:
            tstrains = [(symmop, tstrain) for symmop, tstrain in tstrains
                        if tstrain.get_deformation_matrix().is_independent(tol)
                        and not tstrain in explicit_calcs]
        # For third order
        else:
            strain_states = get_default_strain_states(3)
            # Default stencil in atomate, this maybe shouldn't be hard-coded
            stencil = np.linspace(-0.075, 0.075, 7)
            valid_strains = [
                Strain.from_voigt(s * np.array(strain_state))
                for s, strain_state in product(stencil, strain_states)
            ]
            valid_strains = [v for v in valid_strains if not np.allclose(v, 0)]
            valid_strains = TensorMapping(valid_strains,
                                          [True] * len(valid_strains))
            tstrains = [
                (symmop, tstrain) for symmop, tstrain in tstrains
                if tstrain in valid_strains and not tstrain in explicit_calcs
            ]
        # Add surviving tensors to derived_strains dict
        for symmop, tstrain in tstrains:
            # curr_set = derived_calcs_by_strain[tstrain]
            if tstrain in derived_calcs_by_strain:
                curr_set = derived_calcs_by_strain[tstrain]
                curr_task_ids = [c[1] for c in curr_set]
                if task_id not in curr_task_ids:
                    curr_set.append((symmop, calc['task_id']))
            else:
                derived_calcs_by_strain[tstrain] = [(symmop, calc['task_id'])]

    # Process derived calcs
    explicit_calcs_by_id = {d['task_id']: d for d in explicit_calcs.values()}
    derived_calcs = []
    for strain, calc_set in derived_calcs_by_strain.items():
        symmops, task_ids = zip(*calc_set)
        task_strains = [
            Strain(explicit_calcs_by_id[task_id]['strain'])
            for task_id in task_ids
        ]
        task_stresses = [
            explicit_calcs_by_id[task_id]['cauchy_stress']
            for task_id in task_ids
        ]
        derived_strains = [
            tstrain.transform(symmop)
            for tstrain, symmop in zip(task_strains, symmops)
        ]
        for derived_strain in derived_strains:
            if not np.allclose(derived_strain, strain, atol=2e-3):
                logger.info("Issue with derived strains")
                raise ValueError("Issue with derived strains")
        derived_stresses = [
            tstress.transform(sop)
            for sop, tstress in zip(symmops, task_stresses)
        ]
        input_docs = [{
            "task_id": task_id,
            "strain": task_strain,
            "cauchy_stress": task_stress,
            "symmop": symmop
        } for task_id, task_strain, task_stress, symmop in zip(
            task_ids, task_strains, task_stresses, symmops)]
        calc = {
            "strain": strain,
            "cauchy_stress": Stress(np.average(derived_stresses, axis=0)),
            "deformation": strain.get_deformation_matrix(),
            "input_tasks": input_docs,
            "type": "derived"
        }
        calc['pk_stress'] = calc['cauchy_stress'].piola_kirchoff_2(
            calc['deformation'])
        derived_calcs.append(calc)

    return list(explicit_calcs.values()), derived_calcs
Example #43
0
class SpacegroupAnalyzerTest(PymatgenTest):

    def setUp(self):
        p = Poscar.from_file(os.path.join(test_dir, 'POSCAR'))
        self.structure = p.structure
        self.sg = SpacegroupAnalyzer(self.structure, 0.001)
        self.disordered_structure = self.get_structure('Li10GeP2S12')
        self.disordered_sg = SpacegroupAnalyzer(self.disordered_structure, 0.001)
        s = p.structure.copy()
        site = s[0]
        del s[0]
        s.append(site.species_and_occu, site.frac_coords)
        self.sg3 = SpacegroupAnalyzer(s, 0.001)
        graphite = self.get_structure('Graphite')
        graphite.add_site_property("magmom", [0.1] * len(graphite))
        self.sg4 = SpacegroupAnalyzer(graphite, 0.001)

    def test_get_space_symbol(self):
        self.assertEqual(self.sg.get_spacegroup_symbol(), "Pnma")
        self.assertEqual(self.disordered_sg.get_spacegroup_symbol(),
                         "P4_2/nmc")
        self.assertEqual(self.sg3.get_spacegroup_symbol(), "Pnma")
        self.assertEqual(self.sg4.get_spacegroup_symbol(), "R-3m")

    def test_get_space_number(self):
        self.assertEqual(self.sg.get_spacegroup_number(), 62)
        self.assertEqual(self.disordered_sg.get_spacegroup_number(), 137)
        self.assertEqual(self.sg4.get_spacegroup_number(), 166)

    def test_get_hall(self):
        self.assertEqual(self.sg.get_hall(), '-P 2ac 2n')
        self.assertEqual(self.disordered_sg.get_hall(), 'P 4n 2n -1n')

    def test_get_pointgroup(self):
        self.assertEqual(self.sg.get_point_group(), 'mmm')
        self.assertEqual(self.disordered_sg.get_point_group(), '4/mmm')

    def test_get_symmetry_dataset(self):
        ds = self.sg.get_symmetry_dataset()
        self.assertEqual(ds['international'], 'Pnma')

    def test_get_crystal_system(self):
        crystal_system = self.sg.get_crystal_system()
        self.assertEqual('orthorhombic', crystal_system)
        self.assertEqual('tetragonal', self.disordered_sg.get_crystal_system())

    def test_get_symmetry_operations(self):
        fracsymmops = self.sg.get_symmetry_operations()
        symmops = self.sg.get_symmetry_operations(True)
        self.assertEqual(len(symmops), 8)
        latt = self.structure.lattice
        for fop, op in zip(fracsymmops, symmops):
            for site in self.structure:
                newfrac = fop.operate(site.frac_coords)
                newcart = op.operate(site.coords)
                self.assertTrue(np.allclose(latt.get_fractional_coords(newcart),
                                            newfrac))
                found = False
                newsite = PeriodicSite(site.species_and_occu, newcart, latt,
                                       coords_are_cartesian=True)
                for testsite in self.structure:
                    if newsite.is_periodic_image(testsite, 1e-3):
                        found = True
                        break
                self.assertTrue(found)

    def test_get_refined_structure(self):
        for a in self.sg.get_refined_structure().lattice.angles:
            self.assertEqual(a, 90)
        refined = self.disordered_sg.get_refined_structure()
        for a in refined.lattice.angles:
            self.assertEqual(a, 90)
        self.assertEqual(refined.lattice.a, refined.lattice.b)
        s = self.get_structure('Li2O')
        sg = SpacegroupAnalyzer(s, 0.001)
        self.assertEqual(sg.get_refined_structure().num_sites, 4 * s.num_sites)

    def test_get_symmetrized_structure(self):
        symm_struct = self.sg.get_symmetrized_structure()
        for a in symm_struct.lattice.angles:
            self.assertEqual(a, 90)
        self.assertEqual(len(symm_struct.equivalent_sites), 5)

        symm_struct = self.disordered_sg.get_symmetrized_structure()
        self.assertEqual(len(symm_struct.equivalent_sites), 8)
        self.assertEqual([len(i) for i in symm_struct.equivalent_sites],
                         [16,4,8,4,2,8,8,8])
        s1 = symm_struct.equivalent_sites[1][1]
        s2 = symm_struct[symm_struct.equivalent_indices[1][1]]
        self.assertEqual(s1, s2)
        self.assertEqual(self.sg4.get_symmetrized_structure()[0].magmom, 0.1)

    def test_find_primitive(self):
        """
        F m -3 m Li2O testing of converting to primitive cell
        """
        parser = CifParser(os.path.join(test_dir, 'Li2O.cif'))
        structure = parser.get_structures(False)[0]
        s = SpacegroupAnalyzer(structure)
        primitive_structure = s.find_primitive()
        self.assertEqual(primitive_structure.formula, "Li2 O1")
        # This isn't what is expected. All the angles should be 60
        self.assertAlmostEqual(primitive_structure.lattice.alpha, 60)
        self.assertAlmostEqual(primitive_structure.lattice.beta, 60)
        self.assertAlmostEqual(primitive_structure.lattice.gamma, 60)
        self.assertAlmostEqual(primitive_structure.lattice.volume,
                               structure.lattice.volume / 4.0)

    def test_get_ir_reciprocal_mesh(self):
        grid=self.sg.get_ir_reciprocal_mesh()
        self.assertEqual(len(grid), 216)
        self.assertAlmostEquals(grid[1][0][0], 0.1)
        self.assertAlmostEquals(grid[1][0][1], 0.0)
        self.assertAlmostEquals(grid[1][0][2], 0.0)
        self.assertEqual(grid[1][1], 2)

    def test_get_conventional_standard_structure(self):
        parser = CifParser(os.path.join(test_dir, 'bcc_1927.cif'))
        structure = parser.get_structures(False)[0]
        s = SpacegroupAnalyzer(structure, symprec=1e-2)
        conv = s.get_conventional_standard_structure()
        self.assertAlmostEqual(conv.lattice.alpha, 90)
        self.assertAlmostEqual(conv.lattice.beta, 90)
        self.assertAlmostEqual(conv.lattice.gamma, 90)
        self.assertAlmostEqual(conv.lattice.a, 9.1980270633769461)
        self.assertAlmostEqual(conv.lattice.b, 9.1980270633769461)
        self.assertAlmostEqual(conv.lattice.c, 9.1980270633769461)

        parser = CifParser(os.path.join(test_dir, 'btet_1915.cif'))
        structure = parser.get_structures(False)[0]
        s = SpacegroupAnalyzer(structure, symprec=1e-2)
        conv = s.get_conventional_standard_structure()
        self.assertAlmostEqual(conv.lattice.alpha, 90)
        self.assertAlmostEqual(conv.lattice.beta, 90)
        self.assertAlmostEqual(conv.lattice.gamma, 90)
        self.assertAlmostEqual(conv.lattice.a, 5.0615106678044235)
        self.assertAlmostEqual(conv.lattice.b, 5.0615106678044235)
        self.assertAlmostEqual(conv.lattice.c, 4.2327080177761687)

        parser = CifParser(os.path.join(test_dir, 'orci_1010.cif'))
        structure = parser.get_structures(False)[0]
        s = SpacegroupAnalyzer(structure, symprec=1e-2)
        conv = s.get_conventional_standard_structure()
        self.assertAlmostEqual(conv.lattice.alpha, 90)
        self.assertAlmostEqual(conv.lattice.beta, 90)
        self.assertAlmostEqual(conv.lattice.gamma, 90)
        self.assertAlmostEqual(conv.lattice.a, 2.9542233922299999)
        self.assertAlmostEqual(conv.lattice.b, 4.6330325651443296)
        self.assertAlmostEqual(conv.lattice.c, 5.373703587040775)

        parser = CifParser(os.path.join(test_dir, 'orcc_1003.cif'))
        structure = parser.get_structures(False)[0]
        s = SpacegroupAnalyzer(structure, symprec=1e-2)
        conv = s.get_conventional_standard_structure()
        self.assertAlmostEqual(conv.lattice.alpha, 90)
        self.assertAlmostEqual(conv.lattice.beta, 90)
        self.assertAlmostEqual(conv.lattice.gamma, 90)
        self.assertAlmostEqual(conv.lattice.a, 4.1430033493799998)
        self.assertAlmostEqual(conv.lattice.b, 31.437979757624728)
        self.assertAlmostEqual(conv.lattice.c, 3.99648651)

        parser = CifParser(os.path.join(test_dir, 'monoc_1028.cif'))
        structure = parser.get_structures(False)[0]
        s = SpacegroupAnalyzer(structure, symprec=1e-2)
        conv = s.get_conventional_standard_structure()
        self.assertAlmostEqual(conv.lattice.alpha, 90)
        self.assertAlmostEqual(conv.lattice.beta, 117.53832420192903)
        self.assertAlmostEqual(conv.lattice.gamma, 90)
        self.assertAlmostEqual(conv.lattice.a, 14.033435583000625)
        self.assertAlmostEqual(conv.lattice.b, 3.96052850731)
        self.assertAlmostEqual(conv.lattice.c, 6.8743926325200002)

        parser = CifParser(os.path.join(test_dir, 'rhomb_1170.cif'))
        structure = parser.get_structures(False)[0]
        s = SpacegroupAnalyzer(structure, symprec=1e-2)
        conv = s.get_conventional_standard_structure()
        self.assertAlmostEqual(conv.lattice.alpha, 90)
        self.assertAlmostEqual(conv.lattice.beta, 90)
        self.assertAlmostEqual(conv.lattice.gamma, 120)
        self.assertAlmostEqual(conv.lattice.a, 3.699919902005897)
        self.assertAlmostEqual(conv.lattice.b, 3.699919902005897)
        self.assertAlmostEqual(conv.lattice.c, 6.9779585500000003)

    def test_get_primitive_standard_structure(self):
        parser = CifParser(os.path.join(test_dir, 'bcc_1927.cif'))
        structure = parser.get_structures(False)[0]
        s = SpacegroupAnalyzer(structure, symprec=1e-2)
        prim = s.get_primitive_standard_structure()
        self.assertAlmostEqual(prim.lattice.alpha, 109.47122063400001)
        self.assertAlmostEqual(prim.lattice.beta, 109.47122063400001)
        self.assertAlmostEqual(prim.lattice.gamma, 109.47122063400001)
        self.assertAlmostEqual(prim.lattice.a, 7.9657251015812145)
        self.assertAlmostEqual(prim.lattice.b, 7.9657251015812145)
        self.assertAlmostEqual(prim.lattice.c, 7.9657251015812145)

        parser = CifParser(os.path.join(test_dir, 'btet_1915.cif'))
        structure = parser.get_structures(False)[0]
        s = SpacegroupAnalyzer(structure, symprec=1e-2)
        prim = s.get_primitive_standard_structure()
        self.assertAlmostEqual(prim.lattice.alpha, 105.015053349)
        self.assertAlmostEqual(prim.lattice.beta, 105.015053349)
        self.assertAlmostEqual(prim.lattice.gamma, 118.80658411899999)
        self.assertAlmostEqual(prim.lattice.a, 4.1579321075608791)
        self.assertAlmostEqual(prim.lattice.b, 4.1579321075608791)
        self.assertAlmostEqual(prim.lattice.c, 4.1579321075608791)

        parser = CifParser(os.path.join(test_dir, 'orci_1010.cif'))
        structure = parser.get_structures(False)[0]
        s = SpacegroupAnalyzer(structure, symprec=1e-2)
        prim = s.get_primitive_standard_structure()
        self.assertAlmostEqual(prim.lattice.alpha, 134.78923546600001)
        self.assertAlmostEqual(prim.lattice.beta, 105.856239333)
        self.assertAlmostEqual(prim.lattice.gamma, 91.276341676000001)
        self.assertAlmostEqual(prim.lattice.a, 3.8428217771014852)
        self.assertAlmostEqual(prim.lattice.b, 3.8428217771014852)
        self.assertAlmostEqual(prim.lattice.c, 3.8428217771014852)

        parser = CifParser(os.path.join(test_dir, 'orcc_1003.cif'))
        structure = parser.get_structures(False)[0]
        s = SpacegroupAnalyzer(structure, symprec=1e-2)
        prim = s.get_primitive_standard_structure()
        self.assertAlmostEqual(prim.lattice.alpha, 90)
        self.assertAlmostEqual(prim.lattice.beta, 90)
        self.assertAlmostEqual(prim.lattice.gamma, 164.985257335)
        self.assertAlmostEqual(prim.lattice.a, 15.854897098324196)
        self.assertAlmostEqual(prim.lattice.b, 15.854897098324196)
        self.assertAlmostEqual(prim.lattice.c, 3.99648651)

        parser = CifParser(os.path.join(test_dir, 'monoc_1028.cif'))
        structure = parser.get_structures(False)[0]
        s = SpacegroupAnalyzer(structure, symprec=1e-2)
        prim = s.get_primitive_standard_structure()
        self.assertAlmostEqual(prim.lattice.alpha, 63.579155761999999)
        self.assertAlmostEqual(prim.lattice.beta, 116.42084423747779)
        self.assertAlmostEqual(prim.lattice.gamma, 148.47965136208569)
        self.assertAlmostEqual(prim.lattice.a, 7.2908007159612325)
        self.assertAlmostEqual(prim.lattice.b, 7.2908007159612325)
        self.assertAlmostEqual(prim.lattice.c, 6.8743926325200002)

        parser = CifParser(os.path.join(test_dir, 'rhomb_1170.cif'))
        structure = parser.get_structures(False)[0]
        s = SpacegroupAnalyzer(structure, symprec=1e-2)
        prim = s.get_primitive_standard_structure()
        self.assertAlmostEqual(prim.lattice.alpha, 90)
        self.assertAlmostEqual(prim.lattice.beta, 90)
        self.assertAlmostEqual(prim.lattice.gamma, 120)
        self.assertAlmostEqual(prim.lattice.a, 3.699919902005897)
        self.assertAlmostEqual(prim.lattice.b, 3.699919902005897)
        self.assertAlmostEqual(prim.lattice.c, 6.9779585500000003)
Example #44
0
    def __init__(self, struct, find_spacegroup=False, symprec=None):
        """
        Args:
            struct (Structure): structure to write
            find_spacegroup (bool): whether to try to determine the spacegroup
            symprec (float): If not none, finds the symmetry of the structure and
                writes the cif with symmetry information. Passes symprec to the
                SpacegroupAnalyzer
        """
        format_str = "{:.8f}"
        
        block = OrderedDict()
        loops = []
        latt = struct.lattice
        comp = struct.composition
        no_oxi_comp = comp.element_composition
        spacegroup = ("P 1", 1)
        if find_spacegroup:
            sf = SpacegroupAnalyzer(struct, 0.001)
            spacegroup = (sf.get_spacegroup_symbol(),
                          sf.get_spacegroup_number())
        block["_symmetry_space_group_name_H-M"] = spacegroup[0]
        for cell_attr in ['a', 'b', 'c']:
            block["_cell_length_" + cell_attr] = format_str.format(
                getattr(latt, cell_attr))
        for cell_attr in ['alpha', 'beta', 'gamma']:
            block["_cell_angle_" + cell_attr] = format_str.format(
                getattr(latt, cell_attr))
        block["_symmetry_Int_Tables_number"] = spacegroup[1]
        block["_chemical_formula_structural"] = no_oxi_comp.reduced_formula
        block["_chemical_formula_sum"] = no_oxi_comp.formula
        block["_cell_volume"] = latt.volume.__str__()

        reduced_comp = no_oxi_comp.reduced_composition
        el = no_oxi_comp.elements[0]
        amt = comp[el]
        fu = int(amt / reduced_comp[Element(el.symbol)])

        block["_cell_formula_units_Z"] = str(fu)

        if symprec is None:
            block["_symmetry_equiv_pos_site_id"] = ["1"]
            block["_symmetry_equiv_pos_as_xyz"] = ["x, y, z"]
        else:
            sf = SpacegroupAnalyzer(struct, symprec)
            ops = [op.as_xyz_string() for op in sf.get_symmetry_operations()]
            block["_symmetry_equiv_pos_site_id"] = \
                ["%d" % i for i in range(1, len(ops) + 1)]
            block["_symmetry_equiv_pos_as_xyz"] = ops

        loops.append(["_symmetry_equiv_pos_site_id",
                      "_symmetry_equiv_pos_as_xyz"])

        contains_oxidation = True
        try:
            symbol_to_oxinum = OrderedDict([
                (el.__str__(), float(el.oxi_state))
                for el in sorted(comp.elements)])
        except AttributeError:
            symbol_to_oxinum = OrderedDict([(el.symbol, 0) for el in 
                                            sorted(comp.elements)])
            contains_oxidation = False
        if contains_oxidation:
            block["_atom_type_symbol"] = symbol_to_oxinum.keys()
            block["_atom_type_oxidation_number"] = symbol_to_oxinum.values()
            loops.append(["_atom_type_symbol", "_atom_type_oxidation_number"])

        atom_site_type_symbol = []
        atom_site_symmetry_multiplicity = []
        atom_site_fract_x = []
        atom_site_fract_y = []
        atom_site_fract_z = []
        atom_site_label = []
        atom_site_occupancy = []
        count = 1
        if symprec is None:
            for site in struct:
                for sp, occu in site.species_and_occu.items():
                    atom_site_type_symbol.append(sp.__str__())
                    atom_site_symmetry_multiplicity.append("1")
                    atom_site_fract_x.append("{0:f}".format(site.a))
                    atom_site_fract_y.append("{0:f}".format(site.b))
                    atom_site_fract_z.append("{0:f}".format(site.c))
                    atom_site_label.append("{}{}".format(sp.symbol, count))
                    atom_site_occupancy.append(occu.__str__())
                    count += 1
        else:
            for group in sf.get_symmetrized_structure().equivalent_sites:
                site = group[0]
                for sp, occu in site.species_and_occu.items():
                    atom_site_type_symbol.append(sp.__str__())
                    atom_site_symmetry_multiplicity.append("%d" % len(group))
                    atom_site_fract_x.append("{0:f}".format(site.a))
                    atom_site_fract_y.append("{0:f}".format(site.b))
                    atom_site_fract_z.append("{0:f}".format(site.c))
                    atom_site_label.append("{}{}".format(sp.symbol, count))
                    atom_site_occupancy.append(occu.__str__())
                    count += 1

        block["_atom_site_type_symbol"] = atom_site_type_symbol
        block["_atom_site_label"] = atom_site_label
        block["_atom_site_symmetry_multiplicity"] = \
            atom_site_symmetry_multiplicity
        block["_atom_site_fract_x"] = atom_site_fract_x
        block["_atom_site_fract_y"] = atom_site_fract_y
        block["_atom_site_fract_z"] = atom_site_fract_z
        block["_atom_site_occupancy"] = atom_site_occupancy
        loops.append(["_atom_site_type_symbol",
                      "_atom_site_label",
                      "_atom_site_symmetry_multiplicity",
                      "_atom_site_fract_x",
                      "_atom_site_fract_y",
                      "_atom_site_fract_z",
                      "_atom_site_occupancy"])
        d = OrderedDict()
        d[comp.reduced_formula] = CifBlock(block, loops, comp.reduced_formula)
        self._cf = CifFile(d)
Example #45
0
    def __init__(self, struct, symprec=None, write_magmoms=False):
        """
        A wrapper around CifFile to write CIF files from pymatgen structures.

        Args:
            struct (Structure): structure to write
            symprec (float): If not none, finds the symmetry of the structure
                and writes the cif with symmetry information. Passes symprec
                to the SpacegroupAnalyzer
            write_magmoms (bool): If True, will write magCIF file. Incompatible
                with symprec
        """

        if write_magmoms and symprec:
            warnings.warn(
                "Magnetic symmetry cannot currently be detected by pymatgen.")
            symprec = None

        format_str = "{:.8f}"

        block = OrderedDict()
        loops = []
        spacegroup = ("P 1", 1)
        if symprec is not None:
            sf = SpacegroupAnalyzer(struct, symprec)
            spacegroup = (sf.get_space_group_symbol(),
                          sf.get_space_group_number())
            # Needs the refined struture when using symprec. This converts
            # primitive to conventional structures, the standard for CIF.
            struct = sf.get_refined_structure()

        latt = struct.lattice
        comp = struct.composition
        no_oxi_comp = comp.element_composition
        block["_symmetry_space_group_name_H-M"] = spacegroup[0]
        for cell_attr in ['a', 'b', 'c']:
            block["_cell_length_" + cell_attr] = format_str.format(
                getattr(latt, cell_attr))
        for cell_attr in ['alpha', 'beta', 'gamma']:
            block["_cell_angle_" + cell_attr] = format_str.format(
                getattr(latt, cell_attr))
        block["_symmetry_Int_Tables_number"] = spacegroup[1]
        block["_chemical_formula_structural"] = no_oxi_comp.reduced_formula
        block["_chemical_formula_sum"] = no_oxi_comp.formula
        block["_cell_volume"] = latt.volume.__str__()

        reduced_comp, fu = no_oxi_comp.get_reduced_composition_and_factor()
        block["_cell_formula_units_Z"] = str(int(fu))

        if symprec is None:
            block["_symmetry_equiv_pos_site_id"] = ["1"]
            block["_symmetry_equiv_pos_as_xyz"] = ["x, y, z"]
        else:
            sf = SpacegroupAnalyzer(struct, symprec)

            symmops = []
            for op in sf.get_symmetry_operations():
                v = op.translation_vector
                symmops.append(
                    SymmOp.from_rotation_and_translation(
                        op.rotation_matrix, v))

            ops = [op.as_xyz_string() for op in symmops]
            block["_symmetry_equiv_pos_site_id"] = \
                ["%d" % i for i in range(1, len(ops) + 1)]
            block["_symmetry_equiv_pos_as_xyz"] = ops

        loops.append(
            ["_symmetry_equiv_pos_site_id", "_symmetry_equiv_pos_as_xyz"])

        contains_oxidation = True
        try:
            symbol_to_oxinum = OrderedDict([(el.__str__(), float(el.oxi_state))
                                            for el in sorted(comp.elements)])
        except AttributeError:
            symbol_to_oxinum = OrderedDict([(el.symbol, 0)
                                            for el in sorted(comp.elements)])
            contains_oxidation = False
        if contains_oxidation:
            block["_atom_type_symbol"] = symbol_to_oxinum.keys()
            block["_atom_type_oxidation_number"] = symbol_to_oxinum.values()
            loops.append(["_atom_type_symbol", "_atom_type_oxidation_number"])

        atom_site_type_symbol = []
        atom_site_symmetry_multiplicity = []
        atom_site_fract_x = []
        atom_site_fract_y = []
        atom_site_fract_z = []
        atom_site_label = []
        atom_site_occupancy = []
        atom_site_moment_label = []
        atom_site_moment_crystalaxis_x = []
        atom_site_moment_crystalaxis_y = []
        atom_site_moment_crystalaxis_z = []
        count = 1
        if symprec is None:
            for site in struct:
                for sp, occu in sorted(site.species_and_occu.items()):
                    atom_site_type_symbol.append(sp.__str__())
                    atom_site_symmetry_multiplicity.append("1")
                    atom_site_fract_x.append("{0:f}".format(site.a))
                    atom_site_fract_y.append("{0:f}".format(site.b))
                    atom_site_fract_z.append("{0:f}".format(site.c))
                    atom_site_label.append("{}{}".format(sp.symbol, count))
                    atom_site_occupancy.append(occu.__str__())

                    magmom = site.properties.get('magmom', Magmom(0))
                    moment = Magmom.get_moment_relative_to_crystal_axes(
                        magmom, latt)
                    if write_magmoms and abs(magmom) > 0:
                        atom_site_moment_label.append("{}{}".format(
                            sp.symbol, count))
                        atom_site_moment_crystalaxis_x.append(moment[0])
                        atom_site_moment_crystalaxis_y.append(moment[1])
                        atom_site_moment_crystalaxis_z.append(moment[2])

                    count += 1
        else:
            # The following just presents a deterministic ordering.
            unique_sites = [
                (sorted(sites,
                        key=lambda s: tuple([abs(x)
                                             for x in s.frac_coords]))[0],
                 len(sites))
                for sites in sf.get_symmetrized_structure().equivalent_sites
            ]
            for site, mult in sorted(unique_sites,
                                     key=lambda t:
                                     (t[0].species_and_occu.average_electroneg,
                                      -t[1], t[0].a, t[0].b, t[0].c)):
                for sp, occu in site.species_and_occu.items():
                    atom_site_type_symbol.append(sp.__str__())
                    atom_site_symmetry_multiplicity.append("%d" % mult)
                    atom_site_fract_x.append("{0:f}".format(site.a))
                    atom_site_fract_y.append("{0:f}".format(site.b))
                    atom_site_fract_z.append("{0:f}".format(site.c))
                    atom_site_label.append("{}{}".format(sp.symbol, count))
                    atom_site_occupancy.append(occu.__str__())
                    count += 1

        block["_atom_site_type_symbol"] = atom_site_type_symbol
        block["_atom_site_label"] = atom_site_label
        block["_atom_site_symmetry_multiplicity"] = \
            atom_site_symmetry_multiplicity
        block["_atom_site_fract_x"] = atom_site_fract_x
        block["_atom_site_fract_y"] = atom_site_fract_y
        block["_atom_site_fract_z"] = atom_site_fract_z
        block["_atom_site_occupancy"] = atom_site_occupancy
        loops.append([
            "_atom_site_type_symbol", "_atom_site_label",
            "_atom_site_symmetry_multiplicity", "_atom_site_fract_x",
            "_atom_site_fract_y", "_atom_site_fract_z", "_atom_site_occupancy"
        ])
        if write_magmoms:
            block["_atom_site_moment_label"] = atom_site_moment_label
            block[
                "_atom_site_moment_crystalaxis_x"] = atom_site_moment_crystalaxis_x
            block[
                "_atom_site_moment_crystalaxis_y"] = atom_site_moment_crystalaxis_y
            block[
                "_atom_site_moment_crystalaxis_z"] = atom_site_moment_crystalaxis_z
            loops.append([
                "_atom_site_moment_label", "_atom_site_moment_crystalaxis_x",
                "_atom_site_moment_crystalaxis_y",
                "_atom_site_moment_crystalaxis_z"
            ])
        d = OrderedDict()
        d[comp.reduced_formula] = CifBlock(block, loops, comp.reduced_formula)
        self._cf = CifFile(d)