@@ -150,26 +150,40 @@ def _step(self, t, y, h, f):
150150class FSIntegrator (BaseIntegrator ):
151151 """A step integrator considering the action of the band
152152 """
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 ,
157157 # perturbSeed=42, perturbFactor=0.1,
158- nTrail = 10 , resetMax = 20 , mXgradE_tol = 0.1
158+ nTrail = 10 , resetMax = 20
159159 ):
160160 super (FSIntegrator , self ).__init__ (band , rhs_fun )
161161
162162 self .action_fun = action_fun
163163
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+
164175 self .i_step = 0
165176 self .n_images = n_images
166177 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)
168179 # self.G :
169180 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
171186
172- self .y_last = np .zeros_like (self .y ) # y -> band
173187 self .step = 0
174188 self .nTrail = nTrail
175189
@@ -184,18 +198,21 @@ def run_for(self, n_steps):
184198 totalRestart = True
185199 resetCount = 0
186200 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,...
189203 eta = 1.0
190204
205+ # In __init__:
206+ # self.band_last[:] = self.band
207+
191208 INNER_DOFS = slice (self .n_dofs_image , - self .n_dofs_image )
192209
193210 while not exitFlag :
194211
195212 if totalRestart :
196213 if self .step > 0 :
197214 print ('Restarting' )
198- self .y [:] = self .y_last
215+ self .band [:] = self .band_old
199216
200217 # Compute from self.band. Do not update the step at this stage:
201218 # This step updates the forces in the G array of the nebm module,
@@ -206,10 +223,10 @@ def run_for(self, n_steps):
206223 self .action = self .action_fun ()
207224
208225 # self.step += 1
209- self .forces_last [:] = self .forces # Scale field??
226+ self .forces_old [:] = self .forces # Scale field??
210227 # self.gradE_last[~_material] = 0.0
211228 # self.gradE[:] = self.gradE_last
212- self .action_last = self .action
229+ self .action_old = self .action
213230 self .trailAction [nStart ] = self .action
214231 nStart = next (trailPool )
215232 eta = 1.0
@@ -218,10 +235,10 @@ def run_for(self, n_steps):
218235 creepCount = 0
219236
220237 # Creep stage: minimise with a fixed eta
221- while creepCount < maxCreep :
238+ while creepCount < self . maxCreep :
222239 # 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 )
225242
226243 self .trailAction
227244
@@ -231,88 +248,72 @@ def run_for(self, n_steps):
231248 self .trailAction [nStart ] = self .action
232249 nStart = next (trailPool )
233250
234- bandNormSquared = 0.0
235251 # Getting averages of forces from the INNER images in the band (no extrema)
236252 # (forces are given by vector G in the chain method code)
253+ # TODO: we might use all band images, not only inner ones
237254 G_norms = np .linalg .norm (self .forces [INNER_DOFS ].reshape (- 1 , 3 ), axis = 1 )
238255 # Compute the root mean square per image
239256 rms_G_norms_per_image = np .sqrt (np .mean (G_norms .reshape (self .n_images - 2 , - 1 ), axis = 1 ))
240257 mean_rms_G_norms_per_image = np .mean (rms_G_norms_per_image )
241258
259+ # Average step difference between trailing action and new action
260+ deltaAction = (np .abs (self .trailAction [nStart ] - self .action )) / self .nTrail
242261
243262 # 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
260309
261310 def set_options (self ):
262311 pass
263312
264313 def _step (self , t , y , h , f ):
265314 """
266315 """
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
316317
317318def normalise_spins (y ):
318319 # Normalise an array of spins y with 3 * N elements
0 commit comments