This document goes over development of a LMC and cosimulation of the log data, conditioned on the vertical stress exhaustive data. Similar to the previous notebook that addressed stress data.
# geospatial packages library(sf) library(gstat) library(raster) library(sp) # statistical normalization and analysis library(ggpubr) library(moments) library(bestNormalize) # data munging & plotting library(tidyverse) library(viridis) source('../R/geospatial.R') source('../R/geostats.R')
See 'geostats_data_prep.Rmd' to generate these files and dump into the cache. Read GeoJSONs from the cache.
We run an aggregation function to take the mean gradient across measurement points because a) we aren't doing 3D kriging, and b) because geostatistics doesn't work with zero distance variance that isn't equal to zero
study_raster = raster('../data/study_raster.tif') sv_sf = st_read('../data/sv_sf.GeoJSON') logs_sf = st_read('../data/facies_data.GeoJSON') shale_sf = logs_sf %>% filter(facies == 'dv_shale') shale_sp = as_Spatial(shale_sf) carb_sf = logs_sf %>% filter(facies == 'dv_carb') carb_sp = as_Spatial(carb_sf)
This section investigates the normality, anisotropy, and variography of each of the log variables (bulk density, vp, vs). We also repeat the SV variography minus the exploration (see geostats_stress.Rmd)
Since Sequential Gaussian Cosimulation is used, we transform variables to normal distributions using the ordered quantile technique
sv_sp = as_Spatial(sv_sf) sv_sp = sv_sp[!is.na(sv_sp$svgrad_kpa_m),] sv_norm = bestNormalize::orderNorm(sv_sp$svgrad_kpa_m) sv_sp$svgrad_norm = sv_sp$svgrad_norm = sv_norm$x.t sv_v = variogram(svgrad_norm~1, sv_sp) sv_vgm = vgm(0.8, "Sph", 40E3, nugget = 0.01) sv_vgm = vgm(0.2, "Sph", 10E3, anis=c(67, 0.75), add.to=sv_vgm) plot(sv_v, sv_vgm)
shale_rhob = shale_sp[!is.na(shale_sp$rhob_mean),] # investigate normality # make anisotropic variograms ggdensity(shale_sf, x='rhob_mean') + stat_overlay_normal_density(color='red', linetype='dashed') moments::skewness(shale_sf$rhob_mean) moments::kurtosis(shale_sf$rhob_mean) # normal transform shale_rhob_norm = bestNormalize::orderNorm(shale_rhob$rhob_mean) shale_rhob$rhob_norm = shale_rhob_norm$x.t ggdensity(as.data.frame(shale_rhob), x='rhob_norm') + stat_overlay_normal_density(color='red', linetype='dashed') moments::skewness(shale_rhob_norm$x.t) moments::kurtosis(shale_rhob_norm$x.t) # what is the global variance (should be 1...) var(shale_rhob$rhob_norm) # make an experimental variogram shale_rhob_v = variogram(rhob_norm~1, shale_rhob) plot(shale_rhob_v) # check directional variograms rhob_v_dir = variogram(rhob_norm~1, shale_rhob, alpha=c(0,22,45,67,90,112,135,157)) plot(rhob_v_dir) # check for geometric anisotropy ggvarmap = ggvariogram_map( formula = rhob_norm~1, sp_df = shale_rhob, cutoff = 100E3, width = 5E3, threshold = 1 ) ggsave('../output/ggvarmap_rhob_shale.pdf', ggvarmap)
carb_rhob = carb_sp[!is.na(carb_sp$rhob_mean),] # investigate normality # make anisotropic variograms ggdensity(carb_sf, x='rhob_mean') + stat_overlay_normal_density(color='red', linetype='dashed') moments::skewness(carb_sf$rhob_mean) moments::kurtosis(carb_sf$rhob_mean) # normal transform carb_rhob_norm = bestNormalize::orderNorm(carb_rhob$rhob_mean) carb_rhob$rhob_norm = carb_rhob_norm$x.t ggdensity(as.data.frame(carb_rhob), x='rhob_norm') + stat_overlay_normal_density(color='red', linetype='dashed') moments::skewness(carb_rhob_norm$x.t) moments::kurtosis(carb_rhob_norm$x.t) # what is the global variance (should be 1...) var(carb_rhob$rhob_norm) # make an experimental variogram carb_rhob_v = variogram(rhob_norm~1, carb_rhob) plot(carb_rhob_v) # check directional variograms rhob_v_dir = variogram(rhob_norm~1, carb_rhob, alpha=c(0,22,45,67,90,112,135,157)) plot(rhob_v_dir) # check for geometric anisotropy ggvariogram_map( formula = rhob_norm~1, sp_df = carb_rhob, cutoff = 100E3, width = 5E3, threshold = 1 ) ggsave('../output/ggvarmap_rhob_carb.pdf', ggvarmap)
shale_dtc = shale_sp[!is.na(shale_sp$dtc_mean),] # investigate normality # make anisotropic variograms ggdensity(shale_sf, x='dtc_mean') + stat_overlay_normal_density(color='red', linetype='dashed') moments::skewness(shale_dtc$dtc_mean) moments::kurtosis(shale_dtc$dtc_mean) # normal transform shale_dtc_norm = bestNormalize::orderNorm(shale_dtc$dtc_mean) shale_dtc$dtc_norm = shale_dtc_norm$x.t ggdensity(as.data.frame(shale_dtc), x='dtc_norm') + stat_overlay_normal_density(color='red', linetype='dashed') moments::skewness(shale_dtc_norm$x.t) moments::kurtosis(shale_dtc_norm$x.t) # what is the global variance (should be 1...) var(shale_dtc$dtc_norm) # make an experimental variogram shale_dtc_v = variogram(dtc_norm~1, shale_dtc) plot(shale_dtc_v) # check directional variograms shale_dtc_v_dir = variogram(dtc_norm~1, shale_dtc, alpha=c(0,22,45,67,90,112,135,157)) plot(shale_dtc_v_dir) # check for geometric anisotropy ggvariogram_map( formula = dtc_norm~1, sp_df = shale_dtc, cutoff = 100E3, width = 5E3, threshold = 1 ) ggsave('../output/ggvarmap_dtc_shale.pdf', ggvarmap)
carb_dtc = carb_sp[!is.na(carb_sp$dtc_mean),] # investigate normality # make anisotropic variograms ggdensity(carb_sf, x='dtc_mean') + stat_overlay_normal_density(color='red', linetype='dashed') moments::skewness(carb_dtc$dtc_mean) moments::kurtosis(carb_dtc$dtc_mean) # normal transform carb_dtc_norm = bestNormalize::orderNorm(carb_dtc$dtc_mean) carb_dtc$dtc_norm = carb_dtc_norm$x.t ggdensity(as.data.frame(carb_dtc), x='dtc_norm') + stat_overlay_normal_density(color='red', linetype='dashed') moments::skewness(carb_dtc_norm$x.t) moments::kurtosis(carb_dtc_norm$x.t) # what is the global variance (should be 1...) var(carb_dtc$dtc_norm) # make an experimental variogram carb_dtc_v = variogram(dtc_norm~1, carb_dtc) plot(carb_dtc_v) # check directional variograms dtc_v_dir = variogram(dtc_norm~1, carb_dtc, alpha=c(0,22,45,67,90,112,135,157)) plot(dtc_v_dir) # check for geometric anisotropy ggvariogram_map( formula = dtc_norm~1, sp_df = carb_dtc, cutoff = 100E3, width = 5E3, threshold = 1 ) ggsave('../output/ggvarmap_dtc_carb.pdf', ggvarmap)
shale_dts = shale_sp[!is.na(shale_sp$dts_mean),] # investigate normality # make anisotropic variograms ggdensity(shale_sf, x='dts_mean') + stat_overlay_normal_density(color='red', linetype='dashed') moments::skewness(shale_dts$dts_mean) moments::kurtosis(shale_dts$dts_mean) # normal transform shale_dts_norm = bestNormalize::orderNorm(shale_dts$dts_mean) shale_dts$dts_norm = shale_dts_norm$x.t ggdensity(as.data.frame(shale_dts), x='dts_norm') + stat_overlay_normal_density(color='red', linetype='dashed') moments::skewness(shale_dts_norm$x.t) moments::kurtosis(shale_dts_norm$x.t) # what is the global variance (should be 1...) var(shale_dts$dts_norm) # make an experimental variogram shale_dts_v = variogram(dts_norm~1, shale_dts) plot(shale_dts_v) # check directional variograms shale_dts_v_dir = variogram(dts_norm~1, shale_dts, alpha=c(0,22,45,67,90,112,135,157)) plot(shale_dts_v_dir) # check for geometric anisotropy ggvariogram_map( formula = dts_norm~1, sp_df = shale_dts, cutoff = 100E3, width = 5E3, threshold = 1 ) ggsave('../output/ggvarmap_dts_shale.pdf', ggvarmap)
carb_dts = carb_sp[!is.na(carb_sp$dts_mean),] # investigate normality # make anisotropic variograms ggdensity(carb_sf, x='dts_mean') + stat_overlay_normal_density(color='red', linetype='dashed') moments::skewness(carb_dts$dts_mean) moments::kurtosis(carb_dts$dts_mean) # normal transform carb_dts_norm = bestNormalize::orderNorm(carb_dts$dts_mean) carb_dts$dts_norm = carb_dts_norm$x.t ggdensity(as.data.frame(carb_dts), x='dts_norm') + stat_overlay_normal_density(color='red', linetype='dashed') moments::skewness(carb_dts_norm$x.t) moments::kurtosis(carb_dts_norm$x.t) # what is the global variance (should be 1...) var(carb_dts$dts_norm) # make an experimental variogram carb_dts_v = variogram(dts_norm~1, carb_dts) plot(carb_dts_v) # check directional variograms dts_v_dir = variogram(dts_norm~1, carb_dts, alpha=c(0,22,45,67,90,112,135,157)) plot(dts_v_dir) # check for geometric anisotropy ggvariogram_map( formula = dts_norm~1, sp_df = carb_dts, cutoff = 100E3, width = 5E3, threshold = 1 ) ggsave('../output/ggvarmap_dts_carb.pdf', ggvarmap)
We are going to cosimulate stress values using a Linear Model of Coregionalization (LMC) via gstat. We could get fancier, but that's not what is important, what is important is that we have heterotopic data underpinned by an exhaustive secondary variable and need to quantify the uncertainty in our stress measurements, and how that compares to our model uncertainty.
We first try with two variables to develop intuition about cross-variogram fitting
# make a prediction grid st_grid <- rasterToPoints(study_raster, spatial = TRUE) gridded(st_grid) <- TRUE st_grid <- as(st_grid, "SpatialPixels")
For the logs we have SV, RHOB, DTC, DTS. SV is exhaustive and the same for both carbonate and shale (the exhaustive link raster). RHOB, DTC, DTS are simulated separately for carbonate and shale
We use the same structures as employed for the stress model for consistency, despite some improvement being seen from adding a mid-range exponential model and adding more variograms. We believe this increases the parsimony of the approach and reduces bias, given all the other uncertainties we are addressing.
Shale first
# setup model structures vmodel = vgm(0, "Sph", 40E3) vmodel = vgm(0, "Sph", 20E3, add.to=vmodel) vmodel = vgm(0, "Lin", 10E3, add.to=vmodel) vmodel = vgm(0, "Nug", 0, add.to=vmodel) # setup problem in gstat g = gstat(id='sv', formula=svgrad_norm~1, data=sv_sp, beta=0, nmax=50, model=vmodel) g = gstat(g, id='rhob', formula=rhob_norm~1, data=shale_rhob, beta=0, nmax=50, model=vmodel) g = gstat(g, id='dtc', formula = dtc_norm~1, data=shale_dtc, beta=0, nmax=50, model=vmodel) g = gstat(g, id='dts', formula = dts_norm~1, data=shale_dts, beta=0, nmax=50, model=vmodel) g = gstat(g, id=c('sv','rhob'), model=vmodel, beta=0, nmax=50) g = gstat(g, id=c('sv','dtc'), model=vmodel, beta=0, nmax=50) g = gstat(g, id=c('sv','dts'), model=vmodel, beta=0, nmax=50) g = gstat(g, id=c('rhob','dtc'), model=vmodel, beta=0, nmax=50) g = gstat(g, id=c('rhob','dts'), model=vmodel, beta=0, nmax=50) g = gstat(g, id=c('dtc','dts'), model=vmodel, beta=0, nmax=50) v = variogram(g, cressie=TRUE)
The ad-hoc fitting process here focuses on the following parameters in order: 1. SV (exhaustive) 2. RHOB 3. VP (DTC) 4. VS (DTS) 5. Cross-variograms
Seeds with good fits (+ indicates preferred): 3, 4, 6+, 9, 15, 19, 22, 25, 32, 33, 48
# setup lmc problem set.seed(6) lmc_in = make_lmc_input(g, vmodel) # run the lmc algorithm res <- fit_linear_model_of_coregionalization( lmc_in$azimuth, lmc_in$dip, lmc_in$nlag, lmc_in$tail, lmc_in$head, lmc_in$gam, lmc_in$model, vartype=lmc_in$vartype, weighting_method=6 ) g = assign_lmc_sills(g, res$sills) plot(v,g)
Save geostatistical models
norm_objs = list(sv_norm, shale_rhob_norm, shale_dtc_norm, shale_dts_norm) names(norm_objs) = c('sv','rhob','dtc', 'dts') data_objs = list(sv_sp, shale_rhob, shale_dtc, shale_dts) names(data_objs) = c('sv','rhob','dtc','dts') vgm_obj = list(v,g,lmc_in, norm_objs, data_objs) names(vgm_obj) = c('v','g','lmc_in','norm_objs', 'data_objs') saveRDS(vgm_obj, '../output/shale_logs_vgmobj.rds')
Carbonate second
# setup model structures vmodel = vgm(0, "Sph", 40E3) vmodel = vgm(0, "Sph", 20E3, add.to=vmodel) vmodel = vgm(0, "Lin", 10E3, add.to=vmodel) vmodel = vgm(0, "Nug", 0, add.to=vmodel) # setup problem in gstat g = gstat(id='sv', formula=svgrad_norm~1, data=sv_sp, beta=0, nmax=50, model=vmodel) g = gstat(g, id='rhob', formula=rhob_norm~1, data=carb_rhob, beta=0, nmax=50, model=vmodel) g = gstat(g, id='dtc', formula = dtc_norm~1, data=carb_dtc, beta=0, nmax=50, model=vmodel) g = gstat(g, id='dts', formula = dts_norm~1, data=carb_dts, beta=0, nmax=50, model=vmodel) g = gstat(g, id=c('sv','rhob'), model=vmodel, beta=0, nmax=50) g = gstat(g, id=c('sv','dtc'), model=vmodel, beta=0, nmax=50) g = gstat(g, id=c('sv','dts'), model=vmodel, beta=0, nmax=50) g = gstat(g, id=c('rhob','dtc'), model=vmodel, beta=0, nmax=50) g = gstat(g, id=c('rhob','dts'), model=vmodel, beta=0, nmax=50) g = gstat(g, id=c('dtc','dts'), model=vmodel, beta=0, nmax=50) v = variogram(g, cressie=TRUE)
Seeds with reasonable fits (+ indicates preferred): 3, 4, 9, 19, 25+, 28, 33, 36+, 40
# setup lmc problem set.seed(25) lmc_in = make_lmc_input(g, vmodel) # run the lmc algorithm res <- fit_linear_model_of_coregionalization( lmc_in$azimuth, lmc_in$dip, lmc_in$nlag, lmc_in$tail, lmc_in$head, lmc_in$gam, lmc_in$model, vartype=lmc_in$vartype, weighting_method=6 ) g = assign_lmc_sills(g, res$sills) plot(v,g)
Save geostatistical models
norm_objs = list(sv_norm, carb_rhob_norm, carb_dtc_norm, carb_dts_norm) names(norm_objs) = c('sv','rhob','dtc', 'dts') data_objs = list(sv_sp, carb_rhob, carb_dtc, carb_dts) names(data_objs) = c('sv','rhob','dtc','dts') vgm_obj = list(v,g,lmc_in, norm_objs, data_objs) names(vgm_obj) = c('v','g','lmc_in','norm_objs', 'data_objs') saveRDS(vgm_obj, '../output/geostatistical_models/carb_logs_vgmobj.rds')
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.