Skip to content

Commit 6b9931b

Browse files
Implemented creep stage and finishing details
1 parent 8d59f84 commit 6b9931b

File tree

1 file changed

+83
-82
lines changed

1 file changed

+83
-82
lines changed

fidimag/common/chain_method_integrators.py

Lines changed: 83 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -150,26 +150,40 @@ def _step(self, t, y, h, f):
150150
class 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

317318
def normalise_spins(y):
318319
# Normalise an array of spins y with 3 * N elements

0 commit comments

Comments
 (0)