def precomp(self): """precompute jacobi iteration matrices""" #precompute merged metric operations D1P1 = util.dia_simple(self.geometry.D1P1) P0D2 = util.dia_simple(self.geometry.P0D2) D21 = self.topology.D21 P10 = self.topology.P10 L = util.coo_matrix(D21 * D1P1 * P10 * P0D2) od = L.row != L.col self.off_diagonal = util.csr_matrix( (L.data[od], (L.row[od], L.col[od])), shape=(self.topology.P0, self.topology.P0)) self._laplace_d2 = util.csr_matrix(L) self.inverse_diagonal = 1.0 / (L.data[od==False][:, None])
def transfer_operators(self): """ construct metric transfer operators, as required for dual-transfer on pseudo-regular grid we need to calculate overlap between fine and coarse dual domains the crux here is in the treatment of the central triangle holy shitballs this is a dense function. there is some cleanup i could do, but this is also simply some insanely hardcore shit algebraicly optimal multigrid transfer operators on a pseudo-regular grid, here we come """ coarse = self fine = self.child all_tris = np.arange(fine.topology.P2).reshape(coarse.topology.P2, 4) central_tris = all_tris[:,0] corner_tris = all_tris[:,1:] #first, compute contribution to transfer matrices from the central refined triangle coarse_dual = coarse.dual fine_dual = fine.dual[central_tris] face_edge_mid = util.gather(fine.topology.FV[0::4], fine.primal) fine_edge_normal = [np.cross(face_edge_mid[:,i-2,:], face_edge_mid[:,i-1,:]) for i in xrange(3)] fine_edge_mid = [(face_edge_mid[:,i-2,:] + face_edge_mid[:,i-1,:])/2 for i in xrange(3)] fine_edge_dual = [np.cross(fine_edge_mid[i], fine_edge_normal[i]) for i in xrange(3)] fine_edge_normal = np.array(fine_edge_normal) fine_edge_mid = np.array(fine_edge_mid) fine_edge_dual = np.array(fine_edge_dual) coarse_areas = [triangle_area_from_corners(coarse_dual, face_edge_mid[:,i-2,:], face_edge_mid[:,i-1,:]) for i in xrange(3)] fine_areas = [triangle_area_from_corners(fine_dual , face_edge_mid[:,i-2,:], face_edge_mid[:,i-1,:]) for i in xrange(3)] fine_areas = [(fine_areas[i-2]+fine_areas[i-1])/2 for i in xrange(3)] coarse_areas = np.array(coarse_areas) fine_areas = np.array(fine_areas) #normal of edge midpoints to coarse dual interior_normal = np.array([np.cross(face_edge_mid[:,i,:], coarse_dual) for i in xrange(3)]) #the 0-3 index of the overlapping domains #biggest of the subtris formed with the coarse dual vertex seems to work; but cant prove why it is so... touching = np.argmax(coarse_areas, axis=0) ## print touching ## print fine_areas ## print coarse_areas #indexing arrays I = np.arange(len(touching)) m = touching #middle pair l = touching-1 #left-rotated pair r = touching-2 #right-rotated pair #compute sliver triangles sliver_r = triangle_area_from_normals( +fine_edge_normal[l, I], +fine_edge_dual [l, I], +interior_normal [r, I]) sliver_l = triangle_area_from_normals( +fine_edge_normal[r, I], -fine_edge_dual [r, I], -interior_normal [l, I]) ## print 'slivers' ## print sliver_l ## print sliver_r assert(np.all(sliver_l>-1e-10)) assert(np.all(sliver_r>-1e-10)) #assemble area contributions of the middle triangle areas = np.empty((len(touching),3,3)) #coarsetris x coarsevert x finevert #the non-overlapping parts areas[I,l,l] = 0 areas[I,r,r] = 0 #triangular slivers disjoint from the m,m intersection areas[I,r,l] = sliver_l areas[I,l,r] = sliver_r #subset of coarse tri bounding sliver areas[I,r,m] = coarse_areas[r,I] - sliver_l areas[I,l,m] = coarse_areas[l,I] - sliver_r #subset of fine tri bounding sliver areas[I,m,l] = fine_areas[l,I] - sliver_l areas[I,m,r] = fine_areas[r,I] - sliver_r #square middle region; may compute as fine or caorse minus its flanking parts areas[I,m,m] = coarse_areas[m,I] - areas[I,m,l] - areas[I,m,r] #we may get numerical negativity for 2x2x2 symmetry, with equilateral fundemantal domain, #or high subdivision levels. or is error at high subdivision due to failing of touching logic? assert(np.all(areas > -1e-10)) #areas maps between coarse vertices and fine edge vertices. #add mapping for coarse to fine vertices too #need to grab coarsetri x 3coarsevert x 3finevert arrays of coarse and fine vertices fine_vertex = np.repeat( fine .topology.FV[0::4, None, :], 3, axis=1) coarse_vertex = np.repeat( coarse.topology.FV[: , : , None], 3, axis=2) def coo_matrix(data, row, col): """construct a coo_matrix from data and index arrays""" return util.coo_matrix( (data.ravel(),(row.ravel(), col.ravel())), shape=(coarse.topology.D2, fine.topology.D2)) center_transfer = coo_matrix(areas, coarse_vertex, fine_vertex) #add corner triangle contributions; this is relatively easy #coarsetri x 3coarsevert x 3finevert corner_vertex = util.gather(corner_tris, fine.topology.FV) corner_dual = util.gather(corner_tris, fine.dual) corner_primal = util.gather(corner_vertex, fine.primal) #coarsetri x 3coarsevert x 3finevert corner_areas = triangle_areas_around_center(corner_dual, corner_primal) #construct matrix corner_transfer = coo_matrix(corner_areas, coarse_vertex, corner_vertex) self.transfer = util.csr_matrix(center_transfer + corner_transfer) #calc normalizations self.coarse_area = self.transfer * np.ones(fine .topology.D2) self.fine_area = self.transfer.T * np.ones(coarse.topology.D2) self.f = np.sqrt( self.fine_area)[:,None] self.c = np.sqrt( self.coarse_area)[:,None] #test for consistency with metric calculations assert(np.allclose(self.coarse_area, coarse.D2P0, 1e-10)) assert(np.allclose(self.fine_area , fine .D2P0, 1e-10))