Skip to content

Commit 5b518f3

Browse files
Add support for VASP DFT code (#425)
* Add support for VASP DFT code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix type hints * Add pyiron_vasp to dependencies * Add first test * rename file * Add old * more tests * Update test_vasp_parser.py * Update test_vasp_parser.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 4500cee commit 5b518f3

File tree

9 files changed

+1414
-0
lines changed

9 files changed

+1414
-0
lines changed

.ci_support/environment-docs.yml

+1
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ dependencies:
2020
- jinja2 =3.1.5
2121
- jupyter-book =1.0.0
2222
- pyiron_lammps =0.4.0
23+
- pyiron_vasp =0.2.4

.ci_support/environment-old.yml

+1
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ dependencies:
2020
- structuretoolkit =0.0.10
2121
- tqdm =4.44.0
2222
- pyiron_lammps =0.3.2
23+
- pyiron_vasp =0.2.4
2324
- sphinx_parser =0.0.1

.ci_support/environment.yml

+1
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ dependencies:
1515
- structuretoolkit =0.0.31
1616
- sphinx_parser =0.0.1
1717
- tqdm =4.67.1
18+
- pyiron_vasp =0.2.4

atomistics/calculators/vasp.py

+219
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
import os
2+
from typing import Optional
3+
4+
from ase.atoms import Atoms
5+
from ase.calculators.vasp.create_input import GenerateVaspInput
6+
from pyiron_vasp.vasp.output import parse_vasp_output
7+
8+
from atomistics.calculators.interface import get_quantities_from_tasks
9+
from atomistics.calculators.wrapper import as_task_dict_evaluator
10+
from atomistics.shared.output import OutputStatic
11+
12+
13+
class OutputParser:
14+
def __init__(self, working_directory, structure):
15+
self._output_dict = parse_vasp_output(
16+
working_directory=working_directory, structure=structure
17+
)
18+
19+
def get_energy(self):
20+
return self._output_dict["generic"]["energy_tot"][-1]
21+
22+
def get_forces(self):
23+
return self._output_dict["generic"]["forces"][-1]
24+
25+
def get_volume(self):
26+
return self._output_dict["generic"]["volume"][-1]
27+
28+
def get_stress(self):
29+
return self._output_dict["generic"]["stresses"][-1]
30+
31+
32+
def write_input(working_directory, atoms, **kwargs):
33+
vip = GenerateVaspInput()
34+
vip.set(**kwargs)
35+
vip.initialize(atoms=atoms)
36+
os.makedirs(working_directory, exist_ok=True)
37+
vip.write_input(atoms=atoms, directory=working_directory)
38+
39+
40+
def calc_static_with_vasp(
41+
structure: Atoms,
42+
working_directory: str,
43+
executable_function: callable,
44+
prec: str = "Accurate",
45+
algo: str = "Fast",
46+
lreal: bool = False,
47+
lwave: bool = False,
48+
lorbit: int = 0,
49+
kpts: Optional[list[int, int, int]] = None,
50+
output_keys: dict = OutputStatic.keys(),
51+
**kwargs,
52+
) -> dict:
53+
if kpts is None:
54+
kpts = [4, 4, 4]
55+
write_input(
56+
working_directory=working_directory,
57+
atoms=structure,
58+
prec=prec,
59+
algo=algo,
60+
lreal=lreal,
61+
lwave=lwave,
62+
lorbit=lorbit,
63+
kpts=kpts,
64+
**kwargs,
65+
)
66+
executable_function(working_directory)
67+
output_obj = OutputParser(working_directory=working_directory, structure=structure)
68+
result_dict = OutputStatic(
69+
forces=output_obj.get_forces,
70+
energy=output_obj.get_energy,
71+
stress=output_obj.get_stress,
72+
volume=output_obj.get_volume,
73+
).get(output_keys=output_keys)
74+
return result_dict
75+
76+
77+
def optimize_positions_with_vasp(
78+
structure: Atoms,
79+
working_directory: str,
80+
executable_function: callable,
81+
prec: str = "Accurate",
82+
algo: str = "Fast",
83+
lreal: bool = False,
84+
lwave: bool = False,
85+
lorbit: int = 0,
86+
isif: int = 2,
87+
ibrion: int = 2,
88+
nsw: int = 100,
89+
kpts: Optional[list[int, int, int]] = None,
90+
**kwargs,
91+
) -> Atoms:
92+
if kpts is None:
93+
kpts = [4, 4, 4]
94+
write_input(
95+
working_directory=working_directory,
96+
atoms=structure,
97+
prec=prec,
98+
algo=algo,
99+
lreal=lreal,
100+
lwave=lwave,
101+
lorbit=lorbit,
102+
kpts=kpts,
103+
isif=isif,
104+
ibrion=ibrion,
105+
nsw=nsw,
106+
**kwargs,
107+
)
108+
executable_function(working_directory)
109+
output_dict = parse_vasp_output(
110+
working_directory=working_directory, structure=structure
111+
)
112+
structure_copy = structure.copy()
113+
structure_copy.positions = output_dict["generic"]["positions"][-1]
114+
return structure_copy
115+
116+
117+
def optimize_positions_and_volume_with_vasp(
118+
structure: Atoms,
119+
working_directory: str,
120+
executable_function: callable,
121+
prec: str = "Accurate",
122+
algo: str = "Fast",
123+
lreal: bool = False,
124+
lwave: bool = False,
125+
lorbit: int = 0,
126+
isif: int = 3,
127+
ibrion: int = 2,
128+
nsw: int = 100,
129+
kpts: Optional[list[int, int, int]] = None,
130+
**kwargs,
131+
) -> Atoms:
132+
if kpts is None:
133+
kpts = [4, 4, 4]
134+
write_input(
135+
working_directory=working_directory,
136+
atoms=structure,
137+
prec=prec,
138+
algo=algo,
139+
lreal=lreal,
140+
lwave=lwave,
141+
lorbit=lorbit,
142+
kpts=kpts,
143+
isif=isif,
144+
ibrion=ibrion,
145+
nsw=nsw,
146+
**kwargs,
147+
)
148+
executable_function(working_directory)
149+
output_dict = parse_vasp_output(
150+
working_directory=working_directory, structure=structure
151+
)
152+
structure_copy = structure.copy()
153+
structure_copy.set_cell(output_dict["generic"]["cells"][-1], scale_atoms=True)
154+
structure_copy.positions = output_dict["generic"]["positions"][-1]
155+
return structure_copy
156+
157+
158+
@as_task_dict_evaluator
159+
def evaluate_with_vasp(
160+
structure: Atoms,
161+
tasks: list,
162+
working_directory: str,
163+
executable_function: callable,
164+
prec: str = "Accurate",
165+
algo: str = "Fast",
166+
lreal: bool = False,
167+
lwave: bool = False,
168+
lorbit: int = 0,
169+
kpts: Optional[list[int, int, int]] = None,
170+
**kwargs,
171+
) -> dict:
172+
if kpts is None:
173+
kpts = [4, 4, 4]
174+
results = {}
175+
if "optimize_positions_and_volume" in tasks:
176+
results["structure_with_optimized_positions_and_volume"] = (
177+
optimize_positions_and_volume_with_vasp(
178+
structure=structure,
179+
working_directory=working_directory,
180+
executable_function=executable_function,
181+
prec=prec,
182+
algo=algo,
183+
lreal=lreal,
184+
lwave=lwave,
185+
lorbit=lorbit,
186+
kpts=kpts,
187+
**kwargs,
188+
)
189+
)
190+
elif "optimize_positions" in tasks:
191+
results["structure_with_optimized_positions"] = optimize_positions_with_vasp(
192+
structure=structure,
193+
working_directory=working_directory,
194+
executable_function=executable_function,
195+
prec=prec,
196+
algo=algo,
197+
lreal=lreal,
198+
lwave=lwave,
199+
lorbit=lorbit,
200+
kpts=kpts,
201+
**kwargs,
202+
)
203+
elif "calc_energy" in tasks or "calc_forces" in tasks or "calc_stress" in tasks:
204+
return calc_static_with_vasp(
205+
structure=structure,
206+
working_directory=working_directory,
207+
executable_function=executable_function,
208+
prec=prec,
209+
algo=algo,
210+
lreal=lreal,
211+
lwave=lwave,
212+
lorbit=lorbit,
213+
kpts=kpts,
214+
output_keys=get_quantities_from_tasks(tasks=tasks),
215+
**kwargs,
216+
)
217+
else:
218+
raise ValueError("The VASP calculator does not implement:", tasks)
219+
return results

binder/environment.yml

+1
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ dependencies:
1616
- dynaphopy =1.17.16
1717
- tqdm =4.67.1
1818
- pyiron_lammps =0.4.0
19+
- pyiron_vasp =0.2.4

pyproject.toml

+3
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ qe = [
7373
tqdm = [
7474
"tqdm==4.67.1"
7575
]
76+
vasp = [
77+
"pyiron_vasp==0.2.4"
78+
]
7679

7780
[tool.setuptools.packages.find]
7881
include = ["atomistics*"]

0 commit comments

Comments
 (0)