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))
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.")
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 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)
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 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.")
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)
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")
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)
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)
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)