Exemple #1
0
def test_differentiate_heff():
    ns = [2, 2, 2]
    mesh = df.BoxMesh(0, 0, 0, 1, 1, 1, *ns)
    sim = Simulation(mesh, 1.2)
    sim.set_m([1, 0, 0])
    sim.add(Demag())
    sim.add(Exchange(2.3 * mu0))

    compute_H = compute_H_func(sim)

    # Check that H_eff is linear without Zeeman
    np.random.seed(1)
    m1 = fnormalise(np.random.randn(*sim.m.shape))
    m2 = fnormalise(np.random.randn(*sim.m.shape))
    # TODO: need to use a non-iterative solver here to increase accuracy
    assert np.max(
        np.abs(compute_H(m1) + compute_H(m2) - compute_H(m1 + m2))) < 1e-6
    # Add the zeeman field now
    sim.add(Zeeman([2.5, 3.5, 4.3]))

    # Check that both fd2 and fd4 give the same result
    assert np.max(
        np.abs(
            differentiate_fd4(compute_H, m1, m2) -
            differentiate_fd2(compute_H, m1, m2))) < 1e-10
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
Exemple #3
0
    def use_slonczewski(self, J, P, d, p, Lambda=2, epsilonprime=0.0, with_time_update=None):
        """
        Activates the computation of the Slonczewski spin-torque term in the LLG.

        *Arguments*

        J is the current density in A/m^2 as a number, dolfin function,
          dolfin expression or Python function. In the last case the
          current density is assumed to be spatially constant but can
          vary with time. Thus J=J(t) should be a function expecting a
          single variable t (the simulation time) and return a number.

          Note that a time-dependent current density can also be given
          as a dolfin Expression, but a python function should be much
          more efficient.

        P is the polarisation (between 0 and 1). It is defined as P = (x-y)/(x+y),
        where x and y are the fractions of spin up/down electrons).

        d is the thickness of the free layer in m.

        p is the direction of the polarisation as a triple (is automatically normalised to unit length).

        - Lambda: the Lambda parameter in the Slonczewski/Xiao spin-torque term

        - epsilonprime: the strength of the secondary spin transfer term

        - with_time_update:

             A function of the form J(t), which accepts a time step `t`
             as its only argument and returns the new current density.

             N.B.: For efficiency reasons, the return value is currently
                   assumed to be a number, i.e. J is assumed to be spatially
                   constant (and only varying with time).

        """
        self.do_slonczewski = True
        self.fun_slonczewski_time_update = with_time_update

        self.Lambda = Lambda
        self.epsilonprime = epsilonprime
        if isinstance(J, df.Expression):
            J = df.interpolate(J, self.S1)
        if not isinstance(J, df.Function):
            func = df.Function(self.S1)
            func.assign(df.Constant(J))
            J = func
        self.J = J.vector().array()
        assert P >= 0.0 and P <= 1.0
        self.P = P
        self.d = d
        polarisation = df.Function(self.S3)
        polarisation.assign(df.Constant((p)))
        # we use fnormalise to ensure that p has unit length
        self.p = helpers.fnormalise(
            polarisation.vector().array()).reshape((3, -1))
Exemple #4
0
    def m_average(self):        

        self._m.vector().set_local(helpers.fnormalise(self.M))
        
        mx = df.assemble(df.dot(self._m, df.Constant([1, 0, 0])) * df.dx)
        my = df.assemble(df.dot(self._m, df.Constant([0, 1, 0])) * df.dx)
        mz = df.assemble(df.dot(self._m, df.Constant([0, 0, 1])) * df.dx)
        
        volume = df.assemble(df.Constant(1)*df.dx, mesh=self.mesh)
        
        return np.array([mx, my, mz])/volume
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 compute_scalar_potential_llg(mesh, m_expr=df.Constant([1, 0, 0]), Ms=1.):
    S3 = df.VectorFunctionSpace(mesh, "Lagrange", 1, dim=3)
    m = Field(S3, value=m_expr)
    m.set_with_numpy_array_debug(helpers.fnormalise(m.get_numpy_array_debug()))

    demag = Demag()
    demag.setup(m, Field(df.FunctionSpace(mesh, 'DG', 0), Ms), unit_length=1)

    phi = demag.compute_potential()
    normalise_phi(phi, mesh)
    return phi
Exemple #7
0
    def set_m(self, value, **kwargs):
        """
        Set the magnetisation (scaled automatically).
       
        There are several ways to use this function. Either you provide
        a 3-tuple of numbers, which will get cast to a dolfin.Constant, or
        a dolfin.Constant directly.
        Then a 3-tuple of strings (with keyword arguments if needed) that will
        get cast to a dolfin.Expression, or directly a dolfin.Expression.
        You can provide a numpy.ndarray of nodal values of shape (3*n,),
        where n is the number of nodes.
        Finally, you can pass a function (any callable object will do) which
        accepts the coordinates of the mesh as a numpy.ndarray of
        shape (3, n) and returns the magnetisation like that as well.

        You can call this method anytime during the simulation. However, when
        providing a numpy array during time integration, the use of
        the attribute m instead of this method is advised for performance
        reasons and because the attribute m doesn't normalise the vector.

        """
        if isinstance(value, tuple):
            if isinstance(value[0], str):
                # a tuple of strings is considered to be the ingredient
                # for a dolfin expression, whereas a tuple of numbers
                # would signify a constant
                val = df.Expression(value, degree=1, **kwargs)
            else:
                val = df.Constant(value)
            new_m = df.interpolate(val, self.S3)
        elif isinstance(value, (df.Constant, df.Expression)):
            new_m = df.interpolate(value, self.S3)
        elif isinstance(value, (list, np.ndarray)):
            new_m = df.Function(self.S3)
            new_m.vector()[:] = value
        elif hasattr(value, '__call__'):
            coords = np.array(zip(*self.S3.mesh().coordinates()))
            new_m = df.Function(self.S3)
            new_m.vector()[:] = value(coords).flatten()
        else:
            raise AttributeError
        new_m.vector()[:] = h.fnormalise(new_m.vector().array())
        self._m.vector()[:] = new_m.vector()[:]

        tmp = df.assemble(
            self._Ms_cell *
            df.dot(df.TestFunction(self.S3), df.Constant([1, 1, 1])) * df.dx)
        self._Ms.vector().set_local(tmp / self.vol)
        self._M.vector().set_local(self.Ms * self.m)
        self.prepare_solver()
Exemple #8
0
    def set_m(self, value, normalise=True, **kwargs):
        """
        Set the magnetisation (if `normalise` is True, it is automatically
        normalised to unit length).

        `value` can have any of the forms accepted by the function
        'finmag.util.helpers.vector_valued_function' (see its
        docstring for details).

        You can call this method anytime during the simulation. However, when
        providing a numpy array during time integration, the use of
        the attribute m instead of this method is advised for performance
        reasons and because the attribute m doesn't normalise the vector.

        """
        m0 = helpers.vector_valued_function(value, self.S3, normalise=False, **kwargs).vector().array()[self.v2d_xxx]

        if np.any(np.isnan(m0)):
            raise ValueError("Attempting to initialise m with NaN(s)")

        if normalise:
            m0 = helpers.fnormalise(m0)
        self._m_field.set_with_ordered_numpy_array_xxx(m0)
Exemple #9
0
C = 1.3e-11  # J/m exchange constant
Ms = 8.6e5  # A/m saturation magnetisation
t = 0  # s
H_app = (0, 0, 0)
H_app = interpolate(Constant(H_app), V)
pins = []

# Defaults overwrite from spinwave program
Ms = 1e6
alpha = 0.02

m0_tuple = (("1", "5 * pow(cos(pi * (x[0] * pow(10, 9) - 11) / 6), 3) \
                * pow(cos(pi * x[1] * pow(10, 9) / 6), 3)", "0"))

M = interpolate(Expression(m0_tuple), V)
M.vector()[:] = h.fnormalise(M.vector().array())

m_arr = M.vector().array()
for i in xrange(nb_nodes):
    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
Exemple #10
0
 def m(self):
     mh = helpers.fnormalise(self.M)
     self._m.vector().set_local(mh)
     return mh