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)
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
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)
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))
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()
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()
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())