def create_ghost_particles(self, particles, mesh, domain, load_balance, comm, iteration=6): """Create initial ghost particles that hug the boundary after load balance """ rank = comm.Get_rank() size = comm.Get_size() # remove current (if any) ghost particles particles.remove_tagged_particles(ParticleTAGS.Ghost) current_size = particles.get_number_of_particles() # create initial ghost particles, particles is now larger # these particles are centered in neighboring boundary leaf # cells of the octree load_balance.create_boundary_particles(particles, rank) # reorder ghost in processors order: exterior have a process id of -1 so # their put before interior ghost particles ghost_proc = np.array(particles["process"][current_size:]) ind = np.argsort(ghost_proc) ghost_proc = ghost_proc[ind] for field in particles.properties.keys(): array = particles[field][current_size:] array[:] = array[ind] # allocate arrays for boundary indices indices = LongArray() corner_ghost = ParticleContainer() # sides boundary_indices = { "left" : LongArray(), "right" : LongArray(), "bottom" : LongArray(), "top" : LongArray(), "left-top" : LongArray(), "left-bottom" : LongArray(), "right-top" : LongArray(), "right-bottom" : LongArray() } send_particles = np.zeros(size, dtype=np.int32) recv_particles = np.zeros(size, dtype=np.int32) # create ghost interior and exterior particles by iteration, using # the mesh to extract the needed neighbors for i in range(iteration): # build the mesh mesh.tessellate() cumsum_neighbors = mesh["number of neighbors"].cumsum() #---------- create exterior ghost particles ----------# # create indices for ghost particles ghost_indices = np.arange(current_size, particles.get_number_of_particles()) # label current ghost as old ghost particles['tag'][ghost_indices] = ParticleTAGS.OldGhost # select exterior ghost particles exterior_ghost = ghost_proc == -1 exterior_ghost_indices = ghost_indices[exterior_ghost] if np.sum(exterior_ghost_indices) > 0: num_exterior_ghost = create_reflect_ghost(particles, boundary_indices, domain, exterior_ghost_indices, ghost_indices, mesh['neighbors'], mesh['number of neighbors'], cumsum_neighbors, -1) #---------- create interior ghost particles ----------# interior_ghost_indices = ghost_indices[~exterior_ghost] interior_ghost_proc = ghost_proc[~exterior_ghost] # bin processors - they are in order interior_ghost_proc_bin = np.bincount(interior_ghost_proc, minlength=size) send_particles[:] = 0 recv_particles[:] = 0 indices.reset() # collect the indices of particles to be export to each process cumsum_proc = interior_ghost_proc_bin.cumsum() for proc in range(size): if interior_ghost_proc_bin[proc] != 0: start = cumsum_proc[proc] - interior_ghost_proc_bin[proc] end = cumsum_proc[proc] send_particles[proc] = find_boundary_particles(indices, interior_ghost_indices[start:end], ghost_indices, mesh['neighbors'], mesh['number of neighbors'], cumsum_neighbors, False) # extract data to send and remove the particles send_data = {} for prop in particles.properties.keys(): send_data[prop] = np.ascontiguousarray(particles[prop][indices.get_npy_array()]) send_data["tag"][:] = ParticleTAGS.Ghost # how many particles are being sent from each process comm.Alltoall(sendbuf=send_particles, recvbuf=recv_particles) num_interior_ghost = np.sum(recv_particles) # resize arrays to give room for incoming particles sp = particles.get_number_of_particles() #particles.resize(current_size + num_exterior_ghost + num_interior_ghost) particles.extend(num_interior_ghost) exchange_particles(particles, send_data, send_particles, recv_particles, sp, comm) #---------- create exterior corner ghost particles ----------# indices.reset() send_particles[:] = 0 recv_particles[:] = 0 # clear out corner ghost corner_ghost.resize(0) if boundary_indices['left'].length > 0: export_reflect(particles, corner_ghost, boundary_indices["left"], indices, send_particles, 'x', domain.xmin, ghost_indices, mesh['neighbors'], mesh['number of neighbors'], cumsum_neighbors, load_balance, current_size, rank, size) if boundary_indices['right'].length > 0: export_reflect(particles, corner_ghost, boundary_indices["right"], indices, send_particles, 'x', domain.xmax, ghost_indices, mesh['neighbors'], mesh['number of neighbors'], cumsum_neighbors, load_balance, current_size, rank, size) if boundary_indices['bottom'].length > 0: export_reflect(particles, corner_ghost, boundary_indices["bottom"], indices, send_particles, 'y', domain.ymin, ghost_indices, mesh['neighbors'], mesh['number of neighbors'], cumsum_neighbors, load_balance, current_size, rank, size) if boundary_indices['top'].length > 0: export_reflect(particles, corner_ghost, boundary_indices["top"], indices, send_particles, 'y', domain.ymax, ghost_indices, mesh['neighbors'], mesh['number of neighbors'], cumsum_neighbors, load_balance, current_size, rank, size) #print rank, current_size, particles.get_number_of_particles(), current_size + num_exterior_ghost + num_interior_ghost sp = particles.get_number_of_particles() comm.Alltoall(sendbuf=send_particles, recvbuf=recv_particles) particles.extend(np.sum(recv_particles)) # to export corners have to be reorderd by process if corner_ghost.get_number_of_particles() > 0: ind = np.argsort(corner_ghost['process']) for field in corner_ghost.properties.keys(): array = corner_ghost[field] array[:] = array[ind] corner_ghost["process"][:] = -1 # exchange patch corners exchange_particles(particles, corner_ghost, send_particles, recv_particles, sp, comm) for bd in boundary_indices: boundary_indices[bd].reset() particles.remove_tagged_particles(ParticleTAGS.OldGhost) # put particles in process order for next loop ind = np.argsort(particles["process"][current_size:]) for field in particles.properties.keys(): array = particles[field][current_size:] array[:] = array[ind] ghost_proc = np.array(particles["process"][current_size:]) print 'rank:', rank, 'fraction of real to ghost:', (particles.get_number_of_particles()-current_size)*1.0/particles.get_number_of_particles()
class VoronoiMesh2D(VoronoiMeshBase): """ 2d voronoi mesh class """ def __init__(self, *arg, **kw): super(VoronoiMesh2D, self).__init__(*arg, **kw) self.dim = 2 self["neighbors"] = None self["number of neighbors"] = None self["faces"] = None self["voronoi vertices"] = None face_vars = { "area": "double", "velocity-x": "double", "velocity-y": "double", "normal-x": "double", "normal-y": "double", "com-x": "double", "com-y": "double", "pair-i": "longlong", "pair-j": "longlong", } self.faces = ParticleContainer(var_dict=face_vars) def compute_cell_info(self, particles): """ compute volume and center of mass of all real particles and compute areas, center of mass, normal face pairs, and number of faces for faces """ num_faces = mesh.number_of_faces(particles, self["neighbors"], self["number of neighbors"]) self.faces.resize(num_faces) vol = particles["volume"] xcom = particles["com-x"] ycom = particles["com-y"] vol[:] = 0.0 xcom[:] = 0.0 ycom[:] = 0.0 mesh.cell_face_info_2d(particles, self.faces, self["neighbors"], self["number of neighbors"], self["faces"], self["voronoi vertices"]) def tessellate(self, particles): """ create 2d voronoi tesselation from particle positions """ # create the tesselation vor = Voronoi(particles.T) # total number of particles num_particles = particles.shape[1] # create neighbor and face graph neighbor_graph = [[] for i in range(num_particles)] face_graph = [[] for i in range(num_particles)] # loop through each face collecting the two particles # that made that face as well as the face itself for i, face in enumerate(vor.ridge_points): p1, p2 = face neighbor_graph[p1].append(p2) neighbor_graph[p2].append(p1) face_graph[p1] += vor.ridge_vertices[i] face_graph[p2] += vor.ridge_vertices[i] # sizes for 1d graphs neighbor_graph_sizes = np.array([len(n) for n in neighbor_graph], dtype=np.int32) # graphs in 1d neighbor_graph = np.array(list( itertools.chain.from_iterable(neighbor_graph)), dtype=np.int32) face_graph = np.array(list(itertools.chain.from_iterable(face_graph)), dtype=np.int32) self["neighbors"] = neighbor_graph self["number of neighbors"] = neighbor_graph_sizes self["faces"] = face_graph self["voronoi vertices"] = vor.vertices
class VoronoiMesh2D(VoronoiMeshBase): """ 2d voronoi mesh class """ def __init__(self, particles): super(VoronoiMesh2D, self).__init__(particles) face_vars = { "area": "double", "velocity-x": "double", "velocity-y": "double", "normal-x": "double", "normal-y": "double", "com-x": "double", "com-y": "double", "pair-i": "longlong", "pair-j": "longlong", } self.faces = ParticleContainer(var_dict=face_vars) def compute_cell_info(self): """ compute volume and center of mass of all real particles and compute areas, center of mass, normal face pairs, and number of faces for faces """ num_faces = mesh.number_of_faces(self.particles, self.graph["neighbors"], self.graph["number of neighbors"]) self.faces.resize(num_faces) # the algorithms are cumulative so we have to zero out the data self.particles["volume"][:] = 0.0 self.particles["com-x"][:] = 0.0 self.particles["com-y"][:] = 0.0 mesh.cell_face_info_2d(self.particles, self.faces, self.graph["neighbors"], self.graph["number of neighbors"], self.graph["faces"], self.graph["voronoi vertices"]) def update_boundary_particles(self): cumsum = np.cumsum(self.graph["number of neighbors"], dtype=np.int32) mesh.flag_boundary_particles(self.particles, self.graph["neighbors"], self.graph["number of neighbors"], cumsum) def update_second_boundary_particles(self): cumsum = np.cumsum(self.graph["number of neighbors"], dtype=np.int32) mesh.flag_second_boundary_particles(self.particles, self.graph["neighbors"], self.graph["number of neighbors"], cumsum) def tessellate(self): """ create 2d voronoi tesselation from particle positions """ pos = np.array( [self.particles["position-x"], self.particles["position-y"]], dtype=np.float64) # create the tesselation vor = Voronoi(pos.T) # total number of particles num_particles = self.particles.get_number_of_particles() # create neighbor and face graph neighbor_graph = [[] for i in range(num_particles)] face_graph = [[] for i in range(num_particles)] # loop through each face collecting the two particles # that made that face as well as the face itself for i, face in enumerate(vor.ridge_points): p1, p2 = face neighbor_graph[p1].append(p2) neighbor_graph[p2].append(p1) face_graph[p1] += vor.ridge_vertices[i] face_graph[p2] += vor.ridge_vertices[i] # sizes for 1d graphs neighbor_graph_sizes = np.array([len(n) for n in neighbor_graph], dtype=np.int32) # graphs in 1d neighbor_graph = np.array(list( itertools.chain.from_iterable(neighbor_graph)), dtype=np.int32) face_graph = np.array(list(itertools.chain.from_iterable(face_graph)), dtype=np.int32) self.graph["neighbors"] = neighbor_graph self.graph["number of neighbors"] = neighbor_graph_sizes self.graph["faces"] = face_graph self.graph["voronoi vertices"] = vor.vertices def build_geometry(self, gamma): pc = self.particles self.tessellate() self.update_boundary_particles() self.update_second_boundary_particles() #tmp delete self.compute_cell_info() indices = np.where((pc['tag'] == ParticleTAGS.Real) | (pc['type'] == ParticleTAGS.Boundary) | (pc['type'] == ParticleTAGS.BoundarySecond))[0] # now update primitive variables vol = pc['volume'][indices] mass = pc['mass'][indices] momx = pc['momentum-x'][indices] momy = pc['momentum-y'][indices] ener = pc['energy'][indices] # update primitive variables pc['density'][indices] = mass / vol pc['velocity-x'][indices] = momx / mass pc['velocity-y'][indices] = momy / mass pc['pressure'][indices] = (ener / vol - 0.5 * (mass / vol) * ((momx / mass)**2 + (momy / mass)**2)) * (gamma - 1.0)