2 Update mass

Now we do the same thing for the mass process using the GRACE data.

2.1 Load data

First we load the GRACE data.

grace_data <- read.table(paste0(dd, "WP2-SolidEarth/BHMinputs/GRACE/GRACE_v02.3b_trends_v03.txt"), header = T)
grace_loc <-  read.table(paste0(dd, "WP2-SolidEarth/BHMinputs/GRACE/GRACE_v02.3b_loc_v03.txt"), skip = 1)
n_grace <- ncol(grace_loc)
n_ll <- (n_grace-4)/2
names(grace_loc) <- c("id", "area", "lon_c", "lat_c", paste0(c("lon", "lat"), rep(1:n_ll, each = 2)))

## Create spatial polygons data frame
Polygon_list <- list()
for(i in 1:nrow(grace_loc)){
  lons <- na.omit(as.numeric(c(grace_loc[i, seq(5,ncol(grace_loc), 2)], grace_loc[i, 5])))
  lats <- na.omit(as.numeric(c(grace_loc[i, seq(6,ncol(grace_loc), 2)], grace_loc[i, 6])))
  Polygon_list[[i]] <- Polygon(cbind(lons, lats))
}

Polygons_list <- lapply(1:length(Polygon_list), function(x) Polygons(list(Polygon_list[[x]]), x))
SpPolygon <- SpatialPolygons(Polygons_list, proj4string = CRS("+proj=longlat"))

grace_sp <- SpatialPolygonsDataFrame(SpPolygon,grace_data)
grace_sp$mmweqt <- ifelse(abs(grace_sp$mmweq) > 20, sign(grace_sp$mmweq)*20, grace_sp$mmweq )
spplot(grace_sp, "mmweqt", at = seq(-21, 21, 2), main = "The GRACE super mascons data (mm/yr ewh)")

## note the area calcuated by R is different from those given in the dataset -- ask Maike

2.2 prior set up

Use the polygon data to have rough estimate of the correlation length and variance.

grace_v <- data.frame(mean = grace_data$mmweq, lon = grace_loc$lon_c, lat = grace_loc$lat_c)
coordinates(grace_v) <- c("lon", "lat")
proj4string(grace_v) <- CRS("+proj=longlat")
v2 <- variogram(mean~1, grace_v) 
plot(v2)

## Priors mean and variance for the parameters: rho and sigma
mu_r <- 2500/6371
v_r <- 1
mu_s <- 10 # area scale to about 100km^2 ~ 1 degree resolution
v_s <- 20^2

## Transform the parameters for the SPDE_GMRF approximation
trho <- Tlognorm(mu_r, v_r)
tsigma <- Tlognorm(mu_s, v_s)

## Build the SPDE model with the prior
lsigma0 <- tsigma[1]
theta1_s <- tsigma[2]
lrho0 <- trho[1]
theta2_s <- trho[2]
lkappa0 <- log(8)/2 - lrho0
ltau0 <- 0.5*log(1/(4*pi)) - lsigma0 - lkappa0

M_spde <- inla.spde2.matern(mesh0, B.tau = matrix(c(ltau0, -1, 1),1,3), B.kappa = matrix(c(lkappa0, 0, -1), 1,3),
                              theta.prior.mean = c(0,0), theta.prior.prec = c(sqrt(1/theta1_s), sqrt(1/theta2_s)))

2.3 Generate mesh

We also need a mesh to represent the mass process. For the same reason as discussed in previous section for SSH, we need a mesh with 1 degree resolution. The GRACE data is all over the globe, so we can use the .

2.5 INLA inference

Now we can run INLA for the Bayesian inference. Do not run on a desktop the process may use up to 64GB memory at peak. We ran this on a server with enough memory.

## Fix altimetry errors as they are known
hyper <- list(prec = list(fixed = TRUE, initial = 0))
prec_scale <- c(1/grace_sp$std^2, rep(1, nrow(A_GRACE_data) + nrow(A_M_pred)))

## The formular for modelling the SSH mean
formula = y ~ -1 +  f(M, model = M_spde)

## Run INLA
res_inla <- inla(formula, data = inla.stack.data(stM, spde = M_spde), family = "gaussian",
                 scale =prec_scale, control.family = list(hyper = hyper),
                 control.predictor=list(A=inla.stack.A(stM), compute =TRUE))

2.6 Results

2.6.1 Assemble and save results

Now assemble the inla inference and prediction results.

INLA_pred <- res_inla$summary.linear.predictor
## Extract and project predictions
pred_idx <- inla.stack.index(stM, tag = "pred")$data
idx_grace <- pred_idx[1:nrow(A_GRACE_data)]
idx_grid <- pred_idx[-(1:nrow(A_GRACE_data))]

## mass
M_m <- INLA_pred$mean[idx_grid] 
M_u <- INLA_pred$sd[idx_grid]
proj <- inla.mesh.projector(mesh0, projection = "longlat", dims = c(360,180), xlim = c(0,359), ylim = c(-89.5, 89.5))
M_grid <- expand.grid(proj$x, proj$y)
M_pred <- data.frame(lon = M_grid[,1], lat = M_grid[,2],
                       mean = M_m,
                       u = M_u)

res_M <- list(res_inla = res_inla, spde = M_spde, st = stM, 
            mesh = mesh0,  M_pred = M_pred, Adata = A_GRACE_data, Apred = A_M_pred)

grace_m <- INLA_pred$mean[idx_grace]
grace_u <- INLA_pred$sd[idx_grace]
grace_sp@data$pred_mean <- grace_m
grace_sp@data$pred_u <- grace_u

save(res_M, grace_sp, file =paste0(dd, "WP1-BHM/Experiment2a/exp2a_M.RData"))

2.6.2 Plot the posteriors of the hyper parameters

load(paste0(dd, "WP1-BHM/Experiment2a/exp2a_M.RData"))
pars_M <- marginal_par(res = res_M, process = "M", plot = TRUE)

## The posterior modes
print(paste("The estimated correlation lengths are:", pars_M$rho_mode*6371,  sep = "  "))
## [1] "The estimated correlation lengths are:  822.056906990587"
print(paste("The estimated marginal variances are:", pars_M$sigma_mode,sep = "  "))
## [1] "The estimated marginal variances are:  186.460541260929"

2.7 Plot the predictions

We first plot the predicted \(X_{mass}\) on a 1 degree grid and then the aggregated \(X_m\) on the same super mascons grid as the GRACE data.

M_pred <- res_M$M_pred

M_pred$mean2 <- ifelse(abs(M_pred$mean) > 19, sign(M_pred$mean)*20, M_pred$mean)
M_pred$u2 <- ifelse(abs(M_pred$u) > 5, 7, M_pred$u)
## plot the mean 
lattice::levelplot(mean2 ~ lon + lat, data = M_pred, aspect = "iso", at = seq(-20, 20, 2),
                     panel = function(x,y,z,...){
                       lattice::panel.levelplot(x,y,z,...)
                       map2 <- map("world2", interior = FALSE, plot = FALSE)
                       lattice::panel.xyplot(x=map2$x, y=map2$y, type = "l", col = "black")
                     },
                     main = "The predicited mass change  (mm/yr ewh)", xlab = "longitude", ylab = "latitude")

## plot the uncertainty
lattice::levelplot(u2 ~ lon + lat, data = M_pred, aspect = "iso", col.regions = topo.colors(20),
                     panel = function(x,y,z,...){
                       lattice::panel.levelplot(x,y,z,...)
                       map2 <- map("world2", interior = FALSE, plot = FALSE)
                       lattice::panel.xyplot(x=map2$x, y=map2$y, type = "l", col = "black")
                     },
                     main = "The predicted uncertainties  (mm/yr ewh)", xlab = "longitude", ylab = "latitude")

grace_sp$pred_mean2 <- ifelse(abs(grace_sp@data$pred_mean) > 20, sign(grace_sp@data$pred_mean)*20, grace_sp@data$pred_mean )
spplot(grace_sp, "pred_mean2", at = seq(-21, 21, 2), main = "The predicted mass (mm/yr ewh)")

spplot(grace_sp, "pred_u", main = "The predicted uncertainties (mm/yr ewh)")

3 Sanity checks on the updated mass

To check whether the updated mass is reasonable, we calculate the integrated mass trend and compare it to existing work.

First we check the global trend of the mass change in mm water equivalence height.

order_id <- order(ice6g$y_center,ice6g$x_center ) # re-order the data according to the coordinates
grid_area2 <- grid_area[order_id]

print(paste0("global average is ", mean(M_pred$mean)))
## [1] "global average is 0.0609729539134967"
print(paste0("weighted global average is ", sum(M_pred$mean*areas/sum(areas))))
## [1] "weighted global average is 0.854202064748128"

Then we check the ocean mass change. We need to first identify which grid belongs to the oceans. The values are similar to and between existing studies.

Ocean <- readOGR(dsn = paste0(dd, "WP1-BHM/maps/ne_110m_ocean"), layer = "ne_110m_ocean")
## OGR data source with driver: ESRI Shapefile 
## Source: "Z:/WP1-BHM/maps/ne_110m_ocean", layer: "ne_110m_ocean"
## with 2 features
## It has 3 fields
coords <- ice6g2[,2:3]
coords[,1] <- ifelse(coords[,1] > 180, coords[,1]-360, coords[,1])
grid_inO <- unlist(over(Ocean, SpatialPoints(coords = coords, proj4string = CRS(proj4string(Ocean))), returnList = TRUE))
print(paste0("average over oceans is ", mean(M_pred$mean[grid_inO])))
## [1] "average over oceans is 1.98792302998572"
print(paste0("weighted average over the oceans is ", sum(M_pred$mean[grid_inO]*areas[grid_inO]/sum(areas))))
## [1] "weighted average over the oceans is 1.5071185557515"
print(paste0("average on land is ", mean(M_pred$mean[-grid_inO])))
## [1] "average on land is -3.80983733262189"
print(paste0("weighted average on land is ", sum(M_pred$mean[-grid_inO]*areas[-grid_inO]/sum(areas))))
## [1] "weighted average on land is -0.65291649100337"