Esempio n. 1
0
            d1[i] = 1
    d2 = {}
    for i in list_b:
        if i in d2:
            d2[i] += 1
        else:
            d2[i] = 1
    for key, value in d1.items():
        if key not in d2 or value > d2[key]:
            return False
    return True


s1 = Structure.from_file('POSCAR.orig')
s2 = Structure.from_file('CONTCAR')
spacegroup1 = SpacegroupAnalyzer(s1)
spacegroup2 = SpacegroupAnalyzer(s2)
print('space group symbol of structure1:',
      spacegroup1.get_space_group_symbol())
print('space group number of structure1:',
      spacegroup1.get_space_group_number())
print('space group symbol of structure2:',
      spacegroup2.get_space_group_symbol())
print('space group number of structure2:',
      spacegroup2.get_space_group_number())
Voronoi = VoronoiCoordFinder(s2, target=None)
site = s2.num_sites
print("s2[0]:", s2[0])
polyhedra = Voronoi.get_voronoi_polyhedra(1)
coordinate = Voronoi.get_coordination_number(1)
coordinate_sites = Voronoi.get_coordinated_sites(1)
Esempio n. 2
0
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
from ase.io import read
from pymatgen.io.ase import AseAtomsAdaptor

s_ase = read("OUTCAR", format = "vasp-out")
s_pmg = AseAtomsAdaptor().get_structure(s_ase)
space_group_number = SpacegroupAnalyzer(s_pmg).get_space_group_number()
space_group_symbol = SpacegroupAnalyzer(s_pmg).get_space_group_symbol()
print("{} ({})".format(space_group_symbol, space_group_number))
Esempio n. 3
0
 def get_plot_2d(self, structure: Structure) -> go.Figure:
     """
     Generates the 2D diffraction pattern of the input structure.
     Args:
         structure (Structure): The input structure.
     Returns:
         Figure
     """
     if self.symprec:
         finder = SpacegroupAnalyzer(structure, symprec=self.symprec)
         structure = finder.get_refined_structure()
     points = self.generate_points(-10, 11)
     tem_dots = self.tem_dots(structure, points)
     xs = []
     ys = []
     hkls = []
     intensities = []
     for dot in tem_dots:
         xs.append(dot.position[0])
         ys.append(dot.position[1])
         hkls.append(str(dot.hkl))
         intensities.append(dot.intensity)
     hkls = list(map(unicodeify_spacegroup, list(map(latexify_spacegroup, hkls))))
     data = [
         go.Scatter(
             x=xs,
             y=ys,
             text=hkls,
             hoverinfo="text",
             mode="markers",
             marker=dict(
                 size=8,
                 cmax=1,
                 cmin=0,
                 color=intensities,
                 colorscale=[[0, "black"], [1.0, "white"]],
             ),
             showlegend=False,
         ),
         go.Scatter(
             x=[0],
             y=[0],
             text="(0, 0, 0): Direct beam",
             hoverinfo="text",
             mode="markers",
             marker=dict(size=14, cmax=1, cmin=0, color="white"),
             showlegend=False,
         ),
     ]
     layout = go.Layout(
         title="2D Diffraction Pattern<br>Beam Direction: " + "".join(str(e) for e in self.beam_direction),
         font=dict(size=14, color="#7f7f7f"),
         hovermode="closest",
         xaxis=dict(
             range=[-4, 4],
             showgrid=False,
             zeroline=False,
             showline=False,
             ticks="",
             showticklabels=False,
         ),
         yaxis=dict(
             range=[-4, 4],
             showgrid=False,
             zeroline=False,
             showline=False,
             ticks="",
             showticklabels=False,
         ),
         width=550,
         height=550,
         paper_bgcolor="rgba(100,110,110,0.5)",
         plot_bgcolor="black",
     )
     fig = go.Figure(data=data, layout=layout)
     return fig
Esempio n. 4
0
    def __init__(
        self,
        structures: Dict[str, Structure],
        symprec: Optional[float] = 0.01,
        perturb_sigma: Optional[float] = None,
        remove_oxidation_states: bool = True,
        reciprocal_coordination: bool = True,
    ):
        # make a deep copy to avoid modifying structures in place
        self.structures = deepcopy(structures)
        self.symprec = symprec
        self.reciprocal_coordination = reciprocal_coordination

        # use this to cache benchmark results
        self._benchmark: Dict[NearNeighbors, Dict[str, List]] = defaultdict(dict)

        for name, structure in structures.items():
            if "coordination" not in structure.site_properties:
                raise AttributeError(
                    "{} structure does not have the 'coordination' site "
                    "property".format(name)
                )

        if perturb_sigma:
            for name, structure in self.structures.items():
                self.structures[name] = perturb_einstein_crystal(
                    structure, perturb_sigma
                )

        # precompute the symmetrized structures to save time during the benchmark. Also,
        # determine the total number of unique cations/anions each structure.
        self.site_information: Dict[str, Dict[str, Any]] = {}
        self.max_nsites = 0

        n_structures_with_oxi = 0
        for name, structure in self.structures.items():
            if self.symprec:
                sga = SpacegroupAnalyzer(structure, symprec=self.symprec)

                equiv_sites = sga.get_symmetrized_structure().equivalent_sites
                unique_site_idxs = np.unique(
                    sga.get_symmetry_dataset()["equivalent_atoms"]
                )
            else:
                equiv_sites = [[s] for s in structure]
                unique_site_idxs = list(range(len(structure)))

            # cation_idxs and anion_idxs are the indexes of the cations/anions
            # in the list of unique sites (not the list of ALL sites).
            cation_degens = []
            cation_idxs = []
            anion_degens = []
            anion_idxs = []
            cations = set()
            anions = set()
            for i, sites in enumerate(equiv_sites):
                # Specie check needed to see if site has an oxidation state at all.
                if (
                    isinstance(sites[0].specie, Specie)
                    and sites[0].specie.oxi_state >= 0
                ):
                    # based on previous implementation, neutral ions will be scored as
                    # cations, however, neutral to neutral bonding is allowed, hence
                    # don't add the element to the cations set as this will be used to
                    # prevent cation-cation bonding later on.
                    cation_degens.append(len(sites))
                    cation_idxs.append(i)
                    if sites[0].specie.oxi_state > 0:
                        cations.add(sites[0].specie.name)
                elif (
                    isinstance(sites[0].specie, Specie)
                    and sites[0].specie.oxi_state < 0
                ):
                    anion_degens.append(len(sites))
                    anion_idxs.append(i)
                    anions.add(sites[0].specie.name)

            all_degens = [len(x) for x in equiv_sites]
            total_all = sum(all_degens)

            total_cations = sum(cation_degens)
            total_anions = sum(anion_degens)

            self.site_information[name] = {
                "unique_idxs": unique_site_idxs,
                "all_idxs": list(range(len(unique_site_idxs))),
                "cation_idxs": cation_idxs,
                "anion_idxs": anion_idxs,
                "all_degens": all_degens,
                "cation_degens": cation_degens,
                "anion_degens": anion_degens,
                "all_total": total_all,
                "cation_total": total_cations,
                "anion_total": total_anions,
                "cations": cations,
                "anions": anions,
            }

            self.max_nsites = max(self.max_nsites, len(unique_site_idxs))

            if cation_idxs or anion_idxs:
                n_structures_with_oxi += 1

        # useful to know if all structures have oxidation states when calculating scores
        self.all_structures_have_oxi = n_structures_with_oxi == len(structures)

        if remove_oxidation_states:
            for structure in self.structures.values():
                structure.remove_oxidation_states()
Esempio n. 5
0
def calc_shiftk(structure, symprec=0.01, angle_tolerance=5):
    """
    Find the values of ``shiftk`` and ``nshiftk`` appropriated for the sampling of the Brillouin zone.

    When the primitive vectors of the lattice do NOT form a FCC or a BCC lattice,
    the usual (shifted) Monkhorst-Pack grids are formed by using nshiftk=1 and shiftk 0.5 0.5 0.5 .
    This is often the preferred k point sampling. For a non-shifted Monkhorst-Pack grid,
    use `nshiftk=1` and `shiftk 0.0 0.0 0.0`, but there is little reason to do that.

    When the primitive vectors of the lattice form a FCC lattice, with rprim::

            0.0 0.5 0.5
            0.5 0.0 0.5
            0.5 0.5 0.0

    the (very efficient) usual Monkhorst-Pack sampling will be generated by using nshiftk= 4 and shiftk::

        0.5 0.5 0.5
        0.5 0.0 0.0
        0.0 0.5 0.0
        0.0 0.0 0.5

    When the primitive vectors of the lattice form a BCC lattice, with rprim::

           -0.5  0.5  0.5
            0.5 -0.5  0.5
            0.5  0.5 -0.5

    the usual Monkhorst-Pack sampling will be generated by using nshiftk= 2 and shiftk::

            0.25  0.25  0.25
           -0.25 -0.25 -0.25

    However, the simple sampling nshiftk=1 and shiftk 0.5 0.5 0.5 is excellent.

    For hexagonal lattices with hexagonal axes, e.g. rprim::

            1.0  0.0       0.0
           -0.5  sqrt(3)/2 0.0
            0.0  0.0       1.0

    one can use nshiftk= 1 and shiftk 0.0 0.0 0.5
    In rhombohedral axes, e.g. using angdeg 3*60., this corresponds to shiftk 0.5 0.5 0.5,
    to keep the shift along the symmetry axis.

    Returns:
        Suggested value of shiftk.
    """
    # Find lattice type.
    from pymatgen.symmetry.analyzer import SpacegroupAnalyzer

    sym = SpacegroupAnalyzer(structure,
                             symprec=symprec,
                             angle_tolerance=angle_tolerance)
    lattice_type, spg_symbol = sym.get_lattice_type(
    ), sym.get_space_group_symbol()

    # Check if the cell is primitive
    is_primitive = len(sym.find_primitive()) == len(structure)

    # Generate the appropriate set of shifts.
    shiftk = None

    if is_primitive:
        if lattice_type == "cubic":
            if "F" in spg_symbol:
                # FCC
                shiftk = [
                    0.5, 0.5, 0.5, 0.5, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.5
                ]

            elif "I" in spg_symbol:
                # BCC
                shiftk = [0.25, 0.25, 0.25, -0.25, -0.25, -0.25]
                # shiftk = [0.5, 0.5, 05])

        elif lattice_type == "hexagonal":
            # Find the hexagonal axis and set the shift along it.
            for i, angle in enumerate(structure.lattice.angles):
                if abs(angle - 120) < 1.0:
                    j = (i + 1) % 3
                    k = (i + 2) % 3
                    hex_ax = [ax for ax in range(3) if ax not in [j, k]][0]
                    break
            else:
                raise ValueError("Cannot find hexagonal axis")

            shiftk = [0.0, 0.0, 0.0]
            shiftk[hex_ax] = 0.5

        elif lattice_type == "tetragonal":
            if "I" in spg_symbol:
                # BCT
                shiftk = [0.25, 0.25, 0.25, -0.25, -0.25, -0.25]

    if shiftk is None:
        # Use default value.
        shiftk = [0.5, 0.5, 0.5]

    return np.reshape(shiftk, (-1, 3))
Esempio n. 6
0
    def structure_lines(
        structure,
        cell_flg=True,
        frac_flg=True,
        anion_shell_flg=True,
        cation_shell_flg=False,
        symm_flg=True,
    ):
        """
        Generates GULP input string corresponding to pymatgen structure.

        Args:
            structure: pymatgen Structure object
            cell_flg (default = True): Option to use lattice parameters.
            fractional_flg (default = True): If True, fractional coordinates
                are used. Else, cartesian coodinates in Angstroms are used.
                ******
                GULP convention is to use fractional coordinates for periodic
                structures and cartesian coordinates for non-periodic
                structures.
                ******
            anion_shell_flg (default = True): If True, anions are considered
                polarizable.
            cation_shell_flg (default = False): If True, cations are
                considered polarizable.
            symm_flg (default = True): If True, symmetry information is also
                written.

        Returns:
            string containing structure for GULP input
        """
        gin = ""
        if cell_flg:
            gin += "cell\n"
            l = structure.lattice
            lat_str = "{0:6f} {1:6f} {2:6f} {3:6f} {4:6f} {5:6f}".format(
                l.a, l.b, l.c, l.alpha, l.beta, l.gamma)
            gin += lat_str + "\n"

        if frac_flg:
            gin += "frac\n"
            coord_attr = "frac_coords"
        else:
            gin += "cart\n"
            coord_attr = "coords"
        for site in structure.sites:
            coord = [str(i) for i in getattr(site, coord_attr)]
            specie = site.specie
            core_site_desc = specie.symbol + " core " + " ".join(coord) + "\n"
            gin += core_site_desc
            if (specie in _anions
                    and anion_shell_flg) or (specie in _cations
                                             and cation_shell_flg):
                shel_site_desc = specie.symbol + " shel " + " ".join(
                    coord) + "\n"
                gin += shel_site_desc
            else:
                pass

        if symm_flg:
            gin += "space\n"
            gin += str(
                SpacegroupAnalyzer(structure).get_space_group_number()) + "\n"
        return gin
Esempio n. 7
0
    def populate(self, structure, prec=1e-5, maxiter=200, verbose=False,
                 precond=True, vsym=True):
        """
        Takes a partially populated tensor, and populates the non-zero
        entries according to the following procedure, iterated until
        the desired convergence (specified via prec) is achieved.

        1. Find non-zero entries
        2. Symmetrize the tensor with respect to crystal symmetry and
           (optionally) voigt symmetry
        3. Reset the non-zero entries of the original tensor
        
        Args:
            structure (structure object)
            prec (float): precision for determining a non-zero value
            maxiter (int): maximum iterations for populating the tensor
            verbose (bool): whether to populate verbosely
            precond (bool): whether to precondition by cycling through
                all symmops and storing new nonzero values, default True
            vsym (bool): whether to enforce voigt symmetry, defaults
                to True
        """
        if precond:
            # Generate the guess from populated
            sops = SpacegroupAnalyzer(structure).get_symmetry_operations()
            guess = Tensor(np.zeros(self.shape))
            mask = abs(self) > prec
            guess[mask] = self[mask]

            def merge(old, new):
                gmask = np.abs(old) > prec
                nmask = np.abs(new) > prec
                new_mask = np.logical_not(gmask)*nmask
                avg_mask = gmask*nmask
                old[avg_mask] = (old[avg_mask] + new[avg_mask]) / 2.
                old[new_mask] = new[new_mask]
            if verbose:
                print("Preconditioning for {} symmops".format(len(sops)))
            for sop in sops:
                rot = guess.transform(sop)
                # Store non-zero entries of new that weren't previously
                # in the guess in the guess
                merge(guess, rot)
            if verbose:
                print("Preconditioning for voigt symmetry".format(len(sops)))
            if vsym:
                v = guess.voigt
                perms = list(itertools.permutations(range(len(v.shape))))
                for perm in perms:
                    vtrans = np.transpose(v, perm)
                    merge(v, vtrans)
                guess = Tensor.from_voigt(v)
        else:
            guess = np.zeros(self.shape)

        assert guess.shape == self.shape, "Guess must have same shape"
        converged = False
        test_new, test_old = [guess.copy()]*2
        for i in range(maxiter):
            test_new = test_old.fit_to_structure(structure)
            if vsym:
                test_new = test_new.voigt_symmetrized
            diff = np.abs(test_old - test_new) 
            converged = (diff < prec).all()
            if converged:
                break
            test_new[mask] = self[mask]
            test_old = test_new
            if verbose:
                print("Iteration {}: {}".format(i, np.max(diff)))
        if not converged:
            max_diff = np.max(np.abs(self - test_new))
            warnings.warn("Warning, populated tensor is not converged "
                          "with max diff of {}".format(max_diff))
        return self.__class__(test_new)
    def apply_transformation(self, structure, return_ranked_list=False):
        """
        Return either a single ordered structure or a sequence of all ordered
        structures.

        Args:
            structure: Structure to order.
            return_ranked_list (bool): Whether or not multiple structures are
                returned. If return_ranked_list is a number, that number of
                structures is returned.

        Returns:
            Depending on returned_ranked list, either a transformed structure
            or a list of dictionaries, where each dictionary is of the form
            {"structure" = .... , "other_arguments"}

            The list of ordered structures is ranked by ewald energy / atom, if
            the input structure is an oxidation state decorated structure.
            Otherwise, it is ranked by number of sites, with smallest number of
            sites first.
        """
        try:
            num_to_return = int(return_ranked_list)
        except ValueError:
            num_to_return = 1

        if self.refine_structure:
            finder = SpacegroupAnalyzer(structure, self.symm_prec)
            structure = finder.get_refined_structure()

        contains_oxidation_state = all(
            [hasattr(sp, "oxi_state") and sp.oxi_state != 0 for sp in
             structure.composition.elements]
        )

        structures = None

        if structure.is_ordered:
            warn("Enumeration skipped for structure with composition {} "
                 "because it is ordered".format(structure.composition))
            structures = [structure.copy()]

        if self.max_disordered_sites:
            ndisordered = sum([1 for site in structure if not site.is_ordered])
            if ndisordered > self.max_disordered_sites:
                raise ValueError(
                    "Too many disordered sites! ({} > {})".format(
                        ndisordered, self.max_disordered_sites))
            max_cell_sizes = range(self.min_cell_size, int(
                    math.floor(self.max_disordered_sites / ndisordered)) + 1)

        else:
            max_cell_sizes = [self.max_cell_size]

        for max_cell_size in max_cell_sizes:
            adaptor = EnumlibAdaptor(
                structure, min_cell_size=self.min_cell_size,
                max_cell_size=max_cell_size,
                symm_prec=self.symm_prec, refine_structure=False,
                enum_precision_parameter=self.enum_precision_parameter,
                check_ordered_symmetry=self.check_ordered_symmetry)
            try:
                adaptor.run()
            except EnumError:
                warn("Unable to enumerate for max_cell_size = %d".format(
                    max_cell_size))
            structures = adaptor.structures
            if structures:
                break

        if structures is None:
            raise ValueError("Unable to enumerate")

        original_latt = structure.lattice
        inv_latt = np.linalg.inv(original_latt.matrix)
        ewald_matrices = {}
        all_structures = []
        for s in structures:
            new_latt = s.lattice
            transformation = np.dot(new_latt.matrix, inv_latt)
            transformation = tuple([tuple([int(round(cell)) for cell in row])
                                    for row in transformation])
            if contains_oxidation_state and self.sort_criteria == "ewald":
                if transformation not in ewald_matrices:
                    s_supercell = structure * transformation
                    ewald = EwaldSummation(s_supercell)
                    ewald_matrices[transformation] = ewald
                else:
                    ewald = ewald_matrices[transformation]
                energy = ewald.compute_sub_structure(s)
                all_structures.append({"num_sites": len(s), "energy": energy,
                                       "structure": s})
            else:
                all_structures.append({"num_sites": len(s), "structure": s})

        def sort_func(s):
            return s["energy"] / s["num_sites"] \
                if contains_oxidation_state and self.sort_criteria == "ewald" \
                else s["num_sites"]

        self._all_structures = sorted(all_structures, key=sort_func)

        if return_ranked_list:
            return self._all_structures[0:num_to_return]
        else:
            return self._all_structures[0]["structure"]
    def apply_transformation(self, structure, return_ranked_list=False):
        """
        Apply MagOrderTransformation to an input structure.
        :param structure: Any ordered structure.
        :param return_ranked_list: As in other Transformations.
        :return:
        """

        if not structure.is_ordered:
            raise ValueError("Create an ordered approximation of "
                             "your  input structure first.")

        # retrieve order parameters
        order_parameters = [MagOrderParameterConstraint.from_dict(op_dict)
                            for op_dict in self.order_parameter]
        # add dummy species on which to perform enumeration
        structure = self._add_dummy_species(structure, order_parameters)

        # trivial case
        if structure.is_ordered:
            structure = self._remove_dummy_species(structure)
            return [structure] if return_ranked_list > 1 else structure

        enum_kwargs = self.enum_kwargs.copy()

        enum_kwargs["min_cell_size"] = max(
            int(self.determine_min_cell(structure)),
            enum_kwargs.get("min_cell_size", 1)
        )

        max_cell = enum_kwargs.get('max_cell_size')
        if max_cell:
            if enum_kwargs["min_cell_size"] > max_cell:
                raise ValueError('Specified max cell size is smaller'
                                 ' than the minimum enumerable cell size')
        else:
            enum_kwargs["max_cell_size"] = enum_kwargs["min_cell_size"]

        t = EnumerateStructureTransformation(**enum_kwargs)

        alls = t.apply_transformation(structure,
                                      return_ranked_list=return_ranked_list)

        # handle the fact that EnumerateStructureTransformation can either
        # return a single Structure or a list
        if isinstance(alls, Structure):
            # remove dummy species and replace Spin.up or Spin.down
            # with spin magnitudes given in mag_species_spin arg
            alls = self._remove_dummy_species(alls)
            alls = self._add_spin_magnitudes(alls)
        else:
            for idx, _ in enumerate(alls):
                alls[idx]["structure"] = self._remove_dummy_species(alls[idx]["structure"])
                alls[idx]["structure"] = self._add_spin_magnitudes(alls[idx]["structure"])

        try:
            num_to_return = int(return_ranked_list)
        except ValueError:
            num_to_return = 1

        if num_to_return == 1 or not return_ranked_list:
            return alls[0]["structure"] if num_to_return else alls

        # remove duplicate structures and group according to energy model
        m = StructureMatcher(comparator=SpinComparator())
        key = lambda x: SpacegroupAnalyzer(x, 0.1).get_space_group_number()
        out = []
        for _, g in groupby(sorted([d["structure"] for d in alls],
                                   key=key), key):
            g = list(g)
            grouped = m.group_structures(g)
            out.extend([{"structure": g[0],
                         "energy": self.energy_model.get_energy(g[0])}
                        for g in grouped])

        self._all_structures = sorted(out, key=lambda d: d["energy"])

        return self._all_structures[0:num_to_return]
Esempio n. 10
0
print("there are {} folders".format(N))
df_100 = df.sort_values(by=['E_eV_atom'])[:N]
df_100['Spg_num_opt'] = np.nan
df_100['Spg_sym_opt'] = np.nan
df_100['Entropy_eV_cell'] = np.nan
print(df.head(10))

# read the CONTCAR in the folders 1 to 3
# and extract the enthalpy and entropy from the OUTCAR/OSZICAR files
for dir_name in range(1, N + 1):
    dir_name = str(dir_name)
    opt_struct = Structure.from_file(dir_name + '/' + "CONTCAR")
    # analyze the space group using SpacegroupAnalyzer, if RuntimeError or TypeError happens, raise the error
    try:
        spg_sym_opt = SpacegroupAnalyzer(opt_struct,
                                         symprec=.1).get_space_group_symbol()
    except RuntimeError:
        print("RuntimError: Space group analysis failed for structure " +
              dir_name)
        spg_sym_opt = "None"
        continue
    except TypeError:
        print("TypeError: Space group analysis failed for structure " +
              dir_name)
        spg_sym_opt = "None"
        continue
    # save spg_sym_opt to the column Spg_sym_opt in the df_100
    # df_100.loc[df_100['preliminary_order'] == dir_name, 'Spg_sym_opt'] = spg_sym_opt
    df_100.loc[df_100['preliminary_order'] == float(dir_name),
               'Spg_sym_opt'] = spg_sym_opt
    print("*****")
Esempio n. 11
0
with open('bulk_modeling.log', 'w') as f:
    start_time = time.time()

    num_ini = 239
    num_fin = 243

    for i in range(num_ini, num_fin):
        mp_id = sorted_entries[i]['material_id']
        formula = sorted_entries[i]['pretty_formula']
        struc = sorted_entries[i]['structure']

        INCAR['SYSTEM'] = formula
        createFolder('%03d_%s' % (i + 1.0, formula))

        # getting conventional unit cell
        sga = SpacegroupAnalyzer(struc)
        conv_struc = sga.get_conventional_standard_structure()
        conv_struc.to(filename="%03d_%s/POSCAR" % (i + 1.0, formula))

        # MP_incar load
        mp_incar = sorted_entries[i]['input.incar']

        # MAGMOM setting using Exception cases
        alkali_element = [x for x in alkali_elements if x in formula]
        if alkali_element is not None:
            INCAR['MAGMOM'] = [0.6, 5.0, 0.6, 0.6, 0.6]
        else:
            INCAR['MAGMOM'] = [5.0, 5.0, 0.6, 0.6, 0.6]

        # Hubbard U setting
        try:
Esempio n. 12
0
 def generate_doc(self, dir_name, vasprun_files):
     """
     Overridden
     """
     try:
         fullpath = os.path.abspath(dir_name)
         d = {k: v for k, v in self.additional_fields.items()}
         d["dir_name"] = fullpath
         d["schema_version"] = VaspToDbTaskDrone.__version__
         d["calculations"] = [
             self.process_vasprun(dir_name, taskname, filename)
             for taskname, filename in vasprun_files.items()
         ]
         d1 = d["calculations"][0]
         d2 = d["calculations"][-1]
         # Now map some useful info to the root level.
         for root_key in [
                 "completed_at", "nsites", "unit_cell_formula",
                 "reduced_cell_formula", "pretty_formula", "elements",
                 "nelements", "cif", "density", "is_hubbard", "hubbards",
                 "run_type"
         ]:
             d[root_key] = d2[root_key]
         d["chemsys"] = "-".join(sorted(d2["elements"]))
         # store any overrides to the exchange correlation functional
         xc = d2["input"]["incar"].get("GGA")
         if xc:
             xc = xc.upper()
         d["input"] = {
             "crystal": d1["input"]["crystal"],
             "is_lasph": d2["input"]["incar"].get("LASPH", False),
             "potcar_spec": d1["input"].get("potcar_spec"),
             "xc_override": xc
         }
         vals = sorted(d2["reduced_cell_formula"].values())
         d["anonymous_formula"] = {
             string.ascii_uppercase[i]: float(vals[i])
             for i in range(len(vals))
         }
         d["output"] = {
             "crystal": d2["output"]["crystal"],
             "final_energy": d2["output"]["final_energy"],
             "final_energy_per_atom": d2["output"]["final_energy_per_atom"]
         }
         d["name"] = "vasp"
         p = d2["input"]["potcar_type"][0].split("_")
         pot_type = p[0]
         functional = "lda" if len(pot_type) == 1 else "_".join(p[1:])
         d["pseudo_potential"] = {
             "functional": functional.lower(),
             "pot_type": pot_type.lower(),
             "labels": d2["input"]["potcar"]
         }
         if len(d["calculations"]) == len(
                 self.runs) or list(vasprun_files.keys())[0] != "relax1":
             d["state"] = "successful" if d2["has_vasp_completed"] \
                 else "unsuccessful"
         else:
             d["state"] = "stopped"
         d["analysis"] = analysis_and_error_checks(d)
         sg = SpacegroupAnalyzer(
             Structure.from_dict(d["output"]["crystal"]), 0.1)
         d["spacegroup"] = {
             "symbol": sg.get_spacegroup_symbol(),
             "number": sg.get_spacegroup_number(),
             "point_group": sg.get_point_group(),
             "source": "spglib",
             "crystal_system": sg.get_crystal_system(),
             "hall": sg.get_hall()
         }
         d["last_updated"] = datetime.datetime.today()
         return d
     except Exception as ex:
         import traceback
         print(traceback.format_exc())
         logger.error("Error in " + os.path.abspath(dir_name) + ".\n" +
                      traceback.format_exc())
         return None
    def test_symmetrization(self):

        # Restricted to primitive_elemental materials due to the risk of
        # broken stoichiometry. For compound materials, use is_polar()

        # Get all slabs for P6/mmm Ti and Fm-3m Ag up to index of 2

        all_Ti_slabs = generate_all_slabs(
            self.ti,
            2,
            10,
            10,
            bonds=None,
            tol=1e-3,
            max_broken_bonds=0,
            lll_reduce=False,
            center_slab=False,
            primitive=True,
            max_normal_search=2,
            symmetrize=True,
        )

        all_Ag_fcc_slabs = generate_all_slabs(
            self.agfcc,
            2,
            10,
            10,
            bonds=None,
            tol=1e-3,
            max_broken_bonds=0,
            lll_reduce=False,
            center_slab=False,
            primitive=True,
            max_normal_search=2,
            symmetrize=True,
        )

        all_slabs = [all_Ti_slabs, all_Ag_fcc_slabs]

        for i, slabs in enumerate(all_slabs):

            assymetric_count = 0
            symmetric_count = 0

            for i, slab in enumerate(slabs):
                sg = SpacegroupAnalyzer(slab)

                # Check if a slab is symmetric
                if not sg.is_laue():
                    assymetric_count += 1
                else:
                    symmetric_count += 1

            # Check if slabs are all symmetric
            self.assertEqual(assymetric_count, 0)
            self.assertEqual(symmetric_count, len(slabs))

        # Check if we can generate symmetric slabs from bulk with no inversion
        all_non_laue_slabs = generate_all_slabs(self.nonlaue,
                                                1,
                                                15,
                                                15,
                                                symmetrize=True)
        self.assertTrue(len(all_non_laue_slabs) > 0)
Esempio n. 14
0
    def __init__(self, structure, symprec=0.01, angle_tolerance=5, atol=1e-8):
        """
        Args:
            structure (Structure): Structure object
            symprec (float): Tolerance for symmetry finding
            angle_tolerance (float): Angle tolerance for symmetry finding.
            atol (float): Absolute tolerance used to compare the input
                structure with the one expected as primitive standard.
                A warning will be issued if the lattices don't match.
        """
        self._structure = structure
        self._sym = SpacegroupAnalyzer(structure, symprec=symprec,
                                       angle_tolerance=angle_tolerance)
        self._prim = self._sym \
            .get_primitive_standard_structure(international_monoclinic=False)
        self._conv = self._sym.get_conventional_standard_structure(international_monoclinic=False)
        self._prim_rec = self._prim.lattice.reciprocal_lattice
        self._kpath = None

        # Note: this warning will be issued for space groups 38-41, since the primitive cell must be
        # reformatted to match Setyawan/Curtarolo convention in order to work with the current k-path
        # generation scheme.
        if not np.allclose(self._structure.lattice.matrix, self._prim.lattice.matrix, atol=atol):
            warnings.warn("The input structure does not match the expected standard primitive! "
                          "The path can be incorrect. Use at your own risk.")

        lattice_type = self._sym.get_lattice_type()
        spg_symbol = self._sym.get_space_group_symbol()

        if lattice_type == "cubic":
            if "P" in spg_symbol:
                self._kpath = self.cubic()
            elif "F" in spg_symbol:
                self._kpath = self.fcc()
            elif "I" in spg_symbol:
                self._kpath = self.bcc()
            else:
                warn("Unexpected value for spg_symbol: %s" % spg_symbol)

        elif lattice_type == "tetragonal":
            if "P" in spg_symbol:
                self._kpath = self.tet()
            elif "I" in spg_symbol:
                a = self._conv.lattice.abc[0]
                c = self._conv.lattice.abc[2]
                if c < a:
                    self._kpath = self.bctet1(c, a)
                else:
                    self._kpath = self.bctet2(c, a)
            else:
                warn("Unexpected value for spg_symbol: %s" % spg_symbol)

        elif lattice_type == "orthorhombic":
            a = self._conv.lattice.abc[0]
            b = self._conv.lattice.abc[1]
            c = self._conv.lattice.abc[2]

            if "P" in spg_symbol:
                self._kpath = self.orc()

            elif "F" in spg_symbol:
                if 1 / a ** 2 > 1 / b ** 2 + 1 / c ** 2:
                    self._kpath = self.orcf1(a, b, c)
                elif 1 / a ** 2 < 1 / b ** 2 + 1 / c ** 2:
                    self._kpath = self.orcf2(a, b, c)
                else:
                    self._kpath = self.orcf3(a, b, c)

            elif "I" in spg_symbol:
                self._kpath = self.orci(a, b, c)

            elif "C" in spg_symbol or "A" in spg_symbol:
                self._kpath = self.orcc(a, b, c)
            else:
                warn("Unexpected value for spg_symbol: %s" % spg_symbol)

        elif lattice_type == "hexagonal":
            self._kpath = self.hex()

        elif lattice_type == "rhombohedral":
            alpha = self._prim.lattice.angles[0]
            if alpha < 90:
                self._kpath = self.rhl1(alpha * pi / 180)
            else:
                self._kpath = self.rhl2(alpha * pi / 180)

        elif lattice_type == "monoclinic":
            a, b, c = self._conv.lattice.abc
            alpha = self._conv.lattice.angles[0]

            if "P" in spg_symbol:
                self._kpath = self.mcl(b, c, alpha * pi / 180)

            elif "C" in spg_symbol:
                kgamma = self._prim_rec.angles[2]
                if kgamma > 90:
                    self._kpath = self.mclc1(a, b, c, alpha * pi / 180)
                if kgamma == 90:
                    self._kpath = self.mclc2(a, b, c, alpha * pi / 180)
                if kgamma < 90:
                    if b * cos(alpha * pi / 180) / c \
                            + b ** 2 * sin(alpha * pi / 180) ** 2 / a ** 2 < 1:
                        self._kpath = self.mclc3(a, b, c, alpha * pi / 180)
                    if b * cos(alpha * pi / 180) / c \
                            + b ** 2 * sin(alpha * pi / 180) ** 2 / a ** 2 == 1:
                        self._kpath = self.mclc4(a, b, c, alpha * pi / 180)
                    if b * cos(alpha * pi / 180) / c \
                            + b ** 2 * sin(alpha * pi / 180) ** 2 / a ** 2 > 1:
                        self._kpath = self.mclc5(a, b, c, alpha * pi / 180)
            else:
                warn("Unexpected value for spg_symbol: %s" % spg_symbol)

        elif lattice_type == "triclinic":
            kalpha = self._prim_rec.angles[0]
            kbeta = self._prim_rec.angles[1]
            kgamma = self._prim_rec.angles[2]
            if kalpha > 90 and kbeta > 90 and kgamma > 90:
                self._kpath = self.tria()
            if kalpha < 90 and kbeta < 90 and kgamma < 90:
                self._kpath = self.trib()
            if kalpha > 90 and kbeta > 90 and kgamma == 90:
                self._kpath = self.tria()
            if kalpha < 90 and kbeta < 90 and kgamma == 90:
                self._kpath = self.trib()

        else:
            warn("Unknown lattice type %s" % lattice_type)
Esempio n. 15
0
def get_bs_extrema(bs,
                   coeff_file,
                   nk_ibz=17,
                   v_cut=1e4,
                   min_normdiff=0.05,
                   Ecut=None,
                   nex_max=0,
                   return_global=False,
                   niter=10):
    """
    returns a dictionary of p-type (valence) and n-type (conduction) band
        extrema k-points by looking at the 1st and 2nd derivatives of the bands
    Args:
        bs (pymatgen BandStructure object): must containt Structure and have
            the same number of valence electrons and settings as the vasprun.xml
            from which coeff_file is generated.
        coeff_file (str): path to the cube file from BoltzTraP run
        nk_ibz (int): maximum number of k-points in one direction in IBZ
        v_cut (float): threshold under which the derivative is assumed 0 [cm/s]
        min_normdiff (float): the minimum allowed distance norm(fractional k)
            in extrema; this is important to avoid numerical instability errors
        Ecut (float or dict): max energy difference with CBM/VBM allowed for
            extrema
        nex_max (int): max number of low-velocity kpts tested for being extrema
        return_global (bool): in addition to the extrema, return the actual
            CBM (global minimum) and VBM (global maximum) w/ their k-point
        niter (int): number of iterations in basinhoopping for finding the
            global extremum
    Returns (dict): {'n': list of extrema fractional coordinates, 'p': same}
    """
    #TODO: MAJOR cleanup needed in this function; also look into parallelizing get_analytical_energy at all kpts if it's time consuming
    #TODO: if decided to only include one of many symmetrically equivalent extrema, write a method to keep only one of symmetrically equivalent extrema as a representative
    Ecut = Ecut or 10 * k_B * 300
    if not isinstance(Ecut, dict):
        Ecut = {'n': Ecut, 'p': Ecut}
    actual_cbm_vbm = {'n': {}, 'p': {}}
    vbm_idx, _ = get_bindex_bspin(bs.get_vbm(), is_cbm=False)
    # vbm_idx = bs.get_vbm()['band_index'][Spin.up][0]
    ibands = [1, 2]  # in this notation, 1 is the last valence band
    ibands = [i + vbm_idx for i in ibands]
    ibz = HighSymmKpath(bs.structure)
    sg = SpacegroupAnalyzer(bs.structure)
    kmesh = sg.get_ir_reciprocal_mesh(mesh=(nk_ibz, nk_ibz, nk_ibz))
    kpts = [k_n_w[0] for k_n_w in kmesh]
    kpts.extend(
        insert_intermediate_kpoints(ibz.kpath['kpoints'].values(), n=10))

    # grid = {'energy': [], 'velocity': [], 'mass': [], 'normv': []}
    extrema = {'n': [], 'p': []}
    engre, nwave, nsym, nstv, vec, vec2, out_vec2, br_dir = get_energy_args(
        coeff_file=coeff_file, ibands=ibands)

    cbmk = np.array(bs.get_cbm()['kpoint'].frac_coords)
    vbmk = np.array(bs.get_cbm()['kpoint'].frac_coords)
    bounds = [(-0.5, 0.5), (-0.5, 0.5), (-0.5, 0.5)]
    func = lambda x: calc_analytical_energy(x,
                                            engre[1],
                                            nwave,
                                            nsym,
                                            nstv,
                                            vec,
                                            vec2,
                                            out_vec2,
                                            br_dir,
                                            sgn=-1,
                                            scissor=0)[0]
    opt = basinhopping(func,
                       x0=cbmk,
                       niter=20,
                       T=0.1,
                       minimizer_kwargs={'bounds': bounds})
    kpts.append(opt.x)

    func = lambda x: -calc_analytical_energy(x,
                                             engre[0],
                                             nwave,
                                             nsym,
                                             nstv,
                                             vec,
                                             vec2,
                                             out_vec2,
                                             br_dir,
                                             sgn=+1,
                                             scissor=0)[0]
    opt = basinhopping(func,
                       x0=vbmk,
                       niter=niter,
                       T=0.1,
                       minimizer_kwargs={'bounds': bounds})
    kpts.append(opt.x)
    for iband in range(len(ibands)):
        is_cb = [False, True][iband]
        tp = ['p', 'n'][iband]
        energies = []
        velocities = []
        normv = []
        masses = []

        ######################################################################
        # kpts_list = [np.array([ 0.,  0.,  0.]),
        #              [-0.5, -0.5, -0.5],
        #              [-0.5, 0.0, 0.0],
        #              [0., -0.5, 0.],
        #              [0., 0., -0.5],
        #              [0.5, 0.5, 0.5],
        #              [0.5, 0.0, 0.0],
        #              [0., 0.5, 0.],
        #              [0., 0., 0.5],
        #              np.array([0., 0., 0.]),
        #              [ 0.5,  0.,  0.5],
        #             [0., -0.5, -0.5],
        #              [0.5 , 0.5 , 0.],
        #              [-0.5, 0., -0.5],
        #              [0., 0.5, 0.5],
        #              [-0.5, -0.5, 0.]
        #              ]

        #        kpts_list = [ [0.0, 0.0, 0.0],
        #            [ 0.  ,  0.44,  0.44],
        # [ 0.44,  0.44  ,0.  ],
        # [ 0. ,  -0.44, -0.44],
        # [-0.44 ,-0.44,  0.  ],
        # [ 0.44 , 0.  ,  0.44],
        # [-0.44 , 0.  , -0.44],
        # [ 0. ,  -0.44 ,-0.44],
        # [-0.44 ,-0.44 , 0.  ],
        # [ 0. ,   0.44 , 0.44],
        # [ 0.44,  0.44 , 0.  ],
        # [-0.44 , 0. ,  -0.44],
        # [ 0.44 , 0. ,   0.44] ,
        #   [ 0.5,  0. ,  0.5],
        # [ 0. ,  0.5,  0.5],
        # [ 0.5 , 0.5,  0. ],
        # [-0.5 , 0.  ,-0.5],
        # [ 0. , -0.5 ,-0.5],
        # [-0.5 ,-0.5 , 0. ]     ]
        #        print('here kpts_list:')
        #        print(kpts_list)
        #        print('here the energies')
        #        for ik, kpt in enumerate(kpts_list):
        #            en, v, mass = calc_analytical_energy(kpt, engre[iband], nwave,
        #                nsym, nstv, vec, vec2, out_vec2, br_dir, sgn=1, scissor=0)
        #            print en

        ######################################################################

        for ik, kpt in enumerate(kpts):
            en, v, mass = calc_analytical_energy(kpt,
                                                 engre[iband],
                                                 nwave,
                                                 nsym,
                                                 nstv,
                                                 vec,
                                                 vec2,
                                                 out_vec2,
                                                 br_dir,
                                                 sgn=1,
                                                 scissor=0)
            energies.append(en)
            velocities.append(abs(v))
            normv.append(norm(v))
            masses.append(mass.trace() / 3)
        indexes = np.argsort(normv)
        energies = [energies[i] for i in indexes]
        normv = [normv[i] for i in indexes]
        velocities = [velocities[i] for i in indexes]
        masses = [masses[i] for i in indexes]
        kpts = [np.array(kpts[i]) for i in indexes]

        # print('here')
        # cbmk = np.array([ 0.44,  0.44,  0.  ])
        # print(np.vstack((bs.get_sym_eq_kpoints(cbmk),bs.get_sym_eq_kpoints(-cbmk))))
        # cbmk = np.array([ 0.5,  0. ,  0.5])
        # print(np.vstack((bs.get_sym_eq_kpoints(cbmk),bs.get_sym_eq_kpoints(-cbmk))))

        # print('here values')
        # print energies[:10]
        # print normv[:10]
        # print kpts[:10]
        # print masses[:10]
        if is_cb:
            iextrem = np.argmin(energies)
            extremum0 = energies[iextrem]  # extremum0 is numerical CBM here
            actual_cbm_vbm[tp]['energy'] = extremum0
            actual_cbm_vbm[tp]['kpoint'] = kpts[iextrem]
            # The following is in case CBM doesn't have a zero numerical norm(v)
            closest_cbm = get_closest_k(
                kpts[iextrem],
                np.vstack((bs.get_sym_eq_kpoints(cbmk),
                           bs.get_sym_eq_kpoints(-cbmk))))
            if norm(np.array(kpts[iextrem]) - closest_cbm) < min_normdiff and \
                            abs(bs.get_cbm()['energy']-extremum0) < 0.05:
                extrema['n'].append(cbmk)
            else:
                extrema['n'].append(kpts[iextrem])
        else:
            iextrem = np.argmax(energies)
            extremum0 = energies[iextrem]
            actual_cbm_vbm[tp]['energy'] = extremum0
            actual_cbm_vbm[tp]['kpoint'] = kpts[iextrem]
            closest_vbm = get_closest_k(
                kpts[iextrem],
                np.vstack((bs.get_sym_eq_kpoints(vbmk),
                           bs.get_sym_eq_kpoints(-vbmk))))
            if norm(np.array(kpts[iextrem]) - closest_vbm) < min_normdiff and \
                            abs(bs.get_vbm()['energy']-extremum0) < 0.05:
                extrema['p'].append(vbmk)
            else:
                extrema['p'].append(kpts[iextrem])

        if normv[0] > v_cut:
            raise ValueError('No extremum point (v<{}) found!'.format(v_cut))
        for i in range(0, len(kpts[:nex_max])):
            # if (velocities[i] > v_cut).all() :
            if normv[i] > v_cut:
                break
            else:
                far_enough = True
                for k in extrema[tp]:
                    if norm(
                            get_closest_k(kpts[i],
                                          np.vstack(
                                              (bs.get_sym_eq_kpoints(k),
                                               bs.get_sym_eq_kpoints(-k))),
                                          return_diff=True)) <= min_normdiff:
                        # if norm(kpts[i] - k) <= min_normdiff:
                        far_enough = False
                if far_enough \
                        and abs(energies[i] - extremum0) < Ecut[tp] \
                        and masses[i] * ((-1) ** (int(is_cb) + 1)) >= 0:
                    extrema[tp].append(kpts[i])
    if not return_global:
        return extrema
    else:
        return extrema, actual_cbm_vbm
Esempio n. 16
0
from mpinterfaces.nanoparticle import Nanoparticle

# -----------------------------------
# nanoparticle specifications
# -----------------------------------
# max radius in angstroms
rmax = 15
# surface families to be chopped off
hkl_family = [(1, 0, 0), (1, 1, 1)]
# surfac energies could be in any units, will be normalized
surface_energies = [28, 25]

# -----------------------------------
# initial structure
# -----------------------------------
# caution: set the structure wrt which the the miller indices are
# specified. use your own key
structure = get_struct_from_mp('PbS', MAPI_KEY="")
# primitive ---> conventional cell
sa = SpacegroupAnalyzer(structure)
structure_conventional = sa.get_conventional_standard_structure()

# -----------------------------------
# create nanoparticle
# -----------------------------------
nanoparticle = Nanoparticle(structure_conventional, rmax=rmax,
                            hkl_family=hkl_family,
                            surface_energies=surface_energies)
nanoparticle.create()
nanoparticle.to(fmt='xyz', filename='nanoparticle.xyz')
Esempio n. 17
0
    def get_valences(self, structure):
        """
        Returns a list of valences for the structure. This currently works only
        for ordered structures only.

        Args:
            structure: Structure to analyze

        Returns:
            A list of valences for each site in the structure (for an ordered
            structure), e.g., [1, 1, -2] or a list of lists with the
            valences for each fractional element of each site in the
            structure (for an unordered structure),
            e.g., [[2, 4], [3], [-2], [-2], [-2]]

        Raises:
            A ValueError if the valences cannot be determined.
        """
        els = [Element(el.symbol) for el in structure.composition.elements]

        if not set(els).issubset(set(BV_PARAMS.keys())):
            raise ValueError("Structure contains elements not in set of BV parameters!")

        # Perform symmetry determination and get sites grouped by symmetry.
        if self.symm_tol:
            finder = SpacegroupAnalyzer(structure, self.symm_tol)
            symm_structure = finder.get_symmetrized_structure()
            equi_sites = symm_structure.equivalent_sites
        else:
            equi_sites = [[site] for site in structure]

        # Sort the equivalent sites by decreasing electronegativity.
        equi_sites = sorted(equi_sites, key=lambda sites: -sites[0].species.average_electroneg)

        # Get a list of valences and probabilities for each symmetrically
        # distinct site.
        valences = []
        all_prob = []
        if structure.is_ordered:
            for sites in equi_sites:
                test_site = sites[0]
                nn = structure.get_neighbors(test_site, self.max_radius)
                prob = self._calc_site_probabilities(test_site, nn)
                all_prob.append(prob)
                val = list(prob.keys())
                # Sort valences in order of decreasing probability.
                val = sorted(val, key=lambda v: -prob[v])
                # Retain probabilities that are at least 1/100 of highest prob.
                valences.append(list(filter(lambda v: prob[v] > 0.01 * prob[val[0]], val)))
        else:
            full_all_prob = []
            for sites in equi_sites:
                test_site = sites[0]
                nn = structure.get_neighbors(test_site, self.max_radius)
                prob = self._calc_site_probabilities_unordered(test_site, nn)
                all_prob.append(prob)
                full_all_prob.extend(prob.values())
                vals = []
                for (elsp, occ) in get_z_ordered_elmap(test_site.species):
                    val = list(prob[elsp.symbol].keys())
                    # Sort valences in order of decreasing probability.
                    val = sorted(val, key=lambda v: -prob[elsp.symbol][v])
                    # Retain probabilities that are at least 1/100 of highest
                    # prob.
                    vals.append(
                        list(
                            filter(
                                lambda v: prob[elsp.symbol][v] > 0.001 * prob[elsp.symbol][val[0]],
                                val,
                            )
                        )
                    )
                valences.append(vals)

        # make variables needed for recursion
        if structure.is_ordered:
            nsites = np.array([len(i) for i in equi_sites])
            vmin = np.array([min(i) for i in valences])
            vmax = np.array([max(i) for i in valences])

            self._n = 0
            self._best_score = 0
            self._best_vset = None

            def evaluate_assignment(v_set):
                el_oxi = collections.defaultdict(list)
                for i, sites in enumerate(equi_sites):
                    el_oxi[sites[0].specie.symbol].append(v_set[i])
                max_diff = max([max(v) - min(v) for v in el_oxi.values()])
                if max_diff > 1:
                    return
                score = functools.reduce(operator.mul, [all_prob[i][v] for i, v in enumerate(v_set)])
                if score > self._best_score:
                    self._best_vset = v_set
                    self._best_score = score

            def _recurse(assigned=[]):
                # recurses to find permutations of valences based on whether a
                # charge balanced assignment can still be found
                if self._n > self.max_permutations:
                    return None

                i = len(assigned)
                highest = vmax.copy()
                highest[:i] = assigned
                highest *= nsites
                highest = np.sum(highest)

                lowest = vmin.copy()
                lowest[:i] = assigned
                lowest *= nsites
                lowest = np.sum(lowest)

                if highest < 0 or lowest > 0:
                    self._n += 1
                    return None

                if i == len(valences):
                    evaluate_assignment(assigned)
                    self._n += 1
                    return None
                for v in valences[i]:
                    new_assigned = list(assigned)
                    _recurse(new_assigned + [v])
                return None

        else:
            nsites = np.array([len(i) for i in equi_sites])
            tmp = []
            attrib = []
            for insite, nsite in enumerate(nsites):
                for val in valences[insite]:
                    tmp.append(nsite)
                    attrib.append(insite)
            new_nsites = np.array(tmp)
            fractions = []
            elements = []
            for sites in equi_sites:
                for sp, occu in get_z_ordered_elmap(sites[0].species):
                    elements.append(sp.symbol)
                    fractions.append(occu)
            fractions = np.array(fractions, np.float)
            new_valences = []
            for vals in valences:
                for val in vals:
                    new_valences.append(val)
            vmin = np.array([min(i) for i in new_valences], np.float)
            vmax = np.array([max(i) for i in new_valences], np.float)

            self._n = 0
            self._best_score = 0
            self._best_vset = None

            def evaluate_assignment(v_set):
                el_oxi = collections.defaultdict(list)
                jj = 0
                for i, sites in enumerate(equi_sites):
                    for specie, occu in get_z_ordered_elmap(sites[0].species):
                        el_oxi[specie.symbol].append(v_set[jj])
                        jj += 1
                max_diff = max([max(v) - min(v) for v in el_oxi.values()])
                if max_diff > 2:
                    return

                score = functools.reduce(
                    operator.mul,
                    [all_prob[attrib[iv]][elements[iv]][vv] for iv, vv in enumerate(v_set)],
                )
                if score > self._best_score:
                    self._best_vset = v_set
                    self._best_score = score

            def _recurse(assigned=[]):
                # recurses to find permutations of valences based on whether a
                # charge balanced assignment can still be found
                if self._n > self.max_permutations:
                    return None

                i = len(assigned)
                highest = vmax.copy()
                highest[:i] = assigned
                highest *= new_nsites
                highest *= fractions
                highest = np.sum(highest)

                lowest = vmin.copy()
                lowest[:i] = assigned
                lowest *= new_nsites
                lowest *= fractions
                lowest = np.sum(lowest)

                if highest < -self.charge_neutrality_tolerance or lowest > self.charge_neutrality_tolerance:
                    self._n += 1
                    return None

                if i == len(new_valences):
                    evaluate_assignment(assigned)
                    self._n += 1
                    return None

                for v in new_valences[i]:
                    new_assigned = list(assigned)
                    _recurse(new_assigned + [v])

                return None

        _recurse()

        if self._best_vset:
            if structure.is_ordered:
                assigned = {}
                for val, sites in zip(self._best_vset, equi_sites):
                    for site in sites:
                        assigned[site] = val

                return [int(assigned[site]) for site in structure]
            assigned = {}
            new_best_vset = []
            for ii in range(len(equi_sites)):
                new_best_vset.append(list())
            for ival, val in enumerate(self._best_vset):
                new_best_vset[attrib[ival]].append(val)
            for val, sites in zip(new_best_vset, equi_sites):
                for site in sites:
                    assigned[site] = val

            return [[int(frac_site) for frac_site in assigned[site]] for site in structure]
        raise ValueError("Valences cannot be assigned!")
Esempio n. 18
0
    def _gen_input_file(self):
        """
        Generate the necessary struct_enum.in file for enumlib. See enumlib
        documentation for details.
        """
        coord_format = "{:.6f} {:.6f} {:.6f}"
        # Using symmetry finder, get the symmetrically distinct sites.
        fitter = SpacegroupAnalyzer(self.structure, self.symm_prec)
        symmetrized_structure = fitter.get_symmetrized_structure()
        logger.debug("Spacegroup {} ({}) with {} distinct sites".format(
            fitter.get_space_group_symbol(),
            fitter.get_space_group_number(),
            len(symmetrized_structure.equivalent_sites))
        )

        """
        Enumlib doesn"t work when the number of species get too large. To
        simplify matters, we generate the input file only with disordered sites
        and exclude the ordered sites from the enumeration. The fact that
        different disordered sites with the exact same species may belong to
        different equivalent sites is dealt with by having determined the
        spacegroup earlier and labelling the species differently.
        """

        # index_species and index_amounts store mappings between the indices
        # used in the enum input file, and the actual species and amounts.
        index_species = []
        index_amounts = []

        # Stores the ordered sites, which are not enumerated.
        ordered_sites = []
        disordered_sites = []
        coord_str = []
        for sites in symmetrized_structure.equivalent_sites:
            if sites[0].is_ordered:
                ordered_sites.append(sites)
            else:
                sp_label = []
                species = {k: v for k, v in sites[0].species.items()}
                if sum(species.values()) < 1 - EnumlibAdaptor.amount_tol:
                    # Let us first make add a dummy element for every single
                    # site whose total occupancies don't sum to 1.
                    species[DummySpecie("X")] = 1 - sum(species.values())
                for sp in species.keys():
                    if sp not in index_species:
                        index_species.append(sp)
                        sp_label.append(len(index_species) - 1)
                        index_amounts.append(species[sp] * len(sites))
                    else:
                        ind = index_species.index(sp)
                        sp_label.append(ind)
                        index_amounts[ind] += species[sp] * len(sites)
                sp_label = "/".join(["{}".format(i) for i in sorted(sp_label)])
                for site in sites:
                    coord_str.append("{} {}".format(
                        coord_format.format(*site.coords),
                        sp_label))
                disordered_sites.append(sites)

        def get_sg_info(ss):
            finder = SpacegroupAnalyzer(Structure.from_sites(ss),
                                        self.symm_prec)
            return finder.get_space_group_number()

        target_sgnum = get_sg_info(symmetrized_structure.sites)
        curr_sites = list(itertools.chain.from_iterable(disordered_sites))
        sgnum = get_sg_info(curr_sites)
        ordered_sites = sorted(ordered_sites, key=lambda sites: len(sites))
        logger.debug("Disordered sites has sg # %d" % (sgnum))
        self.ordered_sites = []

        # progressively add ordered sites to our disordered sites
        # until we match the symmetry of our input structure
        if self.check_ordered_symmetry:
            while sgnum != target_sgnum and len(ordered_sites) > 0:
                sites = ordered_sites.pop(0)
                temp_sites = list(curr_sites) + sites
                new_sgnum = get_sg_info(temp_sites)
                if sgnum != new_sgnum:
                    logger.debug("Adding %s in enum. New sg # %d"
                                 % (sites[0].specie, new_sgnum))
                    index_species.append(sites[0].specie)
                    index_amounts.append(len(sites))
                    sp_label = len(index_species) - 1
                    for site in sites:
                        coord_str.append("{} {}".format(
                            coord_format.format(*site.coords),
                            sp_label))
                    disordered_sites.append(sites)
                    curr_sites = temp_sites
                    sgnum = new_sgnum
                else:
                    self.ordered_sites.extend(sites)

        for sites in ordered_sites:
            self.ordered_sites.extend(sites)

        self.index_species = index_species

        lattice = self.structure.lattice

        output = [self.structure.formula, "bulk"]
        for vec in lattice.matrix:
            output.append(coord_format.format(*vec))
        output.append("%d" % len(index_species))
        output.append("%d" % len(coord_str))
        output.extend(coord_str)

        output.append("{} {}".format(self.min_cell_size, self.max_cell_size))
        output.append(str(self.enum_precision_parameter))
        output.append("full")

        ndisordered = sum([len(s) for s in disordered_sites])
        base = int(ndisordered*lcm(*[f.limit_denominator(ndisordered * self.max_cell_size).denominator
                                     for f in map(fractions.Fraction, index_amounts)]))

        # This multiplicative factor of 10 is to prevent having too small bases
        # which can lead to rounding issues in the next step.
        # An old bug was that a base was set to 8, with a conc of 0.4:0.6. That
        # resulted in a range that overlaps and a conc of 0.5 satisfying this
        # enumeration. See Cu7Te5.cif test file.
        base *= 10

        # base = ndisordered #10 ** int(math.ceil(math.log10(ndisordered)))
        # To get a reasonable number of structures, we fix concentrations to the
        # range expected in the original structure.
        total_amounts = sum(index_amounts)
        for amt in index_amounts:
            conc = amt / total_amounts

            if abs(conc * base - round(conc * base)) < 1e-5:
                output.append("{} {} {}".format(int(round(conc * base)),
                                                int(round(conc * base)),
                                                base))
            else:
                min_conc = int(math.floor(conc * base))
                output.append("{} {} {}".format(min_conc - 1, min_conc + 1,
                                                base))
        output.append("")
        logger.debug("Generated input file:\n{}".format("\n".join(output)))
        with open("struct_enum.in", "w") as f:
            f.write("\n".join(output))
Esempio n. 19
0
    def get_ieee_rotation(structure):
        """
        Given a structure associated with a tensor, determines
        the rotation matrix for IEEE conversion according to
        the 1987 IEEE standards.

        Args:
            structure (Structure): a structure associated with the
                tensor to be converted to the IEEE standard
        """

        # Check conventional setting:
        sga = SpacegroupAnalyzer(structure)
        dataset = sga.get_symmetry_dataset()
        trans_mat = dataset['transformation_matrix']
        conv_latt = Lattice(np.transpose(np.dot(np.transpose(
            structure.lattice.matrix), np.linalg.inv(trans_mat))))
        xtal_sys = sga.get_crystal_system()

        vecs = conv_latt.matrix
        lengths = np.array(conv_latt.abc)
        angles = np.array(conv_latt.angles)
        rotation = np.zeros((3, 3))

        # IEEE rules: a,b,c || x1,x2,x3
        if xtal_sys == "cubic":
            rotation = [vecs[i] / lengths[i] for i in range(3)]

        # IEEE rules: a=b in length; c,a || x3, x1
        elif xtal_sys == "tetragonal":
            rotation = np.array([vec / mag for (mag, vec) in
                                 sorted(zip(lengths, vecs),
                                        key=lambda x: x[0])])
            if abs(lengths[2] - lengths[1]) < abs(lengths[1] - lengths[0]):
                rotation[0], rotation[2] = rotation[2], rotation[0].copy()
            rotation[1] = get_uvec(np.cross(rotation[2], rotation[0]))

        # IEEE rules: c<a<b; c,a || x3,x1
        elif xtal_sys == "orthorhombic":
            rotation = [vec / mag for (mag, vec) in sorted(zip(lengths, vecs))]
            rotation = np.roll(rotation, 2, axis=0)

        # IEEE rules: c,a || x3,x1, c is threefold axis
        # Note this also includes rhombohedral crystal systems
        elif xtal_sys in ("trigonal", "hexagonal"):
            # find threefold axis:
            tf_index = np.argmin(abs(angles - 120.))
            non_tf_mask = np.logical_not(angles == angles[tf_index])
            rotation[2] = get_uvec(vecs[tf_index])
            rotation[0] = get_uvec(vecs[non_tf_mask][0])
            rotation[1] = get_uvec(np.cross(rotation[2], rotation[0]))

        # IEEE rules: b,c || x2,x3; alpha=beta=90, c<a
        elif xtal_sys == "monoclinic":
            # Find unique axis
            u_index = np.argmax(abs(angles - 90.))
            n_umask = np.logical_not(angles == angles[u_index])
            rotation[1] = get_uvec(vecs[u_index])
            # Shorter of remaining lattice vectors for c axis
            c = [vec / mag for (mag, vec) in
                 sorted(zip(lengths[n_umask], vecs[n_umask]))][0]
            rotation[2] = np.array(c)
            rotation[0] = np.cross(rotation[1], rotation[2])

        # IEEE rules: c || x3
        elif xtal_sys == "triclinic":
            rotation = [vec / mag for (mag, vec) in sorted(zip(lengths, vecs))]
            rotation = np.roll(rotation, 2, axis=0)
            rotation[1] = get_uvec(np.cross(rotation[2], rotation[1]))
            rotation[0] = np.cross(rotation[1], rotation[2])

        return rotation
Esempio n. 20
0
 def get_sg_info(ss):
     finder = SpacegroupAnalyzer(Structure.from_sites(ss),
                                 self.symm_prec)
     return finder.get_space_group_number()
Esempio n. 21
0
    def write_etree(self,
                    celltype,
                    cartesian=False,
                    bandstr=False,
                    symprec=0.4,
                    angle_tolerance=5):
        """
        :param celltype:
        :param cartesian:
        :param bandstr:
        :param symprec:
        :param angle_tolerance:
        :return:
        """
        root = ET.Element('input')
        root.set(
            '{http://www.w3.org/2001/XMLSchema-instance}noNamespaceSchemaLocation',
            'http://xml.exciting-code.org/excitinginput.xsd')
        title = ET.SubElement(root, 'title')
        title.text = self.title
        if cartesian:
            structure = ET.SubElement(root,
                                      'structure',
                                      cartesian="true",
                                      speciespath="./")
        else:
            structure = ET.SubElement(root, 'structure', speciespath="./")

        crystal = ET.SubElement(structure, 'crystal')
        # set scale such that lattice vector can be given in Angstrom
        ang2bohr = const.value('Angstrom star') / const.value('Bohr radius')
        crystal.set('scale', str(ang2bohr))
        # determine which structure to use
        finder = SpacegroupAnalyzer(self.structure,
                                    symprec=symprec,
                                    angle_tolerance=angle_tolerance)
        if celltype == 'primitive':
            new_struct = finder.get_primitive_standard_structure(
                international_monoclinic=False)
        elif celltype == 'conventional':
            new_struct = finder.get_conventional_standard_structure(
                international_monoclinic=False)
        elif celltype == 'unchanged':
            new_struct = self.structure
        else:
            raise ValueError('Type of unit cell not recognized!')

        # write lattice
        basis = new_struct.lattice.matrix
        for i in range(3):
            basevect = ET.SubElement(crystal, 'basevect')
            basevect.text = "%16.8f %16.8f %16.8f" % (basis[i][0], basis[i][1],
                                                      basis[i][2])
        # write atomic positions for each species
        index = 0
        for i in sorted(new_struct.types_of_species, key=lambda el: el.X):
            species = ET.SubElement(structure,
                                    'species',
                                    speciesfile=i.symbol + '.xml')
            sites = new_struct.indices_from_symbol(i.symbol)

            for j in sites:
                coord = "%16.8f %16.8f %16.8f" % (new_struct[j].frac_coords[0],
                                                  new_struct[j].frac_coords[1],
                                                  new_struct[j].frac_coords[2])
                # obtain cartesian coords from fractional ones if needed
                if cartesian:
                    coord2 = []
                    for k in range(3):
                        inter = (new_struct[j].frac_coords[k] * basis[0][k] +
                                 new_struct[j].frac_coords[k] * basis[1][k] +
                                 new_struct[j].frac_coords[k] *
                                 basis[2][k]) * ang2bohr
                        coord2.append(inter)
                    coord = "%16.8f %16.8f %16.8f" % (coord2[0], coord2[1],
                                                      coord2[2])

                # write atomic positions
                index = index + 1
                _ = ET.SubElement(species, 'atom', coord=coord)
        # write bandstructure if needed
        if bandstr and celltype == 'primitive':
            kpath = HighSymmKpath(new_struct,
                                  symprec=symprec,
                                  angle_tolerance=angle_tolerance)
            prop = ET.SubElement(root, 'properties')
            bandstrct = ET.SubElement(prop, 'bandstructure')
            for i in range(len(kpath.kpath['path'])):
                plot = ET.SubElement(bandstrct, 'plot1d')
                path = ET.SubElement(plot, 'path', steps='100')
                for j in range(len(kpath.kpath['path'][i])):
                    symbol = kpath.kpath['path'][i][j]
                    coords = kpath.kpath['kpoints'][symbol]
                    coord = "%16.8f %16.8f %16.8f" % (coords[0], coords[1],
                                                      coords[2])
                    if symbol == '\\Gamma':
                        symbol = 'GAMMA'
                    _ = ET.SubElement(path, 'point', coord=coord, label=symbol)
        elif bandstr and celltype != 'primitive':
            raise ValueError("Bandstructure is only implemented for the \
                              standard primitive unit cell!")
        return root
Esempio n. 22
0
    'Ba': 2,
    'Ca': 2,
    'Ti': 4,
    'O': 2,
    'F': 1,
    'La': 3,
    'Mn': 3,
    'Er': 3,
    'Ni': 3
}
''' Define args for minimizer. '''

num_atoms = structure.composition.num_atoms  # number of atoms in cell
lattice = structure.lattice()  # lattice vectors
# Check and save space group number
space_group = SpacegroupAnalyzer(structure,
                                 symprec=0.01).get_spacegroup_number()
# wyckoff list used to sets bounds for space group symmetries
wycks = SpacegroupAnalyzer(structure,
                           symprec=0.01).get_symmetry_dataset()['wyckoffs']
Species_list = structure.species  #list of ion names.
'''sum of Shannon ionic radii to account for hard spheres distances of anions'''
Shannon_anion_anion = 2.7

myargs = (cations, anions, lattice, Species_list, num_atoms, cutoff_distance, \
BVpara, Formal_Valence, wycks, space_group, b0, center_atom_in_regular_poly, max_angle,\
 nearest_neighbor_distance, Shannon_anion_anion)
'''Define site symmetries for each wyckoff position in cell.
Currently requires user input, that can be obtained from SITESYM on the Bilbao Cyst. Server.
Possible Python wrapper for this may exist to automate process in Compuational Crystallography toolbox 
https://cctbx.github.io'''
site_operations = {
Esempio n. 23
0
    def get_conventional_cell(self):
        #
        # infomation of original primitive cell
        #
        self.prim_atom_info = {}
        data_set = self.spg.get_symmetry_dataset()
        wyckoffs = data_set['wyckoffs']
        natoms = len(self.atoms)
        atom_info_tmp = []
        for iatom in range(natoms):
            element = self.atoms[iatom],
            wyckoff_letter = wyckoffs[iatom]
            info = {'element': self.atoms[iatom],
                    'wyckoff_letter': wyckoffs[iatom]}
            atom_info_tmp.append(info)

        element_list = []
        atom_info = []
        for info in atom_info_tmp:
            element = info['element']
            wyckoff_letter = info['wyckoff_letter']
            if element not in element_list:
                element_list.append(element)
                info = {}
                info['element'] = element
                info['wyckoff_letter'] = [wyckoff_letter]
                atom_info.append(info)
            else:
                atom_info[-1]['wyckoff_letter'].append(wyckoff_letter)

        self.primitive_cell_info = {'ispg': self.ispg,
                                    'natoms': natoms,
                                    'atom_info': atom_info}

        #
        # conventional unit cell
        #
        data = str(self.spg.get_conventional_standard_structure())
        data_set = self.spg.get_symmetry_dataset()
        wyckoffs = data_set['wyckoffs']
        data_region = False
        atom_info = []
        self.atoms = []
        atomic_positions = []
        for line in data.split('\n'):
            linebuf = line.strip()
            if re.search('abc\s+:', linebuf):
                lattice_length = \
                    [float(x) for x in linebuf.split(':')[1].split()]
            if re.search('angles\s*:', linebuf):
                lattice_angle = \
                    [float(x) for x in linebuf.split(':')[1].split()]
            if re.search('^#\s+SP', linebuf):
                data_region = True
                continue
            if data_region:
                if re.search('^---', linebuf):
                    continue
                data_line = linebuf.split()
                element = data_line[1]
                position = [float(x) for x in data_line[2:]]
                self.atoms.append(element)
                atomic_positions.append(position)
        #
        # generate conventional infomation
        #
        a, b, c = lattice_length
        alpha, beta, gamma = lattice_angle
        lattice = mg.Lattice.from_parameters(a, b, c, alpha, beta, gamma)
        try:
            self.structure = mg.Structure(lattice, self.atoms,
                                          atomic_positions)
            self.spg = SpacegroupAnalyzer(self.structure,
                                          symprec=self.symprec,
                                          angle_tolerance=self.angle_trelance)
            self.ispg = self.spg.get_space_group_number()
            self.hmname = self.spg.get_space_group_symbol()
        except TypeError:
            print()
            print(' **** INTERNAL LIBRARY WARNING ****')
            print('   pymatgen and spglib cannot identify space group', end='')
            print(' for smaller primitive cell.')
            print('   we expect this bug will be fixed near future.', end='')
            print(' metis uses original primitive cell.')
            self.spg = None
            self.ispg = None
            self.hmname = None
        return self
Esempio n. 24
0
def site_weighted_spectrum(xas_list: List["XAS"],
                           num_samples: int = 500) -> "XAS":
    """
    Obtain site-weighted XAS object based on site multiplicity for each
    absorbing index and its corresponding site-wise spectrum.

    Args:
        xas_list([XAS]): List of XAS object to be weighted
        num_samples(int): Number of samples for interpolation

    Returns:
        XAS object: The site-weighted spectrum
    """
    m = StructureMatcher()
    groups = m.group_structures([i.structure for i in xas_list])
    if len(groups) > 1:
        raise ValueError("The input structures mismatch")
    if not len({i.absorbing_element
                for i in xas_list}) == len({i.edge
                                            for i in xas_list}) == 1:
        raise ValueError(
            "Can only perform site-weighting for spectra with same absorbing element and same absorbing edge."
        )
    if len({i.absorbing_index
            for i in xas_list
            }) == 1 or None in {i.absorbing_index
                                for i in xas_list}:
        raise ValueError(
            "Need at least two site-wise spectra to perform site-weighting")

    sa = SpacegroupAnalyzer(groups[0][0])
    ss = sa.get_symmetrized_structure()
    maxes, mines = [], []
    fs = []
    multiplicities = []

    for xas in xas_list:
        multiplicity = len(ss.find_equivalent_sites(ss[xas.absorbing_index]))
        multiplicities.append(multiplicity)
        maxes.append(max(xas.x))
        mines.append(min(xas.x))
        # use 3rd-order spline interpolation for mu (idx 3) vs energy (idx 0).
        f = interp1d(
            np.asarray(xas.x),
            np.asarray(xas.y),
            bounds_error=False,
            fill_value=0,
            kind="cubic",
        )
        fs.append(f)
    # Interpolation within the intersection of x-axis ranges.
    x_axis = np.linspace(max(mines), min(maxes), num=num_samples)
    weighted_spectrum = np.zeros(num_samples)
    sum_multiplicities = sum(multiplicities)

    for i, j in enumerate(multiplicities):
        weighted_spectrum += (j * fs[i](x_axis)) / sum_multiplicities

    return XAS(
        x_axis,
        weighted_spectrum,
        ss,
        xas.absorbing_element,
        xas.edge,
        xas.spectrum_type,
    )
                return supercell_size_test
        else:
            supercell_size = supercell_size_test


# Set Materials Project structure ID here:
structure_id = 'mp-1265'

rester = pymatgen.MPRester(os.environ['PMG_MAPI_KEY'])

pmg_structure = rester.get_structure_by_material_id(structure_id)
pmg_band = rester.get_bandstructure_by_material_id(structure_id)

material_name = pmg_structure.formula.replace('1', '').replace(' ', '')

spa = SpacegroupAnalyzer(pmg_structure)

conventional = spa.get_conventional_standard_structure()
primitive = spa.get_primitive_standard_structure()

print('Conventional cell')
print conventional
primitive_matrix = np.dot(np.linalg.inv(conventional.lattice.matrix),
                          primitive.lattice.matrix)
primitive_matrix = np.round(primitive_matrix, decimals=6).tolist()

structure = StructureData(pymatgen=conventional).store()

print('Primitive matrix')
print primitive_matrix
Esempio n. 26
0
from pymatgen.core.structure import Structure
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
from jarvis.lammps.NEW_LAMMPS import main_func, write_lammps_data, write_lammps_in
from pymatgen.io.vasp.inputs import Poscar
"""
Step-1: writing LAMMPS data file
Import VASP's POSCAR or cif format file,make it 15x15x15 with conventional cell
"""
c_size = 15
p = Structure.from_file("POSCAR")
sg_mat = SpacegroupAnalyzer(p)
mat_cvn = sg_mat.get_conventional_standard_structure()
#mat_cvn.to(fmt='poscar',filename='POSCAR_cvb.vasp') #can be visualized using VESTA
dim1 = int((float(c_size) / float(max(abs(mat_cvn.lattice.matrix[0]))))) + 1
dim2 = int(float(c_size) / float(max(abs(mat_cvn.lattice.matrix[1])))) + 1
dim3 = int(float(c_size) / float(max(abs(mat_cvn.lattice.matrix[2])))) + 1
p.make_supercell([dim1, dim2, dim3])
write_lammps_data(p, file='dat.dat')  #can be visulaized using Ovito
"""
Step-2: writing LAMMPS input file
Adjust parameters according to LAMMPS executable, force-field file, main input file that you'll be using, pair style and atom style
"""
parameters = {
    'exec': 'mpirun /cluster/bin/lmp_ctcms-14439-knc6 <in.elastic >out',
    'pair_coeff':
    '/users/knc6/Software/jarvis/jarvis/lammps/examples/Mishin-Ni-Al-2009.eam.alloy',
    'control_file': '/users/knc6/inelast.mod',
    'pair_style': 'eam/alloy',
    'atom_style': 'charge',
    'cluster': 'pbs'
}
Esempio n. 27
0
def geo_table_row(cl=None,
                  st=None,
                  name='',
                  show_angle=0,
                  mnpo4_hack=False,
                  param_order=None):
    #return list of geo data for cl, which can be used for table
    """
    mnpo4_hack (bool) - if true exchange a and c for mnpo4 phase

    param_order - default [0,1,2]
    """
    po = param_order
    if po is None:
        po = [0, 1, 2]

    if cl:
        st = cl.end
    # if not name:
    if header.pymatgen_flag:
        spg = st.get_space_group_info()
        spg = latex_spg(spg[0])

        #transform to standard
        st_mp = st.convert2pymatgen()
        symprec = 0.1
        sf = SpacegroupAnalyzer(st_mp, symprec=symprec)

        st_mp_prim = sf.find_primitive()
        st_mp_conv = sf.get_conventional_standard_structure()
    else:
        spg = '-'

    # st_mp_prim.
    # print('primitive,', st_mp_prim.lattice)
    # print('conventio,', st_mp_conv.lattice)

    # st_mp_prim = sf.get_primitive_standard_structure()
    # st_mp_prim = sf.get_conventional_standard_structure()

    if not name:
        # print(dir(st_mp ))
        # st.printme()
        name = st.get_reduced_formula()

    name = latex_chem(name)

    alpha, beta, gamma = st.get_angles()
    elem = np.array(st.get_elements())

    if 'a' in show_angle:
        angle = '& {:5.2f}'.format(alpha)
    elif 'b' in show_angle:
        angle = '& {:5.2f}'.format(beta)
    elif 'g' in show_angle:
        angle = '& {:5.2f}'.format(gamma)
    else:
        angle = ''

    v = st.vlength
    a, b, c = v
    if mnpo4_hack and 'MnPO' in name:
        c, b, a = v

    ps = [a, b, c]

    p = []
    # print(ps)
    # print(po)
    for o in po:
        p.append(ps[o])
    # print(p)
    # sys.exit()

    return '{:15s} &{:s} & {:5.2f} & {:5.2f} & {:5.2f} '.format(
        name, 'DFT+U', p[0], p[1], p[2]) + angle + '& {:5.1f} & {:s}'.format(
            st.vol, spg)
Esempio n. 28
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 = jsanitize(self.additional_fields, strict=True)
            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]

            # copy band gap and properties into output
            d["output"].update({
                "bandgap": calc["output"]["bandgap"],
                "cbm": calc["output"]["cbm"],
                "vbm": calc["output"]["vbm"],
                "is_gap_direct": calc["output"]["is_gap_direct"]
            })
            try:
                d["output"].update({"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:
                    logger.error(traceback.format_exc())
                    logger.error("Error in " + os.path.abspath(dir_name) +
                                 ".\n" + traceback.format_exc())
                    raise

            # Store symmetry information
            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()
            }

            # store dieelctric and piezo information
            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.utcnow()
            return d

        except Exception:
            logger.error(traceback.format_exc())
            logger.error("Error in " + os.path.abspath(dir_name) + ".\n" +
                         traceback.format_exc())
            raise
Esempio n. 29
0
 def get_plot_2d_concise(self, structure: Structure) -> go.Figure:
     """
     Generates the concise 2D diffraction pattern of the input structure of a smaller size and without layout.
     Does not display.
     Args:
         structure (Structure): The input structure.
     Returns:
         Figure
     """
     if self.symprec:
         finder = SpacegroupAnalyzer(structure, symprec=self.symprec)
         structure = finder.get_refined_structure()
     points = self.generate_points(-10, 11)
     tem_dots = self.tem_dots(structure, points)
     xs = []
     ys = []
     hkls = []
     intensities = []
     for dot in tem_dots:
         if dot.hkl != (0, 0, 0):
             xs.append(dot.position[0])
             ys.append(dot.position[1])
             hkls.append(dot.hkl)
             intensities.append(dot.intensity)
     data = [
         go.Scatter(
             x=xs,
             y=ys,
             text=hkls,
             mode="markers",
             hoverinfo="skip",
             marker=dict(
                 size=4,
                 cmax=1,
                 cmin=0,
                 color=intensities,
                 colorscale=[[0, "black"], [1.0, "white"]],
             ),
             showlegend=False,
         )
     ]
     layout = go.Layout(
         xaxis=dict(
             range=[-4, 4],
             showgrid=False,
             zeroline=False,
             showline=False,
             ticks="",
             showticklabels=False,
         ),
         yaxis=dict(
             range=[-4, 4],
             showgrid=False,
             zeroline=False,
             showline=False,
             ticks="",
             showticklabels=False,
         ),
         plot_bgcolor="black",
         margin={"l": 0, "r": 0, "t": 0, "b": 0},
         width=121,
         height=121,
     )
     fig = go.Figure(data=data, layout=layout)
     fig.layout.update(showlegend=False)
     return fig
Esempio n. 30
0
    def process_item(self, item):
        """
        Process the tasks and materials into a dielectrics collection

        Args:
            item dict: a dict of material_id, structure, and tasks

        Returns:
            dict: a dieletrics dictionary
        """
        def poly(matrix):
            diags = np.diagonal(matrix)
            return np.prod(diags) / np.sum(
                np.prod(comb) for comb in combinations(diags, 2))

        d = {self.dielectric.key: item[self.materials.key]}

        structure = Structure.from_dict(item["structure"])

        if item.get("dielectric", False):
            ionic = Tensor(
                item["dielectric"]["ionic"]).symmetrized.fit_to_structure(
                    structure).convert_to_ieee(structure)
            static = Tensor(
                item["dielectric"]["static"]).symmetrized.fit_to_structure(
                    structure).convert_to_ieee(structure)
            total = ionic + static

            d["dielectric"] = {
                "total": total,
                "ionic": ionic,
                "static": static,
                "e_total": poly(total),
                "e_ionic": poly(ionic),
                "e_static": poly(static)
            }

        sga = SpacegroupAnalyzer(structure)
        # Update piezo if non_centrosymmetric
        if item.get("piezo", False) and not sga.is_laue():
            static = PiezoTensor.from_voigt(np.array(
                item['piezo']["static"])).symmetrized.fit_to_structure(
                    structure).convert_to_ieee(structure).voigt
            ionic = PiezoTensor.from_voigt(np.array(
                item['piezo']["ionic"])).symmetrized.fit_to_structure(
                    structure).convert_to_ieee(structure).voigt
            total = ionic + static

            directions, charges, strains = np.linalg.svd(total)

            max_index = np.argmax(np.abs(charges))
            d["piezo"] = {
                "total": total,
                "ionic": ionic,
                "static": static,
                "e_ij_max": charges[max_index],
                "max_direction": directions[max_index],
                "strain_for_max": strains[max_index]
            }

        if len(d) > 1:
            return d

        return None