@@ -150,26 +150,40 @@ def _step(self, t, y, h, f):
150
150
class FSIntegrator (BaseIntegrator ):
151
151
"""A step integrator considering the action of the band
152
152
"""
153
- def __init__ (self , band , forces , action , rhs_fun , action_fun , n_images , n_dofs_image ,
154
- max_steps = 1000 ,
155
- maxCreep = 5 , eta_scale = 1.0 , stopping_dE = 1e-6 , dEta = 2 ,
156
- etaMin = 0.001 ,
153
+ def __init__ (self , band , forces , rhs_fun , action_fun , n_images , n_dofs_image ,
154
+ maxSteps = 1000 ,
155
+ maxCreep = 5 , actionTol = 1e-10 , forcesTol = 1e-6 ,
156
+ etaScale = 1.0 , dEta = 2 , minEta = 0.001 ,
157
157
# perturbSeed=42, perturbFactor=0.1,
158
- nTrail = 10 , resetMax = 20 , mXgradE_tol = 0.1
158
+ nTrail = 10 , resetMax = 20
159
159
):
160
160
super (FSIntegrator , self ).__init__ (band , rhs_fun )
161
161
162
162
self .action_fun = action_fun
163
163
164
+ # Integration parameters
165
+ # TODO: move to run function?
166
+ self .dEta = dEta
167
+ self .minEta = minEta
168
+ self .resetMax = resetMax
169
+ self .maxCreep = maxCreep
170
+ self .etaScale = etaScale
171
+ self .forcesTol = forcesTol
172
+ self .actionTol = actionTol
173
+ self .maxSteps = maxSteps
174
+
164
175
self .i_step = 0
165
176
self .n_images = n_images
166
177
self .n_dofs_image = n_dofs_image
167
- self .forces_prev = np .zeros_like (band ).reshape (n_images , - 1 )
178
+ # self.forces_prev = np.zeros_like(band).reshape(n_images, -1)
168
179
# self.G :
169
180
self .forces = forces
170
- self .max_steps = max_steps
181
+ self .forces_old = np .zeros_like (forces )
182
+
183
+ # Rename y to band
184
+ self .band = self .y
185
+ self .band_old = np .zeros_like (self .band ) # y -> band
171
186
172
- self .y_last = np .zeros_like (self .y ) # y -> band
173
187
self .step = 0
174
188
self .nTrail = nTrail
175
189
@@ -184,18 +198,21 @@ def run_for(self, n_steps):
184
198
totalRestart = True
185
199
resetCount = 0
186
200
creepCount = 0
187
- self .trailAction = np .zeros (nTrail )
188
- trailPool = cycle (range (nTrail )) # cycle through 0,1,...,(nTrail-1),0,1,...
201
+ self .trailAction = np .zeros (self . nTrail )
202
+ trailPool = cycle (range (self . nTrail )) # cycle through 0,1,...,(nTrail-1),0,1,...
189
203
eta = 1.0
190
204
205
+ # In __init__:
206
+ # self.band_last[:] = self.band
207
+
191
208
INNER_DOFS = slice (self .n_dofs_image , - self .n_dofs_image )
192
209
193
210
while not exitFlag :
194
211
195
212
if totalRestart :
196
213
if self .step > 0 :
197
214
print ('Restarting' )
198
- self .y [:] = self .y_last
215
+ self .band [:] = self .band_old
199
216
200
217
# Compute from self.band. Do not update the step at this stage:
201
218
# This step updates the forces in the G array of the nebm module,
@@ -206,10 +223,10 @@ def run_for(self, n_steps):
206
223
self .action = self .action_fun ()
207
224
208
225
# self.step += 1
209
- self .forces_last [:] = self .forces # Scale field??
226
+ self .forces_old [:] = self .forces # Scale field??
210
227
# self.gradE_last[~_material] = 0.0
211
228
# self.gradE[:] = self.gradE_last
212
- self .action_last = self .action
229
+ self .action_old = self .action
213
230
self .trailAction [nStart ] = self .action
214
231
nStart = next (trailPool )
215
232
eta = 1.0
@@ -218,10 +235,10 @@ def run_for(self, n_steps):
218
235
creepCount = 0
219
236
220
237
# Creep stage: minimise with a fixed eta
221
- while creepCount < maxCreep :
238
+ while creepCount < self . maxCreep :
222
239
# Update spin. Avoid pinned or zero-Ms sites
223
- self .y [:] = self .y_last + eta * eta_scale * self .forces
224
- normalise_spins (self .y )
240
+ self .band [:] = self .band_old + eta * self . etaScale * self .forces
241
+ normalise_spins (self .band )
225
242
226
243
self .trailAction
227
244
@@ -231,88 +248,72 @@ def run_for(self, n_steps):
231
248
self .trailAction [nStart ] = self .action
232
249
nStart = next (trailPool )
233
250
234
- bandNormSquared = 0.0
235
251
# Getting averages of forces from the INNER images in the band (no extrema)
236
252
# (forces are given by vector G in the chain method code)
253
+ # TODO: we might use all band images, not only inner ones
237
254
G_norms = np .linalg .norm (self .forces [INNER_DOFS ].reshape (- 1 , 3 ), axis = 1 )
238
255
# Compute the root mean square per image
239
256
rms_G_norms_per_image = np .sqrt (np .mean (G_norms .reshape (self .n_images - 2 , - 1 ), axis = 1 ))
240
257
mean_rms_G_norms_per_image = np .mean (rms_G_norms_per_image )
241
258
259
+ # Average step difference between trailing action and new action
260
+ deltaAction = (np .abs (self .trailAction [nStart ] - self .action )) / self .nTrail
242
261
243
262
# 10 seems like a magic number; we set here a minimum number of evaulations
244
- if (nStart > nTrail * 10 ) and :
245
-
246
-
247
-
248
- # while abs(self.i_step - steps) > EPSILON:
249
- st = 1
250
- while st < n_steps :
251
- self .i_step = self ._step (self .t , self .y , self .stepsize , self .rhs )
252
-
253
- self .rhs_evals_nb += 0
254
-
255
- if self .i_step > self .max_steps :
256
- break
257
-
258
- st += 1
259
- return 0
263
+ if (nStart > self .nTrail * 10 ) and (deltaAction < self .actionTol ):
264
+ print ('Change in action is negligible' )
265
+ exitFlag = True
266
+ break # creep loop
267
+
268
+ if (n_steps >= self .maxSteps ):
269
+ print ('Number of steps reached maximum' )
270
+ exitFlag = True
271
+ break # creep loop
272
+
273
+ # If action increases compared to last step, decrease eta and start creeping again
274
+ if (self .action_old < self .action ):
275
+ print ('Action increased. Start new creep stage from last step' )
276
+ creepCount = 0
277
+ eta = eta / (self .dEta * self .dEta )
278
+
279
+ # If eta is too small, reset and start again
280
+ if (eta < 1e-3 ):
281
+ print ('' )
282
+ resetCount += 1
283
+ bestAction = self .action_old
284
+ RefinePath (self .band ) # Resets the path to equidistant structures (smoothing kinks?)
285
+ # PathChanged[:] = True
286
+
287
+ if resetCount > self .resetMax :
288
+ print ('Failed to converge!' )
289
+ # Otherwise, just
290
+ else :
291
+ self .band [:] = self .band_old
292
+ self .forces [:] = self .forces_old
293
+ # If action decreases, move to next creep step
294
+ else :
295
+ creepCount += 1
296
+ self .action_old = self .action
297
+ self .band_old [:] = self .band
298
+ self .forces_old [:] = self .forces
299
+
300
+ if (mean_rms_G_norms_per_image < self .forcesTol ):
301
+ print ('Change of mean of the force RMSquares negligible' )
302
+ exitFlag = True
303
+ break # creep loop
304
+ # END creep while loop
305
+
306
+ # After creep loop:
307
+ self .eta = self .eta * self .dEta
308
+ resetCount = 0
260
309
261
310
def set_options (self ):
262
311
pass
263
312
264
313
def _step (self , t , y , h , f ):
265
314
"""
266
315
"""
267
-
268
- f (t , y )
269
- force_images = self .forces
270
- force_images .shape = (self .n_images , - 1 )
271
- # In this case f represents the force: a = dy/dt = f/m
272
- # * self.m_inv[:, np.newaxis]
273
- y .shape = (self .n_images , - 1 )
274
- # print(force_images[2])
275
-
276
- # Loop through every image in the band
277
- for i in range (1 , self .n_images - 1 ):
278
-
279
- force = force_images [i ]
280
- velocity = self .velocity [i ]
281
- velocity_new = self .velocity_new [i ]
282
-
283
- # Update coordinates from Newton eq, which uses the "acceleration"
284
- # At step 0 velocity is zero
285
- y [i ][:] = y [i ] + h * (velocity + (h / (2 * self .mass )) * force )
286
-
287
- # Update the velocity from a mean with the prev step
288
- # (Velocity Verlet)
289
- velocity [:] = velocity_new [:] + (h / (2 * self .mass )) * (self .forces_prev [i ] + force )
290
-
291
- # Project the force of the image into the velocity vector: < v | F >
292
- velocity_proj = np .einsum ('i,i' , force , velocity )
293
-
294
- # Set velocity to zero when the proj = 0
295
- if velocity_proj <= 0 :
296
- velocity_new [:] = 0.0
297
- else :
298
- # Norm of the force squared <F | F>
299
- force_norm_2 = np .einsum ('i,i' , force , force )
300
- factor = velocity_proj / force_norm_2
301
- # Set updated velocity: v = v * (<v|F> / |F|^2)
302
- velocity_new [:] = factor * force
303
-
304
- # New velocity from Newton equation (old Verlet)
305
- # velocity[:] = velocity_new + (h / self.mass) * force
306
-
307
- # Store the force for the Velocity Verlet algorithm
308
- self .forces_prev [:] = force_images
309
-
310
- y .shape = (- 1 ,)
311
- force_images .shape = (- 1 ,)
312
- normalise_spins (y )
313
- tp = t + h
314
- return tp
315
-
316
+ pass
316
317
317
318
def normalise_spins (y ):
318
319
# Normalise an array of spins y with 3 * N elements
0 commit comments