Skip to content

Commit b0eb23e

Browse files
nebm fs: implemented calc of action
1 parent b5824ff commit b0eb23e

File tree

2 files changed

+56
-23
lines changed

2 files changed

+56
-23
lines changed

fidimag/common/chain_method_integrators.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ def run_for(self, n_steps):
227227
# TODO: remove time from chain method rhs
228228
# make a specific function to update G??
229229
self.rhs(t, self.y)
230-
self.action = self.action_fun()
230+
self.action = self.ChainObj.compute_action()
231231

232232
# self.step += 1
233233
self.forces_old[:] = self.forces # Scale field??
@@ -259,15 +259,20 @@ def run_for(self, n_steps):
259259

260260
# Getting averages of forces from the INNER images in the band (no extrema)
261261
# (forces are given by vector G in the chain method code)
262-
# TODO: we might use all band images, not only inner ones
263-
G_norms = np.linalg.norm(self.forces[INNER_DOFS].reshape(-1, 3), axis=1)
262+
# TODO: we might use all band images, not only inner ones, although G is zero at the extrema
263+
Gnorms2 = np.sum(self.forces[INNER_DOFS].reshape(-1, 3)**2, axis=1)
264264
# Compute the root mean square per image
265-
rms_G_norms_per_image = np.sqrt(np.mean(G_norms.reshape(self.n_images - 2, -1), axis=1))
265+
rms_G_norms_per_image = np.sqrt(np.mean(Gnorms2.reshape(self.n_images - 2, -1), axis=1))
266266
mean_rms_G_norms_per_image = np.mean(rms_G_norms_per_image)
267267

268268
# Average step difference between trailing action and new action
269269
deltaAction = (np.abs(self.trailAction[nStart] - self.action)) / self.nTrail
270270

271+
# Print log
272+
print(f'Step {self.i_step} ⟨RMS(G)〉= {mean_rms_G_norms_per_image:.5e} ',
273+
f'deltaAction = {deltaAction:.5e} Creep n = {creepCount:>3} resetC = {resetCount:>3} ',
274+
f'eta = {eta:>5.4e}')
275+
271276
# 10 seems like a magic number; we set here a minimum number of evaulations
272277
if (nStart > self.nTrail * 10) and (deltaAction < self.actionTol):
273278
print('Change in action is negligible')
@@ -289,7 +294,7 @@ def run_for(self, n_steps):
289294
if (eta < 1e-3):
290295
print('')
291296
resetCount += 1
292-
bestAction = self.action_old
297+
# bestAction = self.action_old
293298
self.refine_path(self.ChainObj.distances, self.band) # Resets the path to equidistant structures (smoothing kinks?)
294299
# PathChanged[:] = True
295300

@@ -329,13 +334,13 @@ def refine_path(self, distances, band):
329334
cs = si.CubicSpline(distances, bandrs[:, i])
330335
bandrs[:, i] = cs(new_dist)
331336

332-
def set_options(self):
333-
pass
337+
# def set_options(self):
338+
# pass
334339

335-
def _step(self, t, y, h, f):
336-
"""
337-
"""
338-
pass
340+
# def _step(self, t, y, h, f):
341+
# """
342+
# """
343+
# pass
339344

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

fidimag/common/nebm_FS.py

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@
77
from .chain_method_tools import m_to_zero_nomaterial
88
from .chain_method_base import ChainMethodBase
99

10+
from .chain_method_integrators import FSIntegrator
11+
1012
import scipy.integrate as spi
1113

1214
import logging
1315
logging.basicConfig(level=logging.DEBUG)
1416
log = logging.getLogger(name="fidimag")
1517

1618

19+
# TODO: we can just inherit from the geodesic nebm class!
1720
class NEBM_FS(ChainMethodBase):
1821
"""
1922
ARGUMENTS -----------------------------------------------------------------
@@ -153,16 +156,9 @@ def initialise_integrator(self):
153156
self.iterations = 0
154157
self.ode_count = 1
155158

156-
self.integrator = FSIntegrator(self.band, # y
157-
self.G, # forces
158-
self.step_RHS,
159-
self.n_images,
160-
self.n_dofs_image,
161-
stepsize=1e-4)
162-
self.integrator.set_options()
163-
# In Verlet algorithm we only use the total force G and not YxYxG:
164-
self._llg_evolve = False
165-
159+
# Use default integrator parameters. Pass this class object to the integrator
160+
self.integrator = FSIntegrator(self)
161+
166162
def generate_initial_band(self, method='linear'):
167163
"""
168164
method :: linear, rotation
@@ -258,7 +254,9 @@ def compute_effective_field_and_energy(self, y):
258254
self.energies[i] = self.sim.compute_energy()
259255

260256
# Compute the gradient norm per every image
261-
self.gradientENorm[:] = np.linalg.norm(self.gradientE, axis=1)
257+
Gnorms2 = np.sum(self.gradientE.reshape(-1, 3)**2, axis=1)
258+
# Compute the root mean square per image
259+
self.gradientENorm[:] = np.sqrt(np.mean(Gnorms2.reshape(self.n_images, -1), axis=1))
262260

263261
y = y.reshape(-1)
264262
self.gradientE = self.gradientE.reshape(-1)
@@ -314,9 +312,39 @@ def compute_spring_force(self, y):
314312
)
315313

316314
def compute_action(self):
315+
"""
316+
"""
317+
# Check that action is computed AFTER calculating and projecting the forces
318+
319+
# nebm_clib.image_distances_GreatCircle(self.distances,
320+
# self.path_distances,
321+
# y,
322+
# self.n_images,
323+
# self.n_dofs_image,
324+
# self._material_int,
325+
# self.n_dofs_image_material
326+
# )
327+
317328
# TODO: we can use a better quadrature such as Gaussian
329+
# notice that the gradient norm here is using the RMS
318330
action = spi.trapezoid(self.gradientENorm, self.distances)
319-
# TODO: Add spring fore term to the action
331+
332+
# The spring term in the action is added as |F_k|^2 / (2 * self.k) = self.k * x^2 / 2
333+
# (CHECK) This assumes the spring force is orthogonal to the force gradient (after projection)
334+
# Knorms2 = np.sum(self.spring_force.reshape(-1, 3)**2, axis=1)
335+
# # Compute the root mean square per image
336+
# springF_norms = np.sqrt(np.mean(Knorms2.reshape(self.n_images, -1), axis=1))
337+
338+
# Norm of the spring force per image (assuming tangents as unit vectors)
339+
# These are the norms of the inner images
340+
dist_plus_norm = self.distances[1:]
341+
dist_minus_norm = self.distances[:-1]
342+
# dY_plus_norm = distances[i];
343+
# dY_minus_norm = distances[i - 1];
344+
springF2 = self.k * ((dist_plus_norm - dist_minus_norm)**2)
345+
# CHECK: do we need to scale?
346+
action += np.sum(springF2) / (self.n_images - 2)
347+
320348
return action
321349

322350
def compute_min_action(self):

0 commit comments

Comments
 (0)