diff --git a/README.md b/README.md index 5dbb142d8..950d9179f 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,34 @@ The labels provided in the `OUTCAR`, i.e. energies, forces and virials (if any), The `System` or `LabeledSystem` can be constructed from the following file formats with the `format key` in the table passed to argument `fmt`: +| Software| format | multi frames | labeled | class | format key | +| ------- | :--- | :---: | :---: | :--- | :--- | +| vasp | poscar | False | False | System | 'vasp/poscar' | +| vasp | outcar | True | True | LabeledSystem | 'vasp/outcar' | +| vasp | xml | True | True | LabeledSystem | 'vasp/xml' | +| lammps | lmp | False | False | System | 'lammps/lmp' | +| lammps | dump | True | False | System | 'lammps/dump' | +| deepmd | raw | True | False | System | 'deepmd/raw' | +| deepmd | npy | True | False | System | 'deepmd/npy' | +| deepmd | raw | True | True | LabeledSystem | 'deepmd/raw' | +| deepmd | npy | True | True | LabeledSystem | 'deepmd/npy' | +| gaussian| log | False | True | LabeledSystem | 'gaussian/log'| +| gaussian| log | True | True | LabeledSystem | 'gaussian/md' | +| siesta | output | False | True | LabeledSystem | 'siesta/output'| +| siesta | aimd_output | True | True | LabeledSystem | 'siesta/aimd_output' | +| cp2k | output | False | True | LabeledSystem | 'cp2k/output' | +| cp2k | aimd_output | True | True | LabeledSystem | 'cp2k/aimd_output' | +| QE | log | False | True | LabeledSystem | 'qe/pw/scf' | +| QE | log | True | False | System | 'qe/cp/traj' | +| QE | log | True | True | LabeledSystem | 'qe/cp/traj' | +|quip/gap|xyz|True|True|MultiSystems|'quip/gap/xyz'| +| PWmat | atom.config | False | False | System | 'pwmat/atom.config' | +| PWmat | movement | True | True | LabeledSystem | 'pwmat/movement' | +| PWmat | OUT.MLMD | True | True | LabeledSystem | 'pwmat/out.mlmd' | +| Amber | multi | True | True | LabeledSystem | 'amber/md' | +| Gromacs | gro | False | False | System | 'gromacs/gro' | + + The Class `dpdata.MultiSystems` can read data from a dir which may contains many files of different systems, or from single xyz file which contains different systems. Use `dpdata.MultiSystems.from_dir` to read from a directory, `dpdata.MultiSystems` will walk in the directory @@ -82,35 +110,8 @@ xyz_multi_systems.systems['B1C9'].to_deepmd_raw('./my_work_dir/B1C9_raw') # dump all systems xyz_multi_systems.to_deepmd_raw('./my_deepmd_data/') - - ``` -| Software| format | multi frames | labeled | class | format key | -| ------- | :--- | :---: | :---: | :--- | :--- | -| vasp | poscar | False | False | System | 'vasp/poscar' | -| vasp | outcar | True | True | LabeledSystem | 'vasp/outcar' | -| vasp | xml | True | True | LabeledSystem | 'vasp/xml' | -| lammps | lmp | False | False | System | 'lammps/lmp' | -| lammps | dump | True | False | System | 'lammps/dump' | -| deepmd | raw | True | False | System | 'deepmd/raw' | -| deepmd | npy | True | False | System | 'deepmd/npy' | -| deepmd | raw | True | True | LabeledSystem | 'deepmd/raw' | -| deepmd | npy | True | True | LabeledSystem | 'deepmd/npy' | -| gaussian| log | False | True | LabeledSystem | 'gaussian/log'| -| gaussian| log | True | True | LabeledSystem | 'gaussian/md' | -| siesta | output | False | True | LabeledSystem | 'siesta/output'| -| siesta | aimd_output | True | True | LabeledSystem | 'siesta/aimd_output' | -| cp2k | output | False | True | LabeledSystem | 'cp2k/output' | -| cp2k | aimd_output | True | True | LabeledSystem | 'cp2k/aimd_output' | -| QE | log | False | True | LabeledSystem | 'qe/pw/scf' | -| QE | log | True | False | System | 'qe/cp/traj' | -| QE | log | True | True | LabeledSystem | 'qe/cp/traj' | -|quip/gap|xyz|True|True|MultiSystems|'quip/gap/xyz'| -| PWmat | atom.config | False | False | System | 'pwmat/atom.config' | -| PWmat | movement | True | True | LabeledSystem | 'pwmat/movement' | -| PWmat | OUT.MLMD | True | True | LabeledSystem | 'pwmat/out.mlmd' | -| Amber | multi | True | True | LabeledSystem | 'amber/md' | ## Access data These properties stored in `System` and `LabeledSystem` can be accessed by operator `[]` with the key of the property supplied, for example ```python @@ -130,7 +131,6 @@ Available properties are (nframe: number of frames in the system, natoms: total | 'virials' | np.ndarray | nframes x 3 x 3 | True | The virial tensor of each frame - ## Dump data The data stored in `System` or `LabeledSystem` can be dumped in 'lammps/lmp' or 'vasp/poscar' format, for example: ```python @@ -142,7 +142,6 @@ d_outcar.to('vasp/poscar', 'POSCAR', frame_idx=-1) ``` The last frames of `d_outcar` will be dumped to 'POSCAR'. - The data stored in `LabeledSystem` can be dumped to deepmd-kit raw format, for example ```python d_outcar.to('deepmd/raw', 'dpmd_raw') @@ -157,6 +156,7 @@ dpdata.LabeledSystem('OUTCAR').sub_system([0,-1]).to('deepmd/raw', 'dpmd_raw') ``` by which only the first and last frames are dumped to `dpmd_raw`. + ## replicate dpdata will create a super cell of the current atom configuration. ```python @@ -164,6 +164,7 @@ dpdata.System('./POSCAR').replicate((1,2,3,) ) ``` tuple(1,2,3) means don't copy atom configuration in x direction, make 2 copys in y direction, make 3 copys in z direction. + ## perturb By the following example, each frame of the original system (`dpdata.System('./POSCAR')`) is perturbed to generate three new frames. For each frame, the cell is perturbed by 5% and the atom positions are perturbed by 0.6 Angstrom. `atom_pert_style` indicates that the perturbation to the atom positions is subject to normal distribution. Other available options to `atom_pert_style` are`uniform` (uniform in a ball), and `const` (uniform on a sphere). ```python diff --git a/dpdata/gromacs/__init__.py b/dpdata/gromacs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dpdata/gromacs/gro.py b/dpdata/gromacs/gro.py new file mode 100644 index 000000000..6e12fb1e1 --- /dev/null +++ b/dpdata/gromacs/gro.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +import numpy as np + +nm2ang = 10. + +def _get_line(line): + atom_name = line[10:15].split()[0] + atom_idx = int(line[15:20].split()[0]) + posis = [float(line[ii:ii+8]) for ii in range(20,44,8)] + posis = np.array(posis) * nm2ang + return atom_name, atom_idx, posis + +def _get_cell(line): + cell = np.zeros([3,3]) + lengths = [float(ii) for ii in line.split()] + if len(lengths) >= 3: + for dd in range(3): + cell[dd][dd] = lengths[dd] + else: + raise RuntimeError('wrong box format: ', line) + if len(lengths) == 9: + cell[0][1] = lengths[3] + cell[0][2] = lengths[4] + cell[1][0] = lengths[5] + cell[1][2] = lengths[6] + cell[2][0] = lengths[7] + cell[2][1] = lengths[8] + cell = cell * nm2ang + return cell + +def file_to_system_data(fname): + names = [] + idxs = [] + posis = [] + with open(fname) as fp: + fp.readline() + natoms = int(fp.readline()) + for ii in range(natoms): + n, i, p = _get_line(fp.readline()) + names.append(n) + idxs.append(i) + posis.append(p) + cell = _get_cell(fp.readline()) + posis = np.array(posis) + system = {} + system['orig'] = np.array([0, 0, 0]) + system['atom_names'] = list(set(names)) + system['atom_names'].sort() + system['atom_numbs'] = [names.count(ii) for ii in system['atom_names']] + system['atom_types'] = [system['atom_names'].index(ii) for ii in names] + system['atom_types'] = np.array(system['atom_types'], dtype = int) + system['coords'] = np.array([posis]) + system['cells'] = np.array([cell]) + return system diff --git a/dpdata/system.py b/dpdata/system.py index 25cb8158e..e861328ff 100644 --- a/dpdata/system.py +++ b/dpdata/system.py @@ -22,6 +22,7 @@ import dpdata.pwmat.movement import dpdata.pwmat.atomconfig import dpdata.fhi_aims.output +import dpdata.gromacs.gro from copy import deepcopy from monty.json import MSONable from monty.serialization import loadfn,dumpfn @@ -607,6 +608,19 @@ def from_deepmd_raw(self, folder, type_map = None) : if tmp_data is not None : self.data = tmp_data + @register_from_funcs.register_funcs("gro") + @register_from_funcs.register_funcs("gromacs/gro") + def from_gromacs_gro(self, file_name) : + """ + Load gromacs .gro file + + Parameters + ---------- + file_name : str + The input file name + """ + self.data = dpdata.gromacs.gro.file_to_system_data(file_name) + @register_to_funcs.register_funcs("deepmd/npy") def to_deepmd_npy(self, folder, set_size = 5000, prec=np.float32) : """ diff --git a/tests/gromacs/1h.gro b/tests/gromacs/1h.gro new file mode 100644 index 000000000..abd2c3d99 --- /dev/null +++ b/tests/gromacs/1h.gro @@ -0,0 +1,12 @@ + +9 + 1SOL O 1 0.135 0.183 0.341 + 1SOL H 2 0.177 0.149 0.262 + 1SOL H 3 0.046 0.149 0.339 + 2SOL O 4 0.520 0.447 0.111 + 2SOL H 5 0.567 0.481 0.035 + 2SOL H 6 0.568 0.481 0.186 + 3SOL O 7 0.651 0.539 0.335 + 3SOL H 8 0.653 0.634 0.336 + 3SOL H 9 0.743 0.512 0.336 + 0.7822838765564372 0.7353572647182051 0.9036518515423753 diff --git a/tests/gromacs/1h.tri.gro b/tests/gromacs/1h.tri.gro new file mode 100644 index 000000000..83fbbe6fd --- /dev/null +++ b/tests/gromacs/1h.tri.gro @@ -0,0 +1,12 @@ + +9 + 1SOL O 1 0.135 0.183 0.341 + 1SOL H 2 0.177 0.149 0.262 + 1SOL H 3 0.046 0.149 0.339 + 2SOL O 4 0.520 0.447 0.111 + 2SOL H 5 0.567 0.481 0.035 + 2SOL H 6 0.568 0.481 0.186 + 3SOL O 7 0.651 0.539 0.335 + 3SOL H 8 0.653 0.634 0.336 + 3SOL H 9 0.743 0.512 0.336 + 0.7822838765564372 0.7353572647182051 0.9036518515423753 0.0 0.1 0.2 0.3 0.4 0.5 diff --git a/tests/test_gromacs_gro.py b/tests/test_gromacs_gro.py new file mode 100644 index 000000000..1acb4d27f --- /dev/null +++ b/tests/test_gromacs_gro.py @@ -0,0 +1,46 @@ +import os +import numpy as np +import unittest +from context import dpdata + +class TestGromacsGro(unittest.TestCase): + def test_read_file(self): + system = dpdata.System('gromacs/1h.gro') + self.assertEqual(system['atom_names'], ['H', 'O']) + self.assertEqual(system['atom_numbs'], [6, 3]) + for cc,ii in enumerate([1, 0, 0, 1, 0, 0, 1, 0, 0]): + self.assertEqual(system['atom_types'][cc], ii) + self.assertEqual(len(system['cells']), 1) + self.assertEqual(len(system['coords']), 1) + for ii in range(3): + for jj in range(3): + if ii != jj: + self.assertAlmostEqual(system['cells'][0][ii][jj], 0) + self.assertAlmostEqual(system['cells'][0][0][0], 7.822838765564372) + self.assertAlmostEqual(system['cells'][0][1][1], 7.353572647182051) + self.assertAlmostEqual(system['cells'][0][2][2], 9.036518515423753) + self.assertAlmostEqual(system['coords'][0][8][0], 7.43) + self.assertAlmostEqual(system['coords'][0][8][1], 5.12) + self.assertAlmostEqual(system['coords'][0][8][2], 3.36) + + def test_read_file_tri(self): + system = dpdata.System('gromacs/1h.tri.gro') + self.assertEqual(system['atom_names'], ['H', 'O']) + self.assertEqual(system['atom_numbs'], [6, 3]) + for cc,ii in enumerate([1, 0, 0, 1, 0, 0, 1, 0, 0]): + self.assertEqual(system['atom_types'][cc], ii) + self.assertEqual(len(system['cells']), 1) + self.assertEqual(len(system['coords']), 1) + count = 0 + for ii in range(3): + for jj in range(3): + if ii != jj: + self.assertAlmostEqual(system['cells'][0][ii][jj], count) + count += 1 + self.assertAlmostEqual(system['cells'][0][0][0], 7.822838765564372) + self.assertAlmostEqual(system['cells'][0][1][1], 7.353572647182051) + self.assertAlmostEqual(system['cells'][0][2][2], 9.036518515423753) + self.assertAlmostEqual(system['coords'][0][8][0], 7.43) + self.assertAlmostEqual(system['coords'][0][8][1], 5.12) + self.assertAlmostEqual(system['coords'][0][8][2], 3.36) + system.to('vasp/poscar', 'POSCAR')