Skip to content

Commit f2afb74

Browse files
Merge pull request #64 from ZLI-afk/v1.2
V1.2 update
2 parents 109af0d + 92b9ea6 commit f2afb74

File tree

10 files changed

+122
-28
lines changed

10 files changed

+122
-28
lines changed

README.md

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
</div>
44

55
# APEX: Alloy Property EXplorer
6+
[![](https://img.shields.io/badge/release-1.2.0-blue.svg)](https://github.com/deepmodeling/APEX)
67

78
[APEX](https://github.com/deepmodeling/APEX): Alloy Property EXplorer 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 calculations. This package enables users to conveniently establish a wide range of cloud-native property-test workflows by utilizing various computational approaches, including LAMMPS, VASP, ABACUS, and others.
89

10+
## v1.2 Main Features Update
911
* Add a `retrieve` sub-command to allow results to be retrieved independently and manually for multiple properties (Remove `Distributor` and `Collector` OP)
1012
* Support common **dflow operations** with terminal commands
1113
* Incorporate results `archive` function to both local paths and NoSQL database ([MongoDB](https://www.mongodb.com/) and [DynamoDB](https://aws.amazon.com/cn/dynamodb/))
@@ -15,9 +17,24 @@
1517
* Add four additional **ML** pair styles (`snap`, `gap`, `rann` and `mace`) and an extra `meam-spline` in LAMMPS interation type support
1618
* Modify the single-step run command from `test` to `do` for improved clarity and consistencey
1719

20+
## APEX Bohrium App
21+
[![](https://img.shields.io/badge/APP-BohriumApp-orange.svg)](https://app.bohrium.dp.tech/apex/)
22+
23+
APEX also provides a web-based [Bohrium App](https://app.bohrium.dp.tech/apex/) for rapid and easy alloy property calculations without intensive JSON configuration (Note: one will need a Bohrium account to access this service).
24+
25+
## How to cite APEX
26+
[![](https://img.shields.io/badge/DOI-10.48550/arXiv.2404.17330-red.svg)](https://doi.org/10.48550/arXiv.2404.17330)
27+
28+
If you use APEX in your research, please cite the following paper for general purpose:
29+
30+
> Z. Li, T. Wen, Y. Zhang, X. Liu, C. Zhang, A. S. L. S. Pattamatta, X. Gong, B. Ye, H.Wang, L. Zhang, D. J. Srolovitz, An extendable cloud-native alloy property explorer (2024). arXiv:2404.17330.
31+
1832
## Table of Contents
1933

2034
- [APEX: Alloy Property EXplorer](#apex-alloy-property-explorer)
35+
- [v1.2 Main Features Update](#v12-main-features-update)
36+
- [APEX Bohrium App](#apex-bohrium-app)
37+
- [How to cite APEX](#how-to-cite-apex)
2138
- [Table of Contents](#table-of-contents)
2239
- [1. Overview](#1-overview)
2340
- [2. Easy Install](#2-easy-install)
@@ -109,7 +126,8 @@ The instructions regarding global configuration, [dflow](https://github.com/deep
109126
| group_size | Int | 1 | Number of tasks per parallel run group |
110127
| pool_size | Int | 1 | For multi tasks per parallel group, the pool size of multiprocessing pool to handle each task (1 for serial, -1 for infinity) |
111128
| upload_python_package | Optional[List] | None | Additional python packages required in the container |
112-
| debug_pool_workers | Int | 1 | Pool size of parallel tasks running in the debug mode |
129+
| debug_pool_workers | Int | 1 | Pool size of parallel tasks running in the debug mode |
130+
| flow_name | String | None | Specify name of workflow to be submitted (default: work path name) |
113131
| submit_only | Bool | False | Submit workflow only without automatic result retrieving |
114132

115133
* **Dflow config**
@@ -252,11 +270,12 @@ Below are three examples (for detailed explanations of each parameter, please re
252270
| vol_abs | Bool | False | Whether to treat vol_start and vol_end as absolute volume, default = False |
253271

254272
##### 3.1.2.2. Elastic
255-
| Key words | Data structure | Example | Description |
256-
|:-------------|----------------|---------|----------------------------------------------------|
257-
| norm_deform | Float | 0.01 | The deformation in xx, yy, zz, defaul = 1e-2 |
258-
| shear_deform | Float | 0.01 | The deformation in other directions, default = 1e-2 |
259-
| conventional | Bool | False | Whether adopt conventional cell for deformation |
273+
| Key words | Data structure | Example | Description |
274+
|:-------------|----------------|---------|-----------------------------------------------------------------------------------------------------------------------------------|
275+
| norm_deform | Float | 0.01 | The deformation in xx, yy, zz, defaul = 1e-2 |
276+
| shear_deform | Float | 0.01 | The deformation in other directions, default = 1e-2 |
277+
| conventional | Bool | False | Whether adopt conventional cell for deformation |
278+
| ieee | Bool | True | Whether rotate relaxed structure into IEEE-standard format before deformation ([ref](https://ieeexplore.ieee.org/document/26560)) |
260279

261280
##### 3.1.2.3. Surface
262281
| Key words | Data structure | Example | Description |

apex/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,9 @@ def header():
2727
header_str += " AAA AAA PPP EEEEEEEEEE XXX XXX\n"
2828
header_str += "---------------------------------------------------------------\n"
2929
header_str += f"==>> Alloy Property EXplorer using simulations (v{__version__})\n"
30+
header_str += "Please cite DOI: 10.48550/arXiv.2404.17330\n"
31+
header_str += "Li et al, An extendable cloud-native alloy property explorer (2024).\n"
32+
header_str += "See https://github.com/deepmodeling/APEX for more information.\n"
33+
header_str += "---------------------------------------------------------------\n"
3034
header_str += "Checking input files..."
3135
print(header_str)

apex/archive.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ def archive2db_from_json(config, json_file):
199199
if config.archive_key:
200200
data_id = config.archive_key
201201
else:
202-
data_id = str(data_dict["work_path"])
202+
data_id = data_dict['archive_key']
203203
data_dict['_id'] = data_id
204204

205205
archive2db(config, data_dict, data_id)

apex/config.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class Config:
5050
remote_password: str = None
5151
port: int = 22
5252

53-
# calculator config
53+
# basic run config
5454
run_image_name: str = None
5555
run_command: str = None
5656
apex_image_name: str = "zhuoyli/apex_amd64"
@@ -63,8 +63,11 @@ class Config:
6363
vasp_run_command: str = None
6464
abacus_image_name: str = None
6565
abacus_run_command: str = None
66+
67+
# common APEX config
6668
is_bohrium_dflow: bool = False
6769
submit_only: bool = False
70+
flow_name: str = None
6871

6972
database_type: str = 'local'
7073
archive_method: str = 'sync'

apex/core/property/Elastic.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
import re
55
from shutil import copyfile
66

7-
import numpy as np
87
from monty.serialization import dumpfn, loadfn
98
from pymatgen.analysis.elasticity.elastic import ElasticTensor
109
from pymatgen.analysis.elasticity.strain import DeformedStructureSet, Strain
1110
from pymatgen.analysis.elasticity.stress import Stress
1211
from pymatgen.core.structure import Structure
12+
from pymatgen.core.tensors import Tensor
13+
from pymatgen.core.operations import SymmOp
1314
from pymatgen.io.vasp import Incar, Kpoints
1415

1516
from apex.core.calculator.lib import abacus_utils
@@ -32,6 +33,8 @@ def __init__(self, parameter, inter_param=None):
3233
self.shear_deform = parameter["shear_deform"]
3334
parameter.setdefault("conventional", False)
3435
self.conventional = parameter["conventional"]
36+
parameter.setdefault("ieee", True)
37+
self.ieee = parameter["ieee"]
3538
parameter.setdefault("cal_type", "relaxation")
3639
self.cal_type = parameter["cal_type"]
3740
default_cal_setting = {
@@ -141,6 +144,13 @@ def make_confs(self, path_to_work, path_to_equi, refine=False):
141144
ss = st.conventional_structure
142145
ss.to(os.path.join(path_to_work, "POSCAR.conv"), "POSCAR")
143146

147+
# convert to IEEE-standard
148+
if self.ieee:
149+
rot = Tensor.get_ieee_rotation(ss)
150+
op = SymmOp.from_rotation_and_translation(rot)
151+
ss.apply_operation(op)
152+
ss.to(os.path.join(path_to_work, "POSCAR.ieee"), "POSCAR")
153+
144154
dfm_ss = DeformedStructureSet(
145155
ss,
146156
symmetry=False,
@@ -169,7 +179,7 @@ def make_confs(self, path_to_work, path_to_equi, refine=False):
169179
dfm_ss.deformed_structures[ii].to("POSCAR", "POSCAR")
170180
if self.inter_param["type"] == "abacus":
171181
abacus_utils.poscar2stru("POSCAR", self.inter_param, "STRU")
172-
os.remove("POSCAR")
182+
#os.remove("POSCAR")
173183
# record strain
174184
df = Strain.from_deformation(dfm_ss.deformations[ii])
175185
dumpfn(df.as_dict(), "strain.json", indent=4)

apex/flow.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,23 @@ def __init__(
6969
self.executor = executor
7070
self.upload_python_packages = upload_python_packages
7171

72+
@staticmethod
73+
def regulate_name(name):
74+
"""
75+
Adjusts the given workflow name to conform to RFC 1123 subdomain requirements.
76+
It ensures the name is lowercase, contains only alphanumeric characters and hyphens,
77+
and starts and ends with an alphanumeric character.
78+
"""
79+
# lowercase the name
80+
name = name.lower()
81+
# substitute invalid characters with hyphens
82+
name = re.sub(r'[^a-z0-9\-]', '-', name)
83+
# make sure the name starts and ends with an alphanumeric character
84+
name = re.sub(r'^[^a-z0-9]+', '', name)
85+
name = re.sub(r'[^a-z0-9]+$', '', name)
86+
87+
return name
88+
7289
def _monitor_relax(self):
7390
print('Waiting for relaxation result...')
7491
while True:
@@ -77,7 +94,10 @@ def _monitor_relax(self):
7794
wf_status = self.workflow.query_status()
7895
if wf_status == 'Failed':
7996
raise RuntimeError(f'Workflow failed (ID: {self.workflow.id}, UID: {self.workflow.uid})')
80-
relax_post = step_info.get_step(name='relaxation-cal')[0]
97+
try:
98+
relax_post = step_info.get_step(name='relaxation-cal')[0]
99+
except IndexError:
100+
continue
81101
if relax_post['phase'] == 'Succeeded':
82102
print(f'Relaxation finished (ID: {self.workflow.id}, UID: {self.workflow.uid})')
83103
print('Retrieving completed tasks to local...')
@@ -246,12 +266,15 @@ def submit_relax(
246266
download_path: Union[os.PathLike, str],
247267
relax_parameter: dict,
248268
submit_only: bool = False,
269+
name: Optional[str] = None,
249270
labels: Optional[dict] = None
250271
) -> str:
251272
self.upload_path = upload_path
252273
self.download_path = download_path
253274
self.relax_param = relax_parameter
254-
self.workflow = Workflow(name='relaxation', labels=labels)
275+
flow_name = name if name else self.regulate_name(os.path.basename(download_path))
276+
flow_name += '-relax'
277+
self.workflow = Workflow(name=flow_name, labels=labels)
255278
relaxation = self._set_relax_flow(
256279
input_work_dir=upload_artifact(upload_path),
257280
relax_parameter=relax_parameter
@@ -272,12 +295,15 @@ def submit_props(
272295
download_path: Union[os.PathLike, str],
273296
props_parameter: dict,
274297
submit_only: bool = False,
298+
name: Optional[str] = None,
275299
labels: Optional[dict] = None
276300
) -> str:
277301
self.upload_path = upload_path
278302
self.download_path = download_path
279303
self.props_param = props_parameter
280-
self.workflow = Workflow(name='property', labels=labels)
304+
flow_name = name if name else self.regulate_name(os.path.basename(download_path))
305+
flow_name += '-props'
306+
self.workflow = Workflow(name=flow_name, labels=labels)
281307
subprops_list, subprops_key_list = self._set_props_flow(
282308
input_work_dir=upload_artifact(upload_path),
283309
props_parameter=props_parameter
@@ -299,13 +325,16 @@ def submit_joint(
299325
relax_parameter: dict,
300326
props_parameter: dict,
301327
submit_only: bool = False,
328+
name: Optional[str] = None,
302329
labels: Optional[dict] = None
303330
) -> str:
304331
self.upload_path = upload_path
305332
self.download_path = download_path
306333
self.relax_param = relax_parameter
307334
self.props_param = props_parameter
308-
self.workflow = Workflow(name='joint', labels=labels)
335+
flow_name = name if name else self.regulate_name(os.path.basename(download_path))
336+
flow_name += '-joint'
337+
self.workflow = Workflow(name=flow_name, labels=labels)
309338
relaxation = self._set_relax_flow(
310339
input_work_dir=upload_artifact(upload_path),
311340
relax_parameter=self.relax_param

apex/main.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ def parse_args():
7676
choices=['relax', 'props', 'joint'],
7777
help="(Optional) Specify type of workflow to submit: (relax | props | joint)"
7878
)
79+
parser_submit.add_argument(
80+
"-n", "--name",
81+
type=str, default=None,
82+
help="(Optional) Specify name of the workflow",
83+
)
7984

8085
##########################################
8186
# Do single step locally
@@ -526,6 +531,7 @@ def main():
526531
config_file=args.config,
527532
work_dirs=args.work,
528533
indicated_flow_type=args.flow,
534+
flow_name=args.name,
529535
submit_only=args.submit_only,
530536
is_debug=args.debug
531537
)

apex/report.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def report_local(input_path_list):
5050
data_dict = loadfn(kk)
5151
try:
5252
workdir_id = data_dict.pop('work_path')
53+
_ = data_dict.pop('archive_key')
5354
except KeyError:
5455
logging.warning(msg=f'Invalid json for result archive, will skip: {kk}')
5556
continue

apex/submit.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ def pack_upload_dir(
4141
os.chdir(work_dir)
4242
relax_confs = relax_param.get("structures", []) if relax_param else []
4343
prop_confs = prop_param.get("structures", []) if prop_param else []
44+
relax_prefix = relax_param["interaction"].get("potcar_prefix", None) if relax_param else None
45+
prop_prefix = prop_param["interaction"].get("potcar_prefix", None) if prop_param else None
46+
include_dirs = set()
47+
if relax_prefix:
48+
relax_prefix_base = relax_prefix.split('/')[0]
49+
include_dirs.add(relax_prefix_base)
50+
if prop_prefix:
51+
prop_prefix_base = prop_prefix.split('/')[0]
52+
include_dirs.add(prop_prefix_base)
4453
confs = relax_confs + prop_confs
4554
assert len(confs) > 0, "No configuration path indicated!"
4655
conf_dirs = []
@@ -66,13 +75,11 @@ def pack_upload_dir(
6675
backup_path(path_to_prop)
6776

6877
"""copy necessary files and directories into temp upload directory"""
69-
# exclude 'all_result.json' from copy
70-
conf_root_list = [conf.split('/')[0] for conf in conf_dirs]
71-
conf_root_list = list(set(conf_root_list))
72-
conf_root_list.sort()
73-
ignore_copy_list = conf_root_list
74-
ignore_copy_list.append("all_result.json")
75-
copy_all_other_files(work_dir, upload_dir, ignore_list=ignore_copy_list)
78+
copy_all_other_files(
79+
work_dir, upload_dir,
80+
exclude_files=["all_result.json"],
81+
include_dirs=list(include_dirs)
82+
)
7683
for ii in conf_dirs:
7784
build_conf_path = os.path.join(upload_dir, ii)
7885
os.makedirs(build_conf_path, exist_ok=True)
@@ -131,13 +138,15 @@ def submit(
131138
)
132139

133140
flow_id = None
141+
flow_name = wf_config.flow_name
134142
submit_only = wf_config.submit_only
135143
if flow_type == 'relax':
136144
flow_id = flow.submit_relax(
137145
upload_path=tmp_dir,
138146
download_path=work_dir,
139147
relax_parameter=relax_param,
140148
submit_only=submit_only,
149+
name=flow_name,
141150
labels=labels
142151
)
143152
elif flow_type == 'props':
@@ -146,6 +155,7 @@ def submit(
146155
download_path=work_dir,
147156
props_parameter=props_param,
148157
submit_only=submit_only,
158+
name=flow_name,
149159
labels=labels
150160
)
151161
elif flow_type == 'joint':
@@ -155,6 +165,7 @@ def submit(
155165
props_parameter=props_param,
156166
relax_parameter=relax_param,
157167
submit_only=submit_only,
168+
name=flow_name,
158169
labels=labels
159170
)
160171

@@ -169,6 +180,7 @@ def submit_workflow(
169180
config_dict: dict,
170181
work_dirs: List[os.PathLike],
171182
indicated_flow_type: str,
183+
flow_name: str = None,
172184
submit_only=False,
173185
is_debug=False,
174186
labels=None
@@ -188,6 +200,9 @@ def submit_workflow(
188200
config["debug_workdir"] = config_dict.get("debug_workdir", tmp_work_dir.name)
189201
s3_config["storage_client"] = None
190202

203+
if flow_name:
204+
wf_config.flow_name = flow_name
205+
191206
# judge basic flow info from user indicated parameter files
192207
(run_op, calculator, flow_type,
193208
relax_param, props_param) = judge_flow(parameter_dicts, indicated_flow_type)
@@ -269,6 +284,7 @@ def submit_from_args(
269284
config_file: os.PathLike,
270285
work_dirs,
271286
indicated_flow_type: str,
287+
flow_name: str = None,
272288
submit_only=False,
273289
is_debug=False,
274290
):
@@ -278,6 +294,7 @@ def submit_from_args(
278294
config_dict=load_config_file(config_file),
279295
work_dirs=work_dirs,
280296
indicated_flow_type=indicated_flow_type,
297+
flow_name=flow_name,
281298
submit_only=submit_only,
282299
is_debug=is_debug,
283300
)

0 commit comments

Comments
 (0)