def __init__(self, mesh, spin, magnetisation, magnetisation_inv, field, pins, interactions, name, data_saver): # --------------------------------------------------------------------- # These are (ideally) references to arrays taken from the Simulation # class. Variables with underscore are arrays changed by a property in # the simulation class self.mesh = mesh self.spin = spin # A reference to either mu_s or Ms to use a common lib for this # minimiser self._magnetisation = magnetisation self._magnetisation_inv = magnetisation_inv self.field = field self._pins = pins self.interactions = interactions # Strings are not referenced, this is a copy: self.name = name self.data_saver = data_saver # --------------------------------------------------------------------- # Variables defined in this class self.spin_last = np.ones_like(self.spin) self.n = self.mesh.n # VTK saver for the magnetisation/spin field self.VTK = VTK(self.mesh, directory='{}_vtks'.format(self.name), filename='m') self.scale = 1.
def __init__(self, mesh, name='unnamed', directory=None): self.name = name if directory is None: self.directory = '{}_vtks'.format(name) else: self.directory = directory # Initiate a VTK object self.VTK = VTK(mesh, directory=self.directory, filename=name)
def __init__(self, sim, initial_images, interpolations=None, spring_constant=1e5, name='unnamed', climbing_image=None, dof=2, openmp=False): self.openmp = openmp # Degrees of Freedom per spin self.dof = dof self.sim = sim self.mesh = self.sim.mesh self.name = name # Number of spins in the system self.n_spins = len(self.mesh.coordinates) # We will use this filter to know which sites of the system has # material, i.e. M_s or mu_s > 0 and norm(m) = 1 if self.sim._micromagnetic: self._material = np.repeat(self.sim.Ms, self.dof) > 1e-10 else: # We will assume, for now, that the magnetic moment in atomistic # simulations is in units of mu_B self._material = np.repeat(self.sim.mu_s / const.mu_B, self.dof) > 1e-10 # self._material = self._material # For C, we use 1 and 0s self._material_int = np.copy(self._material).astype(np.int32) self.n_dofs_image_material = np.sum(self._material) # VTK saver for the magnetisation/spin field -------------------------- self.VTK = VTK(self.mesh, directory='vtks'.format(self.name), filename='image') # Functions to convert the energy band coordinates to Cartesian # coordinates when saving VTK and NPY files We assume Cartesian # coordinates by default, i.e. we do not transform anything self.files_convert_f = None # Initial states ------------------------------------------------------ # We assume the extremes are fixed self.initial_images = initial_images if interpolations: self.interpolations = interpolations else: self.interpolations = [0 for i in range(len(initial_images) - 1)] # Number of images with/without the extremes self.n_images = len(self.initial_images) + np.sum(self.interpolations) self.n_images_inner_band = self.n_images - 2 # Number of degrees of freedom per image self.n_dofs_image = (self.dof * self.n_spins) # Total number of degrees of freedom in the string/band self.n_band = self.n_images * self.n_dofs_image # Spring constant ----------------------------------------------------- # Spring constant (we could use an array in the future) self.k = spring_constant * np.ones(self.n_images) # Set to True to update spring constant values relative to the energies # (TESTING) self.variable_k = False self.dk = 1 # Climbing Image ------------------------------------------------------ # Set a list with the images where 1 is for climbing image and 0 for # normal self._climbing_image = np.zeros(self.n_images, dtype=np.int32) if climbing_image is not None: self.climbing_image = climbing_image # Chain Method Arrays ------------------------------------------------- # We will initialise every array using the total number of images, # but we must have in mind that the images at the extrema of the band # are kept fixed, so we do not compute their gradient, tangents, etc. # This might be not memory efficient but the code is understood better # when we perform the loops when calculating the effective fields # and forces # The array containing every degree of freedom self.band = np.zeros(self.n_band) # The gradient with respect to the magnetisation (effective field) self.gradientE = np.zeros_like(self.band) # The effective force self.G = np.zeros_like(self.band) self.tangents = np.zeros_like(self.band) self.energies = np.zeros(self.n_images) self.spring_force = np.zeros_like(self.band) self.distances = np.zeros(self.n_images - 1) # Total distance starting from image_0 # (first element shoud always be zero) self.path_distances = np.zeros(self.n_images) self.last_Y = np.zeros_like(self.band) # --------------------------------------------------------------------- # If the integrator uses an LLG-like equation to relax the energy band # we need to set this variable # This variable only affects the StepIntegrators, NOT Sundials self._llg_evolve = False # --------------------------------------------------------------------- # Factors for interpolating the energy band # For now we only have a 3rd order polynomial interp, thus we set # 4 factors self.interp_factors = np.zeros((4, self.n_images)) # Somehow we need to rescale the gradient by the right units. In the # case of micromag, we use mu0 * Ms, and for the atomistic case we # simply use mu_s. This must be related to the way we derive the # effective field to calculate the negative energy gradient, which is # the functional derivative of the energy if self.sim._micromagnetic: self.scale = np.repeat( self.mesh.dx * self.mesh.dy * self.mesh.dz * (self.mesh.unit_length**3.) * const.mu_0 * self.sim.Ms, 3) else: self.scale = np.repeat(self.sim.mu_s, 3) # --------------------------------------------------------------------- self.G_log = []
def __init__(self, mesh, spin, mu_s, mu_s_inv, field, pins, interactions, name, data_saver, use_jac, integrator='sundials'): super(AtomisticDriver, self).__init__() # These are (ideally) references to arrays taken from the Simulation # class. Variables with underscore are arrays changed by a property in # the simulation class self.mesh = mesh self.spin = spin self._mu_s = mu_s self._mu_s_inv = mu_s_inv # Only for LLG STT: (??) self.mu_s_const = 0 self.field = field self._pins = pins self.interactions = interactions # Strings are not referenced, this is a copy: self.name = name # The following are proper of the driver class: (see DriverBase) ------ # See also the set_default_options() function self.n = self.mesh.n self.n_nonzero = self.mesh.n # number of spins that are not zero # We check this in the set_Ms function self.initiate_variables(self.n) self.set_default_options() # Integrator options -------------------------------------------------- # In the old code, it seemed that self.sundials_jtn was not defined # anywhere else. Here, we will use the same integrators than in the # micromagnetic code, where we have self.sundials_jtimes instead of jtn self.set_integrator(integrator, use_jac) self.set_tols() # When initialising the integrator in the self.integrator call, the # CVOde class calls the set_initial_value function (with flag_m=0), # which initialises a new integrator and allocates memory in this # process. Now, when we set the magnetisation, we will use the same # memory setting this flag_m to 1, so instead of calling CVodeInit we # call CVodeReInit. If don't, memory is allocated in every call of # set_m self.flag_m = 1 # Factor for the dmdt magnitude in the relaxation function self._dmdt_factor = 1. # Savers -------------------------------------------------------------- # VTK saver for the magnetisation/spin field self.VTK = VTK(self.mesh, directory='{}_vtks'.format(self.name), filename='m') self.data_saver = data_saver
def test_save_scalar_field_hexagonal_mesh(tmpdir): mesh = HexagonalMesh(1, 3, 3) s = scalar_field(mesh, lambda r: r[0] + r[1]) vtk = VTK(mesh, directory=str(tmpdir), filename="scalar_hexagonal") vtk.save_scalar(s, name="s") assert same_as_ref(vtk.write_file(), REF_DIR)
def test_save_vector_field(tmpdir): mesh = CuboidMesh(4, 3, 2, 4, 3, 2) s = vector_field(mesh, lambda r: (r[0], r[1], r[2])) vtk = VTK(mesh, directory=str(tmpdir), filename="save_vector") vtk.save_vector(s, name="s") assert same_as_ref(vtk.write_file(), REF_DIR)
def __init__(self, mesh, spin, Ms, field, pins, interactions, name, data_saver, integrator='sundials', use_jac=False): super(MicroDriver, self).__init__() # These are (ideally) references to arrays taken from the Simulation # class. Variables with underscore are arrays changed by a property in # the simulation class self.mesh = mesh self.spin = spin self._Ms = Ms # Only for LLG STT: (??) self.Ms_const = 0 self.field = field self._pins = pins self.interactions = interactions # Strings are not referenced, this is a copy: self.name = name # The following are proper of the driver class: (see DriverBase) ------ # See also the set_default_options() function self.n = self.mesh.n self.n_nonzero = self.mesh.n # number of spins that are not zero # We check this in the set_Ms function self.initiate_variables(self.n) self.set_default_options() # Integrator options -------------------------------------------------- self.set_integrator(integrator, use_jac) # Savers -------------------------------------------------------------- # VTK saver for the magnetisation/spin field self.VTK = VTK(self.mesh, directory='{}_vtks'.format(self.name), filename='m') # Initialise the table for the data file with the simulation # information: self.data_saver = data_saver # This should not be necessary: # self.data_saver.entities['skx_num'] = { # 'unit': '<>', # 'get': lambda sim: sim.skyrmion_number(), # 'header': 'skx_num'} self.data_saver.entities['rhs_evals'] = { 'unit': '<>', 'get': lambda sim: self.integrator.rhs_evals(), 'header': 'rhs_evals' } self.data_saver.entities['real_time'] = { 'unit': '<s>', 'get': lambda _: time.time(), # seconds since epoch 'header': 'real_time' } self.data_saver.update_entity_order() # --------------------------------------------------------------------- # OOMMF convention is to check if the spins have moved by ~1 degree in # a nanosecond in order to stop a simulation, so we set this scale for # dm/dt # ONE_DEGREE_PER_NANOSECOND: self._dmdt_factor = (2 * np.pi / 360) / 1e-9
def __init__(self, sim, initial_images, interpolations=None, spring_constant=1e5, name='unnamed', climbing_image=None, dof=2, openmp=False): self.openmp = openmp # Degrees of Freedom per spin self.dof = dof self.sim = sim self.mesh = self.sim.mesh self.name = name # Number of spins in the system self.n_spins = len(self.mesh.coordinates) # Spring constant (we could use an array in the future) self.k = spring_constant # We will use this filter to know which sites of the system has # material, i.e. M_s or mu_s > 0 and norm(m) = 1 if self.sim._micromagnetic: self._material = np.repeat(self.sim.Ms, self.dof) > 1e-10 else: # We will assume, for now, that the magnetic moment in atomistic # simulations is in units of mu_B self._material = np.repeat(self.sim.mu_s / const.mu_B, self.dof) > 1e-10 # self._material = self._material # For C, we use 1 and 0s self._material_int = np.copy(self._material).astype(np.int32) self.n_dofs_image_material = np.sum(self._material) # VTK saver for the magnetisation/spin field -------------------------- self.VTK = VTK(self.mesh, directory='vtks'.format(self.name), filename='image') # Functions to convert the energy band coordinates to Cartesian # coordinates when saving VTK and NPY files We assume Cartesian # coordinates by default, i.e. we do not transform anything self.files_convert_f = None # Initial states ------------------------------------------------------ # We assume the extremes are fixed self.initial_images = initial_images if interpolations: self.interpolations = interpolations else: self.interpolations = [0 for i in range(len(initial_images) - 1)] # Number of images with/without the extremes self.n_images = len(self.initial_images) + np.sum(self.interpolations) self.n_images_inner_band = self.n_images - 2 # Number of degrees of freedom per image self.n_dofs_image = (self.dof * self.n_spins) # Total number of degrees of freedom in the NEBM band self.n_band = self.n_images * self.n_dofs_image # Climbing Image ------------------------------------------------------ if climbing_image is None: self.climbing_image = -1 elif climbing_image in range(self.n_images): self.climbing_image = climbing_image else: raise ValueError('The climbing image must be in the band. ' 'Specify a valid integer.') # NEBM Arrays --------------------------------------------------------- # We will initialise every array using the total number of images, # but we must have in mind that the images at the extrema of the band # are kept fixed, so we do not compute their gradient, tangents, etc. # This might be not memory efficient but the code is understood better # when we perform the loops when calculating the effective fields # and forces # The array containing every degree of freedom self.band = np.zeros(self.n_band) # The gradient with respect to the magnetisation (effective field) self.gradientE = np.zeros_like(self.band) # The effective force self.G = np.zeros_like(self.band) self.tangents = np.zeros_like(self.band) self.energies = np.zeros(self.n_images) self.spring_force = np.zeros_like(self.band) self.distances = np.zeros(self.n_images - 1) self.last_Y = np.zeros_like(self.band)
def test_save_scalar_field_hexagonal_mesh(): mesh = HexagonalMesh(1, 3, 3) s = scalar_field(mesh, lambda r: r[0] + r[1]) vtk = VTK(mesh, directory=OUTPUT_DIR, filename="scalar_hexagonal") vtk.save_scalar(s, name="s")
def test_save_vector_field(): mesh = CuboidMesh(4, 3, 2, 4, 3, 2) s = vector_field(mesh, lambda r: (r[0], r[1], r[2])) vtk = VTK(mesh, directory=OUTPUT_DIR, filename="save_vector") vtk.save_vector(s, name="s")
def test_save_scalar_field(): mesh = CuboidMesh(4, 3, 2, 4, 3, 2) s = scalar_field(mesh, lambda r: r[0] + r[1] + r[2]) vtk = VTK(mesh, directory=OUTPUT_DIR, filename="save_scalar") vtk.save_scalar(s, name="s")
def __init__(self, mesh, name='unnamed'): self.name = name # Initiate a VTK object self.VTK = VTK(mesh, directory='{}_vtks'.format(name), filename=name)