def setup(): """ Create a cuboid mesh representing a magnetic material and two dolfin.Functions defined on this mesh: m -- unit magnetisation (linearly varying across the sample) Ms_func -- constant function representing the saturation magnetisation Ms *Returns* A triple (m_space, m, Ms_func), where m_space is the VectorFunctionSpace (of type "continuous Lagrange") on which the magnetisation m is defined and m, Ms_funct are as above. """ m_space = df.VectorFunctionSpace(mesh, "CG", 1) m = Field(m_space, value=df.Expression(("1e-9", "x[0]/10", "0"), degree=1)) m.set_with_numpy_array_debug(fnormalise(m.get_numpy_array_debug())) Ms_space = df.FunctionSpace(mesh, "DG", 0) Ms_func = df.interpolate(df.Constant(Ms), Ms_space) return m_space, m, Ms_func
def test_anisotropy_energy_analytical(fixt): """ Compare one UniaxialAnisotropy energy with the corresponding analytical result. The magnetisation is m = (0, sqrt(1 - x^2), x) and the easy axis still a = (0, 0, 1). The squared dot product in the energy integral thus gives dot(a, m)^2 = x^2. Integrating x^2 gives (x^3)/3 and the analytical result with the constants we have chosen is 1 - 1/3 = 2/3. """ mesh = df.UnitCubeMesh(1, 1, 1) functionspace = df.VectorFunctionSpace(mesh, "Lagrange", 1) K1 = 1 Ms = Field(df.FunctionSpace(mesh, 'DG', 0), 1) a = df.Constant((0, 0, 1)) m = Field(functionspace) m.set(df.Expression(("0", "sqrt(1 - pow(x[0], 2))", "x[0]"), degree=1)) anis = UniaxialAnisotropy(K1, a) anis.setup(m, Ms) E = anis.compute_energy() expected_E = float(2) / 3 print "With m = (0, sqrt(1-x^2), x), expecting E = {}. Got E = {}.".format( expected_E, E) #assert abs(E - expected_E) < TOLERANCE assert np.allclose(E, expected_E, atol=1e-14, rtol=TOLERANCE)
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_bloch_parameter_constant(self): A = Field(self.functionspace, 2) K1 = Field(self.functionspace, 0.5) bloch_parameter = ls.bloch_parameter(A, K1) assert np.allclose(bloch_parameter.get_numpy_array_debug(), 2)
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_length_constant(self): A = Field(self.functionspace, 2/mu0) Ms = Field(self.functionspace, 1/mu0) lex = ls.exchange_length(A, Ms) assert np.allclose(lex.get_numpy_array_debug(), 2)
def test_time_dependent_field_switched_off(): # Check the time update (including switching off) with a varying field field_expr = df.Expression(("0", "t", "0"), t=0, degree=1) H_ext = TimeZeeman(field_expr, t_off=1) H_ext.setup(m, Field(df.FunctionSpace(m.mesh(), 'DG', 0), Ms)) assert diff(H_ext, np.array([0, 0, 0])) < TOL assert (H_ext.switched_off == False) H_ext.update(0.9) assert diff(H_ext, np.array([0, 0.9, 0])) < TOL assert (H_ext.switched_off == False) H_ext.update(2) assert diff(H_ext, np.array([0, 0, 0])) < TOL # It's off! assert (H_ext.switched_off == True) # The same again with a constant field a = [42, 0, 5] H_ext = TimeZeeman(a, t_off=1) H_ext.setup(m, Field(df.FunctionSpace(m.mesh(), 'DG', 0), Ms)) assert diff(H_ext, a) < TOL assert (H_ext.switched_off == False) H_ext.update(0.9) assert diff(H_ext, a) < TOL assert (H_ext.switched_off == False) H_ext.update(2) assert diff(H_ext, np.array([0, 0, 0])) < TOL # It's off! assert (H_ext.switched_off == True)
def test_thin_film_argument_saves_time_on_thin_film(): mesh = box(0, 0, 0, 500, 50, 1, maxh=2.0, directory="meshes") Ms = Field(df.FunctionSpace(mesh, 'DG', 0), 8e5) unit_length = 1e-9 S3 = df.VectorFunctionSpace(mesh, "Lagrange", 1) m_function = df.Function(S3) m_function.assign(df.Constant((0, 0, 1))) m = Field(S3, m_function) demag = FKDemag() demag.setup(m, Ms, unit_length) now = time.time() H = demag.compute_field() elapsed = time.time() - now del (demag) demag = FKDemag(thin_film=True) demag.setup(m, Ms, unit_length) now = time.time() H = demag.compute_field() elapsed_thin_film = time.time() - now saved_relative = (elapsed - elapsed_thin_film) / elapsed print "FKDemag thin film settings saved {:.1%} of time.".format( saved_relative) assert elapsed_thin_film < elapsed # This was 20% initially, but in order to make tests more robust this # value is reduced to 5% assert saved_relative > 0.05
def test_helical_period_constant(self): A = Field(self.functionspace, 1/np.pi) D = Field(self.functionspace, 4) helical_period = ls.helical_period(A, D) assert np.allclose(helical_period.get_numpy_array_debug(), 1)
def test_demag_2d(plot=False): mesh = df.UnitSquareMesh(4, 4) Ms = 1.0 S3 = df.VectorFunctionSpace(mesh, "Lagrange", 1, dim=3) m0 = df.Expression(("0", "0", "1"), degree=1) m = Field(S3, m0) h = 0.001 demag = Demag2D(thickness=h) demag.setup(m, Ms) print demag.compute_field() f0 = demag.compute_field() m.set_with_numpy_array_debug(f0) print demag.m.probe(0., 0., 0) print demag.m.probe(1., 0., 0) print demag.m.probe(0., 1., 0) print demag.m.probe(1., 1., 0) print '=' * 50 print demag.m.probe(0., 0., h) print demag.m.probe(1., 0., h) print demag.m.probe(0., 1., h) print demag.m.probe(1., 1., h) if plot: df.plot(m.f) df.interactive()
def Ms(self, value): self._Ms_dg = Field(df.FunctionSpace(self.mesh, 'DG', 0), value) self._Ms_dg.name = 'Ms' self.volumes = df.assemble(df.TestFunction(self.S1) * df.dx) Ms = df.assemble(self._Ms_dg.f * df.TestFunction(self.S1) * df.dx).array() / self.volumes self._Ms = Ms.copy() self.Ms_av = np.average(self._Ms_dg.vector().array())
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 __init__(self, mesh, name='FePt', unit_length=1): self.mesh = mesh self.name = name self.S1 = df.FunctionSpace(mesh, "Lagrange", 1) self.S3 = df.VectorFunctionSpace(mesh, "Lagrange", 1, dim=3) self.nxyz = mesh.num_vertices() self._m = Field(self.S3, name='m') self._T = np.zeros(self.nxyz) self._Ms = np.zeros(3 * self.nxyz) self._m_e = np.zeros(3 * self.nxyz) self.inv_chi_par = np.zeros(self.nxyz) self.h = np.zeros(3 * self.nxyz) self.unit_length = unit_length self.alpha = 0.5 self.gamma_LL = consts.gamma if self.name == 'FePt': self.Tc = 660 self.Ms0 = 1047785.4656 self.A0 = 2.148042e-11 self.K0 = 8.201968e6 self.mu_a = 2.99e-23 elif self.name == 'Nickel': self.Tc = 630 self.Ms0 = 4.9e5 self.A0 = 9e-12 self.K0 = 0 self.mu_a = 0.61e-23 elif self.name == 'Permalloy': self.Tc = 870 self.Ms0 = 8.6e5 self.A0 = 13e-12 self.K0 = 0 # TODO: find the correct mu_a for permalloy self.mu_a = 1e-23 else: raise NotImplementedError("Only FePt and Nickel available") self.volumes = df.assemble( df.dot(df.TestFunction(self.S3), df.Constant([1, 1, 1])) * df.dx).array() self.real_vol = self.volumes * self.unit_length**3 self.mat = native_llb.Materials(self.Ms0, self.Tc, self.A0, self.K0, self.mu_a) dg = df.FunctionSpace(mesh, "DG", 0) self._A_dg = df.Function(dg) self._m_e_dg = df.Function(dg) self.T = 0 self.Ms = self.Ms0 * self._m_e_dg.vector().array()
def test_exchange_length_varying(self): A_expression = df.Expression('4/mu0*x[0] + 1e-100', mu0=mu0, degree=1) Ms_expression = df.Expression('2/mu0*x[0] + 1e-100', mu0=mu0, degree=1) A = Field(self.functionspace, A_expression) Ms = Field(self.functionspace, Ms_expression) lex = ls.exchange_length(A, Ms) assert abs(lex.probe((0.5, 0.5, 0.5)) - 2) < 0.05
def test_bloch_parameter_varying(self): A_expression = df.Expression('4*x[0] + 1e-100', degree=1) K1_expression = df.Expression('x[0] + 1e-100', degree=1) A = Field(self.functionspace, A_expression) K1 = Field(self.functionspace, K1_expression) bloch_parameter = ls.bloch_parameter(A, K1) assert abs(bloch_parameter.probe((0.5, 0.5, 0.5)) - 2) < 0.05
def test_dipolar_field_class(tmpdir): os.chdir(str(tmpdir)) H_dipole = DipolarField(pos=[0, 0, 0], m=[1, 0, 0], magnitude=3e9) mesh = df.BoxMesh(df.Point(-50, -50, -50), df.Point(50, 50, 50), 20, 20, 20) V = df.VectorFunctionSpace(mesh, 'CG', 1, dim=3) m_field = Field(V, value=df.Constant((1, 0, 0))) H_dipole.setup(m_field, Field(df.FunctionSpace(m.mesh(), 'DG', 0), 8.6e5), unit_length=1e-9)
def test_helical_period_varying(self): A_expression = df.Expression('2/pi*x[0] + 1e-100', pi=np.pi, degree=1) D_expression = df.Expression('8*x[0] + 1e-100', degree=1) A = Field(self.functionspace, A_expression) D = Field(self.functionspace, D_expression) helical_period = ls.helical_period(A, D) assert abs(helical_period.probe((0.5, 0.5, 0.5)) - 1) < 0.05
def setup_demag_sphere(Ms): mesh = sphere(r=radius, maxh=maxh) Ms_field = Field(df.FunctionSpace(mesh, 'DG', 0), Ms) S3 = df.VectorFunctionSpace(mesh, "Lagrange", 1) m_function = df.Function(S3) m_function.assign(df.Constant((1, 0, 0))) m = Field(S3, m_function) demag = FKDemag() demag.setup(m, Ms_field, unit_length) return demag
def compute_scalar_potential_native_gcr(mesh, m_expr=df.Constant([1, 0, 0]), Ms=1.0): gcrdemag = Demag("GCR") V = df.VectorFunctionSpace(mesh, "Lagrange", 1) m = Field(V, value=m_expr) m.set_with_numpy_array_debug(helpers.fnormalise(m.get_numpy_array_debug())) gcrdemag.setup(m, Ms, unit_length=1) phi1 = gcrdemag.compute_potential() normalise_phi(phi1, mesh) return phi1
def Ms(self, value): dg_fun = Field(self.DG, value) self._Ms_dg.vector().set_local(dg_fun.vector().get_local()) # FIXME: change back to DG space. #self._Ms_dg=helpers.scalar_valued_function(value, self.S1) self._Ms_dg.name = 'Saturation magnetisation' self.volumes = df.assemble(df.TestFunction(self.S1) * df.dx) Ms = df.assemble(self._Ms_dg.f * df.TestFunction(self.S1) * df.dx).array() / self.volumes.array() self._Ms = Ms.copy() self.Ms_av = np.average(self._Ms_dg.vector().array())
def check_energy_for_m(m, E_expected): """ Helper function to compare the computed energy for a given magnetisation with an expected analytical value. """ m_field = Field(S3) m_field.set(df.Constant(m)) H_ext = Zeeman(H * np.array([1, 0, 0])) H_ext.setup(m_field, Ms, unit_length=unit_length) E_computed = H_ext.compute_energy() assert np.allclose(E_computed, E_expected, atol=0, rtol=1e-12)
def test_regression_Ms_numpy_type(): mesh = sphere(r=radius, maxh=maxh) S3 = df.VectorFunctionSpace(mesh, "Lagrange", 1) m_function = df.Function(S3) m_function.assign(df.Constant((1, 0, 0))) m = Field(S3, m_function) Ms = np.sqrt(6.0 / mu0) # math.sqrt(6.0 / mu0) would work demag = FKDemag() Ms_field = Field(df.FunctionSpace(mesh, 'DG', 0), Ms) demag.setup(m, Ms_field, unit_length) # this used to fail
def test_dmi_uses_unit_length_2dmesh(): """ Set up a helical state in two meshes (one expressed in SI units the other expressed in nanometers) and compute energies and fields. """ A = 8.78e-12 # J/m D = 1.58e-3 # J/m^2 Ms = 3.84e5 # A/m energies = [] # unit_lengths 1e-9 and 1 are common, let's throw in an intermediate length # just to challenge the system a little: for unit_length in (1, 1e-4, 1e-9): radius = 200e-9 / unit_length maxh = 5e-9 / unit_length helical_period = (4 * pi * A / D) / unit_length k = 2 * pi / helical_period # HF 27 April 2014: The next command fails in dolfin 1.3 # mesh = df.CircleMesh(df.Point(0, 0), radius, maxh) # The actual shape of the domain shouldn't matter for the test, # so let's use a Rectangular mesh which should work the same: nx = ny = int(round(radius / maxh)) mesh = df.RectangleMesh(df.Point(0, 0), df.Point(radius, radius), nx, ny) S3 = df.VectorFunctionSpace(mesh, "CG", 1, dim=3) m_expr = df.Expression(("0", "cos(k * x[0])", "sin(k * x[0])"), k=k, degree=1) m = Field(S3, m_expr, name='m') dmi = DMI(D) Ms_dg = Field(df.FunctionSpace(mesh, 'DG', 0), Ms) dmi.setup(m, Ms_dg, unit_length=unit_length) energies.append(dmi.compute_energy()) H = df.Function(S3) H.vector()[:] = dmi.compute_field() print H(0.0, 0.0) print "Using unit_length = {}.".format(unit_length) print "Helical period {}.".format(helical_period) print "Energy {}.".format(dmi.compute_energy()) rel_diff_energies = abs(energies[0] - energies[1]) / abs(energies[1]) print "Relative difference of energy {}.".format(rel_diff_energies) assert rel_diff_energies < 1e-13 rel_diff_energies2 = abs(energies[0] - energies[2]) / abs(energies[2]) print "Relative difference2 of energy {}.".format(rel_diff_energies2) assert rel_diff_energies2 < 1e-13
def helical_period(A, D): """ Computes the helical period when exchange constant A and the constant D are given. Both A and D are Field objects. """ dg_functionspace = df.FunctionSpace(A.mesh(), 'DG', 0) helical_period = Field(dg_functionspace) function = df.project(df.sqrt(4 * pi * A.f / D.f), dg_functionspace) helical_period.set(function) return helical_period
def demag_energy(): mesh = from_geofile(os.path.join(MODULE_DIR, "sphere_fine.geo")) S3 = df.VectorFunctionSpace(mesh, "Lagrange", 1) m_function = df.interpolate(df.Constant((1, 0, 0)), S3) m = Field(S3, m_function) demag = Demag('FK') demag.setup(m, Field(df.FunctionSpace(mesh, 'DG', 0), Ms), unit_length=1) E = demag.compute_energy() rel_error = abs(E - E_analytical) / abs(E_analytical) print "Energy with FK method: {}.".format(E) return E, rel_error
def compute_finmag_anis(m_gen, Ms, K1, axis, dolfin_mesh): 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) anis = UniaxialAnisotropy(K1, axis) anis.setup(m, Field(df.FunctionSpace(dolfin_mesh, 'DG', 0), Ms)) anis_field = df.Function(S3) anis_field.vector()[:] = anis.compute_field() return anis_field
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 test_dmi_pbc2d(): mesh = df.BoxMesh(df.Point(0, 0, 0), df.Point(1, 1, 0.1), 2, 2, 1) pbc = PeriodicBoundary2D(mesh) S3 = df.VectorFunctionSpace(mesh, "Lagrange", 1, constrained_domain=pbc) m_expr = df.Expression(("0", "0", "1"), degree=1) m = Field(S3, m_expr, name='m') dmi = DMI(1) dmi.setup(m, Field(df.FunctionSpace(mesh, 'DG', 0), 1)) field = dmi.compute_field() assert np.max(field) < 1e-15
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 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}