diff --git a/README.md b/README.md index 5b12bcd..b3ac4a3 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [APEX](https://github.com/deepmodeling/APEX): Alloy Property EXplorer using simulations, is a component of the [AI Square](https://aissquare.com/) project that involves the restructuring of the [DP-Gen](https://github.com/deepmodeling/dpgen) `auto_test` module to develop a versatile and extensible Python package for general alloy property testing. This package enables users to conveniently establish a wide range of property-test workflows by utilizing various computational approaches, including support for LAMMPS, VASP, and ABACUS. ## New Features Update (v1.0) +* Add calculation function of `phonon` spectrum (v1.1.0) * Decouple property calculations into individual sub-workflow to facilitate the customization of complex property functions * Support one-click parallel submission of multiple workflows * Support `run` step in the single step test mode (Interaction method similar to `auto_test`) @@ -28,6 +29,7 @@ - [3.1.2.4. Vacancy](#3124-vacancy) - [3.1.2.5. Interstitial](#3125-interstitial) - [3.1.2.6. Gamma Line](#3126-gamma-line) + - [3.1.2.7. Phonon Spectrum](#3127-phonon-spectrum) - [3.2. Command](#32-command) - [3.2.1. Workflow Submission](#321-workflow-submission) - [3.2.2. Single-Step Test](#322-single-step-test) @@ -43,7 +45,7 @@ APEX adopts the functionality of the second-generation alloy properties calculat The comprehensive architecture of APEX is demonstrated below:
- Fig1 + Fig1

Figure 1. APEX schematic diagram

@@ -344,7 +346,31 @@ The parameters related to Gamma line calculation are listed below: "n_steps": 10 } ``` - **It should be noted that for various crystal structures, users can further define slip parameters within the respective nested dictionaries, which will be prioritized for adoption. In above example, the slip system configuration within the "hcp" dictionary will be utilized.** + It should be noted that for various crystal structures, **users can further define slip parameters within the respective nested dictionaries, which will be prioritized for adoption**. In above example, the slip system configuration within the "hcp" dictionary will be utilized. + +##### 3.1.2.7. Phonon Spectrum +This function incorporates part of [dflow-phonon](https://github.com/Chengqian-Zhang/dflow-phonon) codes into APEX to make it more complete. This workflow is realized via [Phonopy](https://github.com/phonopy/phonopy), and plus [phonoLAMMPS](https://github.com/abelcarreras/phonolammps) for LAMMPS calculation. + +*IMPORTANT!!*: it should be noticed that one will need the **phonoLAMMPS** package pre-installed within one's `run_image` for proper `LAMMPS` calculation of phonon spectrum. + +The parameters related to `Phonon` calculation are listed below: + | Key words | Data structure | Default | Description | + | :------------ | ----- | ----- | ------------------- | + | primitive | Bool | False | Whether to find primitive lattice structure for phonon calculation | + | approach | String | "linear" | Specify phonon calculation method when using `VASP`; Two options: 1. "linear" for the *Linear Response Method*, and 2. "displacement" for the *Finite Displacement Method* | + | supercell_size | Sequence[Int] | [2, 2, 2] | Size of supercell created for calculation | + | MESH | Sequence[Int] | None | Specify the dimensions of the grid in reciprocal space for which the phonon frequencies and eigenvectors are to be calculated. For example: [8, 8, 8]; Refer to [Phonopy MESH](http://phonopy.github.io/phonopy/setting-tags.html#mesh-sampling-tags) | + | PRIMITIVE_AXES | String | None | To define the basis vectors of a primitive cell in terms of the basis vectors of a conventional cell for input cell transformation. For example: "0.0 0.5 0.5 0.5 0.0 0.5 0.5 0.5 0.0"; Refer to [Phonopy PRIMITIVE_AXES](http://phonopy.github.io/phonopy/setting-tags.html#primitive-axes-or-primitive-axis) | + | BAND | String | None | Indicate band path in reciprocal space as format of [Phonopy BAND](http://phonopy.github.io/phonopy/setting-tags.html#band-and-band-points); For example: "0 0 0 1/2 0 1/2, 1/2 1/2 1 0 0 0 1/2 1/2 1/2" | + | BAND_POINTS | Int | 51 | Number of sampling points including the path ends | + | BAND_CONNECTION | Bool | True | With this option, band connections are estimated from eigenvectors and band structure is drawn considering band crossings. In sensitive cases, to obtain better band connections, it requires to increase number of points calculated in band segments by the `BAND_POINTS` tag. | + +When utilize the `VASP`, you have **two** primary calculation methods at your disposal: the **Linear Response Method** and the **Finite Displacement Method**. + +The **Linear Response Method** has an edge over the Finite Displacement Method in that it eliminates the need for creating super-cells, thereby offering computational efficiency in certain cases. Additionally, this method is particularly well-suited for systems with anomalous phonon dispersion (like systems with Kohn anomalies), as it can precisely calculate the phonons at the specified points. + +On the other hand, the **Finite Displacement Method**'s advantage lies in its versatility; it functions as an add-on compatible with any code, including those beyond the scope of density functional theory. The only requirement is that the external code can compute forces. For instance, ABACUS may lack an implementation of the Linear Response Method, but can effectively utilize the Finite Displacement Method implemented in phonon calculation. + ### 3.2. Command APEX currently supports two seperate run modes: **workflow submission** (running via dflow) and **single-step test** (running without dflow). diff --git a/apex/__init__.py b/apex/__init__.py index 2b3a52a..629101b 100644 --- a/apex/__init__.py +++ b/apex/__init__.py @@ -1,5 +1,5 @@ import os -__version__ = '1.0.2' +__version__ = '1.1.1' LOCAL_PATH = os.getcwd() diff --git a/apex/__main__.py b/apex/__main__.py index 0e592cc..bc89e4d 100644 --- a/apex/__main__.py +++ b/apex/__main__.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -from .main import main +from apex.main import main if __name__ == '__main__': main() diff --git a/apex/core/calculator/ABACUS.py b/apex/core/calculator/ABACUS.py index 5e475fc..b1f0d6e 100644 --- a/apex/core/calculator/ABACUS.py +++ b/apex/core/calculator/ABACUS.py @@ -4,8 +4,7 @@ from dpdata import LabeledSystem from monty.serialization import dumpfn -import apex.core.calculator.lib.abacus as abacus -import apex.core.calculator.lib.abacus_scf as abacus_scf +from apex.core.calculator.lib import abacus_utils, abacus_scf #from dpgen import dlog from apex.core.calculator.Task import Task from apex.utils import sepline @@ -98,7 +97,7 @@ def make_input_file(self, output_dir, task_type, task_param): incar_relax = abacus_scf.get_abacus_input_parameters(relax_incar_path) # deal with relaxation - + prop_type = task_param.get("type", "relaxation") cal_type = task_param["cal_type"] cal_setting = task_param["cal_setting"] @@ -158,7 +157,7 @@ def make_input_file(self, output_dir, task_type, task_param): raise RuntimeError("not supported calculation type for ABACUS") # modify STRU file base on the value of fix_atom - abacus.stru_fix_atom(os.path.join(output_dir, "STRU"), fix_atom) + abacus_utils.stru_fix_atom(os.path.join(output_dir, "STRU"), fix_atom) if "basis_type" not in incar: logging.info("'basis_type' is not defined, set to be 'pw'!") @@ -169,7 +168,7 @@ def make_input_file(self, output_dir, task_type, task_param): % incar["basis_type"] ) raise RuntimeError(mess) - abacus.write_input(os.path.join(output_dir, "../INPUT"), incar) + abacus_utils.write_input(os.path.join(output_dir, "../INPUT"), incar) cwd = os.getcwd() os.chdir(output_dir) if not os.path.islink("INPUT"): @@ -182,7 +181,7 @@ def make_input_file(self, output_dir, task_type, task_param): if "kspacing" in incar: kspacing = float(incar["kspacing"]) if os.path.isfile(os.path.join(output_dir, "STRU")): - kpt = abacus.make_kspacing_kpt( + kpt = abacus_utils.make_kspacing_kpt( os.path.join(output_dir, "STRU"), kspacing ) kpt += [0, 0, 0] @@ -195,7 +194,7 @@ def make_input_file(self, output_dir, task_type, task_param): mess += "You can set key word 'kspacing' (unit in 1/bohr) as a float value in INPUT\n" mess += "or set key word 'K_POINTS' as a list in 'cal_setting', e.g. [1,2,3,0,0,0]\n" raise RuntimeError(mess) - abacus.write_kpt(os.path.join(output_dir, "KPT"), kpt) + abacus_utils.write_kpt(os.path.join(output_dir, "KPT"), kpt) def compute(self, output_dir): if not os.path.isfile(os.path.join(output_dir, "INPUT")): diff --git a/apex/core/calculator/Lammps.py b/apex/core/calculator/Lammps.py index c9aa52f..7707675 100644 --- a/apex/core/calculator/Lammps.py +++ b/apex/core/calculator/Lammps.py @@ -4,15 +4,15 @@ from monty.serialization import dumpfn, loadfn -import apex.core.calculator.lib.lammps as lammps +from apex.core.calculator.lib import lammps_utils #from dpgen import dlog -from apex.core.calculator.lib.lammps import ( +from apex.core.calculator.lib.lammps_utils import ( inter_deepmd, inter_eam_alloy, inter_eam_fs, inter_meam, ) -from apex.core.calculator.Task import Task +from .Task import Task from dflow.python import upload_packages upload_packages.append(__file__) @@ -129,10 +129,10 @@ def make_potential_files(self, output_dir): dumpfn(self.inter, os.path.join(output_dir, "inter.json"), indent=4) def make_input_file(self, output_dir, task_type, task_param): - lammps.cvt_lammps_conf( + lammps_utils.cvt_lammps_conf( os.path.join(output_dir, "POSCAR"), os.path.join(output_dir, "conf.lmp"), - lammps.element_list(self.type_map), + lammps_utils.element_list(self.type_map), ) # dumpfn(task_param, os.path.join(output_dir, 'task.json'), indent=4) @@ -195,7 +195,7 @@ def make_input_file(self, output_dir, task_type, task_param): relax_vol = cal_setting["relax_vol"] if [relax_pos, relax_shape, relax_vol] == [True, False, False]: - fc = lammps.make_lammps_equi( + fc = lammps_utils.make_lammps_equi( "conf.lmp", self.type_map, self.inter_func, @@ -207,7 +207,7 @@ def make_input_file(self, output_dir, task_type, task_param): False, ) elif [relax_pos, relax_shape, relax_vol] == [True, True, True]: - fc = lammps.make_lammps_equi( + fc = lammps_utils.make_lammps_equi( "conf.lmp", self.type_map, self.inter_func, @@ -225,7 +225,7 @@ def make_input_file(self, output_dir, task_type, task_param): ] and not task_type == "eos": if "scale2equi" in task_param: scale2equi = task_param["scale2equi"] - fc = lammps.make_lammps_press_relax( + fc = lammps_utils.make_lammps_press_relax( "conf.lmp", self.type_map, scale2equi[int(output_dir[-6:])], @@ -239,7 +239,7 @@ def make_input_file(self, output_dir, task_type, task_param): maxeval, ) else: - fc = lammps.make_lammps_equi( + fc = lammps_utils.make_lammps_equi( "conf.lmp", self.type_map, self.inter_func, @@ -256,7 +256,7 @@ def make_input_file(self, output_dir, task_type, task_param): False, ] and task_type == "eos": task_param["cal_setting"]["relax_shape"] = False - fc = lammps.make_lammps_equi( + fc = lammps_utils.make_lammps_equi( "conf.lmp", self.type_map, self.inter_func, @@ -268,7 +268,7 @@ def make_input_file(self, output_dir, task_type, task_param): False, ) elif [relax_pos, relax_shape, relax_vol] == [False, False, False]: - fc = lammps.make_lammps_eval( + fc = lammps_utils.make_lammps_eval( "conf.lmp", self.type_map, self.inter_func, self.model_param ) @@ -276,7 +276,7 @@ def make_input_file(self, output_dir, task_type, task_param): raise RuntimeError("not supported calculation setting for LAMMPS") elif cal_type == "static": - fc = lammps.make_lammps_eval( + fc = lammps_utils.make_lammps_eval( "conf.lmp", self.type_map, self.inter_func, self.model_param ) @@ -442,7 +442,7 @@ def compute(self, output_dir): _tmp = self.type_map #dlog.debug(_tmp) - type_map_list = lammps.element_list(self.type_map) + type_map_list = lammps_utils.element_list(self.type_map) type_map_idx = list(range(len(type_map_list))) atom_numbs = [] diff --git a/apex/core/calculator/VASP.py b/apex/core/calculator/VASP.py index 1842c19..89c3604 100644 --- a/apex/core/calculator/VASP.py +++ b/apex/core/calculator/VASP.py @@ -6,10 +6,10 @@ from pymatgen.core.structure import Structure from pymatgen.io.vasp import Incar, Kpoints -import apex.core.calculator.lib.vasp as vasp #from dpgen import dlog from apex.core.calculator.Task import Task -from apex.core.calculator.lib.vasp import incar_upper +from apex.core.calculator.lib import vasp_utils +from apex.core.calculator.lib.vasp_utils import incar_upper from apex.utils import sepline from dflow.python import upload_packages upload_packages.append(__file__) @@ -81,7 +81,7 @@ def make_input_file(self, output_dir, task_type, task_param): incar_relax = incar_upper(Incar.from_file(relax_incar_path)) # deal with relaxation - + prop_type = task_param.get("type", "relaxation") cal_type = task_param["cal_type"] cal_setting = task_param["cal_setting"] @@ -89,10 +89,28 @@ def make_input_file(self, output_dir, task_type, task_param): if "input_prop" in cal_setting and os.path.isfile(cal_setting["input_prop"]): incar_prop = os.path.abspath(cal_setting["input_prop"]) incar = incar_upper(Incar.from_file(incar_prop)) + logging.info(f"Will use user specified INCAR (path: {incar_prop}) for {prop_type} calculation") # revise INCAR based on the INCAR provided in the "interaction" else: - incar = incar_relax + if prop_type == "phonon": + approach = task_param.get("approach", "linear") + logging.info(f"No specification of INCAR for {prop_type} calculation, will auto generate") + if approach == "linear": + incar = incar_upper(Incar.from_str( + vasp_utils.make_vasp_phonon_dfpt_incar( + ecut=650, ediff=0.0000001, npar=None, kpar=None + ))) + elif approach == "displacement": + incar = incar_upper(Incar.from_str( + vasp_utils.make_vasp_static_incar( + ecut=650, ediff=0.0000001, npar=8, kpar=1 + ))) + else: + if not prop_type == "relaxation": + logging.info(f"No specification of INCAR for {prop_type} calculation, will use INCAR for relaxation") + incar = incar_relax + if cal_type == "relaxation": relax_pos = cal_setting["relax_pos"] relax_shape = cal_setting["relax_shape"] @@ -192,7 +210,7 @@ def make_input_file(self, output_dir, task_type, task_param): os.remove("INCAR") os.symlink("../INCAR", "INCAR") os.chdir(cwd) - ret = vasp.make_kspacing_kpoints(self.path_to_poscar, kspacing, kgamma) + ret = vasp_utils.make_kspacing_kpoints(self.path_to_poscar, kspacing, kgamma) kp = Kpoints.from_str(ret) kp.write_file(os.path.join(output_dir, "KPOINTS")) diff --git a/apex/core/calculator/calculator.py b/apex/core/calculator/calculator.py index 54de864..316f008 100644 --- a/apex/core/calculator/calculator.py +++ b/apex/core/calculator/calculator.py @@ -4,6 +4,7 @@ from dflow.python import upload_packages upload_packages.append(__file__) +LAMMPS_TYPE = ["deepmd", "meam", "eam_fs", "eam_alloy"] def make_calculator(inter_parameter, path_to_poscar): """ @@ -14,7 +15,7 @@ def make_calculator(inter_parameter, path_to_poscar): return VASP(inter_parameter, path_to_poscar) elif inter_type == "abacus": return ABACUS(inter_parameter, path_to_poscar) - elif inter_type in ["deepmd", "meam", "eam_fs", "eam_alloy"]: + elif inter_type in LAMMPS_TYPE: return Lammps(inter_parameter, path_to_poscar) # if inter_type == 'siesta': # return Siesta(inter_parameter, path_to_poscar) diff --git a/apex/core/calculator/lib/abacus_scf.py b/apex/core/calculator/lib/abacus_scf.py index 93a77c6..788fd69 100644 --- a/apex/core/calculator/lib/abacus_scf.py +++ b/apex/core/calculator/lib/abacus_scf.py @@ -3,7 +3,7 @@ import numpy as np from dpdata.abacus.scf import get_cell, get_coords, get_nele_from_stru -from apex.core.calculator.lib import vasp +from . import vasp_utils from dflow.python import upload_packages from dargs.dargs import Argument upload_packages.append(__file__) @@ -423,7 +423,7 @@ def make_kspacing_kpoints_stru(stru, kspacing): if type(kspacing) is not list: kspacing = [kspacing, kspacing, kspacing] box = stru["cells"] - rbox = vasp.reciprocal_box(box) + rbox = vasp_utils.reciprocal_box(box) kpoints = [ max(1, (np.ceil(2 * np.pi * np.linalg.norm(ii) / ks).astype(int))) for ii, ks in zip(rbox, kspacing) diff --git a/apex/core/calculator/lib/abacus.py b/apex/core/calculator/lib/abacus_utils.py similarity index 94% rename from apex/core/calculator/lib/abacus.py rename to apex/core/calculator/lib/abacus_utils.py index 3551cde..d3262b1 100644 --- a/apex/core/calculator/lib/abacus.py +++ b/apex/core/calculator/lib/abacus_utils.py @@ -1,11 +1,12 @@ #!/usr/bin/python3 -import os +import logging +import os, re import dpdata import numpy as np from pymatgen.core.structure import Structure -import apex.core.calculator.lib.abacus_scf as abacus_scf +from . import abacus_scf from dflow.python import upload_packages upload_packages.append(__file__) @@ -499,3 +500,22 @@ def modify_stru_path(strucf, tpath): with open(strucf, "w") as f1: f1.writelines(lines) + +def append_orb_file_to_stru(stru, orb: dict = None, prefix: str = ""): + with open(stru, 'r') as fin: + all_string = fin.read() + + if not re.search("NUMERICAL_ORBITAL", all_string) and orb: + logging.info(msg="Append user specific orbital files to STRU") + orbital_string = "NUMERICAL_ORBITAL\n" + for key, item in orb.items(): + orbital_string += str(os.path.join(prefix, item)) + orbital_string += '\n' + all_string_new = orbital_string + "\n" + all_string + + if os.path.isfile(stru) or os.path.islink(stru): + os.remove(stru) + + with open(stru, 'w') as fout: + fout.write(all_string_new) + diff --git a/apex/core/calculator/lib/lammps.py b/apex/core/calculator/lib/lammps_utils.py similarity index 99% rename from apex/core/calculator/lib/lammps.py rename to apex/core/calculator/lib/lammps_utils.py index fcc8fa8..19f0bd3 100644 --- a/apex/core/calculator/lib/lammps.py +++ b/apex/core/calculator/lib/lammps_utils.py @@ -9,7 +9,7 @@ from dpdata.periodic_table import Element from packaging.version import Version -import apex.core.lib.util as util +from apex.core.lib import util from dflow.python import upload_packages upload_packages.append(__file__) diff --git a/apex/core/calculator/lib/vasp.py b/apex/core/calculator/lib/vasp_utils.py similarity index 98% rename from apex/core/calculator/lib/vasp.py rename to apex/core/calculator/lib/vasp_utils.py index ab337dd..a3a6b8f 100644 --- a/apex/core/calculator/lib/vasp.py +++ b/apex/core/calculator/lib/vasp_utils.py @@ -5,7 +5,7 @@ import numpy as np from pymatgen.io.vasp import Incar, Kpoints, Potcar -import apex.core.lib.util as util +from apex.core.lib import util #from apex.calculator.lib.vasp import incar_upper from dflow.python import upload_packages upload_packages.append(__file__) @@ -364,11 +364,12 @@ def make_vasp_relax_incar( return ret -def make_vasp_phonon_incar( - ecut, ediff, npar, kpar, kspacing=0.5, kgamma=True, ismear=1, sigma=0.2 +def make_vasp_phonon_dfpt_incar( + ecut, ediff, npar, kpar, kspacing=0.3, kgamma=True, ismear=1, sigma=0.22 ): isif = 2 ret = "" + ret += "ADDGRID=True\n" ret += "PREC=A\n" ret += "ENCUT=%d\n" % ecut ret += "# ISYM=0\n" @@ -377,7 +378,7 @@ def make_vasp_phonon_incar( ret += "EDIFFG=-0.01\n" ret += "LREAL=A\n" # ret += 'NPAR=%d\n' % npar - ret += "KPAR=%d\n" % kpar + # ret += "KPAR=%d\n" % kpar ret += "\n" ret += "ISMEAR=%d\n" % ismear ret += "SIGMA=%f\n" % sigma diff --git a/apex/core/common_equi.py b/apex/core/common_equi.py index 11e49d9..2c6be70 100644 --- a/apex/core/common_equi.py +++ b/apex/core/common_equi.py @@ -3,9 +3,8 @@ import shutil import logging from monty.serialization import dumpfn -import apex.core.calculator.lib.abacus as abacus -import apex.core.lib.crys as crys -import apex.core.lib.util as util +from apex.core.calculator.lib import abacus_utils +from apex.core.lib import crys from apex.core.calculator.calculator import make_calculator from apex.core.lib.utils import create_path from apex.core.lib.dispatcher import make_submission @@ -67,7 +66,7 @@ def make_equi(confs, inter_param, relax_param): crys.sc(ele_list[element_label]).to("POSCAR", "POSCAR") if inter_param["type"] == "abacus" and not os.path.exists("STRU"): - abacus.poscar2stru("POSCAR", inter_param, "STRU") + abacus_utils.poscar2stru("POSCAR", inter_param, "STRU") os.remove("POSCAR") os.chdir(cwd) @@ -82,7 +81,7 @@ def make_equi(confs, inter_param, relax_param): if "mp-" in crys_type and not os.path.exists(os.path.join(ii, "POSCAR")): get_structure(crys_type).to("POSCAR", os.path.join(ii, "POSCAR")) if inter_param["type"] == "abacus" and not os.path.exists("STRU"): - abacus.poscar2stru( + abacus_utils.poscar2stru( os.path.join(ii, "POSCAR"), inter_param, os.path.join(ii, "STRU") ) os.remove(os.path.join(ii, "POSCAR")) @@ -91,7 +90,7 @@ def make_equi(confs, inter_param, relax_param): POSCAR = "POSCAR" if inter_param["type"] == "abacus": shutil.copyfile(os.path.join(ii, "STRU"), os.path.join(ii, "STRU.bk")) - abacus.modify_stru_path(os.path.join(ii, "STRU"), "pp_orb/") + abacus_utils.modify_stru_path(os.path.join(ii, "STRU"), "pp_orb/") poscar = os.path.abspath(os.path.join(ii, "STRU")) POSCAR = "STRU" if not os.path.exists(poscar): diff --git a/apex/core/common_prop.py b/apex/core/common_prop.py index 7ab8ffb..5c116f3 100644 --- a/apex/core/common_prop.py +++ b/apex/core/common_prop.py @@ -12,6 +12,7 @@ from apex.core.lib.dispatcher import make_submission from apex.core.property.Surface import Surface from apex.core.property.Vacancy import Vacancy +from apex.core.property.Phonon import Phonon from apex.utils import sepline, get_task_type from dflow.python import upload_packages upload_packages.append(__file__) @@ -36,6 +37,8 @@ def make_property_instance(parameters, inter_param): return Surface(parameters, inter_param) elif prop_type == "gamma": return Gamma(parameters, inter_param) + elif prop_type == "phonon": + return Phonon(parameters, inter_param) else: raise RuntimeError(f"unknown dflowautotest type {prop_type}") diff --git a/apex/core/gen_confs.py b/apex/core/gen_confs.py index 6e8e9c1..9029757 100644 --- a/apex/core/gen_confs.py +++ b/apex/core/gen_confs.py @@ -7,7 +7,7 @@ from pymatgen.analysis.structure_matcher import StructureMatcher from pymatgen.ext.matproj import Composition, MPRester -import apex.core.lib.crys as crys +from apex.core.lib import crys from dflow.python import upload_packages upload_packages.append(__file__) diff --git a/apex/core/lib/util.py b/apex/core/lib/util.py index d18bf2f..32831dc 100644 --- a/apex/core/lib/util.py +++ b/apex/core/lib/util.py @@ -5,13 +5,14 @@ import requests #from dpgen import dlog -from apex.core.calculator.lib import abacus, lammps, vasp +from apex.core.calculator.lib import abacus_utils, lammps_utils, vasp_utils from apex.core.lib.utils import cmd_append_log from dflow.python import upload_packages upload_packages.append(__file__) lammps_task_type = ["deepmd", "meam", "eam_fs", "eam_alloy"] # 06/13 revised + def voigt_to_stress(inpt): ret = np.zeros((3, 3)) ret[0][0] = inpt[0] @@ -98,13 +99,13 @@ def collect_task(all_task, task_type): if task_type == "vasp": output_file = "OUTCAR" - check_finished = vasp.check_finished + check_finished = vasp_utils.check_finished elif task_type in lammps_task_type: output_file = "log.lammps" - check_finished = lammps.check_finished + check_finished = lammps_task_type.check_finished elif task_type == "abacus": output_file = "OUT.ABACUS/running_relax.log" - check_finished = abacus.check_finished + check_finished = abacus_utils.check_finished run_tasks_ = [] for ii in all_task: diff --git a/apex/core/mpdb.py b/apex/core/mpdb.py index e986fbc..a38cab6 100644 --- a/apex/core/mpdb.py +++ b/apex/core/mpdb.py @@ -1,6 +1,5 @@ import os -from pymatgen.core import Structure from pymatgen.ext.matproj import MPRester, MPRestError from dflow.python import upload_packages upload_packages.append(__file__) diff --git a/apex/core/property/EOS.py b/apex/core/property/EOS.py index b17a5d0..896437a 100644 --- a/apex/core/property/EOS.py +++ b/apex/core/property/EOS.py @@ -7,9 +7,9 @@ import numpy as np from monty.serialization import dumpfn, loadfn -import apex.core.calculator.lib.abacus as abacus -import apex.core.calculator.lib.vasp as vasp -import apex.core.calculator.lib.abacus_scf as abacus_scf +from apex.core.calculator.lib import abacus_utils +from apex.core.calculator.lib import vasp_utils +from apex.core.calculator.lib import abacus_scf from apex.core.property.Property import Property from apex.core.refine import make_refine from apex.core.reproduce import make_repro, post_repro @@ -176,7 +176,7 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): if self.inter_param["type"] == "abacus": equi_contcar = os.path.join( - path_to_equi, abacus.final_stru(path_to_equi) + path_to_equi, abacus_utils.final_stru(path_to_equi) ) else: equi_contcar = os.path.join(path_to_equi, "CONTCAR") @@ -193,7 +193,7 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): / np.array(stru_data["atom_numbs"]).sum() ) else: - vol_to_poscar = vasp.poscar_vol(equi_contcar) / vasp.poscar_natoms( + vol_to_poscar = vasp_utils.poscar_vol(equi_contcar) / vasp_utils.poscar_natoms( equi_contcar ) self.parameter["scale2equi"] = [] @@ -210,11 +210,11 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): if self.inter_param["type"] == "abacus": POSCAR = "STRU" POSCAR_orig = "STRU.orig" - scale_func = abacus.stru_scale + scale_func = abacus_utils.stru_scale else: POSCAR = "POSCAR" POSCAR_orig = "POSCAR.orig" - scale_func = vasp.poscar_scale + scale_func = vasp_utils.poscar_scale for ii in [ "INCAR", diff --git a/apex/core/property/Elastic.py b/apex/core/property/Elastic.py index 5f391d1..e4d94d0 100644 --- a/apex/core/property/Elastic.py +++ b/apex/core/property/Elastic.py @@ -11,12 +11,12 @@ from pymatgen.core.structure import Structure from pymatgen.io.vasp import Incar, Kpoints -import apex.core.calculator.lib.abacus as abacus -import apex.core.calculator.lib.vasp as vasp -import apex.core.calculator.lib.abacus_scf as abacus_scf +from apex.core.calculator.lib import abacus_utils +from apex.core.calculator.lib import vasp_utils +from apex.core.calculator.lib import abacus_scf from apex.core.property.Property import Property from apex.core.refine import make_refine -from apex.core.calculator.lib.vasp import incar_upper +from apex.core.calculator.lib.vasp_utils import incar_upper from dflow.python import upload_packages upload_packages.append(__file__) @@ -87,7 +87,7 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): cwd = os.getcwd() if self.inter_param["type"] == "abacus": - CONTCAR = abacus.final_stru(path_to_equi) + CONTCAR = abacus_utils.final_stru(path_to_equi) POSCAR = "STRU" else: CONTCAR = "CONTCAR" @@ -153,7 +153,7 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): raise RuntimeError("please do relaxation first") if self.inter_param["type"] == "abacus": - ss = abacus.stru2Structure(equi_contcar) + ss = abacus_utils.stru2Structure(equi_contcar) else: ss = Structure.from_file(equi_contcar) dfm_ss = DeformedStructureSet( @@ -183,7 +183,7 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): task_list.append(output_task) dfm_ss.deformed_structures[ii].to("POSCAR", "POSCAR") if self.inter_param["type"] == "abacus": - abacus.poscar2stru("POSCAR", self.inter_param, "STRU") + abacus_utils.poscar2stru("POSCAR", self.inter_param, "STRU") os.remove("POSCAR") # record strain df = Strain.from_deformation(dfm_ss.deformations[ii]) @@ -209,12 +209,12 @@ def post_process(self, task_list): input_aba = abacus_scf.get_abacus_input_parameters("INPUT") if "kspacing" in input_aba: kspacing = float(input_aba["kspacing"]) - kpt = abacus.make_kspacing_kpt(poscar_start, kspacing) + kpt = abacus_utils.make_kspacing_kpt(poscar_start, kspacing) kpt += [0, 0, 0] - abacus.write_kpt("KPT", kpt) + abacus_utils.write_kpt("KPT", kpt) del input_aba["kspacing"] os.remove("INPUT") - abacus.write_input("INPUT", input_aba) + abacus_utils.write_input("INPUT", input_aba) else: os.rename(os.path.join(task_list[0], "KPT"), "./KPT") else: @@ -223,7 +223,7 @@ def post_process(self, task_list): ) kspacing = incar.get("KSPACING") kgamma = incar.get("KGAMMA", False) - ret = vasp.make_kspacing_kpoints(poscar_start, kspacing, kgamma) + ret = vasp_utils.make_kspacing_kpoints(poscar_start, kspacing, kgamma) kp = Kpoints.from_string(ret) if os.path.isfile("KPOINTS"): os.remove("KPOINTS") diff --git a/apex/core/property/Gamma.py b/apex/core/property/Gamma.py index 5533b01..d12361f 100644 --- a/apex/core/property/Gamma.py +++ b/apex/core/property/Gamma.py @@ -11,8 +11,8 @@ from pymatgen.core.surface import SlabGenerator from pymatgen.analysis.diffraction.tem import TEMCalculator -import apex.core.calculator.lib.abacus as abacus -import apex.core.calculator.lib.vasp as vasp +from apex.core.calculator.lib import abacus_utils +from apex.core.calculator.lib import vasp_utils from apex.core.property.Property import Property from apex.core.refine import make_refine from apex.core.reproduce import make_repro, post_repro @@ -180,7 +180,7 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): else: if self.inter_param["type"] == "abacus": - CONTCAR = abacus.final_stru(path_to_equi) + CONTCAR = abacus_utils.final_stru(path_to_equi) POSCAR = "STRU" else: CONTCAR = "CONTCAR" @@ -197,11 +197,11 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): if self.inter_param["type"] == "abacus": stru = dpdata.System(equi_contcar, fmt="stru") stru.to("contcar", "CONTCAR.tmp") - ptypes = vasp.get_poscar_types("CONTCAR.tmp") + ptypes = vasp_utils.get_poscar_types("CONTCAR.tmp") ss = Structure.from_file("CONTCAR.tmp") os.remove("CONTCAR.tmp") else: - ptypes = vasp.get_poscar_types(equi_contcar) + ptypes = vasp_utils.get_poscar_types(equi_contcar) # read structure from relaxed CONTCAR ss = Structure.from_file(equi_contcar) @@ -286,10 +286,10 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): ) # make confs obtained_slab.to("POSCAR.tmp", "POSCAR") - vasp.regulate_poscar("POSCAR.tmp", "POSCAR") - vasp.sort_poscar("POSCAR", "POSCAR", ptypes) + vasp_utils.regulate_poscar("POSCAR.tmp", "POSCAR") + vasp_utils.sort_poscar("POSCAR", "POSCAR", ptypes) if self.inter_param["type"] == "abacus": - abacus.poscar2stru("POSCAR", self.inter_param, "STRU") + abacus_utils.poscar2stru("POSCAR", self.inter_param, "STRU") os.remove("POSCAR") # vasp.perturb_xz('POSCAR', 'POSCAR', self.pert_xz) # record miller diff --git a/apex/core/property/Interstitial.py b/apex/core/property/Interstitial.py index a6347c1..aab33c1 100644 --- a/apex/core/property/Interstitial.py +++ b/apex/core/property/Interstitial.py @@ -9,8 +9,8 @@ from pymatgen.analysis.defects.generators import InterstitialGenerator from pymatgen.core.structure import Structure -import apex.core.calculator.lib.abacus as abacus -import apex.core.calculator.lib.lammps as lammps +from apex.core.calculator.lib import abacus_utils +from apex.core.calculator.lib import lammps_utils from apex.core.property.Property import Property from apex.core.refine import make_refine from apex.core.reproduce import make_repro, post_repro @@ -171,7 +171,7 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): else: if self.inter_param["type"] == "abacus": - CONTCAR = abacus.final_stru(path_to_equi) + CONTCAR = abacus_utils.final_stru(path_to_equi) POSCAR = "STRU" else: CONTCAR = "CONTCAR" @@ -182,7 +182,7 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): raise RuntimeError("please do relaxation first") if self.inter_param["type"] == "abacus": - ss = abacus.stru2Structure(equi_contcar) + ss = abacus_utils.stru2Structure(equi_contcar) else: ss = Structure.from_file(equi_contcar) @@ -370,7 +370,7 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): for ii in range(total_task): output_task = os.path.join(self.path_to_work, "task.%06d" % ii) os.chdir(output_task) - abacus.poscar2stru("POSCAR", self.inter_param, "STRU") + abacus_utils.poscar2stru("POSCAR", self.inter_param, "STRU") os.remove("POSCAR") os.chdir(cwd) @@ -424,7 +424,7 @@ def post_process(self, task_list): conf_line = fin2.read().split("\n") insert_line = conf_line[-2] type_map = loadfn(inter)["type_map"] - type_map_list = lammps.element_list(type_map) + type_map_list = lammps_utils.element_list(type_map) if int(insert_line.split()[1]) > len(type_map_list): type_num = type_map[insert_ele] + 1 conf_line[2] = str(len(type_map_list)) + " atom types" diff --git a/apex/core/property/Phonon.py b/apex/core/property/Phonon.py new file mode 100644 index 0000000..c425b56 --- /dev/null +++ b/apex/core/property/Phonon.py @@ -0,0 +1,449 @@ +import glob +import json +import logging +import os +import shutil +import re +import subprocess +import dpdata +from pathlib import Path +from pymatgen.core.structure import Structure + +from apex.core.structure import StructureInfo +from apex.core.calculator.calculator import LAMMPS_TYPE +from apex.core.calculator.lib import abacus_utils +from apex.core.calculator.lib import vasp_utils +from apex.core.property.Property import Property +from apex.core.refine import make_refine +from apex.core.reproduce import make_repro, post_repro +from dflow.python import upload_packages +upload_packages.append(__file__) + + +class Phonon(Property): + def __init__(self, parameter, inter_param=None): + parameter["reproduce"] = parameter.get("reproduce", False) + self.reprod = parameter["reproduce"] + if not self.reprod: + if not ("init_from_suffix" in parameter and "output_suffix" in parameter): + self.primitive = parameter.get('primitive', False) + self.approach = parameter.get('approach', 'linear') + self.supercell_size = parameter.get('supercell_size', [2, 2, 2]) + self.MESH = parameter.get('MESH', None) + self.PRIMITIVE_AXES = parameter.get('PRIMITIVE_AXES', None) + self.BAND = parameter.get('BAND', None) + self.BAND_POINTS = parameter.get('BAND_POINTS', None) + self.BAND_CONNECTION = parameter.get('BAND_CONNECTION', True) + parameter["cal_type"] = parameter.get("cal_type", "relaxation") + self.cal_type = parameter["cal_type"] + default_cal_setting = { + "relax_pos": True, + "relax_shape": False, + "relax_vol": False, + } + if "cal_setting" not in parameter: + parameter["cal_setting"] = default_cal_setting + else: + if "relax_pos" not in parameter["cal_setting"]: + parameter["cal_setting"]["relax_pos"] = default_cal_setting[ + "relax_pos" + ] + if "relax_shape" not in parameter["cal_setting"]: + parameter["cal_setting"]["relax_shape"] = default_cal_setting[ + "relax_shape" + ] + if "relax_vol" not in parameter["cal_setting"]: + parameter["cal_setting"]["relax_vol"] = default_cal_setting[ + "relax_vol" + ] + self.cal_setting = parameter["cal_setting"] + else: + parameter["cal_type"] = "static" + self.cal_type = parameter["cal_type"] + default_cal_setting = { + "relax_pos": False, + "relax_shape": False, + "relax_vol": False, + } + if "cal_setting" not in parameter: + parameter["cal_setting"] = default_cal_setting + else: + if "relax_pos" not in parameter["cal_setting"]: + parameter["cal_setting"]["relax_pos"] = default_cal_setting[ + "relax_pos" + ] + if "relax_shape" not in parameter["cal_setting"]: + parameter["cal_setting"]["relax_shape"] = default_cal_setting[ + "relax_shape" + ] + if "relax_vol" not in parameter["cal_setting"]: + parameter["cal_setting"]["relax_vol"] = default_cal_setting[ + "relax_vol" + ] + self.cal_setting = parameter["cal_setting"] + parameter["init_from_suffix"] = parameter.get("init_from_suffix", "00") + self.init_from_suffix = parameter["init_from_suffix"] + self.parameter = parameter + self.inter_param = inter_param if inter_param is not None else {"type": "vasp"} + + def make_confs(self, path_to_work, path_to_equi, refine=False): + path_to_work = os.path.abspath(path_to_work) + if os.path.exists(path_to_work): + #dlog.warning("%s already exists" % path_to_work) + logging.warning("%s already exists" % path_to_work) + else: + os.makedirs(path_to_work) + path_to_equi = os.path.abspath(path_to_equi) + + if "start_confs_path" in self.parameter and os.path.exists( + self.parameter["start_confs_path"] + ): + init_path_list = glob.glob( + os.path.join(self.parameter["start_confs_path"], "*") + ) + struct_init_name_list = [] + for ii in init_path_list: + struct_init_name_list.append(ii.split("/")[-1]) + struct_output_name = path_to_work.split("/")[-2] + assert struct_output_name in struct_init_name_list + path_to_equi = os.path.abspath( + os.path.join( + self.parameter["start_confs_path"], + struct_output_name, + "relaxation", + "relax_task", + ) + ) + + task_list = [] + cwd = os.getcwd() + + if self.reprod: + print("phonon reproduce starts") + if "init_data_path" not in self.parameter: + raise RuntimeError("please provide the initial data path to reproduce") + init_data_path = os.path.abspath(self.parameter["init_data_path"]) + task_list = make_repro( + self.inter_param, + init_data_path, + self.init_from_suffix, + path_to_work, + self.parameter.get("reprod_last_frame", True), + ) + os.chdir(cwd) + + else: + if refine: + print("phonon refine starts") + task_list = make_refine( + self.parameter["init_from_suffix"], + self.parameter["output_suffix"], + path_to_work, + ) + os.chdir(cwd) + + else: + if self.inter_param["type"] == "abacus": + CONTCAR = abacus_utils.final_stru(path_to_equi) + POSCAR = "STRU" + else: + CONTCAR = "CONTCAR" + POSCAR = "POSCAR" + + equi_contcar = os.path.join(path_to_equi, CONTCAR) + if not os.path.exists(equi_contcar): + raise RuntimeError("please do relaxation first") + + if self.inter_param["type"] == "abacus": + stru = dpdata.System(equi_contcar, fmt="stru") + stru.to("contcar", "CONTCAR.tmp") + ptypes = vasp_utils.get_poscar_types("CONTCAR.tmp") + ss = Structure.from_file("CONTCAR.tmp") + os.remove("CONTCAR.tmp") + else: + ptypes = vasp_utils.get_poscar_types(equi_contcar) + ss = Structure.from_file(equi_contcar) + # gen structure + + # get user input parameter for specific structure + st = StructureInfo(ss) + self.structure_type = st.lattice_structure + type_param = self.parameter.get(self.structure_type, None) + if type_param: + self.primitive = type_param.get("primitive", self.primitive) + self.approach = type_param.get("approach", self.approach) + self.supercell_size = type_param.get("supercell_size", self.supercell_size) + self.MESH = type_param.get("MESH", self.MESH) + self.PRIMITIVE_AXES = type_param.get("PRIMITIVE_AXES", self.PRIMITIVE_AXES) + self.BAND = type_param.get("BAND", self.BAND) + self.BAND_POINTS = type_param.get("BAND_POINTS", self.BAND_POINTS) + self.BAND_CONNECTION = type_param.get("BAND_CONNECTION", self.BAND_CONNECTION) + + os.chdir(path_to_work) + if os.path.isfile(POSCAR): + os.remove(POSCAR) + if os.path.islink(POSCAR): + os.remove(POSCAR) + os.symlink(os.path.relpath(equi_contcar), POSCAR) + # task_poscar = os.path.join(output, 'POSCAR') + + if not self.BAND: + raise RuntimeError('No band_path input for phonon calculation!') + ret = "" + ret += "ATOM_NAME =" + for ii in ptypes: + ret += " %s" % ii + ret += "\n" + ret += "DIM = %s %s %s\n" % ( + self.supercell_size[0], + self.supercell_size[1], + self.supercell_size[2] + ) + if self.MESH: + ret += "MESH = %s %s %s\n" % ( + self.MESH[0], self.MESH[1], self.MESH[2] + ) + if self.PRIMITIVE_AXES: + ret += "PRIMITIVE_AXES = %s\n" % self.PRIMITIVE_AXES + ret += "BAND = %s\n" % self.BAND + if self.BAND_POINTS: + ret += "BAND_POINTS = %s\n" % self.BAND_POINTS + if self.BAND_CONNECTION: + ret += "BAND_CONNECTION = %s\n" % self.BAND_CONNECTION + + ret_force_read = ret + "FORCE_CONSTANTS=READ\n" + + task_list = [] + # ------------make for abacus--------------- + if self.inter_param["type"] == "abacus": + # make setting.conf + ret_sc = "" + ret_sc += "DIM=%s %s %s\n" % ( + self.supercell_size[0], + self.supercell_size[1], + self.supercell_size[2] + ) + ret_sc += "ATOM_NAME =" + for atom in ptypes: + ret_sc += " %s" % (atom) + ret_sc += "\n" + with open("setting.conf", "a") as fp: + fp.write(ret_sc) + # append NUMERICAL_ORBITAL to STRU after relaxation + orb_file = self.inter_param.get("orb_files", None) + abacus_utils.append_orb_file_to_stru("STRU", orb_file, prefix='pp_orb') + ## generate STRU-00x + cmd = "phonopy setting.conf --abacus -d" + subprocess.call(cmd, shell=True) + + with open("band.conf", "a") as fp: + fp.write(ret) + # generate task.000* + stru_list = glob.glob("STRU-0*") + for ii in range(len(stru_list)): + task_path = os.path.join(path_to_work, 'task.%06d' % ii) + os.makedirs(task_path, exist_ok=True) + os.chdir(task_path) + task_list.append(task_path) + os.symlink(os.path.join(path_to_work, stru_list[ii]), 'STRU') + os.symlink(os.path.join(path_to_work, 'STRU'), 'STRU.ori') + os.symlink(os.path.join(path_to_work, 'band.conf'), 'band.conf') + os.symlink(os.path.join(path_to_work, 'phonopy_disp.yaml'), 'phonopy_disp.yaml') + try: + os.symlink(os.path.join(path_to_work, 'KPT'), 'KPT') + except: + pass + os.chdir(cwd) + return task_list + + # ------------make for vasp and lammps------------ + if self.primitive: + subprocess.call('phonopy --symmetry', shell=True) + subprocess.call('cp PPOSCAR POSCAR', shell=True) + shutil.copyfile("PPOSCAR", "POSCAR-unitcell") + else: + shutil.copyfile("POSCAR", "POSCAR-unitcell") + + # make tasks + if self.inter_param["type"] == 'vasp': + cmd = "phonopy -d --dim='%d %d %d' -c POSCAR" % ( + int(self.supercell_size[0]), + int(self.supercell_size[1]), + int(self.supercell_size[2]) + ) + subprocess.call(cmd, shell=True) + # linear response method + if self.approach == 'linear': + task_path = os.path.join(path_to_work, 'task.000000') + os.makedirs(task_path, exist_ok=True) + os.chdir(task_path) + task_list.append(task_path) + os.symlink(os.path.join(path_to_work, "SPOSCAR"), "POSCAR") + os.symlink(os.path.join(path_to_work, "POSCAR-unitcell"), "POSCAR-unitcell") + with open("band.conf", "a") as fp: + fp.write(ret_force_read) + # finite displacement method + elif self.approach == 'displacement': + poscar_list = glob.glob("POSCAR-0*") + for ii in range(len(poscar_list)): + task_path = os.path.join(path_to_work, 'task.%06d' % ii) + os.makedirs(task_path, exist_ok=True) + os.chdir(task_path) + task_list.append(task_path) + os.symlink(os.path.join(path_to_work, poscar_list[ii]), 'POSCAR') + os.symlink(os.path.join(path_to_work, "POSCAR-unitcell"), "POSCAR-unitcell") + + os.chdir(path_to_work) + with open("band.conf", "a") as fp: + fp.write(ret) + shutil.copyfile("band.conf", "task.000000/band.conf") + shutil.copyfile("phonopy_disp.yaml", "task.000000/phonopy_disp.yaml") + + else: + raise RuntimeError( + f'Unsupported phonon approach input: {self.approach}. ' + f'Please choose from "linear" and "displacement".' + ) + os.chdir(cwd) + return task_list + # ----------make for lammps------------- + elif self.inter_param["type"] in LAMMPS_TYPE: + task_path = os.path.join(path_to_work, 'task.000000') + os.makedirs(task_path, exist_ok=True) + os.chdir(task_path) + task_list.append(task_path) + os.symlink(os.path.join(path_to_work, "POSCAR-unitcell"), POSCAR) + + with open("band.conf", "a") as fp: + fp.write(ret_force_read) + os.chdir(cwd) + return task_list + else: + raise RuntimeError( + f'Unsupported interaction type input: {self.inter_param["type"]}' + ) + + def post_process(self, task_list): + cwd = os.getcwd() + inter_type = self.inter_param["type"] + if inter_type in LAMMPS_TYPE: + # prepare in.lammps + for ii in task_list: + os.chdir(ii) + with open("in.lammps", 'r') as f1: + contents = f1.readlines() + for jj in range(len(contents)): + is_pair_coeff = re.search("pair_coeff", contents[jj]) + if is_pair_coeff: + pair_line_id = jj + break + del contents[pair_line_id + 1:] + + with open("in.lammps", 'w') as f2: + for jj in range(len(contents)): + f2.write(contents[jj]) + # dump phonolammps command + phonolammps_cmd = "phonolammps in.lammps -c POSCAR --dim %s %s %s " %( + self.supercell_size[0], self.supercell_size[1], self.supercell_size[2] + ) + with open("run_command", 'w') as f3: + f3.write(phonolammps_cmd) + elif inter_type == "vasp": + pass + elif inter_type == "abacus": + pass + os.chdir(cwd) + + def task_type(self): + return self.parameter["type"] + + def task_param(self): + return self.parameter + + def _compute_lower(self, output_file, all_tasks, all_res): + cwd = Path.cwd() + work_path = Path(output_file).parent + output_file = os.path.abspath(output_file) + res_data = {} + ptr_data = os.path.dirname(output_file) + "\n" + + if not self.reprod: + os.chdir(work_path) + if self.inter_param["type"] == 'abacus': + shutil.copyfile("task.000000/band.conf", "band.conf") + shutil.copyfile("task.000000/STRU.ori", "STRU") + shutil.copyfile("task.000000/phonopy_disp.yaml", "phonopy_disp.yaml") + os.system('phonopy -f task.0*/OUT.ABACUS/running_scf.log') + os.system('phonopy -f task.0*/OUT.ABACUS/running_scf.log') + if os.path.exists("FORCE_SETS"): + print('FORCE_SETS is created') + else: + print('FORCE_SETS can not be created') + os.system('phonopy band.conf --abacus') + os.system('phonopy-bandplot --gnuplot band.yaml > band.dat') + + elif self.inter_param["type"] == 'vasp': + shutil.copyfile("task.000000/band.conf", "band.conf") + shutil.copyfile("task.000000/POSCAR-unitcell", "POSCAR-unitcell") + + if self.approach == "linear": + os.chdir(all_tasks[0]) + assert os.path.isfile('vasprun.xml'), "vasprun.xml not found" + os.system('phonopy --fc vasprun.xml') + assert os.path.isfile('FORCE_CONSTANTS'), "FORCE_CONSTANTS not created" + os.system('phonopy --dim="%s %s %s" -c POSCAR-unitcell band.conf' % ( + self.supercell_size[0], + self.supercell_size[1], + self.supercell_size[2])) + os.system('phonopy-bandplot --gnuplot band.yaml > band.dat') + print('band.dat is created') + shutil.copyfile("band.dat", work_path/"band.dat") + + elif self.approach == "displacement": + shutil.copyfile("task.000000/band.conf", "band.conf") + shutil.copyfile("task.000000/phonopy_disp.yaml", "phonopy_disp.yaml") + os.system('phonopy -f task.0*/vasprun.xml') + if os.path.exists("FORCE_SETS"): + print('FORCE_SETS is created') + else: + print('FORCE_SETS can not be created') + os.system('phonopy --dim="%s %s %s" -c POSCAR-unitcell band.conf' % ( + self.supercell_size[0], + self.supercell_size[1], + self.supercell_size[2])) + os.system('phonopy-bandplot --gnuplot band.yaml > band.dat') + + elif self.inter_param["type"] in LAMMPS_TYPE: + os.chdir(all_tasks[0]) + assert os.path.isfile('FORCE_CONSTANTS'), "FORCE_CONSTANTS not created" + os.system('phonopy --dim="%s %s %s" -c POSCAR band.conf' % ( + self.supercell_size[0], self.supercell_size[1], self.supercell_size[2]) + ) + os.system('phonopy-bandplot --gnuplot band.yaml > band.dat') + shutil.copyfile("band.dat", work_path/"band.dat") + + else: + if "init_data_path" not in self.parameter: + raise RuntimeError("please provide the initial data path to reproduce") + init_data_path = os.path.abspath(self.parameter["init_data_path"]) + res_data, ptr_data = post_repro( + init_data_path, + self.parameter["init_from_suffix"], + all_tasks, + ptr_data, + self.parameter.get("reprod_last_frame", True), + ) + + os.chdir(work_path) + with open('band.dat', 'r') as f: + ptr_data = f.read() + + result_points = ptr_data.split('\n')[1][4:] + result_lines = ptr_data.split('\n')[2:] + res_data[result_points] = result_lines + + with open(output_file, "w") as fp: + json.dump(res_data, fp, indent=4) + + os.chdir(cwd) + return res_data, ptr_data diff --git a/apex/core/property/Surface.py b/apex/core/property/Surface.py index 4cd4506..cb6f0eb 100644 --- a/apex/core/property/Surface.py +++ b/apex/core/property/Surface.py @@ -10,8 +10,8 @@ from pymatgen.core.structure import Structure from pymatgen.core.surface import generate_all_slabs -import apex.core.calculator.lib.abacus as abacus -import apex.core.calculator.lib.vasp as vasp +from apex.core.calculator.lib import abacus_utils +from apex.core.calculator.lib import vasp_utils from apex.core.property.Property import Property from apex.core.refine import make_refine from apex.core.reproduce import make_repro, post_repro @@ -166,7 +166,7 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): else: if self.inter_param["type"] == "abacus": - CONTCAR = abacus.final_stru(path_to_equi) + CONTCAR = abacus_utils.final_stru(path_to_equi) POSCAR = "STRU" else: CONTCAR = "CONTCAR" @@ -179,11 +179,11 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): if self.inter_param["type"] == "abacus": stru = dpdata.System(equi_contcar, fmt="stru") stru.to("contcar", "CONTCAR.tmp") - ptypes = vasp.get_poscar_types("CONTCAR.tmp") + ptypes = vasp_utils.get_poscar_types("CONTCAR.tmp") ss = Structure.from_file("CONTCAR.tmp") os.remove("CONTCAR.tmp") else: - ptypes = vasp.get_poscar_types(equi_contcar) + ptypes = vasp_utils.get_poscar_types(equi_contcar) # gen structure ss = Structure.from_file(equi_contcar) @@ -221,11 +221,11 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): ) # make confs all_slabs[ii].to("POSCAR.tmp", "POSCAR") - vasp.regulate_poscar("POSCAR.tmp", "POSCAR") - vasp.sort_poscar("POSCAR", "POSCAR", ptypes) - vasp.perturb_xz("POSCAR", "POSCAR", self.pert_xz) + vasp_utils.regulate_poscar("POSCAR.tmp", "POSCAR") + vasp_utils.sort_poscar("POSCAR", "POSCAR", ptypes) + vasp_utils.perturb_xz("POSCAR", "POSCAR", self.pert_xz) if self.inter_param["type"] == "abacus": - abacus.poscar2stru("POSCAR", self.inter_param, "STRU") + abacus_utils.poscar2stru("POSCAR", self.inter_param, "STRU") os.remove("POSCAR") # record miller dumpfn(all_slabs[ii].miller_index, "miller.json") diff --git a/apex/core/property/Vacancy.py b/apex/core/property/Vacancy.py index c16af16..e92c2ee 100644 --- a/apex/core/property/Vacancy.py +++ b/apex/core/property/Vacancy.py @@ -9,7 +9,7 @@ from pymatgen.analysis.defects.generators import VacancyGenerator from pymatgen.core.structure import Structure -import apex.core.calculator.lib.abacus as abacus +from apex.core.calculator.lib import abacus_utils from apex.core.property.Property import Property from apex.core.refine import make_refine from apex.core.reproduce import make_repro, post_repro @@ -156,7 +156,7 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): os.chdir(cwd) else: if self.inter_param["type"] == "abacus": - CONTCAR = abacus.final_stru(path_to_equi) + CONTCAR = abacus_utils.final_stru(path_to_equi) POSCAR = "STRU" else: CONTCAR = "CONTCAR" @@ -167,7 +167,7 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): raise RuntimeError("please do relaxation first") if self.inter_param["type"] == "abacus": - ss = abacus.stru2Structure(equi_contcar) + ss = abacus_utils.stru2Structure(equi_contcar) else: ss = Structure.from_file(equi_contcar) @@ -205,7 +205,7 @@ def make_confs(self, path_to_work, path_to_equi, refine=False): task_list.append(output_task) dss[ii].to("POSCAR", "POSCAR") if self.inter_param["type"] == "abacus": - abacus.poscar2stru("POSCAR", self.inter_param, "STRU") + abacus_utils.poscar2stru("POSCAR", self.inter_param, "STRU") os.remove("POSCAR") # np.savetxt('supercell.out', self.supercell, fmt='%d') dumpfn(self.supercell, "supercell.json") diff --git a/apex/core/refine.py b/apex/core/refine.py index 3159229..3fb4124 100644 --- a/apex/core/refine.py +++ b/apex/core/refine.py @@ -2,7 +2,7 @@ import os import re -import apex.core.calculator.lib.abacus as abacus +from apex.core.calculator.lib import abacus_utils from dflow.python import upload_packages upload_packages.append(__file__) @@ -47,7 +47,7 @@ def make_refine(init_from_suffix, output_suffix, path_to_work): os.path.join(init_from_task, "STRU") ): # if there has INPUT and STRU files in this path, we believe this is a ABACUS job - CONTCAR = abacus.final_stru(init_from_task) + CONTCAR = abacus_utils.final_stru(init_from_task) POSCAR = "STRU" else: CONTCAR = "CONTCAR" diff --git a/apex/core/reproduce.py b/apex/core/reproduce.py index fb6690e..843d80a 100644 --- a/apex/core/reproduce.py +++ b/apex/core/reproduce.py @@ -4,7 +4,7 @@ import numpy as np from monty.serialization import loadfn -import apex.core.calculator.lib.abacus as abacus +from apex.core.calculator.lib import abacus_utils from dflow.python import upload_packages upload_packages.append(__file__) @@ -85,7 +85,7 @@ def make_repro( else: task_result.to("vasp/poscar", "POSCAR", frame_idx=jj) if inter_param["type"] == "abacus": - abacus.poscar2stru("POSCAR", inter_param, "STRU") + abacus_utils.poscar2stru("POSCAR", inter_param, "STRU") os.remove("POSCAR") os.chdir(cwd) diff --git a/apex/flow.py b/apex/flow.py index 0a3e0ae..8de33eb 100644 --- a/apex/flow.py +++ b/apex/flow.py @@ -21,6 +21,9 @@ from apex.op.relaxation_ops import RelaxMake, RelaxPost from apex.op.property_ops import PropsMake, PropsPost +from dflow.python import upload_packages +upload_packages.append(__file__) + def json2dict(function): def wrapper(*args, **kwargs): diff --git a/apex/main.py b/apex/main.py index 1980f8a..8113e60 100644 --- a/apex/main.py +++ b/apex/main.py @@ -4,9 +4,8 @@ header, __version__, ) -from .run_step import run_step -from .submit import submit_workflow - +from apex.run_step import run_step +from apex.submit import submit_workflow def parse_args(): diff --git a/apex/op/RunLAMMPS.py b/apex/op/RunLAMMPS.py index 060256b..7da9460 100644 --- a/apex/op/RunLAMMPS.py +++ b/apex/op/RunLAMMPS.py @@ -35,7 +35,11 @@ def get_output_sign(cls): def execute(self, op_in: OPIO) -> OPIO: cwd = os.getcwd() os.chdir(op_in["input_lammps"]) - cmd = op_in["run_command"] + if os.path.exists("run_command"): + with open("run_command", 'r') as f: + cmd = f.read() + else: + cmd = op_in["run_command"] exit_code = subprocess.call(cmd, shell=True) if exit_code == 0: print("Call Lammps command successfully!") diff --git a/apex/op/property_ops.py b/apex/op/property_ops.py index 7fa5444..39be3f7 100644 --- a/apex/op/property_ops.py +++ b/apex/op/property_ops.py @@ -1,4 +1,4 @@ -import os, glob, pathlib, shutil, subprocess +import os, glob, pathlib, shutil, subprocess, logging from pathlib import Path from typing import List from dflow.python import ( @@ -8,13 +8,8 @@ Artifact, upload_packages ) -try: - from monty.serialization import loadfn - from apex.utils import return_prop_list - from apex.core.common_prop import make_property_instance - from .utils import recursive_search -except: - pass + +from apex.op.utils import recursive_search upload_packages.append(__file__) @@ -139,8 +134,8 @@ def execute( self, op_in: OPIO, ) -> OPIO: - from apex.core.common_prop import make_property_instance - from apex.core.calculator.calculator import make_calculator + from ..core.common_prop import make_property_instance + from ..core.calculator.calculator import make_calculator input_work_path = op_in["input_work_path"] path_to_prop = op_in["path_to_prop"] @@ -160,7 +155,7 @@ def execute( poscar = os.path.join(kk, "POSCAR") inter = make_calculator(inter_param, poscar) inter.make_potential_files(kk) - # dlog.debug(prop.task_type()) ### debug + logging.debug(prop.task_type()) ### debug inter.make_input_file(kk, prop.task_type(), prop.task_param()) prop.post_process( task_list @@ -169,6 +164,7 @@ def execute( task_list.sort() os.chdir(input_work_path) task_list_str = glob.glob(path_to_prop + '/' + 'task.*') + task_list_str.sort() all_jobs = task_list njobs = len(all_jobs) @@ -213,7 +209,7 @@ def get_output_sign(cls): @OP.exec_sign_check def execute(self, op_in: OPIO) -> OPIO: - from apex.core.common_prop import make_property_instance + from ..core.common_prop import make_property_instance cwd = os.getcwd() input_post = op_in["input_post"] input_all = op_in["input_all"] diff --git a/apex/op/relaxation_ops.py b/apex/op/relaxation_ops.py index f2de913..3210b34 100644 --- a/apex/op/relaxation_ops.py +++ b/apex/op/relaxation_ops.py @@ -8,13 +8,8 @@ Artifact, upload_packages ) -try: - from monty.serialization import loadfn - from apex.utils import return_prop_list - from apex.core.common_equi import (make_equi, post_equi) - from .utils import recursive_search -except: - pass + +from apex.op.utils import recursive_search upload_packages.append(__file__) @@ -48,7 +43,7 @@ def execute( self, op_in: OPIO, ) -> OPIO: - from apex.core.common_equi import make_equi + from ..core.common_equi import make_equi cwd = os.getcwd() os.chdir(op_in["input"]) @@ -113,7 +108,7 @@ def get_output_sign(cls): @OP.exec_sign_check def execute(self, op_in: OPIO) -> OPIO: - from apex.core.common_equi import post_equi + from ..core.common_equi import post_equi cwd = os.getcwd() param_argv = op_in['param'] inter_param = param_argv["interaction"] diff --git a/apex/submit.py b/apex/submit.py index 17c2e00..61eb343 100644 --- a/apex/submit.py +++ b/apex/submit.py @@ -1,14 +1,15 @@ import glob +import os.path import tempfile from typing import Type from multiprocessing import Pool from dflow import config, s3_config from dflow.python import OP from monty.serialization import loadfn -import fpop, dpdata, apex +import fpop, dpdata, apex, phonolammps from apex.utils import get_task_type, get_flow_type -from .config import Config -from .flow import FlowFactory +from apex.config import Config +from apex.flow import FlowFactory def judge_flow(parameter, specify) -> (Type[OP], str, str, dict, dict): @@ -160,8 +161,9 @@ def submit_workflow( executor = wf_config.get_executor(wf_config.dispatcher_config_dict) upload_python_packages = wf_config.basic_config_dict["upload_python_packages"] upload_python_packages.extend(list(apex.__path__)) - upload_python_packages.extend(list(dpdata.__path__)) upload_python_packages.extend(list(fpop.__path__)) + upload_python_packages.extend(list(dpdata.__path__)) + upload_python_packages.extend(list(phonolammps.__path__)) flow = FlowFactory( make_image=make_image, @@ -178,7 +180,7 @@ def submit_workflow( # submit the workflows work_dir_list = [] for ii in work_dir: - glob_list = glob.glob(ii) + glob_list = glob.glob(os.path.abspath(ii)) work_dir_list.extend(glob_list) if len(work_dir_list) > 1: n_processes = len(work_dir_list) @@ -193,5 +195,7 @@ def submit_workflow( pool.join() elif len(work_dir_list) == 1: submit(flow, flow_type, work_dir_list[0], relax_param, props_param, labels=labels) + else: + raise RuntimeError('Empty work directory indicated, please check your argument') print('Completed!') diff --git a/apex/superop/PropertyFlow.py b/apex/superop/PropertyFlow.py index 6f82568..79cc029 100644 --- a/apex/superop/PropertyFlow.py +++ b/apex/superop/PropertyFlow.py @@ -143,6 +143,7 @@ def _build( name="Distributor", template=PythonOPTemplate(DistributeProps, image=make_image, + python_packages=upload_python_packages, command=["python3"]), artifacts={"input_work_path": self.inputs.artifacts["input_work_path"]}, parameters={"param": self.inputs.parameters["parameter"]}, @@ -199,6 +200,7 @@ def _build( name="PropsCollector", template=PythonOPTemplate(CollectProps, image=make_image, + python_packages=upload_python_packages, command=["python3"]), artifacts={"input_all": self.inputs.artifacts["input_work_path"], "input_post": propscal.outputs.artifacts["output_post"]}, diff --git a/apex/superop/RelaxationFlow.py b/apex/superop/RelaxationFlow.py index 9a8799e..eeefc23 100644 --- a/apex/superop/RelaxationFlow.py +++ b/apex/superop/RelaxationFlow.py @@ -142,6 +142,7 @@ def _build( name="Relaxmake", template=PythonOPTemplate(make_op, image=make_image, + python_packages=upload_python_packages, command=["python3"]), artifacts={"input": self.inputs.artifacts["input_work_path"]}, parameters={"param": self.inputs.parameters["parameter"]}, @@ -211,6 +212,7 @@ def _build( pool_size=pool_size ), image=run_image, + python_packages=upload_python_packages, command=["python3"] ) runcal = Step( diff --git a/apex/superop/SimplePropertySteps.py b/apex/superop/SimplePropertySteps.py index e56bd94..d10e918 100644 --- a/apex/superop/SimplePropertySteps.py +++ b/apex/superop/SimplePropertySteps.py @@ -140,8 +140,13 @@ def _build( ): # Step for property make make = Step( - name="prop-make", - template=PythonOPTemplate(make_op, image=make_image, command=["python3"]), + name="Props-make", + template=PythonOPTemplate( + make_op, + image=make_image, + python_packages=upload_python_packages, + command=["python3"] + ), artifacts={"input_work_path": self.inputs.artifacts["input_work_path"]}, parameters={"prop_param": self.inputs.parameters["prop_param"], "inter_param": self.inputs.parameters["inter_param"], @@ -173,7 +178,8 @@ def _build( parameters={ "run_image_config": {"command": run_command}, "task_name": make.outputs.parameters["task_names"], - "backward_list": ["INCAR", "POSCAR", "OUTCAR", "CONTCAR"] + "backward_list": ["INCAR", "POSCAR", "OUTCAR", "CONTCAR", + "vasprun.xml"] }, artifacts={ "task_path": make.outputs.artifacts["task_paths"] @@ -211,6 +217,7 @@ def _build( pool_size=pool_size ), image=run_image, + python_packages=upload_python_packages, command=["python3"] ) runcal = Step( @@ -232,6 +239,7 @@ def _build( template=PythonOPTemplate( post_op, image=post_image, + python_packages=upload_python_packages, command=["python3"] ), artifacts={ diff --git a/docs/Dockerfile b/docs/Dockerfile index 648eb5f..e105bef 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -4,6 +4,7 @@ RUN pip install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple RUN pip install monty -i https://pypi.tuna.tsinghua.edu.cn/simple RUN pip install pymatgen>=2023.8.10 -i https://pypi.tuna.tsinghua.edu.cn/simple RUN pip install pymatgen.analysis.defects>=2023.8.22 -i https://pypi.tuna.tsinghua.edu.cn/simple +RUN pip install phonopy -i https://pypi.tuna.tsinghua.edu.cn/simple RUN pip install dpdispatcher -i https://pypi.tuna.tsinghua.edu.cn/simple RUN pip install pydflow -i https://pypi.tuna.tsinghua.edu.cn/simple RUN pip install urllib3 -i https://pypi.tuna.tsinghua.edu.cn/simple diff --git a/docs/images/apex_demo.png b/docs/images/apex_demo.png index 0cf4847..53938bb 100644 Binary files a/docs/images/apex_demo.png and b/docs/images/apex_demo.png differ diff --git a/docs/images/apex_demo_v1.0.png b/docs/images/apex_demo_v1.0.png deleted file mode 100644 index b061666..0000000 Binary files a/docs/images/apex_demo_v1.0.png and /dev/null differ diff --git a/examples/abacus_demo/confs/fcc-Al/STRU b/examples/abacus_demo/confs/fcc-Al/STRU index 776f55a..22a10ab 100644 --- a/examples/abacus_demo/confs/fcc-Al/STRU +++ b/examples/abacus_demo/confs/fcc-Al/STRU @@ -1,20 +1,23 @@ ATOMIC_SPECIES -Al 26.9815 pp_orb/Al_ONCV_PBE-1.0.upf +Al 26.982 pp_orb/Al.PD04.PBE.UPF + +NUMERICAL_ORBITAL +pp_orb/Al_gga_10au_100Ry_3s3p2d.orb LATTICE_CONSTANT -1.8897261254578281 +1.8897261254578281 LATTICE_VECTORS -4.05 0.0 0.0 -0.0 4.05 0.0 -0.0 0.0 4.05 +4.04495 0.0 0.0 +0.0 4.04495 0.0 +0.0 0.0 4.04495 ATOMIC_POSITIONS -Cartesian # Cartesian(Unit is LATTICE_CONSTANT) +Direct Al -0.0 -4 -0.000000000000 0.000000000000 0.000000000000 1 1 1 -0.000000000000 2.025000000000 2.025000000000 1 1 1 -2.025000000000 0.000000000000 2.025000000000 1 1 1 -2.025000000000 2.025000000000 0.000000000000 1 1 1 +0.0 +4 +0.00 0.00 0.00 1 1 1 +0.50 0.50 0.00 1 1 1 +0.50 0.00 0.50 1 1 1 +0.00 0.50 0.50 1 1 1 diff --git a/examples/abacus_demo/global_bohrium.json b/examples/abacus_demo/global_bohrium.json index 7885beb..c7d5f99 100644 --- a/examples/abacus_demo/global_bohrium.json +++ b/examples/abacus_demo/global_bohrium.json @@ -4,10 +4,10 @@ "email": "YOUR_EMAIL", "password": "YOUR_PASSWD", "program_id": 1234, - "apex_image_name":"registry.dp.tech/dptech/prod-11045/apex-dependency:1.0", - "abacus_image_name":"registry.dp.tech/dptech/prod-11461/abacus:phonopy", - "abacus_run_command":"ulimit -s unlimited && ulimit -m unlimited && OMP_NUM_THREADS=1 mpirun -np 32 abacus > log", + "apex_image_name":"registry.dp.tech/dptech/prod-11045/apex-dependency:1.1.0", + "abacus_image_name":"registry.dp.tech/dptech/abacus:3.2.3", + "abacus_run_command":"mpirun -n 32 abacus", "batch_type": "Bohrium", "context_type": "Bohrium", - "scass_type":"c32_m64_cpu" + "scass_type":"c64_m128_cpu" } diff --git a/examples/abacus_demo/param_props.json b/examples/abacus_demo/param_props.json index 152f80b..208f11a 100644 --- a/examples/abacus_demo/param_props.json +++ b/examples/abacus_demo/param_props.json @@ -1,16 +1,16 @@ { - "structures": ["confs/fcc-Al"], - "interaction": { - "type": "abacus", - "incar": "abacus_input/INPUT", - "potcar_prefix": "abacus_input", - "potcars": {"Al": "Al_ONCV_PBE-1.0.upf"}, - "orb_files": {"Al":"Al_gga_9au_100Ry_4s4p1d.orb"} - }, + "structures": ["confs/fcc-Al"], + "interaction": { + "type": "abacus", + "incar": "abacus_input/INPUT_phonon", + "potcar_prefix": "abacus_input", + "potcars": {"Al": "Al.PD04.PBE.UPF"}, + "orb_files": {"Al":"Al_gga_10au_100Ry_3s3p2d.orb"} + }, "properties": [ { "type": "eos", - "skip": false, + "skip": true, "vol_start": 0.6, "vol_end": 1.4, "vol_step": 0.05 @@ -23,7 +23,7 @@ }, { "type": "surface", - "skip": false, + "skip": true, "min_slab_size": 10, "min_vacuum_size":11, "max_miller": 2, @@ -49,6 +49,21 @@ "vacuum_size": 15, "add_fix": ["true","true","false"], "n_steps": 20 + }, + { + "type": "phonon", + "skip": false, + "BAND": "0.5000 0.5000 0.5000 0.0000 0.0000 0.0000 0.5000 0.0000 0.5000 0.5000 0.2500 0.7500", + "supercell_size":[1,1,1], + "MESH":[8,8,8], + "PRIMITIVE_AXES": "0 1/2 1/2 1/2 0 1/2 1/2 1/2 0", + "BAND_POINTS":21, + "BAND_CONNECTION" :true, + "cal_setting": { + "K_POINTS": [6,6,6,0,0,0], + "input_prop": "abacus_input/INPUT_phonon" } + } + ] } diff --git a/examples/abacus_demo/param_relax.json b/examples/abacus_demo/param_relax.json index 115ed6e..6919b20 100644 --- a/examples/abacus_demo/param_relax.json +++ b/examples/abacus_demo/param_relax.json @@ -4,8 +4,9 @@ "type": "abacus", "incar": "abacus_input/INPUT", "potcar_prefix": "abacus_input", - "potcars": {"Al": "Al_ONCV_PBE-1.0.upf"}, - "orb_files": {"Al":"Al_gga_9au_100Ry_4s4p1d.orb"} + "potcars": {"Al": "Al.PD04.PBE.UPF"}, + "orb_files": {"Al":"Al_gga_10au_100Ry_3s3p2d.orb"} + }, "relaxation": { "cal_type": "relaxation", diff --git a/examples/lammps_demo/global_bohrium.json b/examples/lammps_demo/global_bohrium.json index 15eecb1..db9dcfa 100644 --- a/examples/lammps_demo/global_bohrium.json +++ b/examples/lammps_demo/global_bohrium.json @@ -1,15 +1,15 @@ { "dflow_host": "https://workflows.deepmodeling.com", "k8s_api_server": "https://workflows.deepmodeling.com", - "email": "YOUR@EMAIL", - "password": "YOURPASSWD", + "email": "YOUR_EMAIL", + "password": "YOUR_PASSWD", "program_id": 1234, - "apex_image_name":"registry.dp.tech/dptech/prod-11045/apex-dependency:1.0", - "lammps_image_name": "registry.dp.tech/dptech/prod-11045/deepmd-kit:deepmd-kit2.1.1_cuda11.6_gpu", - "group_size": 2, + "apex_image_name":"registry.dp.tech/dptech/prod-11045/apex-dependency:1.1.0", + "lammps_image_name": "registry.dp.tech/dptech/prod-11461/phonopy:v1.2", + "group_size": 4, "pool_size": 1, "run_command":"lmp -in in.lammps", "batch_type": "Bohrium", "context_type": "Bohrium", - "scass_type":"c8_m31_1 * NVIDIA T4" + "scass_type":"c16_m62_1 * NVIDIA T4" } diff --git a/examples/lammps_demo/param_joint.json b/examples/lammps_demo/param_joint.json index 46c6b82..6de5dc4 100644 --- a/examples/lammps_demo/param_joint.json +++ b/examples/lammps_demo/param_joint.json @@ -1,5 +1,5 @@ { - "structures": ["confs/std-*"], + "structures": ["confs/std-bcc"], "interaction": { "type": "deepmd", "model": "frozen_model.pb", @@ -23,7 +23,7 @@ }, { "type": "elastic", - "skip": false, + "skip": true, "norm_deform": 1e-2, "shear_deform": 1e-2, "cal_setting": {"etol": 0, @@ -44,7 +44,6 @@ "insert_ele": ["Mo"] }, { - "type": "vacancy", "skip": true, "supercell": [2, 2, 2] }, @@ -62,7 +61,13 @@ "vacuum_size": 15, "add_fix": ["true","true","false"], "n_steps": 20 - } + }, + { + "type": "phonon", + "skip": false, + "BAND": "0.0000 0.0000 0.5000 0.0000 0.0000 0.0000 0.5000 -0.5000 0.5000 0.25000 0.2500 0.2500 0 0 0", + "supercell_size":[4,4,4], + "PRIMITIVE_AXES": "Auto" + } ] - } diff --git a/examples/vasp_demo/global_bohrium.json b/examples/vasp_demo/global_bohrium.json index ee1a079..64fef43 100644 --- a/examples/vasp_demo/global_bohrium.json +++ b/examples/vasp_demo/global_bohrium.json @@ -4,8 +4,8 @@ "email": "YOUR_EMAIL", "password": "YOUR_PASSWD", "program_id": 1234, - "group_size": 2, - "apex_image_name":"registry.dp.tech/dptech/prod-11045/apex-dependency:1.0", + "group_size": 1, + "apex_image_name":"registry.dp.tech/dptech/prod-11045/apex-dependency:1.1.0", "vasp_image_name":"registry.dp.tech/dptech/vasp:5.4.4-dflow", "vasp_run_command":"bash -c \"source /opt/intel/oneapi/setvars.sh && ulimit -s unlimited && mpirun -n 32 /opt/vasp.5.4.4/bin/vasp_std \"", "batch_type": "Bohrium", diff --git a/examples/vasp_demo/param_props.json b/examples/vasp_demo/param_props.json index 4a8ae7d..c5c64be 100644 --- a/examples/vasp_demo/param_props.json +++ b/examples/vasp_demo/param_props.json @@ -1,5 +1,5 @@ { - "structures": ["confs/std-*"], + "structures": ["confs/std-bcc"], "interaction": { "type": "vasp", "incar": "vasp_input/INCAR", @@ -9,14 +9,14 @@ "properties": [ { "type": "eos", - "skip": false, + "skip": true, "vol_start": 0.6, "vol_end": 1.4, "vol_step": 0.4 }, { "type": "eos", - "skip": false, + "skip": true, "suffix": "4_step", "vol_start": 0.6, "vol_end": 1.4, @@ -56,6 +56,21 @@ "vacuum_size": 15, "add_fix": ["true","true","false"], "n_steps": 20 - } + }, + { + "type": "phonon", + "skip": false, + "suffix": "linear", + "BAND": "0.0000 0.0000 0.5000 0.0000 0.0000 0.0000 0.5000 -0.5000 0.5000 0.25000 0.2500 0.2500 0 0 0", + "supercell_size":[2,2,2] + }, + { + "type": "phonon", + "skip": false, + "suffix": "displacement", + "approach": "displacement", + "BAND": "0.0000 0.0000 0.5000 0.0000 0.0000 0.0000 0.5000 -0.5000 0.5000 0.25000 0.2500 0.2500 0 0 0", + "supercell_size":[2,2,2] + } ] } diff --git a/examples/vasp_demo/param_relax.json b/examples/vasp_demo/param_relax.json index b2e9a50..4141a86 100644 --- a/examples/vasp_demo/param_relax.json +++ b/examples/vasp_demo/param_relax.json @@ -1,5 +1,5 @@ { - "structures": ["confs/std-*"], + "structures": ["confs/std-bcc"], "interaction": { "type": "vasp", "incar": "vasp_input/INCAR", diff --git a/setup.py b/setup.py index 4697ffe..849dfe9 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="apex-flow", - version="1.0.2", + version="1.1.1", author="Zhuoyuan Li, Tongqi Wen", author_email="zhuoyli@outlook.com", description="Alloy Properties EXplorer using simulations", @@ -19,6 +19,8 @@ 'pymatgen-analysis-defects>=2023.8.22', "dpdata>=0.2.13", "dpdispatcher", + "phonoLAMMPS", + "phonopy", "matplotlib", "seekpath", "fpop>=0.0.7", diff --git a/tests/context.py b/tests/context.py index c858012..ecc35c3 100644 --- a/tests/context.py +++ b/tests/context.py @@ -1,6 +1,6 @@ import os import sys -from apex.core.calculator.lib.vasp import * +from apex.core.calculator.lib.vasp_utils import * sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) diff --git a/tests/test_abacus.py b/tests/test_abacus.py index a8de988..d0e29e1 100644 --- a/tests/test_abacus.py +++ b/tests/test_abacus.py @@ -7,7 +7,7 @@ from monty.serialization import loadfn from apex.core.calculator.ABACUS import ABACUS -from apex.core.calculator.lib import abacus +from apex.core.calculator.lib import abacus_utils from apex.core.calculator.lib import abacus_scf sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) @@ -90,7 +90,7 @@ def test_make_input_file_1(self): self.assertEqual(abacus_input["calculation"].lower(), "cell-relax") self.assertEqual(abacus_input["fixed_axes"].lower(), "volume") self.assertTrue( - abacus.check_stru_fixed(os.path.join(self.equi_path, "STRU"), fixed=False) + abacus_utils.check_stru_fixed(os.path.join(self.equi_path, "STRU"), fixed=False) ) def test_make_input_file_2(self): @@ -109,7 +109,7 @@ def test_make_input_file_2(self): "fixed_axes" not in abacus_input or abacus_input["fixed_axes"] == "None" ) self.assertTrue( - abacus.check_stru_fixed(os.path.join(self.equi_path, "STRU"), fixed=False) + abacus_utils.check_stru_fixed(os.path.join(self.equi_path, "STRU"), fixed=False) ) def test_make_input_file_3(self): @@ -134,7 +134,7 @@ def test_make_input_file_3(self): "fixed_axes" not in abacus_input or abacus_input["fixed_axes"] == "None" ) self.assertTrue( - abacus.check_stru_fixed(os.path.join(self.equi_path, "STRU"), fixed=False) + abacus_utils.check_stru_fixed(os.path.join(self.equi_path, "STRU"), fixed=False) ) def test_make_input_file_4(self): @@ -157,7 +157,7 @@ def test_make_input_file_4(self): self.assertEqual(abacus_input["calculation"].lower(), "cell-relax") self.assertEqual(abacus_input["fixed_axes"].lower(), "volume") self.assertTrue( - abacus.check_stru_fixed(os.path.join(self.equi_path, "STRU"), fixed=True) + abacus_utils.check_stru_fixed(os.path.join(self.equi_path, "STRU"), fixed=True) ) def test_make_input_file_5(self): @@ -182,7 +182,7 @@ def test_make_input_file_5(self): "fixed_axes" not in abacus_input or abacus_input["fixed_axes"] == "None" ) self.assertTrue( - abacus.check_stru_fixed(os.path.join(self.equi_path, "STRU"), fixed=True) + abacus_utils.check_stru_fixed(os.path.join(self.equi_path, "STRU"), fixed=True) ) def test_make_input_file_kspacing(self): diff --git a/tests/test_lammps.py b/tests/test_lammps.py index 38b49af..eda0e3c 100644 --- a/tests/test_lammps.py +++ b/tests/test_lammps.py @@ -10,7 +10,7 @@ from monty.serialization import dumpfn, loadfn from apex.core.calculator.Lammps import Lammps -from apex.core.calculator.lib.lammps import inter_deepmd +from apex.core.calculator.lib.lammps_utils import inter_deepmd #from .context import make_kspacing_kpoints, setUpModule diff --git a/tests/test_phonon.py b/tests/test_phonon.py new file mode 100644 index 0000000..2fa5c9b --- /dev/null +++ b/tests/test_phonon.py @@ -0,0 +1,81 @@ +import glob +import os +import shutil +import sys +import unittest + +import dpdata +import numpy as np +from monty.serialization import loadfn +from pymatgen.io.vasp import Incar +from apex.core.property.Phonon import Phonon + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) +__package__ = "tests" + + +class TestPhonon(unittest.TestCase): + def setUp(self): + _jdata = { + "structures": ["confs/std-bcc"], + "interaction": { + "type": "vasp", + "potcar_prefix": "vasp_input", + "potcars": {"Mo": "POTCAR_Mo"}, + }, + "properties": [ + { + "type": "phonon", + "skip": False, + "BAND": "0.0000 0.0000 0.5000 0.0000 0.0000 0.0000 0.5000 -0.5000 0.5000 0.25000 0.2500 0.2500 0 0 0", + "supercell_matrix": [2, 2, 2] + }, + ], + } + + self.equi_path = "confs/hp-Mo/relaxation/relax_task" + self.source_path = "equi/vasp" + self.target_path = "confs/hp-Mo/phonon_00" + self.res_data = "output/phonon_00/result.json" + self.ptr_data = "output/phonon_00/result.out" + + if not os.path.exists(self.equi_path): + os.makedirs(self.equi_path) + if not os.path.exists(self.target_path): + os.makedirs(self.target_path) + + self.confs = _jdata["structures"] + self.inter_param = _jdata["interaction"] + self.prop_param = _jdata["properties"] + + self.phonon = Phonon(_jdata["properties"][0]) + + def tearDown(self): + if os.path.exists(self.equi_path): + shutil.rmtree(self.equi_path) + if os.path.exists(self.target_path): + shutil.rmtree(self.target_path) + if os.path.exists(self.res_data): + os.remove(self.res_data) + if os.path.exists(self.ptr_data): + os.remove(self.ptr_data) + + def test_task_type(self): + self.assertEqual("phonon", self.phonon.task_type()) + + def test_task_param(self): + self.assertEqual(self.prop_param[0], self.phonon.task_param()) + + def test_make_phonon_conf(self): + if not os.path.exists(os.path.join(self.equi_path, "CONTCAR")): + with self.assertRaises(RuntimeError): + self.phonon.make_confs(self.target_path, self.equi_path) + shutil.copy( + os.path.join(self.source_path, "CONTCAR_Mo_bcc"), + os.path.join(self.equi_path, "CONTCAR"), + ) + task_list = self.phonon.make_confs(self.target_path, self.equi_path) + dfm_dirs = glob.glob(os.path.join(self.target_path, "task.*")) + self.assertEqual(len(dfm_dirs), 1) + self.assertTrue(os.path.isfile(os.path.join(self.target_path, "phonopy_disp.yaml"))) + self.assertTrue(os.path.isfile(os.path.join(self.target_path, "task.000000/band.conf"))) diff --git a/tests/test_vasp.py b/tests/test_vasp.py index 16163c2..4f67ba8 100644 --- a/tests/test_vasp.py +++ b/tests/test_vasp.py @@ -13,7 +13,7 @@ __package__ = "tests" from apex.core.calculator.VASP import VASP -from apex.core.calculator.lib.vasp import incar_upper +from apex.core.calculator.lib.vasp_utils import incar_upper class TestVASP(unittest.TestCase):