Ejemplo n.º 1
0
    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])
Ejemplo n.º 2
0
    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))