CCCPaste

AMR - adaptive mesh refinement

amr <- function(x, ...) UseMethod("amr", x)
is.amr <- function(x) inherits(x, "amr")

#temp
amr_vals <- function(node) if(length(node[["refinement"]])>0) {
    # browser()
    unlist(lapply(node[["refinement"]], amr_vals)) 
    } else node[["value"]]

# x must be a matrix with two columns
amr.matrix <- function(x, weights=NULL, xlim=NULL, ylim=NULL, ...) {
    if(is.null(xlim)) xlim <- c(min(x[,1]), max(x[,1]))
    if(is.null(ylim)) ylim <- c(min(x[,2]), max(x[,2]))
    idx <- which(!is.na(x[,1]) & !is.na(x[,2]))
    amr.default(x[idx,], weights=weights, type="data", xlim=xlim, ylim=ylim, ...)
}

# x must be a function that supports vector arguments
amr.function <- function(x, ...) amr.default(x, type="function", ...)

# the workhorse, since the difference between function and dataset is small
amr.default <- function(x, xlim, ylim, weights=NULL, minlevel=4, maxlevel=8, reltol=0.05, log="", type, ...) {
    pts <- matrix(c(c(xlim,rev(xlim)), rep(ylim, each=2), rep(NA, 4)), byrow=FALSE, ncol=3)
    if(type=="function") pts[1:4, 3] <- x(pts[1:4,1], pts[1:4, 2])
    loglist <- strsplit(log,"")[[1]]
    logx <- "x" %in% loglist
    logy <- "y" %in% loglist
    if(type=="data") zlim <- c(0,1)
    
    pgrad.function <- function(node) {
        vals <- pts[node[["pts_idx"]],3]
        vals <- vals[is.finite(vals)]
        ret <- sd(vals)/(mean(vals)+1)
        # browser()
        return(ret)
    }
    pgrad.data <- function(node) {
        # prob. mass of this rectangle
        ll <- pts[node[["pts_idx"]][1],1:2]
        ur <- pts[node[["pts_idx"]][3],1:2]
        ret <- node[["value"]] * (ur[[1]]-ll[[1]]) * (ur[[2]]-ll[[2]])
        return(ret)
    }
    pgrad <- switch(type, "function"=pgrad.function, "data"=pgrad.data)
    
    rectval.function <- function(node) {
        # mean of the function value at the corners
        vals <- pts[node[["pts_idx"]],3]
        return(mean(vals[is.finite(vals)]))
    }
    rectval.data <- function(node) {
        # probability mass per area unit. should integrate to 1.
        ll <- pts[node[["pts_idx"]][1],1:2]
        ur <- pts[node[["pts_idx"]][3],1:2]
        idx <- (x[,1]>=ll[[1]]) & (x[,1]=ll[[2]]) & (x[,2]10000) browser()
        # ret <- (sum(idx)*total_area) / (nrow(x) * (ur[[1]]-ll[[1]]) * (ur[[2]]-ll[[2]]))
        zlim[[2]] <<- max(c(zlim[[2]], ret))
        return(ret)
    }       
    rectval <- switch(type, "function"=rectval.function, "data"=rectval.data)
       
    refinement <- function(node) {
        ll <- pts[node[["pts_idx"]][1],1:2]
        ur <- pts[node[["pts_idx"]][3],1:2]
        lvl <- node[["level"]]
        midx <- if(logx) sqrt(ll[1]*ur[1]) else (ll[1]+ur[1])/2
        midy <- if(logy) sqrt(ll[2]*ur[2]) else (ll[2]+ur[2])/2
        
        add_pts <- matrix(c(
            midx, ll[2], NA,  # lower middle
            ll[1], midy, NA,  # left middle
            midx, midy, NA, # middle
            ur[1], midy, NA,  # right middle
            midx, ur[2], NA), # upper middle
            byrow=TRUE, ncol=3
        )
        if(type=="function") add_pts[,3] <- x(add_pts[,1], add_pts[,2])
        old_nrow <- nrow(pts)
        pts <<- rbind(pts, add_pts)
        
        node1 <- list(pts_idx=c(node[["pts_idx"]][1], old_nrow+c(1,3,2)), refinement=list(), level=lvl+1)
        node1[["value"]] <- rectval(node1)
        node1[["pgrad"]] <- pgrad(node1)
        node2 <- list(pts_idx=c(old_nrow+1, node[["pts_idx"]][2], old_nrow+c(4,3)), refinement=list(), level=lvl+1)
        node2[["value"]] <- rectval(node2)
        node2[["pgrad"]] <- pgrad(node2)
        node3 <- list(pts_idx=c(old_nrow+c(3,4), node[["pts_idx"]][3], old_nrow+5), refinement=list(), level=lvl+1)
        node3[["value"]] <- rectval(node3)
        node3[["pgrad"]] <- pgrad(node3)
        node4 <- list(pts_idx=c(old_nrow+c(2,3,5), node[["pts_idx"]][4]), refinement=list(), level=lvl+1)
        node4[["value"]] <- rectval(node4)
        node4[["pgrad"]] <- pgrad(node4)
        
        return(list(node1, node2, node3, node4))
    }
    
    needs_refinement <- function(node) {
        if(node[["level"]]=maxlevel) return(FALSE)
        if(!is.numeric(node[["pgrad"]]) | is.nan(node[["pgrad"]])) return(TRUE)
        if(abs(node[["pgrad"]]) > reltol) return(TRUE)
        # if(is.numeric(node[["pgrad"]])) browser()
        # ans <- try(if(node[["pgrad"]] > reltol) return(TRUE))
        # if (inherits(ans, "try-error")) browser()
        
        return(FALSE)
    }
    
    refine_recursively <- function(node) {
        if(needs_refinement(node)) {
            node[["refinement"]] <- refinement(node)
            # browser()
            for (i in seq_len(length(node[["refinement"]]))) 
                node[["refinement"]][[i]] <- refine_recursively(node[["refinement"]][[i]])
        }
        return(node)
    }
    
    root <- list(pts_idx=seq(1,4), refinement=list(), level=0)
    root[["value"]] <- rectval(root)
    root[["pgrad"]] <- pgrad(root)
    root <- refine_recursively(root)
    if(type=="function")  {
        zlim <- c(min(pts[,3]), max(pts[,3]))
        # browser()
    } else if(type=="data") {
        # browser()
        vals <- amr_vals(root)
        zlim <- c(min(vals), max(vals))
    }
    
    res <- list(pts=pts, node=root, zlim=zlim)
    class(res) <- "amr"
    return(res)
}

# use classInt? quantile method not helpful due to irregular sampling
# image has a breaks argument that looks cool
plot.amr <- function(x, col=heat.colors(50), border=NA, xlab="x", ylab="y", zmag=1, ...) {
    zmin <- if(is.finite(x[["zlim"]][[1]])) x[["zlim"]][[1]] else -zmag
    zmax <- if(is.finite(x[["zlim"]][[2]])) x[["zlim"]][[2]] else zmag
    colidx <- seq(zmin, zmax, length.out=length(col))
      
    plot_node <- function(node) {
        ll <- x[["pts"]][node[["pts_idx"]][1],1:2]
        ur <- x[["pts"]][node[["pts_idx"]][3],1:2]
        rect(ll[1], ll[2], ur[1], ur[2], border=border, col=col[findInterval(node[["value"]], colidx)])
        if(length(node[["refinement"]])>0) lapply(node[["refinement"]], plot_node)
    }
    
    root <- x[["node"]]
    ll <- x[["pts"]][root[["pts_idx"]][1], 1:2]
    ur <- x[["pts"]][root[["pts_idx"]][3], 1:2]
    plot_arg <- list(...)
    plot_arg[["x"]] <- 1; plot_arg[["col"]] <- "white"; plot_arg[["type"]] <- "p"; plot_arg[["xlim"]] <- c(ll[1], ur[1])
    plot_arg[["ylim"]] <- c(ll[2], ur[2]); plot_arg[["xlab"]] <- xlab; plot_arg[["ylab"]] <- ylab
    do.call(plot, plot_arg)
    plot_node(root)
}


# may need a different method (weighted mean instead of sum) for function
integrate_amr <- function(x, xlim, ylim) {
    eval_rect <- function(node) {
        ll <- x[["pts"]][node[["pts_idx"]][1],1:2]
        ur <- x[["pts"]][node[["pts_idx"]][3],1:2]
        if(xlim[1]>ur[1] || xlim[2]ur[2] || ylim[2]0) return(unlist(lapply(node[["refinement"]], eval_rect)))
        area <- (min(c(ur[1], xlim[2])) - max(c(ll[1], xlim[1]))) * (min(c(ur[2], ylim[2]))-max(c(ll[2], ylim[1])))
        tot_area <- (ur[1]-ll[1]) * (ur[2]-ll[2])
        return(node[["value"]] * area / tot_area) # this is integreation
    }
    sum(eval_rect(x[["node"]])) # this is integration
}

# guess xlab and ylab
# add breaks argument
image.amr <- function(x, col=rev(gray(0:12/12)), border=NA, xlim=NULL, ylim=NULL, log="", xlab="x", ylab="y", zmag=1, length.out=100, breaks=NULL, ...) {
    if(is.null(xlim)) xlim <- c(min(x[["pts"]][,1]), max(x[["pts"]][,1]))
    if(is.null(ylim)) ylim <- c(min(x[["pts"]][,2]), max(x[["pts"]][,2]))
    
    zmin <- if(is.finite(x[["zlim"]][[1]])) x[["zlim"]][[1]] else -zmag
    zmax <- if(is.finite(x[["zlim"]][[2]])) x[["zlim"]][[2]] else zmag
        
    if(length(length.out)==1) length.out <- rep(length.out,2)
    
    loglist <- strsplit(log,"")[[1]]
    logx <- "x" %in% loglist
    logy <- "y" %in% loglist
    
    xgrid <- if(logx) exp(seq(log(xlim[1]), log(xlim[2]), length.out=length.out[1])) else seq(xlim[1], xlim[2], length.out=length.out[1])
    ygrid <- if(logy) exp(seq(log(ylim[1]), log(ylim[2]), length.out=length.out[2])) else seq(ylim[1], ylim[2], length.out=length.out[2])
    z <- matrix(0, nrow=length.out[1], ncol=length.out[2])
    
    for (i in seq.int(2,length.out[1]))
        for (j in seq.int(2,length.out[2]))
            z[i,j] <- integrate_amr(x, xlim=xgrid[seq.int(i-1,i)], ylim=ygrid[seq.int(j-1,j)])
            
    if(!is.null(breaks) && length(breaks)==1)  {
        breaks <- classIntervals(as.vector(z), n=breaks, style="fisher")$brks
        # browser()
        # breaks <- quantile(as.vector(z), seq(0,1, length.out=(breaks+1)))
    }
    # browser()
    image(x=xgrid, y=ygrid, z=z, zlim=c(zmin, zmax), xlim=xlim, ylim=ylim, col=col, xlab=xlab, ylab=ylab, log=log, breaks=breaks, ...)
    

}