class IrregularGrid(Grid): """ Rectilinear grid from irregular, but sorted, list of node locations """ def __init__(self,node_lists): self.dim = len(node_lists) self.node_lists = np.array(node_lists) # List of np.ndarray cutpoint locations for nl in node_lists: assert nl.ndim == 1 # 1D array assert nl.size >= 2 # At least two nodes assert is_sorted(nl) # Number of cutpoints along each dimension desc = [(nl[0],nl[-1],nl.size) for nl in node_lists] (low,hi,num) = zip(*desc) self.lower_bound = np.array(low) self.upper_bound = np.array(hi) self.num_nodes = np.array(num) self.num_cells = self.num_nodes - 1 # Initialize the indexer self.indexer = Indexer(self.num_nodes) # Fuzz to convert [low,high) to [low,high] self.fuzz = 1e-12 def points_to_cell_coords(self,points): (N,D) = points.shape assert D == self.dim Coords = np.empty((N,D)) for d in xrange(D): # Find the correct position in the dth node list coord = np.searchsorted(self.node_lists[d], points[:,d], side='right') - 1 # The 'right' is important if points are exactly on the node assert (N,) == coord.shape Coords[:,d] = coord # Include the upper boundary ub = self.upper_bound[d] hi_cell = self.num_cells[d] - 1 fuzz_mask = np.logical_and(points[:,d] >= ub, points[:,d] < ub + self.fuzz) Coords[fuzz_mask,d] = hi_cell # Indexer will take care of mapping to correct OOB node #lb = self.lower_bound[d] #oob_mask = np.logical_or(points[:,d] < lb, # points[:,d] >= ub+self.fuzz) #Coords[oob_mask,d] = np.nan return Coords def points_to_indices(self,points): coords = self.points_to_cell_coords(points) return self.indexer.coords_to_indices(coords) def indices_to_lowest_points(self,indices): assert 1 == indices.ndim coords = self.indexer.indices_to_coords(indices) return self.coords_to_lowest_points(coords) def coords_to_lowest_points(self,coords): assert 2 == coords.ndim (N,D) = coords.shape assert self.dim == D points = np.empty((N,D)) for d in xrange(D): points[:,d] = self.node_lists[d,coords[:,d]] return points
class RegularGrid(Grid): def __init__(self,grid_desc): assert isinstance(grid_desc,(list,tuple)) for gd in grid_desc: assert isinstance(gd,(list,tuple)) assert 3 == len(gd) self.dim = len(grid_desc) self.grid_desc = grid_desc # List of (low,high,num) triples (low,hi,num_cells) = zip(*self.grid_desc) self.lower_bound = np.array(low,dtype=np.double) self.upper_bound = np.array(hi,dtype=np.double) self.num_cells = np.array(num_cells,dtype=np.integer) assert not np.any(self.num_cells <= 0) self.num_nodes = self.num_cells + 1 # Cell dimensions self.delta = (self.upper_bound - self.lower_bound) self.delta /= self.num_cells.astype(np.double) # Initialize the indexer self.cell_indexer = Indexer(self.num_cells) self.node_indexer = Indexer(self.num_nodes) # Fuzz to convert [low,high) to [low,high] self.fuzz = 1e-15 def points_to_cell_coords(self,points): """ Figure out where points are. Returns the cell coordinate. """ assert is_mat(points) (N,D) = points.shape assert D == self.dim # Get the OOB info oob = OutOfBounds() oob.build_from_points(self,points) assert oob.check() raw_coords = np.empty((N,D)) for d in xrange(D): (low,high,num_cells) = self.grid_desc[d] # Transform: [low,high) |-> [0,n) transform = num_cells * (points[:,d] - low) / (high - low) transform += self.fuzz raw_coords[:,d] = np.floor(transform).astype(np.integer) # Add a little fuzz to make sure stuff on the boundary is # mapped correctly # Fuzz top boundary to get [low,high] fuzz_mask = np.logical_and(high <= points[:,d], points[:,d] < high + 2*self.fuzz) raw_coords[fuzz_mask,d] = num_cells - 1 # Counts things just a littttle bit greater than last cell # boundary as part of the last cell raw_coords[oob.mask,:] = np.nan assert is_int(raw_coords) coords = Coordinates(raw_coords,oob) assert coords.check() return coords def points_to_cell_indices(self,points): assert is_mat(points) (N,D) = points.shape cell_coords = self.points_to_cell_coords(points) assert isinstance(cell_coords,Coordinates) assert (N,D) == cell_coords.shape cell_indices = self.cell_indexer.coords_to_indices(cell_coords) assert is_vect(cell_indices) assert (N,) == cell_indices.shape return cell_indices def cell_indices_to_cell_coords(self,cell_indices): cell_coords = self.cell_indexer.indices_to_coords(cell_indices) return cell_coords def cell_indices_to_mid_points(self,cell_indices): assert is_vect(cell_indices) low_points = cell_indices_to_low_points(self,cell_indices) mid_points = low_points + row_vect(0.5 * self.delta) assert is_mat(mid_points) assert mid_points.shape[0] == cell_indices.shape[0] return mid_points def cell_indices_to_low_points(self,cell_indices): assert is_vect(cell_indices) cell_coords = self.cell_indexer.indices_to_coords(cell_indices) assert isinstance(cell_coords,Coordinates) assert cell_coords.check() low_points = self.cell_coords_to_low_points(cell_coords) assert is_mat(low_points) assert cell_coords.shape == low_points.shape return low_points def cell_coords_to_low_points(self,cell_coords): assert isinstance(cell_coords,Coordinates) assert self.dim == cell_coords.dim assert cell_coords.check() C = cell_coords.coords oob = cell_coords.oob assert np.all(np.isnan(C[oob.mask,:])) low_points = row_vect(self.lower_bound) + C * row_vect(self.delta) assert is_mat(low_points) assert np.all(np.isnan(low_points[oob.mask,:])) assert cell_coords.shape == low_points.shape return low_points def node_indices_to_node_points(self,node_indices): assert is_vect(node_indices) (N,) = node_indices.shape node_coords = self.node_indexer.indices_to_coords(node_indices) assert isinstance(node_coords,Coordinates) oob = node_coords.oob C = node_coords.coords assert np.all(np.isnan(C[oob.mask,:])) node_points = row_vect(self.lower_bound) + C * row_vect(self.delta) assert is_mat(node_points) assert np.all(np.isnan(node_points[oob.mask,:])) assert node_coords.shape == node_points.shape return node_points def cell_indices_to_vertex_indices(self,cell_indices): assert is_vect(cell_indices) cell_coords = self.cell_indexer.indices_to_coords(cell_indices) assert isinstance(cell_coords,Coordinates) vertex_indices = self.cell_coords_to_vertex_indices(cell_coords) assert is_mat(vertex_indices) # (N x 2**D) matrix return vertex_indices def cell_coords_to_vertex_indices(self,cell_coords): assert isinstance(cell_coords,Coordinates) (N,D) = cell_coords.shape assert self.dim == D """ The low node index in the cell has the same coords in node-land as the cell in cell-land: | | -o - o- | x | -x - o- | | """ low_vertex = self.node_indexer.coords_to_indices(cell_coords) # Array of index offsets to reach every vertex in cell shift = self.node_indexer.cell_shift() assert (2**D,) == shift.shape vertices = col_vect(low_vertex) + row_vect(shift) assert (N,2**D) == vertices.shape """ Handle out of bound nodes. There is a constant offset for converting cell oob indices to node oob indices. Also the difference between max spatial indices. """ oob = cell_coords.oob if oob.has_oob(): # Figure out the right oob node oob_indices = cell_coords.oob.indices[oob.mask] offset = self.node_indexer.get_num_spatial_nodes() vertices[oob.mask,0] = oob_indices + offset vertices[oob.mask,1:] = np.nan return vertices def points_to_low_vertex_rel_distance(self,points,cell_coords): assert is_mat(points) assert isinstance(cell_coords,Coordinates) (N,D) = points.shape assert (N,D) == cell_coords.shape low_vertex = self.cell_coords_to_low_points(cell_coords) dist = np.empty((N,D)) for d in xrange(D): dist[:,d] = (points[:,d] - low_vertex[:,d]) / self.delta[d] # OOB -> 0 distance from OOB node dist[cell_coords.oob.mask,:] = 0.0 assert np.all(dist >= 0.0) assert np.all(dist <= 1.0) return dist def are_points_oob(self,points): """ Check if points are out-of-bounds """ (N,D) = points.shape assert D == self.dim L = np.any(points < row_vect(self.lower_bound),axis=1) U = np.any(points > row_vect(self.upper_bound) + self.fuzz,axis=1) assert (N,) == L.shape assert (N,) == U.shape return np.logical_or(L,U)