def test_hexagonal_demags_2D(): """ Comparison of the FFT approach for hexagonal meshes, named DemagHexagonal, where it is used a system with the double number of nodes along the x direction (i.e. a mesh with twice the number of nodes of the original mesh), against the full calculation of the Demag field """ # Number of atoms N = 15 a = 0.4 mesh = HexagonalMesh(a * 0.5, N, N, unit_length=1e-9, alignment='square') # Centre xc = (mesh.Lx * 0.5) yc = (mesh.Ly * 0.5) mu_s = 2 * const.mu_B sim = Sim(mesh) sim.mu_s = mu_s sim.set_m(lambda pos: m_init_2Dvortex(pos, (xc, yc))) # Brute force demag calculation sim.add(DemagFull()) sim.get_interaction('DemagFull').compute_field() sim.get_interaction('DemagFull').field DemagFull_energy = sim.compute_energy() / const.meV # Demag using the FFT approach and a larger mesh sim2 = Sim(mesh) sim2.mu_s = mu_s sim2.set_m(lambda pos: m_init_2Dvortex(pos, (xc, yc))) sim2.add(DemagHexagonal()) sim2.get_interaction('DemagHexagonal').compute_field() sim2.compute_energy() demag_2fft_energy = sim2.compute_energy() / const.meV # We compare both energies scaled in meV assert (DemagFull_energy - demag_2fft_energy) < 1e-10
exch = UniformExchange(args.J * const.meV) sim.add(exch) dmi = DMI(D=(args.D * const.meV), dmi_type='interfacial') sim.add(dmi) if args.B: zeeman = Zeeman((0, 0, args.B)) sim.add(zeeman, save_field=True) if args.k_u: # Uniaxial anisotropy along + z-axis sim.add(Anisotropy(args.k_u * const.meV, axis=[0, 0, 1])) if args.Demag: sim.add(DemagHexagonal()) # ----------------------------------------------------------------------------- # Debug Information ----------------------------------------------------------- print('Saturation Magnetisation: {} mu_B'.format(args.mu_s)) print('Exchange constant: {} meV'.format(args.J)) print('DMI constant: {} meV'.format(args.D)) if args.k_u: print('Anisotropy constant: {} meV'.format(args.k_u)) if args.B: print('Zeeman field: (0, 0, {}) T'.format(args.B / mu0)) print('--------------------------------------') # -----------------------------------------------------------------------------
def generate_simulation(self, do_precession=False, gamma=1.76e11, load_mu_s=False): """ Generates a simulation according to the self.simulation option gamma :: Default is the free electron gyromagnetic ratio load_mu_s :: A file path to a NPY file with the mu_s information (only for the experimental_sample option) """ # Mesh ---------------------------------------------------------------- if self.simulation == 'experimental_sample': self.sim_from_image = sfi.sim_from_image(sfi.default_image) if not load_mu_s: self.sim_from_image.generate_magnetic_moments(mu_s=self.mu_s) else: self.sim_from_image.generate_magnetic_moments( load_file=load_mu_s) self.sim = self.sim_from_image.sim elif self.simulation == '2D_square': # A square sized hexagonal mesh mesh = HexagonalMesh( self.mesh_a * 0.5, self.mesh_nx, self.mesh_ny, # periodicity=(True, True), alignment='square', unit_length=1e-9) self.sim = Sim(mesh) # If we use polygon mesh tools, we can use a hexagon shaped mesh # self.sim.mu_s = self.mu_s_in_hexagon self.sim.mu_s = self.mu_s elif self.simulation == '1D_chain': # A 1D chain using a cuboid mesh mesh = CuboidMesh( dx=self.mesh_a, nx=self.mesh_nx, ny=1, nz=1, # periodicity=(True, True), unit_length=1e-9) self.sim = Sim(mesh) # If we use polygon mesh tools, we can use a hexagon shaped mesh # self.sim.mu_s = self.mu_s_in_hexagon self.sim.mu_s = self.mu_s self.sim.driver.do_precession = do_precession self.sim.driver.gamma = gamma # Interactions -------------------------------------------------------- exch = UniformExchange(self.J) self.sim.add(exch) dmi = DMI(D=(self.D), dmi_type='interfacial') self.sim.add(dmi) zeeman = Zeeman((self.B[0], self.B[1], self.B[2])) self.sim.add(zeeman, save_field=True) if self.ku: # Uniaxial anisotropy along + z-axis self.sim.add(Anisotropy(self.ku, axis=[0, 0, 1])) if self.Demag: print('Using Demag!') self.sim.add(DemagHexagonal()) # --------------------------------------------------------------------- self.hls = np.ones_like(self.sim.spin.reshape(-1, 3)) self.rgbs = np.ones((self.sim.spin.reshape(-1, 3).shape[0], 4))
def hysteresis_loop(config_file, D=1.56, J=5.88, k_u=0.41, mu_s=3, B=(0, 0, 0), Demag=None, ): """ The config file must have the following parameters: D J k_u mu_s :: Magnitude in Bohr magneton units. A file path can be specified to load a NPY file with the mu_s values, when using the mesh_from_image option Demag :: Set to True for Demag sim_name :: Simulation name initial_state :: A function or a npy file hysteresis_steps :: mesh_from_image :: [IMAGE_PATH, xmin, xmax, ymin, ymax] hexagonal_mesh :: [nx, ny, a] PBC_2D :: Set to True for Periodic boundaries pin_boundaries :: Set to True to pin the spins at the boundaries. Their orientations are already given from the initial_state NPY file llg_dt :: llg_stopping_dmdt :: llg_max_steps :: llg_do_precession :: False as default llg_alpha :: 0.01 as default """ # Parameters -------------------------------------------------------------- cf = {} # execfile(config_file, cf) # Python3: exec(open(config_file).read(), cf) D = cf["D"] * const.meV J = cf["J"] * const.meV k_u = cf["k_u"] * const.meV if isinstance(cf["mu_s"], int) or isinstance(cf["mu_s"], float): mu_s = cf["mu_s"] * const.mu_B if isinstance(cf["initial_state"], str): init_state = np.load(cf["initial_state"]) elif isinstance(cf["initial_state"], types.FunctionType): init_state = cf["initial_state"] # Set up default arguments default_args = {"mesh_alignment": 'diagonal', "mesh_unit_length": 1e-9, "llg_dt": 1e-11, "llg_stopping_dmdt": 1e-2, "llg_max_steps": 4000, "llg_do_precession": False, "llg_alpha": 0.01 } for key in default_args.keys(): if not cf.get(key): print(default_args[key]) cf[key] = default_args[key] # Simulation object ------------------------------------------------------- if cf.get("hexagonal_mesh"): if not cf["PBC_2D"]: mesh = HexagonalMesh(cf["hexagonal_mesh"][2] * 0.5, int(cf["hexagonal_mesh"][0]), int(cf["hexagonal_mesh"][1]), alignment=cf["mesh_alignment"], unit_length=cf["mesh_unit_length"] ) else: mesh = HexagonalMesh(cf["hexagonal_mesh"][2] * 0.5, int(cf["hexagonal_mesh"][0]), int(cf["hexagonal_mesh"][1]), periodicity=(True, True), alignment=cf["mesh_alignment"], unit_length=cf["mesh_unit_length"] ) sim = Sim(mesh, name=cf["sim_name"]) elif cf.get("mesh_from_image"): sim_from_image = sfi.sim_from_image( cf["mesh_from_image"][0], image_range=[float(cf["mesh_from_image"][1]), float(cf["mesh_from_image"][2]), float(cf["mesh_from_image"][3]), float(cf["mesh_from_image"][4]) ], sim_name=cf["sim_name"] ) if isinstance(cf["mu_s"], str): sim_from_image.generate_magnetic_moments(load_file=cf["mu_s"]) else: sim_from_image.generate_magnetic_moments(mu_s=(mu_s)) sim = sim_from_image.sim elif cf.get("truncated_triangle"): if len(cf["truncated_triangle"]) == 3: sim_triangle = TruncatedTriangleSim( cf["truncated_triangle"][0], # L cf["truncated_triangle"][1], # offset cf["truncated_triangle"][2], # a cf["mu_s"], # mu_s name=cf["sim_name"] ) elif len(cf["truncated_triangle"]) == 5: sim_triangle = TruncatedTriangleSim( cf["truncated_triangle"][0], # L [float(offs) for offs in cf["truncated_triangle"][1:4]], # offsets cf["truncated_triangle"][4], # a cf["mu_s"], # mu_s name=cf["sim_name"] ) sim = sim_triangle.sim elif cf.get("hexagon"): sim_hexagon = HexagonSim(cf["hexagon"][0], # R cf["hexagon"][1], # a cf["mu_s"], # mu_s name=cf["sim_name"] ) sim = sim_hexagon.sim # Initial state sim.set_m(init_state) sim.driver.do_precession = cf["llg_do_precession"] sim.driver.alpha = cf["llg_alpha"] # Material parameters ----------------------------------------------------- if cf.get("hexagonal_mesh"): sim.mu_s = mu_s exch = UniformExchange(J) sim.add(exch) dmi = DMI(D=(D), dmi_type='interfacial') sim.add(dmi) zeeman = Zeeman((0, 0, 0)) sim.add(zeeman, save_field=True) if k_u: # Uniaxial anisotropy along + z-axis sim.add(Anisotropy(k_u, axis=[0, 0, 1])) if cf.get("Demag"): print('Using Demag!') sim.add(DemagHexagonal()) # Pin boundaries ---------------------------------------------------------- # We will correct the spin directions according to the specified argument, # in case the spins at the boundaries are pinned if cf.get('pin_boundaries'): ngbs_filter = np.zeros(sim.pins.shape[0]) # Filter rows by checking if any of the elements is less than zero # This means that if any of the neighbours of the i-th lattice site is # -1, we pin the spin direction at that site ngbs_filter = np.any(sim.mesh.neighbours < 0, axis=1, out=ngbs_filter) sim.set_pins(ngbs_filter) # Hysteresis -------------------------------------------------------------- for ext in ['npys', 'vtks']: if not os.path.exists('{}/{}'.format(ext, cf["sim_name"])): os.makedirs('{}/{}'.format(ext, cf["sim_name"])) for ext in ['txts', 'dats']: if not os.path.exists('{}/'.format(ext)): os.makedirs('{}'.format(ext)) Brange = cf["hysteresis_steps"] print('Computing for Fields:', Brange) # We will save the hysteresis steps on this file with every row as: # step_number field_in_Tesla hystfile = '{}_hyst_steps.dat'.format(cf["sim_name"]) # If the file already exists, we will append the new steps, otherwise # we just create a new file (useful for restarting simulations) if not os.path.exists(hystfile): nsteps = 0 fstate = 'w' else: # Move old txt file from the previous simulation, appending an _r # everytime a simulation with the same name is started txtfile = [f for f in os.listdir('.') if f.endswith('txt')][0] txtfile = re.search(r'.*(?=\.txt)', txtfile).group(0) shutil.move(txtfile + '.txt', txtfile + '_r.txt') nsteps = len(np.loadtxt(hystfile)) fstate = 'a' f = open(hystfile, fstate) for i, B in enumerate(Brange): sim.get_interaction('Zeeman').update_field(B) sim.driver.relax(dt=cf["llg_dt"], stopping_dmdt=cf["llg_stopping_dmdt"], max_steps=cf["llg_max_steps"], save_m_steps=None, save_vtk_steps=None ) print('Saving NPY for B = {}'.format(B)) np.save('npys/{0}/step_{1}.npy'.format(cf["sim_name"], i + nsteps), sim.spin) sim.driver.save_vtk() shutil.move('{}_vtks/m_{}.vtk'.format(cf["sim_name"], str(sim.driver.step).zfill(6) ), 'vtks/{0}/step_{1}.vtk'.format(cf["sim_name"], i + nsteps) ) f.write('{} {} {} {}\n'.format(i + nsteps, B[0], B[1], B[2], ) ) f.flush() sim.driver.reset_integrator() os.rmdir('{}_vtks'.format(cf["sim_name"])) shutil.move('{}.txt'.format(cf["sim_name"]), 'txts/') shutil.move(hystfile, 'dats/') f.close()