|
| 1 | +import os |
| 2 | +from pathlib import Path |
| 3 | +from typing import Literal, Optional, Dict, Any |
| 4 | + |
| 5 | +from abacustest.lib_prepare.abacus import ReadInput, WriteInput |
| 6 | +from abacustest.lib_model.comm import check_abacus_inputs |
| 7 | + |
| 8 | +from abacusagent.constant import RY_TO_EV |
| 9 | +from abacusagent.modules.util.comm import run_abacus, generate_work_path, link_abacusjob, collect_metrics |
| 10 | +from abacusagent.modules.util.cube_manipulator import read_gaussian_cube, profile1d |
| 11 | + |
| 12 | +def plot_averaged_elecstat_pot( |
| 13 | + averaged_elecstat_data, |
| 14 | + work_path: Path, |
| 15 | + axis: Literal['x', 'y', 'z'] = 'z', |
| 16 | + plot_filename: Optional[str] = "elecstat_pot_profile.png" |
| 17 | +) -> Dict[str, Any]: |
| 18 | + import matplotlib.pyplot as plt |
| 19 | + plt.plot(averaged_elecstat_data['data'][:, 0], averaged_elecstat_data['data'][:, 1], label='Electrostatic Potential') |
| 20 | + plt.xlim(0, 1) |
| 21 | + plt.xlabel("Fractional Coordinate along " + axis) |
| 22 | + plt.ylabel("Electrostatic Potential (eV)") |
| 23 | + plot_path = os.path.join(work_path, plot_filename) |
| 24 | + plt.savefig(plot_path, dpi=300) |
| 25 | + |
| 26 | + return plot_path |
| 27 | + |
| 28 | +def abacus_cal_work_function( |
| 29 | + abacus_inputs_dir: Path, |
| 30 | + vacuum_direction: Literal['x', 'y', 'z'] = 'z', |
| 31 | +) -> Dict[str, Any]: |
| 32 | + """ |
| 33 | + Calculate the electrostatic potential and work function using ABACUS. |
| 34 | + |
| 35 | + Args: |
| 36 | + abacus_inputs_dir (Path): Path to the ABACUS input files, which contains the INPUT, STRU, KPT, and pseudopotential or orbital files. |
| 37 | + vacuum_direction (Literal['x', 'y', 'z']): The direction of the vacuum. |
| 38 | +
|
| 39 | + Returns: |
| 40 | + A dictionary containing: |
| 41 | + - elecstat_pot_work_function_work_path (Path): Path to the ABACUS job directory calculating electrostatic potential and work function. |
| 42 | + - elecstat_pot_file (Path): Path to the cube file containing the electrostatic potential. |
| 43 | + - averaged_elecstat_pot_plot (Path): Path to the plot of the averaged electrostatic potential. |
| 44 | + - work_function (float): The calculated work function in eV. |
| 45 | + """ |
| 46 | + try: |
| 47 | + is_valid, msg = check_abacus_inputs(abacus_inputs_dir) |
| 48 | + if not is_valid: |
| 49 | + raise RuntimeError(f"Invalid ABACUS input files: {msg}") |
| 50 | + |
| 51 | + work_path = Path(generate_work_path()).absolute() |
| 52 | + link_abacusjob(src=abacus_inputs_dir,dst=work_path,copy_files=["INPUT", "STRU"], exclude_directories=True) |
| 53 | + input_params = ReadInput(os.path.join(work_path, 'INPUT')) |
| 54 | + if input_params.get('nspin', 1) not in [1, 2]: |
| 55 | + raise ValueError('Only non spin-polarized and collinear spin-polarized calculation are supported for calculating electrostatic potential and work function') |
| 56 | + |
| 57 | + input_params['calculation'] = 'scf' |
| 58 | + input_params['out_pot'] = 2 |
| 59 | + WriteInput(input_params, os.path.join(work_path, 'INPUT')) |
| 60 | + |
| 61 | + run_abacus(work_path) |
| 62 | + |
| 63 | + metrics = collect_metrics(work_path, metrics_names=['normal_end', 'converge', 'efermi']) |
| 64 | + if metrics['normal_end'] is not True or metrics['converge'] is not True: |
| 65 | + raise RuntimeError('ABACUS calculation didn\'t end normally or didn\'t reached SCF convergence') |
| 66 | + |
| 67 | + pot_file = os.path.join(work_path, f"OUT.{input_params.get('suffix', 'ABACUS')}/ElecStaticPot.cube") |
| 68 | + pot = read_gaussian_cube(pot_file) |
| 69 | + |
| 70 | + profile_result = profile1d(pot, axis=vacuum_direction, average=True) |
| 71 | + profile_result['data'][:, 1] *= RY_TO_EV # Convert from Rydberg to eV |
| 72 | + v_vacuum = max(profile_result['data'][:, 1]) |
| 73 | + work_function = v_vacuum - metrics['efermi'] |
| 74 | + |
| 75 | + # Plot the averaged electrostatic potential |
| 76 | + plot_path = plot_averaged_elecstat_pot(profile_result, work_path, axis=vacuum_direction) |
| 77 | + |
| 78 | + return {'elecstat_pot_work_function_work_path': Path(work_path).absolute(), |
| 79 | + 'elecstat_pot_file': Path(pot_file).absolute(), |
| 80 | + 'averaged_elecstat_pot_plot': Path(plot_path).absolute(), |
| 81 | + 'work_function': work_function} |
| 82 | + except Exception as e: |
| 83 | + return {'message': f"Calculating electrostatic potential and work function failed: {e}"} |
0 commit comments