class sim_from_image(object): def __init__(self, image_path, image_range=[0, 21.03, 0, 17.79], mesh_a=0.2715, shells=1, # mesh_nx=79, # mesh_ny=78 sim_name='unnamed', bg_colour=(1., 1., 1.), driver='llg' ): """ Generate an object on top of the fidimag simulation class, to generate an atomistic simulation using a hexagonal mesh, where the magnetic moments are populated in the mesh according to the pixels in a PNG image, located in *image_path* The mesh size is generated automatically from the dimensions provided for the image (the *image_range* array), so that the mesh covers the whole picture. It is recommended that the image has white borders since mesh points away from the picture range will take the values from the closest boundary point (see the populate_mesh function) image_range :: An array [xmin, xmax, ymin, ymax] with the ranges of the image, given in nm """ # print 'Mesh spacings are: nx = {}, ny = {}'.format(mesh_a, # mesh_a * np.sqrt(3) * 0.5) self.image_path = image_path self.image_range = image_range mesh_nx = int(np.around(np.abs(image_range[1] - image_range[0]) / mesh_a)) mesh_ny = int(np.around( np.abs(image_range[3] - image_range[2]) / (mesh_a * np.sqrt(3) * 0.5) )) self.mesh = HexagonalMesh(mesh_a * 0.5, mesh_nx, mesh_ny, # periodicity=(True, True), alignment='square', unit_length=1e-9, shells=shells ) self.sim = Sim(self.mesh, name=sim_name, driver=driver) # Generate the 2D matrix with the image # If the image is B/W, every entry will have either [1., 1., 1.] # or [0., 0., 0.] representing the RGB data from every pixel self.image_data = mpl.image.imread(self.image_path) # We will create two new matrices, with the X and Y coordinates # of every point in the image matrix, using the range # provided in the arguments. For doing this, we use a meshgrid, which # automatically generates a 2D mesh of coordinates. The range # in the Y coordinates matrix is reversed since the image matrix # starts from top to bottom to generate the picture. # The shape is [n_rows, n_columns] --> (ny, nx) self.image_coords_x = np.linspace(self.image_range[0], self.image_range[1], self.image_data.shape[1]) self.image_coords_y = np.linspace(self.image_range[3], self.image_range[2], self.image_data.shape[0]) (self.image_coords_x, self.image_coords_y) = np.meshgrid(self.image_coords_x, self.image_coords_y) self.bg_colour = bg_colour def populate_mesh(self, pos, mu_s): """ Magnetisation scalar field function to fill the hexagonal mesh according to the position of the pixels in the image matrix. For a given point of the mesh, this function computes the minimum distance to a point in the image matrix, whose coordinates are given by the mesh matrices image_coords_x and image_coords_y Then it finds the position in the matrix (i, j), where the closest point lies and check if that pixel is black (for now we assume any other pixel is white, i.e. [1., 1., 1.]). If so, it returns a magnetic moment value that point Very far away points from the range of the image, the points in the mesh will take the values from the boundary points. Thus it is recommended to use an image with a white border (?) """ x, y = pos[0], pos[1] # Distance in X and Y (Euclidean) dist_x = self.image_coords_x - x dist_y = self.image_coords_y - y # A matrix with the distances of this point (x, y) with respect # to every point in the image matrix coordinates dist = np.sqrt(dist_x ** 2 + dist_y ** 2) i, j = np.where(dist == np.min(dist)) i = i[0] j = j[0] # Set material for any colour that is not the background colour if not (abs(self.image_data[i, j][0] - self.bg_colour[0]) < 1e-5 and abs(self.image_data[i, j][1] - self.bg_colour[1]) < 1e-5 and abs(self.image_data[i, j][2] - self.bg_colour[2]) < 1e-5): return mu_s else: return 0 def generate_magnetic_moments(self, mu_s=1, load_file=False, save_file=False ): """ save :: A file name to save the magnetic moments array load :: A path to a file with the array of magnetic moments (obtained with the *save* option) """ if not load_file: self.sim.set_mu_s(lambda pos: self.populate_mesh(pos, mu_s)) else: self.sim.set_mu_s(np.load(load_file)) if save_file: np.save(save_file, self.sim.mu_s) def plot_mesh(self, figsize=(20, 7.5)): """ Plot the mesh on top of the image """ fig = plt.figure(figsize=figsize) ax1 = fig.add_subplot(111) ax1.scatter(self.mesh.coordinates[:, 0], self.mesh.coordinates[:, 1], c=self.sim.mu_s, s=35, marker='h', lw=0, cmap=mpl.cm.Reds, # vmin=0, vmax=self.sim.mu_s, # gridsize=(63, 41) ) ax1.imshow(self.image_data, extent=self.image_range) ax1.set_xlim([self.image_range[0], self.image_range[1]] ) ax1.set_ylim([self.image_range[2], self.image_range[3]] ) # ......................................................................... def populate_spin_orientations(self, pos, orientations, colours): """ """ x, y = pos[0], pos[1] # Distance in X and Y (Euclidean) dist_x = self.image_coords_x - x dist_y = self.image_coords_y - y # A matrix with the distances of this point (x, y) with respect # to every point in the image matrix coordinates dist = np.sqrt(dist_x ** 2 + dist_y ** 2) i, j = np.where(dist == np.min(dist)) i = i[0] j = j[0] for k, spin_direction in enumerate(orientations): if (abs(self.image_data[i, j][0] - colours[k][0]) < 1e-5 and abs(self.image_data[i, j][1] - colours[k][1]) < 1e-5 and abs(self.image_data[i, j][2] - colours[k][2]) < 1e-5): return spin_direction return (0., 0., 0.) def generate_magnetic_configuration(self, orientations, colours): """ """ self.sim.set_m(lambda pos: self.populate_spin_orientations(pos, orientations, colours))
elif method == 'linear_interpolations': return base_folder(D) elif method == 'climbing': return base_folder_ci(D, climbing_image[D]) # Data ------------------------------------------------------------------------ mesh = HexagonalMesh(0.125, 320, 185, unit_length=1e-9, alignment='square') # Generate the linear interpolations of the NEBM band from every DMI value from # the arguments for D in args.D_list: sim = Sim(mesh) sim.set_mu_s(0.846 * const.mu_B) sim.add(DemagHexagonal()) sim.add(UniformExchange(J=27.026 * const.meV)) sim.add(DMI(DMIc[D] * const.meV, dmi_type='interfacial')) sim.add(Anisotropy(0.0676 * const.meV, axis=[0, 0, 1])) images = [ np.load(os.path.join(methods(args.method, D), _file)) for _file in sorted(os.listdir(methods(args.method, D)), key=lambda f: int(re.search('\d+', f).group(0))) ] nebm = NEBM_Geodesic(sim, images, spring_constant=1e4) l, E = nebm.compute_polynomial_approximation(500) # Distances for every image from the first image (0th image)