Esempio n. 1
0
    def make_defect_set(self,
                        keywords: Union[str, list, None] = None,
                        specified_defects: Optional[list] = None) -> None:
        """Return defect name list based on DefectInitialSetting object. """
        defects = {}
        # vacancies
        defects.update(
            self._substituted_set(removed_sites=self.irreducible_sites,
                                  inserted_element=None))

        # substituted + antisite
        configs = self.antisite_configs + self.dopant_configs
        for in_elem, out_elem in configs:
            removed_sites = [
                i for i in self.irreducible_sites if out_elem == i.element
            ]
            substituted = self._substituted_set(removed_sites=removed_sites,
                                                inserted_element=in_elem)
            defects.update(substituted)

        # interstitials
        inserted_elements = self.structure.symbol_set + tuple(self.dopants)
        defects.update(
            self._interstitial_set(inserted_elements=inserted_elements))

        # complex defects
        defects.update(self._complex_set())

        defects = select_defects(defect_set=defects,
                                 keywords=keywords,
                                 included=self.included,
                                 excluded=self.excluded,
                                 specified_defects=specified_defects)

        self.defect_entries = []
        for name, defect in defects.items():
            center = defect.pop("center")
            for charge in defect.pop("charges"):
                inserted_indices = \
                    [i["index"] for i in defect["inserted_atoms"]]
                # By default, neighboring atoms are perturbed.
                # If one wants to avoid it, set displacement_distance = 0
                perturbed_structure, neighboring_sites = \
                    perturb_neighboring_atoms(
                        structure=defect["initial_structure"],
                        center=center,
                        cutoff=defect["cutoff"],
                        distance=self.displacement_distance,
                        inserted_atom_indices=inserted_indices)

                defect["initial_structure"].set_charge(charge)
                perturbed_structure.set_charge(charge)

                self.defect_entries.append(
                    DefectEntry(
                        name=name,
                        perturbed_initial_structure=perturbed_structure,
                        charge=charge,
                        neighboring_sites=neighboring_sites,
                        **defect))
Esempio n. 2
0
def defect_entry(args):
    if args.print:
        print(DefectEntry.load_json(args.json))
    elif args.make_defect_entry:
        defect_structure = Structure.from_file(args.defect_poscar)
        perfect_structure = Structure.from_file(args.perfect_poscar)

        defect_entry_from_yaml = DefectEntry.from_defect_structure(
            defect_structure=defect_structure,
            perfect_structure=perfect_structure,
            displacement_distance=args.displacement_distance,
            defect_name=args.defect_name)

        defect_entry_from_yaml.to_json_file(args.json)
    else:
        logger.critical("Set make_defect_entry or print option.")
Esempio n. 3
0
 def test_json_round_trip(self):
     """ round trip test of to_json and from_json """
     tmp_file = tempfile.NamedTemporaryFile()
     self.MgO_Va_O1_2.to_json_file(tmp_file.name)
     expected = self.MgO_Va_O1_2.as_dict()
     actual = DefectEntry.load_json(tmp_file.name).as_dict()
     self.assertTrue(expected, actual)
Esempio n. 4
0
def vertical_transition_input_maker(args):
    if abs(args.additional_charge) != 1:
        raise ValueError(f"{args.additional_charge} is invalid.")

    initial_dirname = Path(args.initial_dir_name)
    de_filename = initial_dirname / "defect_entry.json"
    de = DefectEntry.load_json(de_filename)
    src_filename = initial_dirname / "dft_results.json"
    src = SupercellCalcResults.load_json(src_filename)

    new_charge = de.charge + args.additional_charge
    vis = ViseInputSet.from_prev_calc(initial_dirname,
                                      user_incar_settings={"NSW": 0},
                                      parse_calc_results=False,
                                      contcar_filename=args.contcar,
                                      charge=new_charge)

    de.charge += args.additional_charge
    de.initial_structure = src.final_structure
    de.perturbed_initial_structure = src.final_structure
    de.initial_site_symmetry = src.site_symmetry
    # FIX MAGNETIZATION?
    new_dirname = initial_dirname / f"add_charge_{args.additional_charge}"
    make_dir(str(new_dirname),
             vis,
             force_overwrite=False,
             defect_entry_obj=de,
             add_neighbor=False)
    new_de_filename = new_dirname / "defect_entry.json"
    de.to_json_file(new_de_filename)
Esempio n. 5
0
 def test_from_defect_structure_complex(self):
     perfect_structure = self.get_structure_by_name(name="MgO64atoms")
     expected = self.MgO_complex.as_dict()
     actual = DefectEntry.from_defect_structure(
         defect_structure=self.initial_structure_complex,
         perfect_structure=perfect_structure,
         defect_name="complex_2").as_dict()
     for d in expected:
         self.assertEqual(expected[d], actual[d])
Esempio n. 6
0
def supercell_calc_results(args):

    if args.print:
        print(SupercellCalcResults.load_json(args.json))
        return

    if args.defect_center:
        if len(args.defect_center) != 1 and len(args.defect_center) != 3:
            raise ValueError("Length of the defect center is neither 1 or 3")
        results = SupercellCalcResults.load_json(args.json)
        results.defect_center = args.defect_center
        results.to_json_file(filename=args.json)
        return

    if args.dir_all:
        dirs = glob('*[0-9]/')
        dirs.insert(0, "perfect/")
    else:
        dirs = args.dirs

    for d in dirs:
        if os.path.isdir(d):
            logger.info(f"Parsing data in {d} ...")

            if d in ["perfect", "perfect/"]:
                try:
                    dft_results = SupercellCalcResults.from_vasp_files(
                        directory_path=d,
                        vasprun=args.vasprun,
                        contcar=args.contcar,
                        procar=args.procar,
                        outcar=args.outcar)
                except IOError:
                    raise IOError("Parsing data in perfect failed.")
            else:
                try:
                    de = DefectEntry.load_json(
                        Path(d) / args.defect_entry_name)
                    dft_results = SupercellCalcResults.from_vasp_files(
                        directory_path=d,
                        vasprun=args.vasprun,
                        contcar=args.contcar,
                        outcar=args.outcar,
                        procar=args.procar,
                        cutoff=args.cutoff,
                        defect_entry=de,
                        defect_symprec=args.defect_symprec,
                        angle_tolerance=args.angle_tolerance)
                except IOError as e:
                    logger.warning(f"Parsing data in {d} failed.")
                    logger.warning(e)
                    continue

            dft_results.to_json_file(filename=Path(d) / "dft_results.json")
        else:
            logger.warning(f"{d} does not exist, so nothing is done.")
Esempio n. 7
0
def vertical_transition_energy(args):

    initial_dir = Path(args.initial_dir)
    initial_calc_results = \
        SupercellCalcResults.load_json(initial_dir / "dft_results.json")
    final_dir = Path(args.dir)
    final_calc_results = \
        SupercellCalcResults.load_json(final_dir / "dft_results.json")
    unitcell = UnitcellCalcResults.load_json(args.unitcell_json)

    if args.print:
        vtec = VerticalTransitionEnergyCorrection.load_json(args.json)
        vtec.plot_potential()
        print(vtec)
        if vtec.additional_charge == 1:
            cbm = unitcell.band_edge[1]
            print(f"CBM position (eV): {cbm}")
            band_edge_related_energy = cbm

        else:
            vbm = unitcell.band_edge[0]
            print(f"VBM position (eV): {vbm}")
            band_edge_related_energy = -vbm

        vte_wo_corr = (final_calc_results.total_energy -
                       initial_calc_results.total_energy +
                       band_edge_related_energy)
        vte = vte_wo_corr + vtec.correction_energy
        print(f"Vertical transition energy w/o correction (eV): {vte_wo_corr}")
        print(f"Vertical transition energy w/  correction (eV): {vte}")
        return

    dielectric_tensor = unitcell.total_dielectric_tensor
    static_dielectric_tensor = unitcell.static_dielectric_tensor

    initial_efnv = \
        ExtendedFnvCorrection.load_json(initial_dir / "correction.json")
    initial_calc_results = \
        SupercellCalcResults.load_json(initial_dir / "dft_results.json")

    final_defect_entry = DefectEntry.load_json(final_dir / "defect_entry.json")

    c = VerticalTransitionEnergyCorrection.from_files(
        dielectric_tensor, static_dielectric_tensor, initial_efnv,
        initial_calc_results, final_defect_entry, final_calc_results)
    c.to_json_file(args.json)
    print(c)
    def setUp(self) -> None:
        unitcell = UnitcellCalcResults.load_json(parent / "MgO_unitcell.json")
        dielectric_tensor = unitcell.total_dielectric_tensor
        static_dielectric_tensor = unitcell.static_dielectric_tensor
        initial_efnv_cor = ExtendedFnvCorrection.load_json(
            parent / "MgO_Va_O_1_correction.json")
        initial_calc_results = SupercellCalcResults.load_json(
            parent / "MgO_Va_O_1_dft_results.json")
        final_defect_entry = DefectEntry.load_json(
            parent / "MgO_Va_O_1-added_defect_entry.json")
        final_calc_results = SupercellCalcResults.load_json(
            parent / "MgO_Va_O_1-added_dft_results.json")

        self.vertical_correction = \
            VerticalTransitionEnergyCorrection.from_files(
                dielectric_tensor,
                static_dielectric_tensor,
                initial_efnv_cor,
                initial_calc_results,
                final_defect_entry,
                final_calc_results,
                8)
Esempio n. 9
0
def efnv_correction(args):
    if args.print:
        print(ExtendedFnvCorrection.load_json(args.json_file))
        return

    dirs = glob('*[0-9]/') if args.dir_all else args.dirs

    if args.plot_potential:
        for directory in dirs:
            d = Path(directory)
            c = ExtendedFnvCorrection.load_json(d / "correction.json")
            c.plot_potential(d / "potential.pdf", args.y_range)
        return

    if args.nocorr:
        for directory in dirs:
            c = ManualCorrection(manual_correction_energy=args.manual)
            c.to_json_file(Path(directory) / "correction.json")
        return

    try:
        ucr = UnitcellCalcResults.load_json(args.unitcell_json)
        dielectric_tensor = ucr.total_dielectric_tensor
    except IOError:
        raise FileNotFoundError("JSON for the unitcell info is not found.")

    try:
        perfect_dft_data = SupercellCalcResults.load_json(args.perfect_json)
    except IOError:
        raise FileNotFoundError("JSON for the perfect supercell is not found.")

    # Ewald parameter related
    if not Path(args.ewald_json).is_file():
        logger.info("optimizing ewald...")
        ewald = Ewald.from_optimization(
            structure=perfect_dft_data.final_structure,
            dielectric_tensor=dielectric_tensor,
            initial_ewald_param=args.ewald_initial_param,
            convergence=args.ewald_convergence,
            prod_cutoff_fwhm=args.ewald_accuracy)
        ewald.to_json_file(args.ewald_json)

    for directory in dirs:
        d = Path(directory)
        json_to_make = d / "correction.json"

        if json_to_make.exists() and not args.force_overwrite:
            logger.warning(f"{json_to_make} already exists, so nothing done.")
            continue

        logger.info(f"correcting {directory} ...")
        entry = DefectEntry.load_json(d / "defect_entry.json")
        try:
            defect_dft_data = \
                SupercellCalcResults.load_json(d / "dft_results.json")
        except IOError:
            logger.warning(f"dft_results.json in {directory} does not exist.")
            continue

        c = ExtendedFnvCorrection. \
            compute_correction(defect_entry=entry,
                               defect_dft=defect_dft_data,
                               perfect_dft=perfect_dft_data,
                               dielectric_tensor=dielectric_tensor,
                               defect_center=args.defect_center,
                               ewald=args.ewald_json)

        c.plot_potential(d / "potential.pdf", args.y_range)
        c.to_json_file(d / "correction.json")
Esempio n. 10
0
    def setUp(self):
        """ """
        # DefectEntry class object for a single vacancy
        name = "Va_O1"
        defect_type = DefectType.from_string("vacancy")
        self.initial_structure_vacancy = \
            self.get_structure_by_name(name="MgO64atoms-Va_O_0-unrelaxed")
        perturbed_initial_structure = self.initial_structure_vacancy.copy()
        removed_atoms = [{"element": "O", "index": 32, "coords": [0.25, 0, 0]}]
        inserted_atoms = []
        changes_of_num_elements = {"O": -1}
        charge = 2
        initial_site_symmetry = "m-3m"
        multiplicity = 32
        neighboring_sites = [0, 4, 16, 17, 24, 26]
        cutoff = round(8.419456 / 4 * CUTOFF_FACTOR, 2)
        self.MgO_Va_O1_2 = \
            DefectEntry(name=name,
                        defect_type=defect_type,
                        initial_structure=self.initial_structure_vacancy,
                        perturbed_initial_structure=perturbed_initial_structure,
                        removed_atoms=removed_atoms,
                        inserted_atoms=inserted_atoms,
                        changes_of_num_elements=changes_of_num_elements,
                        charge=charge,
                        initial_site_symmetry=initial_site_symmetry,
                        cutoff=cutoff,
                        neighboring_sites=neighboring_sites,
                        multiplicity=multiplicity)

        name = "complex"
        defect_type = DefectType.from_string("complex")
        self.initial_structure_complex = \
            self.get_structure_by_name(name="MgO64atoms-Va_Mg+Va_O+Ca_i")
        perturbed_initial_structure = self.initial_structure_complex.copy()
        removed_atoms = [{
            "element": "Mg",
            "index": 0,
            "coords": [0, 0, 0]
        }, {
            "element": "O",
            "index": 32,
            "coords": [0.25, 0, 0]
        }]
        inserted_atoms = [{
            "element": "Ca",
            "index": 0,
            "coords": [0.125, 0, 0]
        }]
        changes_of_num_elements = {"Mg": -1, "Ca": 1, "O": -1}
        charge = 2
        initial_site_symmetry = "4mm"
        multiplicity = 192
        neighboring_sites = [16, 17, 24, 26, 47, 48, 55, 57]
        cutoff = round(8.419456 / 4 * CUTOFF_FACTOR, 2)
        self.MgO_complex = \
            DefectEntry(name=name,
                        defect_type=defect_type,
                        initial_structure=self.initial_structure_complex,
                        perturbed_initial_structure=perturbed_initial_structure,
                        removed_atoms=removed_atoms,
                        inserted_atoms=inserted_atoms,
                        changes_of_num_elements=changes_of_num_elements,
                        charge=charge,
                        initial_site_symmetry=initial_site_symmetry,
                        cutoff=cutoff,
                        neighboring_sites=neighboring_sites,
                        multiplicity=multiplicity)
Esempio n. 11
0
class DefectEntryTest(PydefectTest):
    def setUp(self):
        """ """
        # DefectEntry class object for a single vacancy
        name = "Va_O1"
        defect_type = DefectType.from_string("vacancy")
        self.initial_structure_vacancy = \
            self.get_structure_by_name(name="MgO64atoms-Va_O_0-unrelaxed")
        perturbed_initial_structure = self.initial_structure_vacancy.copy()
        removed_atoms = [{"element": "O", "index": 32, "coords": [0.25, 0, 0]}]
        inserted_atoms = []
        changes_of_num_elements = {"O": -1}
        charge = 2
        initial_site_symmetry = "m-3m"
        multiplicity = 32
        neighboring_sites = [0, 4, 16, 17, 24, 26]
        cutoff = round(8.419456 / 4 * CUTOFF_FACTOR, 2)
        self.MgO_Va_O1_2 = \
            DefectEntry(name=name,
                        defect_type=defect_type,
                        initial_structure=self.initial_structure_vacancy,
                        perturbed_initial_structure=perturbed_initial_structure,
                        removed_atoms=removed_atoms,
                        inserted_atoms=inserted_atoms,
                        changes_of_num_elements=changes_of_num_elements,
                        charge=charge,
                        initial_site_symmetry=initial_site_symmetry,
                        cutoff=cutoff,
                        neighboring_sites=neighboring_sites,
                        multiplicity=multiplicity)

        name = "complex"
        defect_type = DefectType.from_string("complex")
        self.initial_structure_complex = \
            self.get_structure_by_name(name="MgO64atoms-Va_Mg+Va_O+Ca_i")
        perturbed_initial_structure = self.initial_structure_complex.copy()
        removed_atoms = [{
            "element": "Mg",
            "index": 0,
            "coords": [0, 0, 0]
        }, {
            "element": "O",
            "index": 32,
            "coords": [0.25, 0, 0]
        }]
        inserted_atoms = [{
            "element": "Ca",
            "index": 0,
            "coords": [0.125, 0, 0]
        }]
        changes_of_num_elements = {"Mg": -1, "Ca": 1, "O": -1}
        charge = 2
        initial_site_symmetry = "4mm"
        multiplicity = 192
        neighboring_sites = [16, 17, 24, 26, 47, 48, 55, 57]
        cutoff = round(8.419456 / 4 * CUTOFF_FACTOR, 2)
        self.MgO_complex = \
            DefectEntry(name=name,
                        defect_type=defect_type,
                        initial_structure=self.initial_structure_complex,
                        perturbed_initial_structure=perturbed_initial_structure,
                        removed_atoms=removed_atoms,
                        inserted_atoms=inserted_atoms,
                        changes_of_num_elements=changes_of_num_elements,
                        charge=charge,
                        initial_site_symmetry=initial_site_symmetry,
                        cutoff=cutoff,
                        neighboring_sites=neighboring_sites,
                        multiplicity=multiplicity)

    def test_from_defect_structure(self):
        perfect_structure = self.get_structure_by_name(name="MgO64atoms")
        expected = self.MgO_Va_O1_2.as_dict()
        actual = DefectEntry.from_defect_structure(
            defect_structure=self.initial_structure_vacancy,
            perfect_structure=perfect_structure,
            defect_name="Va_O1_2").as_dict()
        for d in expected:
            self.assertEqual(expected[d], actual[d])

    def test_from_defect_structure_complex(self):
        perfect_structure = self.get_structure_by_name(name="MgO64atoms")
        expected = self.MgO_complex.as_dict()
        actual = DefectEntry.from_defect_structure(
            defect_structure=self.initial_structure_complex,
            perfect_structure=perfect_structure,
            defect_name="complex_2").as_dict()
        for d in expected:
            self.assertEqual(expected[d], actual[d])

    def test_msonable(self):
        self.assertMSONable(self.MgO_Va_O1_2)
        self.assertMSONable(self.MgO_complex)

    def test_dict_round_trip(self):
        """ round trip test of as_dict and from_dict """
        expected = self.MgO_Va_O1_2.as_dict()
        actual = DefectEntry.from_dict(expected).as_dict()
        self.assertEqual(expected, actual)

    def test_json_round_trip(self):
        """ round trip test of to_json and from_json """
        tmp_file = tempfile.NamedTemporaryFile()
        self.MgO_Va_O1_2.to_json_file(tmp_file.name)
        expected = self.MgO_Va_O1_2.as_dict()
        actual = DefectEntry.load_json(tmp_file.name).as_dict()
        self.assertTrue(expected, actual)

    def test_atom_mapping_to_perfect(self):
        expected = list(range(64))
        expected.pop(32)
        actual = self.MgO_Va_O1_2.atom_mapping_to_perfect
        self.assertEqual(expected, actual)

    def test_atom_mapping_to_perfect_complex(self):
        expected = list(range(64))
        expected.pop(32)
        expected.pop(0)
        expected = [None] + expected
        actual = self.MgO_complex.atom_mapping_to_perfect
        self.assertEqual(expected, actual)

    def test_defect_center(self):
        expected = [0.25, 0, 0]
        actual = self.MgO_Va_O1_2.defect_center_coords
        self.assertArrayEqual(expected, actual)

    def test_defect_center_complex(self):
        expected = [0.125, 0, 0]
        actual = self.MgO_complex.defect_center_coords
        self.assertArrayEqual(expected, actual)

    def test_anchor_atom_index(self):
        expected = 38  # Line 47 [0.75, 0.5, 0.5]
        actual = self.MgO_Va_O1_2.anchor_atom_index
        self.assertEqual(actual, expected)

    def test_anchor_atom_index_complex(self):
        expected = [8, 38]  # 8 or 38
        actual = self.MgO_complex.anchor_atom_index
        self.assertTrue(actual in expected)
Esempio n. 12
0
 def test_dict_round_trip(self):
     """ round trip test of as_dict and from_dict """
     expected = self.MgO_Va_O1_2.as_dict()
     actual = DefectEntry.from_dict(expected).as_dict()
     self.assertEqual(expected, actual)