Exemplo n.º 1
0
    def set_parameters(self,
                       J_profile=(1e10, 0, 0),
                       P=0.5,
                       D=2.5e-4,
                       lambda_sf=5e-9,
                       lambda_J=1e-9,
                       speedup=1):

        self._J = helpers.vector_valued_function(J_profile, self.S3)
        self.J = self._J.vector().array()
        self.compute_gradient_matrix()
        self.H_gradm = df.PETScVector()

        self.P = P

        self.D = D / speedup
        self.lambda_sf = lambda_sf
        self.lambda_J = lambda_J

        self.tau_sf = lambda_sf**2 / D * speedup
        self.tau_sd = lambda_J**2 / D * speedup

        self.compute_laplace_matrix()
        self.H_laplace = df.PETScVector()

        self.nodal_volume_S3 = nodal_volume(self.S3)
Exemplo n.º 2
0
def skyrmion_number_density_function(self):
    """
    This function returns the skyrmion number density function calculated
    from the spin texture in this simulation instance. This function can be
    probed to determine the local nature of the skyrmion number.
    """

    integrand = -0.25 / np.pi * df.dot(
        self.m_field.f,
        df.cross(df.Dx(self.m_field.f, 0), df.Dx(self.m_field.f, 1)))

    # Build the function space of the mesh nodes.
    dofmap = self.S3.dofmap()
    S1 = df.FunctionSpace(self.S3.mesh(),
                          "Lagrange",
                          1,
                          constrained_domain=dofmap.constrained_domain)

    # Find the skyrmion density at the mesh nodes using the above function
    # space.
    nodalSkx = df.dot(integrand, df.TestFunction(S1)) * df.dx
    nodalVolumeS1 = nodal_volume(S1, self.unit_length)
    skDensity = df.assemble(nodalSkx).array() * self.unit_length\
        ** self.S3.mesh().topology().dim() / nodalVolumeS1

    # Build the skyrmion number density dolfin function from the skDensity
    # array.
    skDensityFunc = df.Function(S1)
    skDensityFunc.vector()[:] = skDensity
    return skDensityFunc
Exemplo n.º 3
0
    def compute_gradient_matrix(self):
        """
        compute (J nabla) m , we hope we can use a matrix M such that M*m = (J nabla)m.

        """
        tau = df.TrialFunction(self.S3)
        sigma = df.TestFunction(self.S3)

        self.nodal_volume_S3 = nodal_volume(self.S3) * self.unit_length

        dim = self.S3.mesh().topology().dim()

        ty = tz = 0

        tx = self._J[0] * df.dot(df.grad(tau)[:, 0], sigma)

        if dim >= 2:
            ty = self._J[1] * df.dot(df.grad(tau)[:, 1], sigma)

        if dim >= 3:
            tz = self._J[2] * df.dot(df.grad(tau)[:, 2], sigma)

        self.gradM = df.assemble((tx + ty + tz) * df.dx)
Exemplo n.º 4
0
    def setup(self, E_integrand, m, Ms, unit_length=1):
        """
        Function to be called after the energy object has been constructed.

        *Arguments*

            E_integrand
                dolfin form that represents the term inside the energy
                integral, as a function of m (and maybe Ms) if assembled

            m
                magnetisation field (usually normalised)

            Ms
                Saturation magnetisation (scalar, or scalar dolfin function)

            unit_length
                real length of 1 unit in the mesh

        """
        ###license_placeholder###

        assert isinstance(m, Field)
        assert isinstance(Ms, Field)

        self.E_integrand = E_integrand
        dofmap = m.mesh_dofmap()
        self.S1 = df.FunctionSpace(
            m.mesh(), "CG", 1, constrained_domain=dofmap.constrained_domain)
        self.m = m
        self.Ms = Ms
        self.unit_length = unit_length

        self.E = E_integrand * df.dx
        self.nodal_E = df.dot(E_integrand, df.TestFunction(self.S1)) * df.dx
        self.dE_dm = df.Constant(-1.0 / mu0) * \
            df.derivative(E_integrand / self.Ms.f * df.dx, self.m.f)

        self.dim = m.mesh_dim()
        self.nodal_volume_S1 = nodal_volume(self.S1, self.unit_length)
        # Same as nodal_volume_S1, just three times in an array
        # to have the same number of elements in the array as the
        # field to be able to divide it.
        self.nodal_volume_S3 = nodal_volume(self.m.functionspace)

        if self.method == 'box-assemble':
            self.__compute_field = self.__compute_field_assemble
        elif self.method == 'box-matrix-numpy':
            self.__setup_field_numpy()
            self.__compute_field = self.__compute_field_numpy
        elif self.method == 'box-matrix-petsc':
            self.__setup_field_petsc()
            self.__compute_field = self.__compute_field_petsc
        elif self.method == 'project':
            self.__setup_field_project()
            self.__compute_field = self.__compute_field_project
        elif self.method == 'direct':
            self.__compute_field = self.__compute_field_petsc
        else:
            logger.error("Can't create '{}' object with method '{}'. "
                         "Possible choices are "
                         "{}.".format(self.__class__.__name__, self.method,
                                      self._supported_methods))
            raise ValueError("Unsupported method '{}' should be one of "
                             "{}.".format(self.method,
                                          self._supported_methods))
Exemplo n.º 5
0
    def setup(self, m, Ms, unit_length=1):
        """
        Setup the FKDemag instance. Usually called automatically by the
        Simulation object.

        *Arguments*

        m: finmag.Field

            The unit magnetisation on a finite element space.

        Ms: float

            The saturation magnetisation in A/m.

        unit_length: float

            The length (in m) represented by one unit on the mesh. Default 1.

        """
        assert isinstance(m, Field)
        assert isinstance(Ms, Field)

        self.m = m
        self.Ms = Ms
        self.unit_length = unit_length
        self.S1 = df.FunctionSpace(self.m.mesh(), "Lagrange", 1)

        self._test1 = df.TestFunction(self.S1)
        self._trial1 = df.TrialFunction(self.S1)
        self._test3 = df.TestFunction(self.m.functionspace)
        self._trial3 = df.TrialFunction(self.m.functionspace)

        # for computation of energy
        self._nodal_volumes = nodal_volume(self.S1, unit_length)
        self._H_func = df.Function(m.functionspace)  # we will copy field into
        # this when we need the
        # energy
        self._E_integrand = -0.5 * mu0 * \
            df.dot(self._H_func, self.m.f * self.Ms.f)
        self._E = self._E_integrand * df.dx
        self._nodal_E = df.dot(self._E_integrand, self._test1) * df.dx
        self._nodal_E_func = df.Function(self.S1)

        # for computation of field and scalar magnetic potential
        self._poisson_matrix = self._poisson_matrix()
        self._laplace_zeros = df.Function(self.S1).vector()

        # determine the solver type to be used (Krylov or LU); if the kwarg
        # 'solver_type' is not provided, try to read the setting from the
        # .finmagrc file; use 'Krylov' if this fails.
        solver_type = self.solver_type
        if solver_type is None:
            solver_type = configuration.get_config_option(
                'demag', 'solver_type', 'Krylov')
        if solver_type == 'None':  # if the user set 'solver_type = None' in
            # the .finmagrc file, solver_type will be a
            # string so we need to catch this here.
            solver_type = 'Krylov'
        logger.debug("Using {} solver for demag.".format(solver_type))

        if solver_type == 'Krylov':
            self._poisson_solver = df.KrylovSolver(
                self._poisson_matrix.copy(), self.parameters['phi_1_solver'],
                self.parameters['phi_1_preconditioner'])
            self._poisson_solver.parameters.update(self.parameters['phi_1'])
            self._laplace_solver = df.KrylovSolver(
                self.parameters['phi_2_solver'],
                self.parameters['phi_2_preconditioner'])
            self._laplace_solver.parameters.update(self.parameters['phi_2'])
            # We're setting 'same_nonzero_pattern=True' to enforce the
            # same matrix sparsity pattern across different demag solves,
            # which should speed up things.
            #self._laplace_solver.parameters["preconditioner"][
            #    "structure"] = "same_nonzero_pattern"
        elif solver_type == 'LU':
            self._poisson_solver = df.LUSolver(self._poisson_matrix.copy())
            self._laplace_solver = df.LUSolver()
            self._poisson_solver.parameters["reuse_factorization"] = True
            self._laplace_solver.parameters["reuse_factorization"] = True
        else:
            raise ValueError(
                "Argument 'solver_type' must be either 'Krylov' or 'LU'. "
                "Got: '{}'".format(solver_type))

        with fk_timer('compute BEM'):
            if not hasattr(self, "_bem"):
                if self.macrogeometry is not None:
                    Ts = self.macrogeometry.compute_Ts(self.m.mesh())
                    pbc = BMatrixPBC(self.m.mesh(), Ts)
                    self._b2g_map = np.array(pbc.b2g_map, dtype=np.int)
                    self._bem = pbc.bm
                else:
                    self._bem, self._b2g_map = compute_bem_fk(
                        df.BoundaryMesh(self.m.mesh(), 'exterior', False))
        logger.debug(
            "Boundary element matrix uses {:.2f} MB of memory.".format(
                self._bem.nbytes / 1024.**2))
        # solution of inhomogeneous Neumann problem
        self._phi_1 = df.Function(self.S1)
        # solution of Laplace equation inside domain
        self._phi_2 = df.Function(self.S1)
        self._phi = df.Function(self.S1)  # magnetic potential phi_1 + phi_2

        # To be applied to the vector field m as first step of computation of
        # _phi_1.  This gives us div(M), which is equal to Laplace(_phi_1),
        # equation which is then solved using _poisson_solver.
        self._Ms_times_divergence = df.assemble(
            self.Ms.f * df.inner(self._trial3, df.grad(self._test1)) * df.dx)

        # we move the boundary condition here to avoid create a instance each
        # time when compute the magnetic potential
        self.boundary_condition = df.DirichletBC(self.S1, self._phi_2,
                                                 df.DomainBoundary())
        self.boundary_condition.apply(self._poisson_matrix)

        self._setup_gradient_computation()
Exemplo n.º 6
0
    def setup(self, DG3, m, Ms, unit_length=1):
        """
        Setup the FKDemag instance. Usually called automatically by the Simulation object.

        *Arguments*

        S3: dolfin.VectorFunctionSpace

            The finite element space the magnetisation is defined on.

        m: dolfin.Function on S3

            The unit magnetisation.

        Ms: float

            The saturation magnetisation in A/m.

        unit_length: float

            The length (in m) represented by one unit on the mesh. Default 1.

        """
        self.m = m
        self.Ms = Ms
        self.unit_length = unit_length

        mesh = DG3.mesh()
        self.S1 = df.FunctionSpace(mesh, "Lagrange", 1)
        self.S3 = df.VectorFunctionSpace(mesh, "Lagrange", 1)
        self.dim = mesh.topology().dim()

        self.n = df.FacetNormal(mesh)

        self.DG3 = DG3

        self._test1 = df.TestFunction(self.S1)
        self._trial1 = df.TrialFunction(self.S1)
        self._test3 = df.TestFunction(self.S3)
        self._trial3 = df.TrialFunction(self.S3)
        self._test_dg3 = df.TestFunction(self.DG3)
        self._trial_dg3 = df.TrialFunction(self.DG3)

        # for computation of energy
        self._nodal_volumes = nodal_volume(self.S1, unit_length)
        self._H_func = df.Function(
            DG3)  # we will copy field into this when we need the energy
        self._E_integrand = -0.5 * mu0 * df.dot(self._H_func, self.m * self.Ms)
        self._E = self._E_integrand * df.dx
        self._nodal_E = df.dot(self._E_integrand, self._test1) * df.dx
        self._nodal_E_func = df.Function(self.S1)

        # for computation of field and scalar magnetic potential
        self._poisson_matrix = self._poisson_matrix()
        self._poisson_solver = df.KrylovSolver(
            self._poisson_matrix, self.parameters['phi_1_solver'],
            self.parameters['phi_1_preconditioner'])
        self._poisson_solver.parameters.update(self.parameters['phi_1'])
        self._laplace_zeros = df.Function(self.S1).vector()
        self._laplace_solver = df.KrylovSolver(
            self.parameters['phi_2_solver'],
            self.parameters['phi_2_preconditioner'])
        self._laplace_solver.parameters.update(self.parameters['phi_2'])
        self._laplace_solver.parameters["preconditioner"][
            "same_nonzero_pattern"] = True
        with fk_timed('compute BEM'):
            if not hasattr(self, "_bem"):
                self._bem, self._b2g_map = compute_bem_fk(
                    df.BoundaryMesh(mesh, 'exterior', False))
        self._phi_1 = df.Function(
            self.S1)  # solution of inhomogeneous Neumann problem
        self._phi_2 = df.Function(
            self.S1)  # solution of Laplace equation inside domain
        self._phi = df.Function(self.S1)  # magnetic potential phi_1 + phi_2

        # To be applied to the vector field m as first step of computation of _phi_1.
        # This gives us div(M), which is equal to Laplace(_phi_1), equation
        # which is then solved using _poisson_solver.
        self._Ms_times_divergence = df.assemble(
            self.Ms * df.inner(self._trial_dg3, df.grad(self._test1)) * df.dx)

        self._setup_gradient_computation()
Exemplo n.º 7
0
    def setup(self, m, Ms, unit_length=1):

        self.m = m
        self.Ms = Ms
        self.unit_length = unit_length

        mesh = m.mesh()
        self.S1 = df.FunctionSpace(mesh, "Lagrange", 1)
        self.dim = mesh.topology().dim()

        self._test1 = df.TestFunction(self.S1)
        self._trial1 = df.TrialFunction(self.S1)
        self._test3 = df.TestFunction(self.m.functionspace)
        self._trial3 = df.TrialFunction(self.m.functionspace)

        # for computation of energy
        self._nodal_volumes = nodal_volume(self.S1, unit_length)
        # we will copy field into this when we need the energy
        self._H_func = df.Function(self.m.functionspace)
        self._E_integrand = -0.5 * mu0 * \
            df.dot(self._H_func, self.m.f * self.Ms.f)
        self._E = self._E_integrand * df.dx
        self._nodal_E = df.dot(self._E_integrand, self._test1) * df.dx
        self._nodal_E_func = df.Function(self.S1)

        # for computation of field and scalar magnetic potential
        self._poisson_matrix = self._poisson_matrix()
        self._poisson_solver = df.KrylovSolver(
            self._poisson_matrix.copy(), self.parameters['phi_1_solver'],
            self.parameters['phi_1_preconditioner'])
        self._poisson_solver.parameters.update(self.parameters['phi_1'])
        self._laplace_zeros = df.Function(self.S1).vector()
        self._laplace_solver = df.KrylovSolver(
            self.parameters['phi_2_solver'],
            self.parameters['phi_2_preconditioner'])
        self._laplace_solver.parameters.update(self.parameters['phi_2'])
        # We're setting 'same_nonzero_pattern=True' to enforce the
        # same matrix sparsity pattern across different demag solves,
        # which should speed up things.
        self._laplace_solver.parameters["preconditioner"][
            "structure"] = "same_nonzero_pattern"

        # solution of inhomogeneous Neumann problem
        self._phi_1 = df.Function(self.S1)
        # solution of Laplace equation inside domain
        self._phi_2 = df.Function(self.S1)
        self._phi = df.Function(self.S1)  # magnetic potential phi_1 + phi_2

        # To be applied to the vector field m as first step of computation of _phi_1.
        # This gives us div(M), which is equal to Laplace(_phi_1), equation
        # which is then solved using _poisson_solver.
        self._Ms_times_divergence = df.assemble(
            self.Ms.f * df.inner(self._trial3, df.grad(self._test1)) * df.dx)

        # we move the bounday condition here to avoid create a instance each time when compute the
        # magnetic potential
        self.boundary_condition = df.DirichletBC(self.S1, self._phi_2,
                                                 df.DomainBoundary())
        self.boundary_condition.apply(self._poisson_matrix)

        self._setup_gradient_computation()

        self.mesh = self.m.mesh()

        self.bmesh = df.BoundaryMesh(self.mesh, 'exterior', False)
        #self.b2g_map = self.bmesh.vertex_map().array()
        self._b2g_map = self.bmesh.entity_map(0).array()

        self.compute_triangle_normal()

        self.__compute_bsa()

        fast_sum = FastSum(p=self.p,
                           mac=self.mac,
                           num_limit=self.num_limit,
                           correct_factor=self.correct_factor,
                           type_I=self.type_I)

        coords = self.bmesh.coordinates()
        face_nodes = np.array(self.bmesh.cells(), dtype=np.int32)

        fast_sum.init_mesh(coords, self.t_normals, face_nodes, self.vert_bsa)
        self.fast_sum = fast_sum

        self.phi2_b = np.zeros(self.bmesh.num_vertices())