Beispiel #1
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 is_centro(structure):
            sga = SpacegroupAnalyzer(structure)
            return SymmOp.inversion() in sga.get_symmetry_operations()

        def poly(matrix):
            diags = np.diagonal(matrix)
            return np.prod(diags) / np.sum(
                np.prod(comb) for comb in combinations(diags, 2))

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

        # Start with higher task ids first?
        for t in sorted(item["tasks"],
                        lambda x: x[self.tasks.lu_field],
                        reverse=True):
            if "dielectric" in TaskTagger.task_type(t):
                output = t["calcs_reversed"][0]["output"]
                total = np.sum(output["epsilon_ionic"],
                               output["epsilon_static"])
                d = {
                    "material_id": item['material_id'],
                    "dielectric": {
                        "ionic": output["epsilon_ionic"],
                        "electronic": output["epsilon_static"],
                        "total": total,
                        "e_total": poly(total),
                        "e_ionic": poly(output["epsilon_ionic"]),
                        "e_electronic": poly(output["epsilon_static"]),
                    }
                }

                # Update piezo if non_centrosymmetric
                if not is_centro(item["structure"]):
                    pt_e = PiezoTensor.from_voigt(
                        np.array(output["outcar"]["piezo_tensor"]))
                    pt_i = PiezoTensor.from_voigt(
                        np.array(output["outcar"]["piezo_ionic_tensor"]))
                    pt_total = pt_e + pt_i
                    pt_total = pt_total.symmetrized.fit_to_structure(structure)

                    d["piezo"] = {
                        "piezoelectric_tensor": pt_total.voigt,
                        "eij_max": np.max(pt_total.voigt),
                    }
                    # TODO Add in more analysis: v_max ?
                    # TODO: Add in unstable phonon mode analysis of piezoelectric for potentially ferroelectric

                return d

        return None
Beispiel #2
0
    def post_process(self, mat):
        """
        Any extra post-processing on a material doc
        """

        # Add structure metadata back into document and convert back to conventional standard
        if "structure" in mat:
            structure = Structure.from_dict(mat["structure"])
            sga = SpacegroupAnalyzer(structure, symprec=SYMPREC)
            mat["structure"] = structure.as_dict()
            mat.update(structure_metadata(structure))

        # Deprecate materials with bad structures or energies
        if "structure" in mat["invalid_props"]:
            mat.update({"deprecated": True})
        elif "thermo.energy_per_atom" in mat["invalid_props"]:
            mat.update({"deprecated": True})
        else:
            mat.update({"deprecated": False})

        # Reorder voigt output from VASP to standard voigt notation
        if has(mat, "piezo.ionic"):
            mat["piezo"]["ionic"] = PiezoTensor.from_vasp_voigt(
                mat["piezo"]["ionic"]).voigt.tolist()
        if has(mat, "piezo.static"):
            mat["piezo"]["static"] = PiezoTensor.from_vasp_voigt(
                mat["piezo"]["static"]).voigt.tolist()
Beispiel #3
0
    def from_ionic_and_electronic(cls, ionic: Matrix3D, electronic: Matrix3D):

        total = BasePiezoTensor.from_voigt(np.sum(ionic,
                                                  electronic))  # type: ignore

        directions, charges, strains = np.linalg.svd(total,
                                                     full_matrices=False)
        max_index = np.argmax(np.abs(charges))

        max_direction = directions[max_index]

        # Allow a max miller index of 10
        min_val = np.abs(max_direction)
        min_val = min_val[min_val > (np.max(min_val) /
                                     SETTINGS.MAX_PIEZO_MILLER)]
        min_val = np.min(min_val)

        return cls(
            **{
                "total": total.zeroed().voigt,
                "ionic": ionic,
                "static": electronic,
                "e_ij_max": charges[max_index],
                "max_direction": np.round(max_direction / min_val),
                "strain_for_max": strains[max_index],
            })
Beispiel #4
0
    def runTest(self):

        piezo_struc = self.get_structure('BaNiO3')
        tensor = np.asarray([[0., 0., 0., 0., 0.03839, 0.],
                             [0., 0., 0., 0.03839, 0., 0.],
                             [6.89822, 6.89822, 27.46280, 0., 0., 0.]]).reshape(3, 6)
        pt = PiezoTensor(tensor)
        full_tensor = [[[0., 0., 0.03839],
                        [0., 0., 0.],
                        [0.03839, 0., 0.]],
                       [[0., 0., 0.],
                        [0., 0., 0.03839],
                        [0.,  0.03839,  0.]],
                       [[6.89822, 0., 0.],
                        [0.,  6.89822, 0.],
                        [0.,  0.,  27.4628]]]
        bad_pt = PiezoTensor([[0., 0., 1., 0., 0.03839, 2.],
                              [0., 0., 0., 0.03839, 0., 0.],
                              [6.89822, 6.89822, 27.4628, 0., 0., 0.]])

        sym_pt = bad_pt.symmeterize(piezo_struc)
        alt_tensor = pt.from_full_tensor(full_tensor)

        self.assertArrayAlmostEqual(pt.full_tensor, full_tensor)
        self.assertArrayEqual(pt, alt_tensor)
        self.assertTrue(pt.is_valid(piezo_struc))

        self.assertTrue(sym_pt.is_valid(piezo_struc))
        self.assertArrayAlmostEqual(sym_pt, pt)
Beispiel #5
0
 def test_from_vasp_voigt(self):
     bad_voigt = np.zeros((3, 7))
     pt = PiezoTensor.from_vasp_voigt(self.vasp_matrix)
     self.assertArrayEqual(pt, self.full_tensor_array)
     self.assertRaises(ValueError, PiezoTensor.from_voigt, bad_voigt)
     self.assertArrayEqual(self.voigt_matrix, pt.voigt)
Beispiel #6
0
 def test_new(self):
     pt = PiezoTensor(self.full_tensor_array)
     self.assertArrayAlmostEqual(pt, self.full_tensor_array)
     bad_dim_array = np.zeros((3, 3))
     self.assertRaises(ValueError, PiezoTensor, bad_dim_array)
Beispiel #7
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
Beispiel #8
0
 def test_from_voigt(self):
     bad_voigt = np.zeros((3, 7))
     pt = PiezoTensor.from_voigt(self.voigt_matrix)
     self.assertArrayEqual(pt, self.full_tensor_array)
     self.assertRaises(ValueError, PiezoTensor.from_voigt, bad_voigt)
     self.assertArrayEqual(self.voigt_matrix, pt.voigt)
Beispiel #9
0
    def calc(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))

        if item["bandstructure"]["band_gap"] > 0:

            structure = Structure.from_dict(
                item.get("dielectric", {}).get("structure", None))

            ionic = Tensor(
                item["dielectric"]["ionic"]).convert_to_ieee(structure)
            static = Tensor(
                item["dielectric"]["static"]).convert_to_ieee(structure)
            total = ionic + static

            d = {
                "dielectric": {
                    "total": total,
                    "ionic": ionic,
                    "static": static,
                    "e_total": np.average(np.diagonal(total)),
                    "e_ionic": np.average(np.diagonal(ionic)),
                    "e_static": np.average(np.diagonal(static)),
                    "n": np.sqrt(np.average(np.diagonal(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"]))
                ionic = PiezoTensor.from_voigt(np.array(
                    item["piezo"]["ionic"]))
                total = ionic + static

                # Symmeterize Convert to IEEE orientation
                total = total.convert_to_ieee(structure)
                ionic = ionic.convert_to_ieee(structure)
                static = static.convert_to_ieee(structure)

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

                max_index = np.argmax(np.abs(charges))

                max_direction = directions[max_index]

                # Allow a max miller index of 10
                min_val = np.abs(max_direction)
                min_val = min_val[min_val > (np.max(min_val) /
                                             self.max_miller)]
                min_val = np.min(min_val)

                d["piezo"] = {
                    "total": total.zeroed().voigt,
                    "ionic": ionic.zeroed().voigt,
                    "static": static.zeroed().voigt,
                    "e_ij_max": charges[max_index],
                    "max_direction": np.round(max_direction / min_val),
                    "strain_for_max": strains[max_index],
                }
        else:
            d = {
                "dielectric": {},
                "_warnings": [
                    "Dielectric calculated for likely metal. Values are unlikely to be converged"
                ],
            }

            if item.get("piezo", False):
                d.update({
                    "piezo": {},
                    "_warnings": [
                        "Piezoelectric calculated for likely metal. Values are unlikely to be converged"
                    ],
                })

        return d
Beispiel #10
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 = {"material_id": item["material_id"]}

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

        if item.get("dielectric") is not None:
            ionic = Tensor(d["dielectric"]["ionic"])
            static = Tensor(d["dielectric"]["static"])
            total = ionic + static

            d["dielectric"] = {
                "total":
                total.symmetrized.fit_to_structure(structure).convert_to_ieee(
                    structure),
                "ionic":
                ionic.symmetrized.fit_to_structure(structure).convert_to_ieee(
                    structure),
                "static":
                static.symmetrized.fit_to_structure(structure).convert_to_ieee(
                    structure),
                "e_total":
                poly(total),
                "e_ionic":
                poly(ionic),
                "e_static":
                poly(static)
            }

        # Update piezo if non_centrosymmetric
        if item.get("piezo") is not None:
            static = PiezoTensor.from_voigt(
                np.array(item['piezo']["piezo_tensor"]))
            ionic = PiezoTensor.from_voigt(
                np.array(item['piezo']["piezo_ionic_tensor"]))
            total = ionic + static

            d["piezo"] = {
                "total":
                total.symmetrized.fit_to_structure(structure).convert_to_ieee(
                    structure).voigt,
                "ionic":
                ionic.symmetrized.fit_to_structure(structure).convert_to_ieee(
                    structure).voigt,
                "static":
                static.symmetrized.fit_to_structure(structure).convert_to_ieee(
                    structure).voigt,
                "e_ij_max":
                np.max(total.voigt)
            }

            # TODO Add in more analysis: v_max ?
            # TODO: Add in unstable phonon mode analysis of piezoelectric for potentially ferroelectric

        if len(d) > 1:
            return d

        return None