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 #2
0
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)
Exemple #3
0
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
Exemple #4
0
    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)
Exemple #5
0
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
Exemple #6
0
    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)
Exemple #7
0
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)
Exemple #8
0
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
Exemple #9
0
    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)
Exemple #10
0
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()
Exemple #11
0
 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())
Exemple #12
0
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
Exemple #13
0
    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()
Exemple #14
0
    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
Exemple #15
0
    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
Exemple #16
0
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)
Exemple #17
0
    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
Exemple #18
0
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
Exemple #20
0
 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())
Exemple #21
0
    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)
Exemple #22
0
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
Exemple #23
0
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
Exemple #24
0
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
Exemple #25
0
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
Exemple #26
0
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
Exemple #27
0
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
Exemple #28
0
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
Exemple #29
0
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
Exemple #30
0
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}