def test_exchange_field_supported_methods(fixt): """ Check that all supported methods give the same results as the default method. """ A = 1 REL_TOLERANCE = 1e-12 mesh = df.UnitCubeMesh(10, 10, 10) Ms = Field(df.FunctionSpace(mesh, 'DG', 0), 1) functionspace = df.VectorFunctionSpace(mesh, "CG", 1, 3) m = Field(functionspace) m.set(df.Expression(("0", "sin(x[0])", "cos(x[0])"), degree=1)) exch = Exchange(A) exch.setup(m, Ms) H_default = exch.compute_field() supported_methods = list(Exchange._supported_methods) # no need to compare default method with itself supported_methods.remove(exch.method) # the project method for the exchange is too bad supported_methods.remove("project") for method in supported_methods: exch = Exchange(A, method=method) exch.setup(m, Ms) H = exch.compute_field() print "With method '{}', expecting H =\n{}\n, got H =\n{}.".format( method, H_default.reshape((3, -1)).mean(1), H.reshape((3, -1)).mean(1)) rel_diff = np.abs((H - H_default) / H_default) assert np.nanmax(rel_diff) < REL_TOLERANCE
def test_exchange_energy_analytical_2(): """ Compare one Exchange energy with the corresponding analytical result. """ REL_TOLERANCE = 5e-5 lx = 6 ly = 3 lz = 2 nx = 300 ny = nz = 1 mesh = df.BoxMesh(df.Point(0, 0, 0), df.Point(lx, ly, lz), nx, ny, nz) unit_length = 1e-9 functionspace = df.VectorFunctionSpace(mesh, "CG", 1, 3) Ms = Ms = Field(df.FunctionSpace(mesh, 'DG', 0), 8e5) A = 13e-12 m = Field(functionspace) m.set( df.Expression(['0', 'sin(2*pi*x[0]/l_x)', 'cos(2*pi*x[0]/l_x)'], l_x=lx, degree=1)) exch = Exchange(A) exch.setup(m, Ms, unit_length=unit_length) E_expected = A * 4 * pi ** 2 * \ (ly * unit_length) * (lz * unit_length) / (lx * unit_length) E = exch.compute_energy() print "expected energy: {}".format(E) print "computed energy: {}".format(E_expected) assert abs((E - E_expected) / E_expected) < REL_TOLERANCE
def test_exchange_periodic_boundary_conditions(): mesh1 = df.BoxMesh(df.Point(0, 0, 0), df.Point(1, 1, 0.1), 2, 2, 1) mesh2 = df.UnitCubeMesh(10, 10, 10) print(""" # for debugging, to make sense of output # testrun 0, 1 : mesh1 # testrun 2,3 : mesh2 # testrun 0, 2 : normal # testrun 1,3 : pbc """) testrun = 0 for mesh in [mesh1, mesh2]: pbc = PeriodicBoundary2D(mesh) S3_normal = df.VectorFunctionSpace(mesh, "Lagrange", 1) S3_pbc = df.VectorFunctionSpace(mesh, "Lagrange", 1, constrained_domain=pbc) for S3 in [S3_normal, S3_pbc]: print("Running test {}".format(testrun)) testrun += 1 FIELD_TOLERANCE = 6e-7 ENERGY_TOLERANCE = 0.0 m_expr = df.Expression(("0", "0", "1"), degree=1) m = Field(S3, m_expr, name='m') exch = Exchange(1) exch.setup(m, Field(df.FunctionSpace(mesh, 'DG', 0), 1)) field = exch.compute_field() energy = exch.compute_energy() print("m.shape={}".format(m.vector().array().shape)) print("m=") print(m.vector().array()) print("energy=") print(energy) print("shape=") print(field.shape) print("field=") print(field) H = field print "Asserted zero exchange field for uniform m = (1, 0, 0) " + \ "got H =\n{}.".format(H.reshape((3, -1))) print "np.max(np.abs(H)) =", np.max(np.abs(H)) assert np.max(np.abs(H)) < FIELD_TOLERANCE E = energy print "Asserted zero exchange energy for uniform m = (1, 0, 0), " + \ "Got E = {:g}.".format(E) assert abs(E) <= ENERGY_TOLERANCE
def compute_finmag_exc(dolfin_mesh, m_gen, Ms, A): S3 = df.VectorFunctionSpace(dolfin_mesh, "Lagrange", 1, dim=3) coords = np.array(zip(*dolfin_mesh.coordinates())) m0 = m_gen(coords).flatten() m = Field(S3) m.set_with_numpy_array_debug(m0) exchange = Exchange(A) exchange.setup(m, Field(df.FunctionSpace(dolfin_mesh, 'DG', 0), Ms)) finmag_exc_field = df.Function(S3) finmag_exc_field.vector()[:] = exchange.compute_field() return finmag_exc_field
def fixt(): """ Create an Exchange object that will be re-used during testing. """ mesh = df.UnitCubeMesh(10, 10, 10) functionspace = df.VectorFunctionSpace(mesh, "CG", 1, 3) Ms = Field(df.FunctionSpace(mesh, 'DG', 0), 1) A = 1 m = Field(functionspace) exch = Exchange(A) exch.setup(m, Ms) return {"exch": exch, "m": m, "A": A, "Ms": Ms}
def H_eff(self): """Very temporary function to make things simple.""" H_app = project((Constant((0, 1e5, 0))), self.S3) H_ex = Function(self.S3) # Comment out these two lines if you don't want exchange. exch = Exchange(1.3e-11) print "About to call setup" exch.setup(self._m_field, self.Ms) H_ex.vector().array()[:] = exch.compute_field() H_eff = H_ex + H_app return H_eff
def exchange(mesh, unit_length): S3 = df.VectorFunctionSpace(mesh, "Lagrange", 1) m = Field(S3, value=df.Expression(("x[1]*u", "0", "sqrt(1-pow(x[1]*u, 2))"), u=unit_length, degree=1)) exch = Exchange(A) exch.setup(m, Field(df.FunctionSpace(mesh, 'DG', 0), Ms), unit_length=unit_length) H = exch.compute_field() E = exch.compute_energy() return m.get_numpy_array_debug(), H, E
def three_dimensional_problem(): x_max = 10e-9 y_max = 1e-9 z_max = 1e-9 mesh = df.BoxMesh(df.Point(0, 0, 0), df.Point(x_max, y_max, z_max), 40, 2, 2) V = df.VectorFunctionSpace(mesh, 'Lagrange', 1) Ms = 8.6e5 m0_x = "pow(sin(0.2*x[0]*1e9), 2)" m0_y = "0" m0_z = "pow(cos(0.2*x[0]*1e9), 2)" m = Field(V, value=vector_valued_function((m0_x, m0_y, m0_z), V, normalise=True)) C = 1.3e-11 u_exch = Exchange(C) u_exch.setup(m, Field(df.FunctionSpace(mesh, 'DG', 0), Ms)) finmag_exch = u_exch.compute_field() magpar_result = os.path.join(MODULE_DIR, 'magpar_result', 'test_exch') nodes, magpar_exch = magpar.get_field(magpar_result, 'exch') ## Uncomment the line below to invoke magpar to compute the results, ## rather than using our previously saved results. # nodes, magpar_exch = magpar.compute_exch_magpar(m, A=C, Ms=Ms) print magpar_exch # Because magpar have changed the order of the nodes!!! tmp = df.Function(V) tmp_c = mesh.coordinates() mesh.coordinates()[:] = tmp_c * 1e9 finmag_exch, magpar_exch, \ diff, rel_diff = magpar.compare_field( mesh.coordinates(), finmag_exch, nodes, magpar_exch) return dict(m0=m.get_numpy_array_debug(), mesh=mesh, exch=finmag_exch, magpar_exch=magpar_exch, diff=diff, rel_diff=rel_diff)
def setup_finmag(): mesh = df.IntervalMesh(xn, x0, x1) coords = np.array(zip(*mesh.coordinates())) S3 = df.VectorFunctionSpace(mesh, "Lagrange", 1, dim=3) m = Field(S3) m.set_with_numpy_array_debug(m_gen(coords).flatten()) exchange = Exchange(A) exchange.setup(m, Field(df.FunctionSpace(mesh, 'DG', 0), Ms)) H_exc = df.Function(S3) H_exc.vector()[:] = exchange.compute_field() return dict(m=m, H=H_exc, table=start_table())
def test_exchange_energy_analytical(fixt): """ Compare one Exchange energy with the corresponding analytical result. """ REL_TOLERANCE = 1e-7 A = 1 mesh = df.UnitCubeMesh(10, 10, 10) Ms = Field(df.FunctionSpace(mesh, 'DG', 0), 1) functionspace = df.VectorFunctionSpace(mesh, "CG", 1, 3) m = Field(functionspace) m.set(df.Expression(("x[0]", "x[2]", "-x[1]"), degree=1)) exch = Exchange(A) exch.setup(m, Ms) E = exch.compute_energy() # integrating the vector laplacian, the latter gives 3 already expected_E = 3 print "With m = (0, sqrt(1-x^2), x), " + \ "expecting E = {}. Got E = {}.".format(expected_E, E) assert abs(E - expected_E) / expected_E < REL_TOLERANCE
def test_exchange_energy_density(): """ Compare solution with nmag for now. Should derive the analytical solution here as well. Our initial magnetisation looks like this: ^ ~ | / ---> \ | (Hahahaha! :-D) ~ v """ TOL = 1e-7 # Should be lower when comparing with analytical solution MODULE_DIR = os.path.dirname(os.path.abspath(__file__)) # run nmag cmd = "nsim %s --clean" % os.path.join(MODULE_DIR, "run_nmag_Eexch.py") status, output = commands.getstatusoutput(cmd) if status != 0: print output sys.exit("Error %d: Running %s failed." % (status, cmd)) nmag_data = np.loadtxt( os.path.join(MODULE_DIR, "nmag_exchange_energy_density.txt")) # run finmag mesh = df.IntervalMesh(100, 0, 10e-9) S3 = df.VectorFunctionSpace(mesh, "Lagrange", 1, dim=3) Ms = 42 m = Field(S3, value=df.Expression( ("cos(x[0]*pi/10e-9)", "sin(x[0]*pi/10e-9)", "0"), degree=1)) exch = Exchange(1.3e-11) Ms_field = Field(df.FunctionSpace(mesh, 'DG', 0), Ms) exch.setup(m, Ms_field) finmag_data = exch.energy_density() rel_err = np.abs(nmag_data - finmag_data) / np.linalg.norm(nmag_data) print("Nmag data = %g" % nmag_data[0]) print("Finmag data = %g" % finmag_data[0]) print "Relative error from nmag data (expect array of 0):" print rel_err print "Max relative error:", np.max(rel_err) assert np.max(rel_err) < TOL, \ "Max relative error is %g, should be zero." % np.max(rel_err) print("Work out average energy density and energy") S1 = df.VectorFunctionSpace(mesh, "Lagrange", 1, dim=1) # only approximative -- using assemble would be better average_energy_density = np.average(finmag_data) w = df.TestFunction(S1) vol = sum(df.assemble(df.dot(df.Constant([1]), w) * df.dx)) # finmag 'manually' computed, based on node values of energy density: energy1 = average_energy_density * vol # finmag computed by energy class energy2 = exch.compute_energy() # comparison with Nmag energy3 = np.average(nmag_data) * vol print energy1, energy2, energy3 assert abs(energy1 - energy2) < 1e-12 # actual value is 0, but # that must be pure luck. assert abs(energy1 - energy3) < 5e-8 # actual value
x, y, z = mesh.coordinates()[i] mx = 1 my = 0 mz = 0 if 8e-9 < x < 14e-9 and -3e-9 < y < 3e-9: pass else: m_arr[i] = mx m_arr[i + nb_nodes] = my m_arr[i + 2 * nb_nodes] = mz M.vector()[:] = m_arr # LLG setup exchange = Exchange(C) exchange.setup(V, M, Ms) H_eff = Function(V) H_eff.vector()[:] = exchange.compute_field() p = gamma / (1 + alpha * alpha) q = alpha * p u = TrialFunction(V) v = TestFunction(V) a = inner(u, v) * dx L = inner( -p * cross(M, H_eff) - q * cross(M, cross(M, H_eff)) - c * (inner(M, M) - 1) * M, v) * dx dm = Function(V)
os.path.join(MODULE_DIR, "simple_1D_nmag.py")), shell=True) nd = np.load(os.path.join(MODULE_DIR, "nmag_hansconf.npy")) # run finmag mesh = df.IntervalMesh(100, 0, 10e-9) S3 = df.VectorFunctionSpace(mesh, "Lagrange", 1, dim=3) DG0 = df.FunctionSpace(mesh, "DG", 0) m = Field( S3, df.Expression(("cos(x[0]*pi/10e-9)", "sin(x[0]*pi/10e-9)", "0"), degree=1)) Ms = Field(DG0, 1.0) exchange = Exchange(1.3e-11) exchange.setup(m, Ms) fd = exchange.energy_density() # draw an ASCII table of the findings table_border = "+" + "-" * 8 + "+" + "-" * 64 + "+" table_entries = "| {:<6} | {:<20} {:<20} {:<20} |" table_entries_f = "| {:<6} | {:<20.8f} {:<20.8f} {:<20g} |" print table_border print table_entries.format(" ", "min", "max", "delta") print table_border print table_entries_f.format("finmag", min(fd), max(fd), max(fd) - min(fd)) print table_entries_f.format("nmag", min(nd), max(nd), max(nd) - min(nd)) print table_border # draw a plot of the two exchange energy densities
"got H =\n{}.".format(H.reshape((3, -1))) print "np.max(np.abs(H)) =", np.max(np.abs(H)) assert np.max(np.abs(H)) < FIELD_TOLERANCE E = energy print "Asserted zero exchange energy for uniform m = (1, 0, 0), " + \ "Got E = {:g}.".format(E) assert abs(E) <= ENERGY_TOLERANCE if __name__ == "__main__": mesh = df.BoxMesh(df.Point(0, 0, 0), df.Point(2 * np.pi, 1, 1), 10, 1, 1) S = df.FunctionSpace(mesh, "Lagrange", 1) S3 = df.VectorFunctionSpace(mesh, "Lagrange", 1) expr = df.Expression(("0", "cos(x[0])", "sin(x[0])"), degree=1) m = df.interpolate(expr, S3) exch = Exchange(1, pbc2d=True) exch.setup(S3, m, 1) print exch.compute_field() field = df.Function(S3) field.vector().set_local(exch.compute_field()) df.plot(m) df.plot(field) df.interactive()
x, y, z = mesh.coordinates()[i] mx = 1 my = 0 mz = 0 if 8e-9 < x < 14e-9 and -3e-9 < y < 3e-9: pass else: m_arr[i] = mx m_arr[i + nb_nodes] = my m_arr[i + 2 * nb_nodes] = mz m_func.vector()[:] = m_arr # LLG setup exchange = Exchange(C) exchange.setup(V, m_func, Ms) # LLG solve_for def solve_for(t, y): m_func.vector()[:] = y H_ex = exchange.compute_field() H_eff = H_ex + H_app.vector().array() status, dMdt = csolve(alpha, gamma, c, m_func.vector().array(), H_eff, m_func.vector().array().shape[0], pins) if status == 0: return dMdt
class Nanostrip1dEigenproblemFinmag(AbstractEigenproblem): """ Eigenproblem of the form A*v = omega*v where A is a matrix that represents the right-hand side of the action of the linearised LLG equation (without damping): dv/dt = -gamma * m_0 x H_exchange(v) """ def __init__(self, A_ex, Ms, xmin, xmax, unit_length=1e-9, regular_mesh=True): """ *Arguments* A_ex: float Exchange coupling constant (in J/m). Ms: float Saturation magnetisation (in A/m). xmin, xmax: float The bounds of the interval on which the 1D system is defined. regular_mesh: bool If True (the default), the mesh nodes will be equally spaced in the interval [xmin, xmax]. Otherwise they will be randomly chosen. """ self.A_ex = A_ex self.Ms = Ms self.xmin = xmin self.xmax = xmax self.unit_length = unit_length self.regular_mesh = regular_mesh self.L = (self.xmax - self.xmin) * self.unit_length def compute_exchange_field(self, v): """ Compute the exchange field for the given input vector `v` (which should be a `numpy.array`). """ self.exch.m.set_with_numpy_array_debug(v) return self.exch.compute_field() def compute_action_rhs_linearised_LLG(self, m0, v): """ This function computes the action of the right hand side of the linearised LLG on the vector `v` and returns the result. Explicitly, it computes -gamma * (m0 x H_ex(v)) where `m0` is the equilibrium configuration around which the LLG was linearised. """ H = self.compute_exchange_field(v).reshape(3, -1) return -gamma * m0_cross(m0, H) def compute_action_rhs_linearised_LLG_2K(self, v): """ Compute the action of the right hand side of the linearised LLg equation on the vector `v`, which should have a shape compatible with (2, N). This function assumes that the equilibrium configuration around which the LLG equation was linearised is `m0 = (0, 0, 1)^T`. """ K = self.K m0 = np.array([0, 0, 1]) v_3K = np.zeros(3 * K) v_3K[:2 * K] = v #v_3K.shape = (3, -1) res_3K = self.compute_action_rhs_linearised_LLG(m0, v_3K).ravel() res = res_3K[:2 * K] return res def instantiate(self, N, dtype, regular_mesh=None): """ Return a pair (A, None), where A is a matrix representing the action on a vector v given by the right-hand side of the linearised LLG equation (without damping): A*v = dv/dt = -gamma * m_0 x H_exchange(v) """ if not iseven(N): raise ValueError("N must be even. Got: {}".format(N)) # XXX TODO: Actually, we shouldn't store these values in 'self' # because we can always re-instantiate the problem, # right? self.N = N self.dtype = dtype if regular_mesh == None: regular_mesh = self.regular_mesh self.K = N // 2 if regular_mesh: mesh = df.IntervalMesh(self.K - 1, self.xmin, self.xmax) else: mesh = irregular_interval_mesh(self.xmin, self.xmax, self.K) #raise NotImplementedError() V = df.VectorFunctionSpace(mesh, 'CG', 1, dim=3) v = Field(V) self.exch = Exchange(A=self.A_ex) Ms_field = Field(df.FunctionSpace(mesh, 'DG', 0), self.Ms) self.exch.setup(v, Ms_field, unit_length=self.unit_length) C_2Kx2K = LinearOperator( shape=(N, N), matvec=self.compute_action_rhs_linearised_LLG_2K, dtype=dtype) C_2Kx2K_dense = as_dense_array(C_2Kx2K) return C_2Kx2K_dense, None def get_kth_analytical_eigenvalue(self, k, size=None): i = k // 2 return 1j * (-1)**k * (2 * self.A_ex * gamma) / (mu0 * self.Ms) * (i * pi / self.L)**2 def get_kth_analytical_eigenvector(self, k, size): assert (iseven(size)) i = k // 2 xs = np.linspace(self.xmin * self.unit_length, self.xmax * self.unit_length, size // 2) v1 = np.cos(i * pi / self.L * xs) v2 = np.cos(i * pi / self.L * xs) * 1j * (-1)**i return np.concatenate([v1, v2]) def _set_plot_title(self, fig, title): fig.suptitle(title, fontsize=16, verticalalignment='bottom') # fig.subplots_adjust(top=0.55) # fig.tight_layout() def plot(self, w, fig, label, fmt=None): """ This function plots the real and imaginary parts of the solutions separately, and in addition splits each soluton eigenvector into two halves (which represent the x- and y-component of the magnetisation, respectively). """ if fig.axes == []: fig.add_subplot(2, 2, 1) fig.add_subplot(2, 2, 2) fig.add_subplot(2, 2, 3) fig.add_subplot(2, 2, 4) assert (len(fig.axes) == 4) ax1, ax2, ax3, ax4 = fig.axes N = len(w) K = N // 2 assert (N == 2 * K) # Scale the vector so that its maximum entry is 1.0 w_max = abs(w).max() if w_max != 0.0: w = w / w_max fmt = fmt or '' ax1.plot(w[:K].real, fmt, label=label) ax3.plot(w[:K].imag, fmt, label=label) ax2.plot(w[K:].real, fmt, label=label) ax4.plot(w[K:].imag, fmt, label=label) ax1.set_ylim(-1.1, 1.1) ax2.set_ylim(-1.1, 1.1) ax3.set_ylim(-1.1, 1.1) ax4.set_ylim(-1.1, 1.1)