10
10
11
11
class StepIntegrator (BaseIntegrator ):
12
12
"""
13
- A simple integrator where spins are normalised at every inetegrator step
14
- Integrator options are Euler and RK4
13
+ A simple integrator where spins are normalised at every integrator step.
14
+ Integrator options are `euler` and `rk4`
15
15
"""
16
16
def __init__ (self , spins , rhs_fun , step = "euler" , stepsize = 1e-15 ):
17
17
super (StepIntegrator , self ).__init__ (spins , rhs_fun )
@@ -36,13 +36,13 @@ def set_options(self, rtol=1e-8, atol=1e-8):
36
36
def set_step (self , step ):
37
37
step_choices = {'euler' : euler_step , 'rk4' : runge_kutta_step }
38
38
if step not in step_choices :
39
- raise NotImplemented ("step must be euler or rk4" )
39
+ raise NotImplementedError ("step must be euler or rk4" )
40
40
self ._step = step_choices [step ]
41
41
42
42
43
43
class VerletIntegrator (BaseIntegrator ):
44
- """
45
- A quick Verlet integration in Cartesian coordinates
44
+ """A quick Verlet integration in Cartesian coordinates
45
+
46
46
See: J. Chem. Theory Comput., 2017, 13 (7), pp 3250–3259
47
47
"""
48
48
def __init__ (self , band , forces , rhs_fun , n_images , n_dofs_image ,
@@ -142,6 +142,98 @@ def _step(self, t, y, h, f):
142
142
return tp
143
143
144
144
145
+ # -----------------------------------------------------------------------------
146
+ # NEBM integrator for F-S algorithm
147
+
148
+
149
+ class FSIntegrator (BaseIntegrator ):
150
+ """A step integrator considering the action of the band
151
+ """
152
+ def __init__ (self , band , forces , action , rhs_fun , n_images , n_dofs_image ,
153
+ max_steps = 1000 ):
154
+ super (FSIntegrator , self ).__init__ (band , rhs_fun )
155
+
156
+ self .i_step = 0
157
+ self .n_images = n_images
158
+ self .n_dofs_image = n_dofs_image
159
+ self .forces_prev = np .zeros_like (band ).reshape (n_images , - 1 )
160
+ # self.G :
161
+ self .forces = forces
162
+ self .max_steps = max_steps
163
+
164
+ def run_until (self , t ):
165
+ pass
166
+
167
+ def run_for (self , n_steps ):
168
+ # while abs(self.i_step - steps) > EPSILON:
169
+ st = 1
170
+ while st < n_steps :
171
+ self .i_step = self ._step (self .t , self .y , self .stepsize , self .rhs )
172
+
173
+ self .rhs_evals_nb += 0
174
+
175
+ if self .i_step > self .max_steps :
176
+ break
177
+
178
+ st += 1
179
+ return 0
180
+
181
+ def set_options (self ):
182
+ pass
183
+
184
+ def _step (self , t , y , h , f ):
185
+ """
186
+ """
187
+
188
+ f (t , y )
189
+ force_images = self .forces
190
+ force_images .shape = (self .n_images , - 1 )
191
+ # In this case f represents the force: a = dy/dt = f/m
192
+ # * self.m_inv[:, np.newaxis]
193
+ y .shape = (self .n_images , - 1 )
194
+ # print(force_images[2])
195
+
196
+ # Loop through every image in the band
197
+ for i in range (1 , self .n_images - 1 ):
198
+
199
+ force = force_images [i ]
200
+ velocity = self .velocity [i ]
201
+ velocity_new = self .velocity_new [i ]
202
+
203
+ # Update coordinates from Newton eq, which uses the "acceleration"
204
+ # At step 0 velocity is zero
205
+ y [i ][:] = y [i ] + h * (velocity + (h / (2 * self .mass )) * force )
206
+
207
+ # Update the velocity from a mean with the prev step
208
+ # (Velocity Verlet)
209
+ velocity [:] = velocity_new [:] + (h / (2 * self .mass )) * (self .forces_prev [i ] + force )
210
+
211
+ # Project the force of the image into the velocity vector: < v | F >
212
+ velocity_proj = np .einsum ('i,i' , force , velocity )
213
+
214
+ # Set velocity to zero when the proj = 0
215
+ if velocity_proj <= 0 :
216
+ velocity_new [:] = 0.0
217
+ else :
218
+ # Norm of the force squared <F | F>
219
+ force_norm_2 = np .einsum ('i,i' , force , force )
220
+ factor = velocity_proj / force_norm_2
221
+ # Set updated velocity: v = v * (<v|F> / |F|^2)
222
+ velocity_new [:] = factor * force
223
+
224
+ # New velocity from Newton equation (old Verlet)
225
+ # velocity[:] = velocity_new + (h / self.mass) * force
226
+
227
+ # Store the force for the Velocity Verlet algorithm
228
+ self .forces_prev [:] = force_images
229
+
230
+ y .shape = (- 1 ,)
231
+ force_images .shape = (- 1 ,)
232
+ normalise_spins (y )
233
+ tp = t + h
234
+ return tp
235
+
236
+
145
237
def normalise_spins (y ):
146
238
# Normalise an array of spins y with 3 * N elements
147
239
y .shape = (- 1 , 3 )
0 commit comments