From 040f27ed681c83adcd90ccfbe9e44608c9ea4e0d Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 31 Oct 2023 16:06:57 -0400 Subject: [PATCH 1/5] add psi4/inp format Signed-off-by: Jinzhe Zeng --- dpdata/plugins/psi4.py | 43 ++++++++++++++++++++++++++++++++++ dpdata/psi4/input.py | 53 ++++++++++++++++++++++++++++++++++++++++++ tests/comp_sys.py | 3 +++ tests/test_psi4.py | 33 +++++++++++++++++++++++++- 4 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 dpdata/psi4/input.py diff --git a/dpdata/plugins/psi4.py b/dpdata/plugins/psi4.py index 932d118c5..8f359aeac 100644 --- a/dpdata/plugins/psi4.py +++ b/dpdata/plugins/psi4.py @@ -2,6 +2,7 @@ from dpdata.format import Format from dpdata.psi4.output import read_psi4_output +from dpdata.psi4.input import write_psi4_input from dpdata.unit import EnergyConversion, ForceConversion, LengthConversion length_convert = LengthConversion("bohr", "angstrom").value() @@ -50,3 +51,45 @@ def from_labeled_system(self, file_name: str, **kwargs) -> dict: "orig": np.zeros(3), "nopbc": True, } + + +@Format.register("psi4/inp") +class PSI4InputFormat(Format): + """Psi4 output. + + Note that both the energy and the gradient should be + printed into the output file. + """ + + def to_system(self, data: dict, file_name: str, method: str, basis: str, charge: int = 0, multiplicity: int = 1,frame_idx=0, **kwargs): + """Write PSI4 input. + + Parameters + ---------- + data : dict + system data + file_name : str + file name + method : str + computational method + basis : str + basis set; see https://psicode.org/psi4manual/master/basissets_tables.html + charge : int, default=0 + charge of system + multiplicity : int, default=1 + multiplicity of system + frame_idx : int, default=0 + The index of the frame to dump + **kwargs + keyword arguments + """ + types = np.array(data["atom_names"])[data["atom_types"]] + with open(file_name, "w") as fout: + fout.write(write_psi4_input( + types = types, + coords=data["coords"][frame_idx], + method=method, + basis=basis, + charge=charge, + multiplicity=multiplicity, + )) diff --git a/dpdata/psi4/input.py b/dpdata/psi4/input.py new file mode 100644 index 000000000..154c22e05 --- /dev/null +++ b/dpdata/psi4/input.py @@ -0,0 +1,53 @@ +from typing import List +import numpy as np + + +# Angston is used in Psi4 by default +template = """molecule {{ +{atoms:s} +{charge:d} {multiplicity:d} +}} +set basis {basis:s} +set gradient_write on +G, wfn = gradient("WB97M-D3BJ", return_wfn=True) +wfn.energy() +wfn.gradient().print_out() +""" + +def write_psi4_input( + types: np.ndarray, + coords: np.ndarray, + method: str, + basis: str, + charge: int = 0, + multiplicity: int = 1, +) -> str: + """Write Psi4 input file. + + Parameters + ---------- + types : np.ndarray + atomic symbols + coords : np.ndarray + atomic coordinates + method : str + computational method + basis : str + basis set; see https://psicode.org/psi4manual/master/basissets_tables.html + charge : int, default=0 + charge of system + multiplicity : int, default=1 + multiplicity of system + + Returns + ------- + str + content of Psi4 input file + """ + return template.format( + atoms = "\n".join(["{:s} {:16.9f} {:16.9f} {:16.9f}".format(*ii) for ii in zip(types, *coords.T)]), + charge = charge, + multiplicity = multiplicity, + method = method, + basis = basis, + ) diff --git a/tests/comp_sys.py b/tests/comp_sys.py index 426df412e..cfa92c497 100644 --- a/tests/comp_sys.py +++ b/tests/comp_sys.py @@ -47,6 +47,9 @@ def test_cell(self): def test_coord(self): self.assertEqual(self.system_1.get_nframes(), self.system_2.get_nframes()) # think about direct coord + if self.system_1.nopbc: + # nopbc doesn't need to test cells + return tmp_cell = self.system_1.data["cells"] tmp_cell = np.reshape(tmp_cell, [-1, 3]) tmp_cell_norm = np.reshape(np.linalg.norm(tmp_cell, axis=1), [-1, 1, 3]) diff --git a/tests/test_psi4.py b/tests/test_psi4.py index 618e05174..23cef89c0 100644 --- a/tests/test_psi4.py +++ b/tests/test_psi4.py @@ -1,3 +1,5 @@ +import tempfile +import textwrap import unittest import numpy as np @@ -5,7 +7,7 @@ from context import dpdata -class TestDeepmdLoadDumpHDF5(unittest.TestCase, CompLabeledSys, IsNoPBC): +class TestPsi4Output(unittest.TestCase, CompLabeledSys, IsNoPBC): def setUp(self): length_convert = dpdata.unit.LengthConversion("bohr", "angstrom").value() energy_convert = dpdata.unit.EnergyConversion("hartree", "eV").value() @@ -60,3 +62,32 @@ def setUp(self): self.e_places = 6 self.f_places = 6 self.v_places = 6 + + +class TestPsi4Input(unittest.TestCase): + def test_psi4_input(self): + system = dpdata.LabeledSystem("psi4/psi4.out", fmt="psi4/out") + with tempfile.NamedTemporaryFile('r') as f: + system.to_psi4_inp(f.name, method="WB97M-D3BJ", basis="def2-TZVPPD") + content = f.read() + self.assertEqual( + content, + textwrap.dedent("""\ + molecule { + C 0.692724290 -0.280972290 0.149966626 + C -0.690715864 0.280527594 -0.157432416 + H 1.241584247 -0.707702380 -0.706342645 + H 0.492994289 -1.086482665 0.876517411 + H 1.330104482 0.478557878 0.633157279 + H -1.459385451 -0.498843014 -0.292862623 + H -0.623545813 0.873377524 -1.085142510 + H -1.005665735 0.946387574 0.663566976 + 0 1 + } + set basis def2-TZVPPD + set gradient_write on + G, wfn = gradient("WB97M-D3BJ", return_wfn=True) + wfn.energy() + wfn.gradient().print_out() + """ + )) From 922b2034f2ab2215cce0043f589c0e4c90b3cada Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 31 Oct 2023 20:07:34 +0000 Subject: [PATCH 2/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- dpdata/plugins/psi4.py | 32 ++++++++++++++++++++++---------- dpdata/psi4/input.py | 20 ++++++++++++-------- tests/test_psi4.py | 8 +++++--- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/dpdata/plugins/psi4.py b/dpdata/plugins/psi4.py index 8f359aeac..aa50bb4e5 100644 --- a/dpdata/plugins/psi4.py +++ b/dpdata/plugins/psi4.py @@ -1,8 +1,8 @@ import numpy as np from dpdata.format import Format -from dpdata.psi4.output import read_psi4_output from dpdata.psi4.input import write_psi4_input +from dpdata.psi4.output import read_psi4_output from dpdata.unit import EnergyConversion, ForceConversion, LengthConversion length_convert = LengthConversion("bohr", "angstrom").value() @@ -61,7 +61,17 @@ class PSI4InputFormat(Format): printed into the output file. """ - def to_system(self, data: dict, file_name: str, method: str, basis: str, charge: int = 0, multiplicity: int = 1,frame_idx=0, **kwargs): + def to_system( + self, + data: dict, + file_name: str, + method: str, + basis: str, + charge: int = 0, + multiplicity: int = 1, + frame_idx=0, + **kwargs, + ): """Write PSI4 input. Parameters @@ -85,11 +95,13 @@ def to_system(self, data: dict, file_name: str, method: str, basis: str, charge: """ types = np.array(data["atom_names"])[data["atom_types"]] with open(file_name, "w") as fout: - fout.write(write_psi4_input( - types = types, - coords=data["coords"][frame_idx], - method=method, - basis=basis, - charge=charge, - multiplicity=multiplicity, - )) + fout.write( + write_psi4_input( + types=types, + coords=data["coords"][frame_idx], + method=method, + basis=basis, + charge=charge, + multiplicity=multiplicity, + ) + ) diff --git a/dpdata/psi4/input.py b/dpdata/psi4/input.py index 154c22e05..ad0532817 100644 --- a/dpdata/psi4/input.py +++ b/dpdata/psi4/input.py @@ -1,11 +1,9 @@ -from typing import List import numpy as np - # Angston is used in Psi4 by default template = """molecule {{ {atoms:s} -{charge:d} {multiplicity:d} +{charge:d} {multiplicity:d} }} set basis {basis:s} set gradient_write on @@ -14,6 +12,7 @@ wfn.gradient().print_out() """ + def write_psi4_input( types: np.ndarray, coords: np.ndarray, @@ -45,9 +44,14 @@ def write_psi4_input( content of Psi4 input file """ return template.format( - atoms = "\n".join(["{:s} {:16.9f} {:16.9f} {:16.9f}".format(*ii) for ii in zip(types, *coords.T)]), - charge = charge, - multiplicity = multiplicity, - method = method, - basis = basis, + atoms="\n".join( + [ + "{:s} {:16.9f} {:16.9f} {:16.9f}".format(*ii) + for ii in zip(types, *coords.T) + ] + ), + charge=charge, + multiplicity=multiplicity, + method=method, + basis=basis, ) diff --git a/tests/test_psi4.py b/tests/test_psi4.py index 23cef89c0..aa2bfdebb 100644 --- a/tests/test_psi4.py +++ b/tests/test_psi4.py @@ -67,12 +67,13 @@ def setUp(self): class TestPsi4Input(unittest.TestCase): def test_psi4_input(self): system = dpdata.LabeledSystem("psi4/psi4.out", fmt="psi4/out") - with tempfile.NamedTemporaryFile('r') as f: + with tempfile.NamedTemporaryFile("r") as f: system.to_psi4_inp(f.name, method="WB97M-D3BJ", basis="def2-TZVPPD") content = f.read() self.assertEqual( content, - textwrap.dedent("""\ + textwrap.dedent( + """\ molecule { C 0.692724290 -0.280972290 0.149966626 C -0.690715864 0.280527594 -0.157432416 @@ -90,4 +91,5 @@ def test_psi4_input(self): wfn.energy() wfn.gradient().print_out() """ - )) + ), + ) From dfdbabcc6fc67badbfadc42337ea21c1a4df62d8 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 31 Oct 2023 16:08:24 -0400 Subject: [PATCH 3/5] fix docstring --- dpdata/plugins/psi4.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dpdata/plugins/psi4.py b/dpdata/plugins/psi4.py index aa50bb4e5..ca7c84261 100644 --- a/dpdata/plugins/psi4.py +++ b/dpdata/plugins/psi4.py @@ -55,10 +55,7 @@ def from_labeled_system(self, file_name: str, **kwargs) -> dict: @Format.register("psi4/inp") class PSI4InputFormat(Format): - """Psi4 output. - - Note that both the energy and the gradient should be - printed into the output file. + """Psi4 input file. """ def to_system( From 90016f6a2fcdda8ddecb847dcd496ee52bf9ab25 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 31 Oct 2023 20:09:01 +0000 Subject: [PATCH 4/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- dpdata/plugins/psi4.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dpdata/plugins/psi4.py b/dpdata/plugins/psi4.py index ca7c84261..68919b130 100644 --- a/dpdata/plugins/psi4.py +++ b/dpdata/plugins/psi4.py @@ -55,8 +55,7 @@ def from_labeled_system(self, file_name: str, **kwargs) -> dict: @Format.register("psi4/inp") class PSI4InputFormat(Format): - """Psi4 input file. - """ + """Psi4 input file.""" def to_system( self, From d2ecfbac688354495171199d7a645876b0cd87c0 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 31 Oct 2023 16:23:57 -0400 Subject: [PATCH 5/5] extra space --- tests/test_psi4.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_psi4.py b/tests/test_psi4.py index aa2bfdebb..b9c2124e4 100644 --- a/tests/test_psi4.py +++ b/tests/test_psi4.py @@ -83,7 +83,7 @@ def test_psi4_input(self): H -1.459385451 -0.498843014 -0.292862623 H -0.623545813 0.873377524 -1.085142510 H -1.005665735 0.946387574 0.663566976 - 0 1 + 0 1 } set basis def2-TZVPPD set gradient_write on