Skip to content

Commit 7029230

Browse files
authored
Merge pull request #272 from amcadmus/master
Merge devel into master
2 parents 81c656c + 17af7f7 commit 7029230

File tree

122 files changed

+6112
-3036
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

122 files changed

+6112
-3036
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@ _version.py
2222
!tests/cp2k/aimd/cp2k.log
2323
!tests/cp2k/restart_aimd/ch4.log
2424
__pycache__
25+
docs/_build
26+
docs/formats.csv
27+
docs/api/

docs/conf.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
#
1515
import os
1616
import sys
17+
import subprocess as sp
1718
from datetime import date
1819
sys.path.insert(0, os.path.abspath('..'))
1920

2021

2122
# -- Project information -----------------------------------------------------
2223

2324
project = 'dpdata'
24-
copyright = '2019-%d, Deep Modeling ' % date.today().year
25+
copyright = '2019-%d, DeepModeling ' % date.today().year
2526
author = 'Han Wang'
2627

2728
# The short X.Y version
@@ -40,6 +41,7 @@
4041
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
4142
# ones.
4243
extensions = [
44+
'deepmodeling_sphinx',
4345
'sphinx_rtd_theme',
4446
'sphinx.ext.mathjax',
4547
'sphinx.ext.viewcode',
@@ -170,8 +172,12 @@ def run_apidoc(_):
170172
module = os.path.join(cur_dir, "..", "dpdata")
171173
main(['-M', '--tocfile', 'api', '-H', 'API documentation', '-o', os.path.join(cur_dir, "api"), module, '--force'])
172174

175+
def run_formats(_):
176+
sp.check_output([sys.executable, "make_format.py"])
177+
173178
def setup(app):
174179
app.connect('builder-inited', run_apidoc)
180+
app.connect('builder-inited', run_formats)
175181

176182

177183
intersphinx_mapping = {

docs/formats.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Supported Formats
2+
=================
3+
4+
dpdata supports the following formats:
5+
6+
.. csv-table:: Supported Formats
7+
:file: formats.csv
8+
:header-rows: 1
9+

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Welcome to dpdata's documentation!
1010
:maxdepth: 2
1111
:caption: Contents:
1212

13+
formats
1314
api/api
1415

1516
.. mdinclude:: ../README.md

docs/make_format.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import csv
2+
from collections import defaultdict
3+
4+
# ensure all plugins are loaded!
5+
import dpdata.plugins
6+
from dpdata.format import Format
7+
from dpdata.system import get_cls_name
8+
9+
10+
def get_formats() -> dict:
11+
formats = defaultdict(list)
12+
for kk, ff in Format.get_formats().items():
13+
formats[ff].append(kk)
14+
return formats
15+
16+
def detect_overridden(cls: Format, method: str) -> bool:
17+
"""Check whether a method is override
18+
19+
Parameters
20+
----------
21+
cls : Format
22+
a format
23+
method : str
24+
method name
25+
26+
Returns
27+
-------
28+
bool
29+
whether a method is overridden
30+
"""
31+
return method in cls.__dict__
32+
33+
def get_cls_link(cls: object) -> str:
34+
"""Returns class link.
35+
36+
Parameters
37+
----------
38+
cls : object
39+
the class
40+
41+
Returns
42+
-------
43+
str
44+
the link of a class
45+
"""
46+
return ':class:`%s <%s>`' % (cls.__name__, ".".join([cls.__module__, cls.__name__]))
47+
48+
def check_supported(fmt: Format):
49+
methods = set()
50+
for mtd in [
51+
'from_system', 'to_system',
52+
'from_labeled_system', 'to_labeled_system',
53+
'from_bond_order_system', 'to_bond_order_system',
54+
'from_multi_systems', 'to_multi_systems',
55+
]:
56+
if detect_overridden(fmt, mtd):
57+
methods.add(mtd)
58+
if mtd == 'to_system':
59+
methods.add('to_labeled_system')
60+
if fmt.MultiMode != fmt.MultiModes.NotImplemented:
61+
methods.add('from_multi_systems')
62+
methods.add('to_multi_systems')
63+
return methods
64+
65+
method_links = {
66+
"from_system": ":func:`System() <dpdata.system.System>`",
67+
"to_system": ":func:`System.to() <dpdata.system.System.to>`",
68+
"from_labeled_system": ":func:`LabeledSystem() <dpdata.system.LabeledSystem>`",
69+
"to_labeled_system": ":func:`LabeledSystem.to() <dpdata.system.System.to>`",
70+
"from_bond_order_system": ":func:`BondOrderSystem() <dpdata.bond_order_system.BondOrderSystem>`",
71+
"to_bond_order_system": ":func:`BondOrderSystem.to() <dpdata.system.System.to>`",
72+
"from_multi_systems": ":func:`MultiSystems.load_systems_from_file() <dpdata.system.MultiSystems.load_systems_from_file>`",
73+
"to_multi_systems": ":func:`MultiSystems.to() <dpdata.system.MultiSystems.to>`",
74+
}
75+
76+
if __name__ == "__main__":
77+
formats = get_formats()
78+
with open('formats.csv', 'w', newline='') as csvfile:
79+
fieldnames = [
80+
'Class', 'Alias', 'Supported Functions',
81+
]
82+
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
83+
84+
writer.writeheader()
85+
for kk, vv in formats.items():
86+
writer.writerow({
87+
'Class': get_cls_link(kk),
88+
'Alias': '\n'.join(('``%s``' % vvv for vvv in vv)),
89+
'Supported Functions': '\n'.join(method_links[mtd] for mtd in check_supported(kk)),
90+
})

dpdata/abacus/md.py

Lines changed: 49 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
from ast import dump
12
import os,sys
23
import numpy as np
3-
from .scf import ry2ev, kbar2evperang3, get_block, get_geometry_in, get_cell, get_coords
4+
from .scf import ry2ev, bohr2ang, kbar2evperang3, get_block, get_geometry_in, get_cell, get_coords
5+
import re
46

57
# Read in geometries from an ABACUS MD trajectory.
68
# The atomic coordinates are read in from generated files in OUT.XXXX.
@@ -24,126 +26,49 @@ def get_path_out(fname, inlines):
2426

2527
def get_coord_dump_freq(inlines):
2628
for line in inlines:
27-
if len(line)>0 and "md_dumpmdfred" in line and "md_dumpmdfred" == line.split()[0]:
29+
if len(line)>0 and "md_dumpfreq" in line and "md_dumpfreq" == line.split()[0]:
2830
return int(line.split()[1])
2931
return 1
3032

31-
# set up a cell according to cell info in cif file.
32-
# maybe useful later
33-
'''
34-
def setup_cell(a, b, c, alpha, beta, gamma):
35-
cell = np.zeros(3, 3)
36-
cell[0, 0] = a
37-
cell[1, 0] = b*np.cos(gamma/180*np.pi)
38-
cell[1, 1] = b*np.sin(gamma/180*np.pi)
39-
cell[2, 0] = c*np.cos(beta/180*np.pi)
40-
cell[2, 1] = c*(b*np.cos(alpha/180*np.pi) - cell[1, 0]*np.cos(beta/180*np.pi))/cell[1, 1]
41-
cell[2, 2] = np.sqrt(c**2 - cell[2, 0]**2 - cell[2, 1]**2)
42-
return cell
43-
'''
44-
45-
def get_single_coord_from_cif(pos_file, atom_names, natoms, cell):
46-
assert(len(atom_names) == len(natoms))
47-
nele = len(atom_names)
33+
def get_coords_from_dump(dumplines, natoms):
34+
nlines = len(dumplines)
4835
total_natoms = sum(natoms)
49-
coord = np.zeros([total_natoms, 3])
50-
a = 0
51-
b = 0
52-
c = 0
53-
alpha = 0
54-
beta = 0
55-
gamma = 0
56-
with open(pos_file, "r") as fp:
57-
lines = fp.read().split("\n")
58-
for line in lines:
59-
if "_cell_length_a" in line:
60-
a = float(line.split()[1])
61-
if "_cell_length_b" in line:
62-
b = float(line.split()[1])
63-
if "_cell_length_c" in line:
64-
c = float(line.split()[1])
65-
if "_cell_angle_alpha" in line:
66-
alpha = float(line.split()[1])
67-
if "_cell_angle_beta" in line:
68-
beta = float(line.split()[1])
69-
if "_cell_angle_gamma" in line:
70-
gamma = float(line.split()[1])
71-
assert(a > 0 and b > 0 and c > 0 and alpha > 0 and beta > 0 and gamma > 0)
72-
#cell = setup_cell(a, b, c, alpha, beta, gamma)
73-
coord_lines = get_block(lines=lines, keyword="_atom_site_fract_z", skip=0, nlines = total_natoms)
74-
75-
ia_idx = 0
76-
for it in range(nele):
77-
for ia in range(natoms[it]):
78-
coord_line = coord_lines[ia_idx].split()
79-
assert(coord_line[0] == atom_names[it])
80-
coord[ia_idx, 0] = float(coord_line[1])
81-
coord[ia_idx, 1] = float(coord_line[2])
82-
coord[ia_idx, 2] = float(coord_line[3])
83-
ia_idx+=1
84-
coord = np.matmul(coord, cell)
85-
# important! Coordinates are converted to Cartesian coordinate.
86-
return coord
36+
nframes_dump = int(nlines/(total_natoms + 13))
8737

88-
89-
def get_coords_from_cif(ndump, dump_freq, atom_names, natoms, types, path_out, cell):
90-
total_natoms = sum(natoms)
91-
#cell = np.zeros(ndump, 3, 3)
92-
coords = np.zeros([ndump, total_natoms, 3])
93-
pos_file = os.path.join(path_out, "STRU_READIN_ADJUST.cif")
94-
# frame 0 file is different from any other frames
95-
coords[0] = get_single_coord_from_cif(pos_file, atom_names, natoms, cell)
96-
for dump_idx in range(1, ndump):
97-
pos_file = os.path.join(path_out, "md_pos_%d.cif" %(dump_idx*dump_freq))
98-
#print("dump_idx = %s" %dump_idx)
99-
coords[dump_idx] = get_single_coord_from_cif(pos_file, atom_names, natoms, cell)
100-
return coords
38+
cells = np.zeros([nframes_dump, 3, 3])
39+
stresses = np.zeros([nframes_dump, 3, 3])
40+
forces = np.zeros([nframes_dump, total_natoms, 3])
41+
coords = np.zeros([nframes_dump, total_natoms, 3])
42+
iframe = 0
43+
for iline in range(nlines):
44+
if "MDSTEP" in dumplines[iline]:
45+
# read in LATTICE_CONSTANT
46+
celldm = float(dumplines[iline+1].split(" ")[-1])
47+
# read in LATTICE_VECTORS
48+
for ix in range(3):
49+
cells[iframe, ix] = np.array([float(i) for i in re.split('\s+', dumplines[iline+3+ix])[-3:]]) * celldm
50+
stresses[iframe, ix] = np.array([float(i) for i in re.split('\s+', dumplines[iline+7+ix])[-3:]])
51+
for iat in range(total_natoms):
52+
coords[iframe, iat] = np.array([float(i) for i in re.split('\s+', dumplines[iline+11+iat])[-6:-3]])*celldm
53+
forces[iframe, iat] = np.array([float(i) for i in re.split('\s+', dumplines[iline+11+iat])[-3:]])
54+
iframe += 1
55+
assert(iframe == nframes_dump)
56+
cells *= bohr2ang
57+
coords *= bohr2ang
58+
stresses *= kbar2evperang3
59+
return coords, cells, forces, stresses
10160

102-
def get_energy_force_stress(outlines, inlines, dump_freq, ndump, natoms, atom_names):
103-
stress = None
104-
total_natoms = sum(natoms)
105-
for line in inlines:
106-
if len(line)>0 and "stress" in line and "stress" == line.split()[0] and "1" == line.split()[1]:
107-
stress = np.zeros([ndump, 3, 3])
108-
break
109-
if type(stress) != np.ndarray:
110-
print("The ABACUS program has no stress output. Stress will not be read.")
61+
def get_energy(outlines, ndump, dump_freq):
62+
energy = []
11163
nenergy = 0
112-
nforce = 0
113-
nstress = 0
114-
energy = np.zeros(ndump)
115-
force = np.zeros([ndump, total_natoms, 3])
116-
11764
for line_idx, line in enumerate(outlines):
11865
if "final etot is" in line:
11966
if nenergy%dump_freq == 0:
120-
energy[int(nenergy/dump_freq)] = float(line.split()[-2])
67+
energy.append(float(line.split()[-2]))
12168
nenergy+=1
122-
if "TOTAL-FORCE (eV/Angstrom)" in line:
123-
for iatom in range(0, total_natoms):
124-
force_line = outlines[line_idx+5+iatom]
125-
atom_force = [float(i) for i in force_line.split()[1:]]
126-
assert(len(atom_force) == 3)
127-
atom_force = np.array(atom_force)
128-
if nforce%dump_freq == 0:
129-
force[int(nforce/dump_freq), iatom] = atom_force
130-
nforce+=1
131-
assert(nforce==nenergy)
132-
if "TOTAL-STRESS (KBAR)" in line:
133-
for idx in range(0, 3):
134-
stress_line = outlines[line_idx+4+idx]
135-
single_stress = [float(i) for i in stress_line.split()]
136-
if len(single_stress) != 3:
137-
print(single_stress)
138-
assert(len(single_stress) == 3)
139-
single_stress = np.array(single_stress)
140-
if nstress%dump_freq == 0:
141-
stress[int(nstress/dump_freq), idx] = single_stress
142-
nstress+=1
143-
assert(nstress==nforce)
144-
if type(stress) == np.ndarray:
145-
stress *= kbar2evperang3
146-
return energy, force, stress
69+
assert(ndump == len(energy))
70+
energy = np.array(energy)
71+
return energy
14772

14873

14974
def get_frame (fname):
@@ -164,23 +89,27 @@ def get_frame (fname):
16489
atom_names, natoms, types, coords = get_coords(celldm, cell, geometry_inlines, inlines)
16590
# This coords is not to be used.
16691
dump_freq = get_coord_dump_freq(inlines = inlines)
167-
ndump = int(os.popen("ls -l %s | grep 'md_pos_' | wc -l" %path_out).readlines()[0])
92+
#ndump = int(os.popen("ls -l %s | grep 'md_pos_' | wc -l" %path_out).readlines()[0])
16893
# number of dumped geometry files
169-
coords = get_coords_from_cif(ndump, dump_freq, atom_names, natoms, types, path_out, cell)
170-
171-
# TODO: Read in energies, forces and pressures.
94+
#coords = get_coords_from_cif(ndump, dump_freq, atom_names, natoms, types, path_out, cell)
95+
with open(os.path.join(path_out, "MD_dump"), 'r') as fp:
96+
dumplines = fp.read().split('\n')
97+
coords, cells, force, stress = get_coords_from_dump(dumplines, natoms)
98+
ndump = np.shape(coords)[0]
17299
with open(os.path.join(path_out, "running_md.log"), 'r') as fp:
173100
outlines = fp.read().split('\n')
174-
energy, force, stress = get_energy_force_stress(outlines, inlines, dump_freq, ndump, natoms, atom_names)
175-
if type(stress) == np.ndarray:
176-
stress *= np.linalg.det(cell)
101+
energy = get_energy(outlines, ndump, dump_freq)
102+
for iframe in range(ndump):
103+
stress[iframe] *= np.linalg.det(cells[iframe, :, :].reshape([3, 3]))
104+
if np.sum(np.abs(stress[0])) < 1e-10:
105+
stress = None
177106
data = {}
178107
data['atom_names'] = atom_names
179108
data['atom_numbs'] = natoms
180109
data['atom_types'] = types
181-
data['cells'] = np.zeros([ndump, 3, 3])
182-
for idx in range(ndump):
183-
data['cells'][:, :, :] = cell
110+
data['cells'] = cells
111+
#for idx in range(ndump):
112+
# data['cells'][:, :, :] = cell
184113
data['coords'] = coords
185114
data['energies'] = energy
186115
data['forces'] = force

0 commit comments

Comments
 (0)