Skip to content

Commit 2ac2f48

Browse files
authored
merge devel into master (#363)
2 parents 0db9246 + deead87 commit 2ac2f48

27 files changed

+1099
-66
lines changed

docs/credits.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Authors
2+
=======
3+
4+
.. git-shortlog-authors::

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Welcome to dpdata's documentation!
1313
cli
1414
formats
1515
api/api
16+
credits
1617

1718
.. mdinclude:: ../README.md
1819

dpdata/abacus/relax.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ def get_coords_from_log(loglines,natoms):
6868
else:
6969
cells.append(cells[-1])
7070

71+
coords[-1] = np.array(coords[-1])
7172
if direct_coord:
7273
coords[-1] = coords[-1].dot(cells[-1])
7374

dpdata/abacus/scf.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,11 @@ def get_energy(outlines):
104104
Etot = float(line.split()[1]) # in eV
105105
break
106106
if not Etot:
107-
not_converge = False
108-
for line in outlines:
109-
if "convergence has NOT been achieved!" in line:
110-
not_converge = True
111-
raise RuntimeError("convergence has NOT been achieved in scf!")
112-
break
113-
if not not_converge:
114-
raise RuntimeError("Final total energy cannot be found in output. Unknown problem.")
115-
return Etot
107+
raise RuntimeError("Final total energy cannot be found in output. Unknown problem.")
108+
for line in outlines:
109+
if "convergence has NOT been achieved!" in line:
110+
return Etot,False
111+
return Etot,True
116112

117113
def get_force (outlines, natoms):
118114
force = []
@@ -156,7 +152,15 @@ def get_frame (fname):
156152
celldm, cell = get_cell(geometry_inlines)
157153
atom_names, natoms, types, coords = get_coords(celldm, cell, geometry_inlines, inlines)
158154

159-
energy = get_energy(outlines)
155+
energy,converge = get_energy(outlines)
156+
if not converge:
157+
return {'atom_names':atom_names,\
158+
'atom_numbs':natoms,\
159+
'atom_types':types,\
160+
'cells':[],\
161+
'coords':[],\
162+
'energies':[],\
163+
'forces':[]}
160164
force = get_force (outlines, natoms)
161165
stress = get_stress(outlines)
162166
if stress is not None:

dpdata/cp2k/output.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ def handle_single_log_frame(self, lines):
104104
# CONSERVED QUANTITY [hartree] = -0.279168013085E+04
105105
energy_pattern_2 = re.compile(r' POTENTIAL ENERGY\[hartree\]\s+=\s+(?P<number>\S+)')
106106
energy=None
107-
cell_length_pattern = re.compile(r' INITIAL CELL LNTHS\[bohr\]\s+=\s+(?P<A>\S+)\s+(?P<B>\S+)\s+(?P<C>\S+)')
108-
cell_angle_pattern = re.compile(r' INITIAL CELL ANGLS\[deg\]\s+=\s+(?P<alpha>\S+)\s+(?P<beta>\S+)\s+(?P<gamma>\S+)')
107+
cell_length_pattern = re.compile(r' (INITIAL ){0,1}CELL LNTHS\[bohr\]\s+=\s+(?P<A>\S+)\s+(?P<B>\S+)\s+(?P<C>\S+)')
108+
cell_angle_pattern = re.compile(r' (INITIAL ){0,1}CELL ANGLS\[deg\]\s+=\s+(?P<alpha>\S+)\s+(?P<beta>\S+)\s+(?P<gamma>\S+)')
109109
cell_A, cell_B, cell_C = (0,0,0,)
110110
cell_alpha, cell_beta, cell_gamma=(0,0,0,)
111111
cell_a_pattern = re.compile(r' CELL\| Vector a \[angstrom\]:\s+(?P<ax>\S+)\s+(?P<ay>\S+)\s+(?P<az>\S+)')

dpdata/driver.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,74 @@ def label(self, data: dict) -> dict:
147147
labeled_data['energies'] += lb_data ['energies']
148148
labeled_data['forces'] += lb_data ['forces']
149149
return labeled_data
150+
151+
152+
class Minimizer(ABC):
153+
"""The base class for a minimizer plugin. A minimizer can
154+
minimize geometry.
155+
"""
156+
__MinimizerPlugin = Plugin()
157+
158+
@staticmethod
159+
def register(key: str) -> Callable:
160+
"""Register a minimizer plugin. Used as decorators.
161+
162+
Parameter
163+
---------
164+
key: str
165+
key of the plugin.
166+
167+
Returns
168+
-------
169+
Callable
170+
decorator of a class
171+
172+
Examples
173+
--------
174+
>>> @Minimizer.register("some_minimizer")
175+
... class SomeMinimizer(Minimizer):
176+
... pass
177+
"""
178+
return Minimizer.__MinimizerPlugin.register(key)
179+
180+
@staticmethod
181+
def get_minimizer(key: str) -> "Minimizer":
182+
"""Get a minimizer plugin.
183+
184+
Parameter
185+
---------
186+
key: str
187+
key of the plugin.
188+
189+
Returns
190+
-------
191+
Minimizer
192+
the specific minimizer class
193+
194+
Raises
195+
------
196+
RuntimeError
197+
if the requested minimizer is not implemented
198+
"""
199+
try:
200+
return Minimizer.__MinimizerPlugin.plugins[key]
201+
except KeyError as e:
202+
raise RuntimeError('Unknown minimizer: ' + key) from e
203+
204+
def __init__(self, *args, **kwargs) -> None:
205+
"""Setup the minimizer."""
206+
207+
@abstractmethod
208+
def minimize(self, data: dict) -> dict:
209+
"""Minimize the geometry.
210+
211+
Parameters
212+
----------
213+
data : dict
214+
data with coordinates and atom types
215+
216+
Returns
217+
-------
218+
dict
219+
labeled data with minimized coordinates, energies, and forces
220+
"""

dpdata/fhi_aims/output.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import numpy as np
22
import re
3+
import warnings
34

45
latt_patt="\|\s+([0-9]{1,}[.][0-9]*)\s+([0-9]{1,}[.][0-9]*)\s+([0-9]{1,}[.][0-9]*)"
56
pos_patt_first="\|\s+[0-9]{1,}[:]\s\w+\s(\w+)(\s.*[-]?[0-9]{1,}[.][0-9]*)(\s+[-]?[0-9]{1,}[.][0-9]*)(\s+[-]?[0-9]{1,}[.][0-9]*)"
@@ -63,7 +64,7 @@ def get_fhi_aims_block(fp) :
6364
return blk
6465
return blk
6566

66-
def get_frames (fname, md=True, begin = 0, step = 1) :
67+
def get_frames (fname, md=True, begin = 0, step = 1, convergence_check=True) :
6768
fp = open(fname)
6869
blk = get_fhi_aims_block(fp)
6970
ret = get_info(blk, type_idx_zero = True)
@@ -78,6 +79,7 @@ def get_frames (fname, md=True, begin = 0, step = 1) :
7879
all_virials = []
7980

8081
cc = 0
82+
rec_failed = []
8183
while len(blk) > 0 :
8284
if debug:
8385
with open(str(cc),'w') as f:
@@ -87,9 +89,9 @@ def get_frames (fname, md=True, begin = 0, step = 1) :
8789
coord, _cell, energy, force, virial, is_converge = analyze_block(blk, first_blk=True, md=md)
8890
else:
8991
coord, _cell, energy, force, virial, is_converge = analyze_block(blk, first_blk=False)
90-
if is_converge :
91-
if len(coord) == 0:
92-
break
92+
if len(coord) == 0:
93+
break
94+
if is_converge or not convergence_check:
9395
all_coords.append(coord)
9496

9597
if _cell:
@@ -101,9 +103,16 @@ def get_frames (fname, md=True, begin = 0, step = 1) :
101103
all_forces.append(force)
102104
if virial is not None :
103105
all_virials.append(virial)
106+
if not is_converge:
107+
rec_failed.append(cc+1)
108+
104109
blk = get_fhi_aims_block(fp)
105110
cc += 1
106111

112+
if len(rec_failed) > 0 :
113+
prt = "so they are not collected." if convergence_check else "but they are still collected due to the requirement for ignoring convergence checks."
114+
warnings.warn(f"The following structures were unconverged: {rec_failed}; "+prt)
115+
107116
if len(all_virials) == 0 :
108117
all_virials = None
109118
else :

dpdata/lammps/dump.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
lib_path = os.path.dirname(os.path.realpath(__file__))
66
sys.path.append(lib_path)
77
import lmp
8+
import warnings
9+
class UnwrapWarning(UserWarning):
10+
pass
11+
warnings.simplefilter('once', UnwrapWarning)
12+
813

914
def _get_block (lines, key) :
1015
for idx in range(len(lines)) :
@@ -59,16 +64,17 @@ def get_coordtype_and_scalefactor(keys):
5964
key_su = ['xsu','ysu','zsu'] #scaled and unfolded,sf = lattice parameter
6065
lmp_coor_type = [key_pc,key_uc,key_s,key_su]
6166
sf = [0,0,1,1]
67+
uw = [0,1,0,1] # unwraped or not
6268
for k in range(4):
6369
if all(i in keys for i in lmp_coor_type[k]):
64-
return lmp_coor_type[k],sf[k]
70+
return lmp_coor_type[k], sf[k], uw[k]
6571

66-
def safe_get_posi(lines,cell,orig=np.zeros(3)) :
72+
def safe_get_posi(lines,cell,orig=np.zeros(3), unwrap=False) :
6773
blk, head = _get_block(lines, 'ATOMS')
6874
keys = head.split()
6975
coord_tp_and_sf = get_coordtype_and_scalefactor(keys)
7076
assert coord_tp_and_sf is not None, 'Dump file does not contain atomic coordinates!'
71-
coordtype, sf = coord_tp_and_sf
77+
coordtype, sf, uw = coord_tp_and_sf
7278
id_idx = keys.index('id') - 2
7379
xidx = keys.index(coordtype[0])-2
7480
yidx = keys.index(coordtype[1])-2
@@ -81,8 +87,13 @@ def safe_get_posi(lines,cell,orig=np.zeros(3)) :
8187
posis.sort()
8288
posis = np.array(posis)[:,1:4]
8389
if not sf:
84-
posis = (posis-orig)@np.linalg.inv(cell)# Convert to scaled coordinates for unscaled coordinates
85-
return (posis%1)@cell # Convert scaled coordinates back to Cartesien coordinates
90+
posis = (posis - orig) @ np.linalg.inv(cell) # Convert to scaled coordinates for unscaled coordinates
91+
if uw and unwrap:
92+
return posis @ cell # convert scaled coordinates back to Cartesien coordinates unwrap at the periodic boundaries
93+
else:
94+
if uw and not unwrap:
95+
warnings.warn(message='Your dump file contains unwrapped coordinates, but you did not specify unwrapping (unwrap = True). The default is wrapping at periodic boundaries (unwrap = False).\n',category=UnwrapWarning)
96+
return (posis % 1) @ cell # Convert scaled coordinates back to Cartesien coordinates with wraping at periodic boundary conditions
8697

8798
def get_dumpbox(lines) :
8899
blk, h = _get_block(lines, 'BOX BOUNDS')
@@ -145,9 +156,9 @@ def load_file(fname, begin = 0, step = 1) :
145156
cc += 1
146157
if cc >= begin and (cc - begin) % step == 0 :
147158
buff.append(line)
148-
149159

150-
def system_data(lines, type_map = None, type_idx_zero = True) :
160+
161+
def system_data(lines, type_map = None, type_idx_zero = True, unwrap=False) :
151162
array_lines = split_traj(lines)
152163
lines = array_lines[0]
153164
system = {}
@@ -166,12 +177,12 @@ def system_data(lines, type_map = None, type_idx_zero = True) :
166177
system['cells'] = [np.array(cell)]
167178
natoms = sum(system['atom_numbs'])
168179
system['atom_types'] = get_atype(lines, type_idx_zero = type_idx_zero)
169-
system['coords'] = [safe_get_posi(lines, cell, np.array(orig))]
180+
system['coords'] = [safe_get_posi(lines, cell, np.array(orig), unwrap)]
170181
for ii in range(1, len(array_lines)) :
171182
bounds, tilt = get_dumpbox(array_lines[ii])
172183
orig, cell = dumpbox2box(bounds, tilt)
173184
system['cells'].append(cell)
174-
system['coords'].append(safe_get_posi(array_lines[ii], cell, np.array(orig)))
185+
system['coords'].append(safe_get_posi(array_lines[ii], cell, np.array(orig), unwrap))
175186
system['cells'] = np.array(system['cells'])
176187
system['coords'] = np.array(system['coords'])
177188
return system
@@ -181,7 +192,7 @@ def split_traj(dump_lines) :
181192
marks = []
182193
for idx,ii in enumerate(dump_lines) :
183194
if 'ITEM: TIMESTEP' in ii :
184-
marks.append(idx)
195+
marks.append(idx)
185196
if len(marks) == 0 :
186197
return None
187198
elif len(marks) == 1 :
@@ -191,9 +202,9 @@ def split_traj(dump_lines) :
191202
ret = []
192203
for ii in marks :
193204
ret.append(dump_lines[ii:ii+block_size])
194-
# for ii in range(len(marks)-1):
205+
# for ii in range(len(marks)-1):
195206
# assert(marks[ii+1] - marks[ii] == block_size)
196-
return ret
207+
return ret
197208
return None
198209

199210

dpdata/plugins/3dmol.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from typing import Tuple
2+
import numpy as np
3+
4+
from dpdata.format import Format
5+
from dpdata.xyz.xyz import coord_to_xyz
6+
7+
8+
@Format.register("3dmol")
9+
class Py3DMolFormat(Format):
10+
"""3DMol format.
11+
12+
To use this format, py3Dmol should be installed in advance.
13+
"""
14+
def to_system(self,
15+
data: dict,
16+
f_idx: int = 0,
17+
size: Tuple[int] = (300,300),
18+
style: dict = {"stick":{}, "sphere":{"radius":0.4}},
19+
**kwargs):
20+
"""Show 3D structure of a frame in jupyter.
21+
22+
Parameters
23+
----------
24+
data : dict
25+
system data
26+
f_idx : int
27+
frame index to show
28+
size : tuple[int]
29+
(width, height) of the widget
30+
style : dict
31+
style of 3DMol. Read 3DMol documentation for details.
32+
33+
Examples
34+
--------
35+
>>> system.to_3dmol()
36+
"""
37+
import py3Dmol
38+
types = np.array(data['atom_names'])[data['atom_types']]
39+
xyz = coord_to_xyz(data['coords'][f_idx], types)
40+
viewer = py3Dmol.view(width=size[0], height=size[1])
41+
viewer.addModel(xyz, 'xyz')
42+
viewer.setStyle(style.copy())
43+
viewer.zoomTo()
44+
return viewer

dpdata/plugins/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
importlib.import_module(module_name, PACKAGE_BASE)
1515

1616
# https://setuptools.readthedocs.io/en/latest/userguide/entry_point.html
17-
eps = metadata.entry_points().get('dpdata.plugins', [])
17+
try:
18+
eps = metadata.entry_points(group='dpdata.plugins')
19+
except TypeError:
20+
eps = metadata.entry_points().get('dpdata.plugins', [])
1821
for ep in eps:
1922
plugin = ep.load()

0 commit comments

Comments
 (0)