Skip to content

Commit 593bb1e

Browse files
add psi4/out format (#457)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 52b6eaa commit 593bb1e

5 files changed

Lines changed: 729 additions & 0 deletions

File tree

dpdata/plugins/psi4.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import numpy as np
2+
3+
from dpdata.format import Format
4+
from dpdata.psi4.output import read_psi4_output
5+
from dpdata.unit import EnergyConversion, ForceConversion, LengthConversion
6+
7+
length_convert = LengthConversion("bohr", "angstrom").value()
8+
energy_convert = EnergyConversion("hartree", "eV").value()
9+
force_convert = ForceConversion("hartree/bohr", "eV/angstrom").value()
10+
11+
12+
@Format.register("psi4/out")
13+
class PSI4OutFormat(Format):
14+
"""Psi4 output.
15+
16+
Note that both the energy and the gradient should be
17+
printed into the output file.
18+
"""
19+
20+
def from_labeled_system(self, file_name: str, **kwargs) -> dict:
21+
"""Read from Psi4 output.
22+
23+
Parameters
24+
----------
25+
file_name : str
26+
file name
27+
**kwargs
28+
keyword arguments
29+
30+
Returns
31+
-------
32+
dict
33+
system data
34+
"""
35+
symbols, coord, energy, forces = read_psi4_output(file_name)
36+
37+
atom_names, atom_types, atom_numbs = np.unique(
38+
symbols, return_inverse=True, return_counts=True
39+
)
40+
natoms = coord.shape[0]
41+
42+
return {
43+
"atom_types": atom_types,
44+
"atom_names": list(atom_names),
45+
"atom_numbs": list(atom_numbs),
46+
"coords": (coord * length_convert).reshape((1, natoms, 3)),
47+
"energies": np.array([energy * energy_convert]),
48+
"forces": (forces * force_convert).reshape((1, natoms, 3)),
49+
"cells": np.zeros((1, 3, 3)),
50+
"orig": np.zeros(3),
51+
"nopbc": True,
52+
}

dpdata/psi4/__init__.py

Whitespace-only changes.

dpdata/psi4/output.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from typing import Tuple
2+
3+
import numpy as np
4+
5+
6+
def read_psi4_output(fn: str) -> Tuple[str, np.ndarray, float, np.ndarray]:
7+
"""Read from Psi4 output.
8+
9+
Note that both the energy and the gradient should be printed.
10+
11+
Parameters
12+
----------
13+
fn : str
14+
file name
15+
16+
Returns
17+
-------
18+
str
19+
atomic symbols
20+
np.ndarray
21+
atomic coordinates
22+
float
23+
total potential energy
24+
np.ndarray
25+
atomic forces
26+
"""
27+
coord = None
28+
symbols = None
29+
forces = None
30+
energy = None
31+
with open(fn) as f:
32+
flag = 0
33+
for line in f:
34+
if flag in (1, 3, 4, 5, 6):
35+
flag += 1
36+
elif flag == 2:
37+
s = line.split()
38+
if not len(s):
39+
flag = 0
40+
else:
41+
symbols.append(s[0].capitalize())
42+
coord.append([float(s[1]), float(s[2]), float(s[3])])
43+
elif flag == 7:
44+
s = line.split()
45+
if not len(s):
46+
flag = 0
47+
else:
48+
forces.append([float(s[1]), float(s[2]), float(s[3])])
49+
elif line.startswith(
50+
" Center X Y Z Mass"
51+
):
52+
# coord
53+
flag = 1
54+
coord = []
55+
symbols = []
56+
elif line.startswith(" ## Total Gradient"):
57+
flag = 3
58+
forces = []
59+
elif line.startswith(" Total Energy ="):
60+
energy = float(line.split()[-1])
61+
symbols = np.array(symbols)
62+
forces = -np.array(forces)
63+
coord = np.array(coord)
64+
assert coord.shape == forces.shape
65+
66+
return symbols, coord, energy, forces

0 commit comments

Comments
 (0)