mlatom 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- MLatom/ML_NEA.py +2697 -0
- MLatom/MLatom.py +151 -0
- MLatom/MLatomF +0 -0
- MLatom/MLtasks.py +767 -0
- MLatom/__init__.py +0 -0
- MLatom/args_class.py +396 -0
- MLatom/cs.so +0 -0
- MLatom/doc.py +620 -0
- MLatom/header.py +64 -0
- MLatom/interface_MLatomF.py +87 -0
- MLatom/interfaces/DeePMDkit/DP_inference.py +20 -0
- MLatom/interfaces/DeePMDkit/__init__.py +0 -0
- MLatom/interfaces/DeePMDkit/interface_DeePMDkit.py +327 -0
- MLatom/interfaces/DeePMDkit/template.json +70 -0
- MLatom/interfaces/GAP/__init__.py +0 -0
- MLatom/interfaces/GAP/interface_GAP.py +415 -0
- MLatom/interfaces/PhysNet/PhysNet_predict.py +180 -0
- MLatom/interfaces/PhysNet/PhysNet_train.py +425 -0
- MLatom/interfaces/PhysNet/__init__.py +0 -0
- MLatom/interfaces/PhysNet/interface_PhysNet.py +270 -0
- MLatom/interfaces/TorchANI/TorchANI_predict.py +82 -0
- MLatom/interfaces/TorchANI/TorchANI_train.py +217 -0
- MLatom/interfaces/TorchANI/__init__.py +0 -0
- MLatom/interfaces/TorchANI/interface_TorchANI.py +323 -0
- MLatom/interfaces/__init__.py +0 -0
- MLatom/interfaces/sGDML/__init__.py +0 -0
- MLatom/interfaces/sGDML/interface_sGDML.py +276 -0
- MLatom/interfaces/sGDML/sgdml_dataset_from_extxyz.py +199 -0
- MLatom/ref.json +23 -0
- MLatom/sliceData.py +334 -0
- MLatom/stopper.py +17 -0
- MLatom-2.0.0.data/scripts/MLatomF +12 -0
- MLatom-2.0.0.data/scripts/mlatom +12 -0
- MLatom-2.0.0.dist-info/LICENSE.txt +251 -0
- MLatom-2.0.0.dist-info/METADATA +149 -0
- MLatom-2.0.0.dist-info/RECORD +38 -0
- MLatom-2.0.0.dist-info/WHEEL +5 -0
- MLatom-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
'''
|
|
3
|
+
!---------------------------------------------------------------------------!
|
|
4
|
+
! Interface_PhysNet: Interface between PhysNet and MLatom !
|
|
5
|
+
! Implementations by: Fuchun Ge and Max Pinheiro Jr !
|
|
6
|
+
!---------------------------------------------------------------------------!
|
|
7
|
+
'''
|
|
8
|
+
import numpy as np
|
|
9
|
+
import os, sys, subprocess, time, shutil, re, math, random
|
|
10
|
+
import stopper
|
|
11
|
+
from args_class import ArgsBase
|
|
12
|
+
|
|
13
|
+
filedir = os.path.dirname(__file__)
|
|
14
|
+
|
|
15
|
+
class Args(ArgsBase):
|
|
16
|
+
def __init__(self):
|
|
17
|
+
super().__init__()
|
|
18
|
+
self.add_default_dict_args([
|
|
19
|
+
'learningCurve'
|
|
20
|
+
],
|
|
21
|
+
bool
|
|
22
|
+
)
|
|
23
|
+
self.add_default_dict_args([
|
|
24
|
+
'xyzfile', 'yfile', 'ygradxyzfile','itrainin','itestin','isubtrainin','ivalidatein','mlmodelin','setname'
|
|
25
|
+
],
|
|
26
|
+
""
|
|
27
|
+
)
|
|
28
|
+
self.add_dict_args({
|
|
29
|
+
'mlmodeltype': 'PhysNet',
|
|
30
|
+
'mlmodelout': "PhysNet",
|
|
31
|
+
'sampling': "random",
|
|
32
|
+
'yestfile': "enest.dat",
|
|
33
|
+
'ygradxyzestfile': "gradest.dat",
|
|
34
|
+
'lcNtrains': [],
|
|
35
|
+
'natom': 0,
|
|
36
|
+
'atype': []
|
|
37
|
+
})
|
|
38
|
+
self.parse_input_content([
|
|
39
|
+
'physnet.batch_sizes=0',
|
|
40
|
+
'physnet.restart=0',
|
|
41
|
+
'physnet.num_features=128',
|
|
42
|
+
'physnet.num_basis=64',
|
|
43
|
+
'physnet.num_blocks=5',
|
|
44
|
+
'physnet.num_residual_atomic=2',
|
|
45
|
+
'physnet.num_residual_interaction=3',
|
|
46
|
+
'physnet.num_residual_output=1',
|
|
47
|
+
'physnet.cutoff=10.0',
|
|
48
|
+
'physnet.use_electrostatic=0',
|
|
49
|
+
'physnet.use_dispersion=0',
|
|
50
|
+
'physnet.grimme_s6=0.5',
|
|
51
|
+
'physnet.grimme_s8=0.2130',
|
|
52
|
+
'physnet.grimme_a1=0.0',
|
|
53
|
+
'physnet.grimme_a2=6.0519',
|
|
54
|
+
'physnet.num_train=8',
|
|
55
|
+
'physnet.num_valid=2',
|
|
56
|
+
'physnet.seed=42',
|
|
57
|
+
'physnet.max_steps=10000000',
|
|
58
|
+
'physnet.learning_rate=0.0008',
|
|
59
|
+
'physnet.max_norm=1000.0',
|
|
60
|
+
'physnet.ema_decay=0.999',
|
|
61
|
+
'physnet.keep_prob=1.0',
|
|
62
|
+
'physnet.l2lambda=0.0',
|
|
63
|
+
'physnet.nhlambda=0.01',
|
|
64
|
+
'physnet.decay_steps=10000000',
|
|
65
|
+
'physnet.decay_rate=0.1',
|
|
66
|
+
'physnet.batch_size=12',
|
|
67
|
+
'physnet.valid_batch_size=2',
|
|
68
|
+
'physnet.force_weight=52.91772105638412',
|
|
69
|
+
'physnet.charge_weight=0',
|
|
70
|
+
'physnet.dipole_weight=0',
|
|
71
|
+
'physnet.summary_interval=5',
|
|
72
|
+
'physnet.validation_interval=5',
|
|
73
|
+
'physnet.save_interval=10',
|
|
74
|
+
'physnet.record_run_metadata=0'
|
|
75
|
+
])
|
|
76
|
+
def parse(self, argsraw):
|
|
77
|
+
self.parse_input_content(argsraw)
|
|
78
|
+
self.argProcess()
|
|
79
|
+
|
|
80
|
+
def argProcess(self):
|
|
81
|
+
with open(self.xyzfile,'r') as f:
|
|
82
|
+
self.natom = int(f.readline())
|
|
83
|
+
exec('f.readline()')
|
|
84
|
+
self.atype = [f.readline().split()[0] for i in range(self.natom)]
|
|
85
|
+
if not self.mlmodelin:
|
|
86
|
+
self.mlmodelin = self.mlmodelout
|
|
87
|
+
if not self.ygradxyzfile:
|
|
88
|
+
self.physnet.force_weight=0
|
|
89
|
+
|
|
90
|
+
if self.learningcurve:
|
|
91
|
+
self.lcNtrains = [int(i) for i in str(self.lcNtrains).split(',')]
|
|
92
|
+
if self.physnet.batch_sizes:
|
|
93
|
+
self.physnet.batch_sizes = [int(i) for i in self.lcNtrains.split(',')]
|
|
94
|
+
self.physnet.batch_size = self.physnet.batch_sizes[self.lcNtrains.index(self.ntrain)]
|
|
95
|
+
|
|
96
|
+
class PhysNetCls(object):
|
|
97
|
+
dataConverted = False
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def load(cls):
|
|
101
|
+
loaded=False
|
|
102
|
+
if not loaded:
|
|
103
|
+
|
|
104
|
+
sp_z = {'X': 0, 'H': 1, 'He': 2, 'Li': 3, 'Be': 4, 'B': 5, 'C': 6, 'N': 7, 'O': 8, 'F': 9, 'Ne': 10, 'Na': 11, 'Mg': 12, 'Al': 13, 'Si': 14, 'P': 15, 'S': 16, 'Cl': 17, 'Ar': 18, 'K': 19, 'Ca': 20, 'Sc': 21, 'Ti': 22, 'V': 23, 'Cr': 24, 'Mn': 25, 'Fe': 26, 'Co': 27, 'Ni': 28, 'Cu': 29, 'Zn': 30, 'Ga': 31, 'Ge': 32, 'As': 33, 'Se': 34, 'Br': 35, 'Kr': 36, 'Rb': 37, 'Sr': 38, 'Y': 39, 'Zr': 40, 'Nb': 41, 'Mo': 42, 'Tc': 43, 'Ru': 44, 'Rh': 45, 'Pd': 46, 'Ag': 47, 'Cd': 48, 'In': 49, 'Sn': 50, 'Sb': 51, 'Te': 52, 'I': 53, 'Xe': 54, 'Cs': 55, 'Ba': 56, 'La': 57, 'Ce': 58, 'Pr': 59, 'Nd': 60, 'Pm': 61, 'Sm': 62, 'Eu': 63, 'Gd': 64, 'Tb': 65, 'Dy': 66, 'Ho': 67, 'Er': 68, 'Tm': 69, 'Yb': 70, 'Lu': 71, 'Hf': 72, 'Ta': 73, 'W': 74, 'Re': 75, 'Os': 76, 'Ir': 77, 'Pt': 78, 'Au': 79, 'Hg': 80, 'Tl': 81, 'Pb': 82, 'Bi': 83, 'Po': 84, 'At': 85, 'Rn': 86, 'Fr': 87, 'Ra': 88, 'Ac': 89, 'Th': 90, 'Pa': 91, 'U': 92, 'Np': 93, 'Pu': 94, 'Am': 95, 'Cm': 96, 'Bk': 97, 'Cf': 98, 'Es': 99, 'Fm': 100, 'Md': 101, 'No': 102, 'Lr': 103, 'Rf': 104, 'Db': 105, 'Sg': 106, 'Bh': 107, 'Hs': 108, 'Mt': 109, 'Ds': 110, 'Rg': 111, 'Cn': 112, 'Uut': 113, 'Fl': 114, 'Uup': 115, 'Lv': 116, 'Uus': 117, 'Uuo': 118}
|
|
105
|
+
|
|
106
|
+
z_sp = {v:k for k,v in sp_z.items()}
|
|
107
|
+
try:
|
|
108
|
+
from . import PhysNet_train
|
|
109
|
+
from . import PhysNet_predict
|
|
110
|
+
except:
|
|
111
|
+
stopper.stopMLatom('Please specify PhysNet installation dir in $PhysNet')
|
|
112
|
+
|
|
113
|
+
globals()['z_sp'] = z_sp
|
|
114
|
+
globals()['sp_z'] = sp_z
|
|
115
|
+
globals()['PhysNet_train'] = PhysNet_train
|
|
116
|
+
globals()['PhysNet_predict'] = PhysNet_predict
|
|
117
|
+
loaded=True
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def __init__(self, argsPhysNet = sys.argv[1:]):
|
|
121
|
+
print(' ___________________________________________________________\n\n%s' % __doc__)
|
|
122
|
+
|
|
123
|
+
@classmethod
|
|
124
|
+
def convertdata(cls, argsPhysNet, subdatasets):
|
|
125
|
+
cls.load()
|
|
126
|
+
def convert(fileout, setname, yorgrad=False):
|
|
127
|
+
# convert data to .h5 format
|
|
128
|
+
prefix = ''
|
|
129
|
+
if args.learningcurve: prefix = '../'
|
|
130
|
+
if setname:
|
|
131
|
+
coordfile = prefix+'xyz.dat_'+setname
|
|
132
|
+
yfile = prefix+'y.dat_'+setname
|
|
133
|
+
gradfile = prefix+'grad.dat_'+setname
|
|
134
|
+
else:
|
|
135
|
+
coordfile = args.xyzfile
|
|
136
|
+
if args.yfile and yorgrad: yfile = args.yfile
|
|
137
|
+
if args.ygradxyzfile and yorgrad: gradfile = args.ygradxyzfile
|
|
138
|
+
with open(coordfile ,'r') as fxyz:
|
|
139
|
+
natom = int(fxyz.readline())
|
|
140
|
+
fxyz.seek(0)
|
|
141
|
+
data={}
|
|
142
|
+
data['R'] = np.array([])
|
|
143
|
+
data['Z'] = np.array([])
|
|
144
|
+
data['N'] = np.array([])
|
|
145
|
+
if args.yfile and yorgrad:
|
|
146
|
+
fy = open(yfile,'r')
|
|
147
|
+
data['E'] = np.array([])
|
|
148
|
+
if args.ygradxyzfile and yorgrad:
|
|
149
|
+
fgrad = open(gradfile,'r')
|
|
150
|
+
data['F'] = np.array([])
|
|
151
|
+
for line in fxyz:
|
|
152
|
+
# 1st line
|
|
153
|
+
data['N'] = np.append(data['N'], int(line)).astype('int')
|
|
154
|
+
if args.ygradxyzfile and yorgrad: fgrad.readline()
|
|
155
|
+
if args.yfile and yorgrad: data['E'] =np.append(data['E'], float(fy.readline()))
|
|
156
|
+
# 2nd line
|
|
157
|
+
fxyz.readline()
|
|
158
|
+
if args.ygradxyzfile and yorgrad: fgrad.readline()
|
|
159
|
+
# rest lines
|
|
160
|
+
for i in range(natom):
|
|
161
|
+
ln = fxyz.readline()
|
|
162
|
+
data['Z'] = np.append(data['Z'], sp_z[ln.split()[0].capitalize()])
|
|
163
|
+
data['R'] = np.append(data['R'], [float(i) for i in ln.split()[-3:]])
|
|
164
|
+
if args.ygradxyzfile and yorgrad: data['F'] = np.append(data['F'], [-1*float(i) for i in fgrad.readline().split()[-3:]])
|
|
165
|
+
|
|
166
|
+
if args.yfile and yorgrad: fy.close()
|
|
167
|
+
if args.ygradxyzfile and yorgrad: fgrad.close()
|
|
168
|
+
data['R'] = data['R'].reshape(-1,natom,3)
|
|
169
|
+
data['Z'] = data['Z'].reshape(-1,natom).astype('int')
|
|
170
|
+
if args.ygradxyzfile and yorgrad: data['F'] = data['F'].reshape(-1,natom,3)
|
|
171
|
+
|
|
172
|
+
np.savez(fileout, **data)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
args = Args()
|
|
176
|
+
args.parse(argsPhysNet)
|
|
177
|
+
print('\n Converting data...\n\n')
|
|
178
|
+
if subdatasets:
|
|
179
|
+
if 'Subtrain' in subdatasets:
|
|
180
|
+
for i in subdatasets:
|
|
181
|
+
if i in ['Subtrain', 'Validate']: convert('PhysNet_'+i.lower()+'.npz', i.lower(),True)
|
|
182
|
+
else: convert('PhysNet_'+i.lower()+'.npz', i.lower())
|
|
183
|
+
else:
|
|
184
|
+
for i in subdatasets:
|
|
185
|
+
if i == 'Train': convert('PhysNet_'+i.lower()+'.npz', i.lower(),True)
|
|
186
|
+
else: convert('PhysNet_'+i.lower()+'.npz', i.lower())
|
|
187
|
+
else: convert('PhysNet.npz', '')
|
|
188
|
+
|
|
189
|
+
@classmethod
|
|
190
|
+
def createMLmodel(cls, argsPhysNet, subdatasets):
|
|
191
|
+
cls.load()
|
|
192
|
+
PNargs = Args()
|
|
193
|
+
PNargs.parse(argsPhysNet)
|
|
194
|
+
|
|
195
|
+
if not cls.dataConverted or PNargs.learningcurve:
|
|
196
|
+
cls.convertdata(argsPhysNet, subdatasets)
|
|
197
|
+
cls.dataConverted = True
|
|
198
|
+
|
|
199
|
+
starttime = time.time()
|
|
200
|
+
|
|
201
|
+
PhysNet_train.train(PNargs)
|
|
202
|
+
|
|
203
|
+
endtime = time.time()
|
|
204
|
+
wallclock = endtime - starttime
|
|
205
|
+
return wallclock
|
|
206
|
+
|
|
207
|
+
@classmethod
|
|
208
|
+
def useMLmodel(cls, argsPhysNet, subdatasets):
|
|
209
|
+
cls.load()
|
|
210
|
+
PNargs = Args()
|
|
211
|
+
PNargs.parse(argsPhysNet)
|
|
212
|
+
|
|
213
|
+
if not cls.dataConverted:
|
|
214
|
+
cls.convertdata(argsPhysNet, subdatasets)
|
|
215
|
+
cls.dataConverted = True
|
|
216
|
+
|
|
217
|
+
starttime = time.time()
|
|
218
|
+
PhysNet_predict.predict(PNargs)
|
|
219
|
+
endtime = time.time()
|
|
220
|
+
wallclock = endtime - starttime
|
|
221
|
+
return wallclock
|
|
222
|
+
|
|
223
|
+
def printHelp():
|
|
224
|
+
helpText = __doc__ + '''
|
|
225
|
+
To use Interface_PhysNet, please define $PhysNet to where PhysNet is located
|
|
226
|
+
|
|
227
|
+
Arguments with their default values:
|
|
228
|
+
MLprog=PhysNet enables this interface
|
|
229
|
+
MLmodelType=PhysNet requests PhysNet model
|
|
230
|
+
|
|
231
|
+
physnet.num_features=128 number of input features
|
|
232
|
+
physnet.num_basis=64 number of radial basis functions
|
|
233
|
+
physnet.num_blocks=5 number of stacked modular building blocks
|
|
234
|
+
|
|
235
|
+
physnet.num_residual_atomic=2
|
|
236
|
+
number of residual blocks for
|
|
237
|
+
atom-wise refinements
|
|
238
|
+
physnet.num_residual_interaction=3
|
|
239
|
+
number of residual blocks for
|
|
240
|
+
refinements of proto-message
|
|
241
|
+
physnet.num_residual_output=1
|
|
242
|
+
number of residual blocks in
|
|
243
|
+
output blocks
|
|
244
|
+
physnet.cutoff=10.0 cutoff radius for interactions
|
|
245
|
+
in the neural network
|
|
246
|
+
physnet.seed=42 random seed
|
|
247
|
+
physnet.max_steps=32 max steps to perform in training
|
|
248
|
+
physnet.learning_rate=0.0008
|
|
249
|
+
starting learning rate
|
|
250
|
+
physnet.decay_steps=10000000
|
|
251
|
+
decay steps
|
|
252
|
+
physnet.decay_rate=0.1 decay rate for learning rate
|
|
253
|
+
physnet.batch_size=12 training batch size
|
|
254
|
+
physnet.valid_batch_size=2 validation batch size
|
|
255
|
+
physnet.force_weight=52.91772105638412
|
|
256
|
+
weight for force
|
|
257
|
+
physnet.charge_weight=0 weight for charge
|
|
258
|
+
physnet.dipole_weight=0 weight for dipole
|
|
259
|
+
physnet.summary_interval=5 interval for summary
|
|
260
|
+
physnet.validation_interval=5
|
|
261
|
+
interval for validation
|
|
262
|
+
physnet.save_interval=10 interval for model saving
|
|
263
|
+
|
|
264
|
+
Cite PhysNet:
|
|
265
|
+
O. T. Unke, M. Meuwly, J. Chem. Theory Comput. 2019, 15, 3678
|
|
266
|
+
'''
|
|
267
|
+
print(helpText)
|
|
268
|
+
|
|
269
|
+
if __name__ == '__main__':
|
|
270
|
+
PhysNetCls()
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torchani
|
|
3
|
+
import os
|
|
4
|
+
import math
|
|
5
|
+
import torch.utils.tensorboard
|
|
6
|
+
import tqdm
|
|
7
|
+
import numpy as np
|
|
8
|
+
import h5py
|
|
9
|
+
|
|
10
|
+
def predict(args):
|
|
11
|
+
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
|
12
|
+
|
|
13
|
+
species_order = sorted(set(args.atype), key=lambda x: args.atype.index(x))
|
|
14
|
+
energyOffset = 0
|
|
15
|
+
|
|
16
|
+
# if args.mlmodeltype.lower() == 'ani1x':
|
|
17
|
+
# model = torchani.models.ANI1x(periodic_table_index=True).to(device)
|
|
18
|
+
# elif args.mlmodeltype.lower() == 'ani1ccx':
|
|
19
|
+
# model = torchani.models.ANI1ccx(periodic_table_index=True).to(device)
|
|
20
|
+
# elif args.mlmodeltype.lower() == 'ani2x':
|
|
21
|
+
# model = torchani.models.ANI2x(periodic_table_index=True).to(device)
|
|
22
|
+
if False:
|
|
23
|
+
pass
|
|
24
|
+
else:
|
|
25
|
+
best_model = torch.load(args.mlmodelin)
|
|
26
|
+
|
|
27
|
+
Rcr = best_model['args']['Rcr']
|
|
28
|
+
Rca = best_model['args']['Rca']
|
|
29
|
+
EtaR = best_model['args']['EtaR'].to(device)
|
|
30
|
+
ShfR = best_model['args']['ShfR'].to(device)
|
|
31
|
+
Zeta = best_model['args']['Zeta'].to(device)
|
|
32
|
+
ShfZ = best_model['args']['ShfZ'].to(device)
|
|
33
|
+
EtaA = best_model['args']['EtaA'].to(device)
|
|
34
|
+
ShfA = best_model['args']['ShfA'].to(device)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
num_species = len(species_order)
|
|
38
|
+
|
|
39
|
+
self_energies=best_model['args']['self_energies']
|
|
40
|
+
energyOffset=best_model['args']['energyOffset']
|
|
41
|
+
|
|
42
|
+
aev_computer = torchani.AEVComputer(Rcr, Rca, EtaR, ShfR, EtaA, Zeta, ShfA, ShfZ, num_species)
|
|
43
|
+
aev_dim = aev_computer.aev_length
|
|
44
|
+
networkdic = best_model['network']
|
|
45
|
+
|
|
46
|
+
nn = torchani.ANIModel([networkdic[specie] for specie in species_order])
|
|
47
|
+
nn.load_state_dict(best_model['nn'])
|
|
48
|
+
|
|
49
|
+
model = torchani.nn.Sequential(aev_computer, nn).to(device)
|
|
50
|
+
model.eval()
|
|
51
|
+
|
|
52
|
+
batch_size = args.ani.batch_size
|
|
53
|
+
|
|
54
|
+
if args.setname: args.setname='_'+args.setname
|
|
55
|
+
testfile=h5py.File('ANI'+args.setname+'.h5','r')
|
|
56
|
+
test=testfile.get('dataset').get('molecule')
|
|
57
|
+
all_coordinates=torch.tensor(test.get('coordinates')[()]).to(device).float()
|
|
58
|
+
|
|
59
|
+
with open(args.yestfile,'wb') as fy, open(args.ygradxyzestfile,'wb') as fgrad:
|
|
60
|
+
for i in range(math.ceil(len(all_coordinates)/batch_size)):
|
|
61
|
+
coordinates=all_coordinates[batch_size*i:min(batch_size*(i+1),len(all_coordinates))].requires_grad_(True)
|
|
62
|
+
|
|
63
|
+
species=test.get('species')[()]
|
|
64
|
+
species=torch.tensor([[species_order.index(i.decode('ascii')) for i in species] for j in range(len(coordinates))]).to(device)
|
|
65
|
+
|
|
66
|
+
_, predicted_energies = model((species, coordinates))
|
|
67
|
+
predicted_forces = -torch.autograd.grad(predicted_energies.sum(), coordinates, create_graph=True, retain_graph=True)[0]
|
|
68
|
+
if args.ygradxyzfile:
|
|
69
|
+
predicted_energies = np.array(predicted_energies.cpu().detach().numpy()).reshape(-1,1)+energyOffset
|
|
70
|
+
predicted_forces = np.array(predicted_forces.cpu().detach().numpy()).reshape(-1, args.natom, 3)
|
|
71
|
+
else:
|
|
72
|
+
predicted_energies = np.array(predicted_energies.cpu().detach().numpy()).reshape(-1,1)+energyOffset
|
|
73
|
+
predicted_forces = np.array(predicted_forces.cpu().detach().numpy()).reshape(-1, args.natom, 3)
|
|
74
|
+
|
|
75
|
+
np.savetxt(fy, predicted_energies, fmt='%20.12f', delimiter=" ")
|
|
76
|
+
line="%d\n\n" % args.natom
|
|
77
|
+
for force in predicted_forces:
|
|
78
|
+
fgrad.write(line.encode('utf-8'))
|
|
79
|
+
np.savetxt(fgrad, -1*force, fmt='%20.12f', delimiter=" ")
|
|
80
|
+
|
|
81
|
+
import time
|
|
82
|
+
time.sleep(0.0000001) # Sometimes program hangs without no reason, adding short sleep time helps to exit this module without a problem / P.O.D., 2021-02-17
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torchani
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
import math
|
|
6
|
+
import torch.utils.tensorboard
|
|
7
|
+
import tqdm
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
def train(args):
|
|
11
|
+
|
|
12
|
+
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
|
13
|
+
print('Training with %s'% device)
|
|
14
|
+
|
|
15
|
+
Rcr = args.ani.Rcr
|
|
16
|
+
Rca = args.ani.Rca
|
|
17
|
+
EtaR = args.ani.EtaR.to(device)
|
|
18
|
+
ShfR = args.ani.ShfR.to(device)
|
|
19
|
+
Zeta = args.ani.Zeta.to(device)
|
|
20
|
+
ShfZ = args.ani.ShfZ.to(device)
|
|
21
|
+
EtaA = args.ani.EtaA.to(device)
|
|
22
|
+
ShfA = args.ani.ShfA.to(device)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
max_epochs = args.ani.max_epochs
|
|
27
|
+
early_stopping_learning_rate = args.ani.early_stopping_learning_rate
|
|
28
|
+
if args.ygradxyzfile:
|
|
29
|
+
force_coefficient = args.ani.force_coefficient
|
|
30
|
+
best_model_checkpoint = args.mlmodelout
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
species_order = sorted(set(args.atype), key=lambda x: args.atype.index(x))
|
|
34
|
+
num_species = len(species_order)
|
|
35
|
+
energy_shifter = torchani.utils.EnergyShifter(None)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
trainfile = 'ANI_subtrain.h5'
|
|
39
|
+
validaefile = 'ANI_validate.h5'
|
|
40
|
+
|
|
41
|
+
batch_size = args.ani.batch_size
|
|
42
|
+
if args.ygradxyzfile:
|
|
43
|
+
training = torchani.data.load(trainfile, additional_properties=('forces',)).subtract_self_energies(energy_shifter, species_order).species_to_indices(species_order).shuffle()
|
|
44
|
+
energyOffset=sum([np.array(energy_shifter.self_energies)[species_order.index(i)] for i in args.atype])
|
|
45
|
+
validation= torchani.data.load(validaefile, additional_properties=('forces',)).subtract_self_energies(energy_shifter, species_order).species_to_indices(species_order).shuffle()
|
|
46
|
+
else:
|
|
47
|
+
training = torchani.data.load(trainfile).subtract_self_energies(energy_shifter, species_order).species_to_indices(species_order).shuffle()
|
|
48
|
+
energyOffset=sum([np.array(energy_shifter.self_energies)[species_order.index(i)] for i in args.atype])
|
|
49
|
+
validation = torchani.data.load(validaefile).subtract_self_energies(energy_shifter, species_order).species_to_indices(species_order).shuffle()
|
|
50
|
+
|
|
51
|
+
training = training.collate(batch_size).cache()
|
|
52
|
+
validation = validation.collate(batch_size).cache()
|
|
53
|
+
|
|
54
|
+
# print('Self atomic energies: ', energy_shifter.self_energies)
|
|
55
|
+
|
|
56
|
+
energyOffset1=sum([np.array(energy_shifter.self_energies)[species_order.index(i)] for i in args.atype])
|
|
57
|
+
|
|
58
|
+
deltaOffset=energyOffset-energyOffset1
|
|
59
|
+
|
|
60
|
+
argdic = {'Rcr': Rcr, 'Rca': Rca, 'EtaR': EtaR, 'ShfR': ShfR, 'Zeta': Zeta, 'ShfZ': ShfZ, 'EtaA': EtaA, 'ShfA': ShfA, 'self_energies': np.array(energy_shifter.self_energies),'energyOffset':energyOffset}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
aev_computer = torchani.AEVComputer(Rcr, Rca, EtaR, ShfR, EtaA, Zeta, ShfA, ShfZ, num_species)
|
|
64
|
+
aev_dim = aev_computer.aev_length
|
|
65
|
+
networkdic ={}
|
|
66
|
+
for i, specie in enumerate(species_order):
|
|
67
|
+
networkdic[specie] = torch.nn.Sequential(
|
|
68
|
+
torch.nn.Linear(aev_dim, int(args.ani.Neuron_l1[i])),
|
|
69
|
+
torch.nn.CELU(0.1),
|
|
70
|
+
torch.nn.Linear(int(args.ani.Neuron_l1[i]), int(args.ani.Neuron_l2[i])),
|
|
71
|
+
torch.nn.CELU(0.1),
|
|
72
|
+
torch.nn.Linear(int(args.ani.Neuron_l2[i]), int(args.ani.Neuron_l3[i])),
|
|
73
|
+
torch.nn.CELU(0.1),
|
|
74
|
+
torch.nn.Linear(int(args.ani.Neuron_l3[i]), 1)
|
|
75
|
+
)
|
|
76
|
+
nn = torchani.ANIModel([networkdic[specie] for specie in species_order])
|
|
77
|
+
|
|
78
|
+
def init_params(m):
|
|
79
|
+
if isinstance(m, torch.nn.Linear):
|
|
80
|
+
torch.nn.init.kaiming_normal_(m.weight, a=1.0)
|
|
81
|
+
torch.nn.init.zeros_(m.bias)
|
|
82
|
+
|
|
83
|
+
nn.apply(init_params)
|
|
84
|
+
|
|
85
|
+
print('batch_size: %s' % batch_size)
|
|
86
|
+
|
|
87
|
+
print('Neural Network Structure:\n', nn)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
model = torchani.nn.Sequential(aev_computer, nn).to(device)
|
|
91
|
+
|
|
92
|
+
wlist2d = [
|
|
93
|
+
[
|
|
94
|
+
{'params': [networkdic[specie][0].weight]},
|
|
95
|
+
{'params': [networkdic[specie][2].weight], 'weight_decay': 0.00001},
|
|
96
|
+
{'params': [networkdic[specie][4].weight], 'weight_decay': 0.000001},
|
|
97
|
+
{'params': [networkdic[specie][6].weight]},
|
|
98
|
+
]
|
|
99
|
+
for specie in species_order
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
AdamW = torch.optim.AdamW([i for j in wlist2d for i in j])
|
|
103
|
+
|
|
104
|
+
blist2d = [
|
|
105
|
+
[
|
|
106
|
+
{'params': [networkdic[specie][0].bias]},
|
|
107
|
+
{'params': [networkdic[specie][2].bias]},
|
|
108
|
+
{'params': [networkdic[specie][4].bias]},
|
|
109
|
+
{'params': [networkdic[specie][6].bias]},
|
|
110
|
+
]
|
|
111
|
+
for specie in species_order
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
SGD = torch.optim.SGD([i for j in blist2d for i in j], lr=1e-3)
|
|
115
|
+
|
|
116
|
+
AdamW_scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(AdamW, factor=args.ani.lrfactor, patience=args.ani.patience, threshold=0)
|
|
117
|
+
SGD_scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(SGD, factor=args.ani.lrfactor, patience=args.ani.patience, threshold=0)
|
|
118
|
+
|
|
119
|
+
latest_checkpoint = 'latest.pt'
|
|
120
|
+
|
|
121
|
+
if os.path.isfile(latest_checkpoint):
|
|
122
|
+
checkpoint = torch.load(latest_checkpoint)
|
|
123
|
+
nn.load_state_dict(checkpoint['nn'])
|
|
124
|
+
AdamW.load_state_dict(checkpoint['AdamW'])
|
|
125
|
+
SGD.load_state_dict(checkpoint['SGD'])
|
|
126
|
+
AdamW_scheduler.load_state_dict(checkpoint['AdamW_scheduler'])
|
|
127
|
+
SGD_scheduler.load_state_dict(checkpoint['SGD_scheduler'])
|
|
128
|
+
|
|
129
|
+
def validate():
|
|
130
|
+
# run validation
|
|
131
|
+
mse_sum = torch.nn.MSELoss(reduction='sum')
|
|
132
|
+
total_mse = 0.0
|
|
133
|
+
count = 0
|
|
134
|
+
for properties in validation:
|
|
135
|
+
species = properties['species'].to(device)
|
|
136
|
+
coordinates = properties['coordinates'].to(device).float()
|
|
137
|
+
true_energies = properties['energies'].to(device).float()
|
|
138
|
+
_, predicted_energies = model((species, coordinates))
|
|
139
|
+
# print(true_energies-deltaOffset,'\n\n',predicted_energies)
|
|
140
|
+
total_mse += mse_sum(predicted_energies, true_energies-deltaOffset).item()
|
|
141
|
+
count += predicted_energies.shape[0]
|
|
142
|
+
return math.sqrt(total_mse / count)
|
|
143
|
+
|
|
144
|
+
tensorboard = torch.utils.tensorboard.SummaryWriter()
|
|
145
|
+
|
|
146
|
+
mse = torch.nn.MSELoss(reduction='none')
|
|
147
|
+
|
|
148
|
+
print("training starting from epoch", AdamW_scheduler.last_epoch + 1)
|
|
149
|
+
|
|
150
|
+
for _ in range(AdamW_scheduler.last_epoch + 1, max_epochs):
|
|
151
|
+
rmse = validate()
|
|
152
|
+
print('RMSE:', rmse, 'at epoch', AdamW_scheduler.last_epoch + 1)
|
|
153
|
+
sys.stdout.flush()
|
|
154
|
+
|
|
155
|
+
learning_rate = AdamW.param_groups[0]['lr']
|
|
156
|
+
print('learning_rate:',learning_rate)
|
|
157
|
+
|
|
158
|
+
if learning_rate < early_stopping_learning_rate:
|
|
159
|
+
break
|
|
160
|
+
|
|
161
|
+
if AdamW_scheduler.is_better(rmse, AdamW_scheduler.best):
|
|
162
|
+
torch.save(
|
|
163
|
+
{
|
|
164
|
+
'network': networkdic,
|
|
165
|
+
'args': argdic,
|
|
166
|
+
'nn': nn.state_dict()
|
|
167
|
+
}
|
|
168
|
+
, best_model_checkpoint
|
|
169
|
+
)
|
|
170
|
+
print('best model saved')
|
|
171
|
+
AdamW_scheduler.step(rmse)
|
|
172
|
+
SGD_scheduler.step(rmse)
|
|
173
|
+
|
|
174
|
+
tensorboard.add_scalar('validation_rmse', rmse, AdamW_scheduler.last_epoch)
|
|
175
|
+
tensorboard.add_scalar('best_validation_rmse', AdamW_scheduler.best, AdamW_scheduler.last_epoch)
|
|
176
|
+
tensorboard.add_scalar('learning_rate', learning_rate, AdamW_scheduler.last_epoch)
|
|
177
|
+
for i, properties in tqdm.tqdm(
|
|
178
|
+
enumerate(training),
|
|
179
|
+
total=len(training),
|
|
180
|
+
desc="epoch {}".format(AdamW_scheduler.last_epoch)
|
|
181
|
+
):
|
|
182
|
+
species = properties['species'].to(device)
|
|
183
|
+
|
|
184
|
+
if args.ygradxyzfile:
|
|
185
|
+
coordinates = properties['coordinates'].to(device).float().requires_grad_(True)
|
|
186
|
+
true_energies = properties['energies'].to(device).float()
|
|
187
|
+
true_forces = properties['forces'].to(device).float()
|
|
188
|
+
num_atoms = (species >= 0).sum(dim=1, dtype=true_energies.dtype)
|
|
189
|
+
_, predicted_energies = model((species, coordinates))
|
|
190
|
+
forces = -torch.autograd.grad(predicted_energies.sum(), coordinates, create_graph=True, retain_graph=True)[0]
|
|
191
|
+
energy_loss = (mse(predicted_energies, true_energies) / num_atoms.sqrt()).mean()
|
|
192
|
+
force_loss = (mse(true_forces, forces).sum(dim=(1, 2)) / num_atoms).mean()
|
|
193
|
+
loss = energy_loss + force_coefficient * force_loss
|
|
194
|
+
# print('\n\n',true_energies+energyOffset,'\n\n',predicted_energies+energyOffset)
|
|
195
|
+
else:
|
|
196
|
+
coordinates = properties['coordinates'].to(device).float()
|
|
197
|
+
true_energies = properties['energies'].to(device).float()
|
|
198
|
+
num_atoms = (species >= 0).sum(dim=1, dtype=true_energies.dtype)
|
|
199
|
+
_, predicted_energies = model((species, coordinates))
|
|
200
|
+
loss = (mse(predicted_energies, true_energies) / num_atoms.sqrt()).mean()
|
|
201
|
+
|
|
202
|
+
AdamW.zero_grad()
|
|
203
|
+
SGD.zero_grad()
|
|
204
|
+
loss.backward()
|
|
205
|
+
AdamW.step()
|
|
206
|
+
SGD.step()
|
|
207
|
+
|
|
208
|
+
tensorboard.add_scalar('batch_loss', loss, AdamW_scheduler.last_epoch * len(training) + i)
|
|
209
|
+
|
|
210
|
+
torch.save({
|
|
211
|
+
'nn': nn.state_dict(),
|
|
212
|
+
'AdamW': AdamW.state_dict(),
|
|
213
|
+
'SGD': SGD.state_dict(),
|
|
214
|
+
'AdamW_scheduler': AdamW_scheduler.state_dict(),
|
|
215
|
+
'SGD_scheduler': SGD_scheduler.state_dict(),
|
|
216
|
+
}, latest_checkpoint)
|
|
217
|
+
|
|
File without changes
|