-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #21 from hrashid10/master
3D version of pressure management fractured cases
- Loading branch information
Showing
2 changed files
with
326 additions
and
0 deletions.
There are no files selected for viewing
27 changes: 27 additions & 0 deletions
27
examples/pressure_management_inj/monte_carlo_fracture_3D.jl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import Random | ||
Random.seed!(1) | ||
include("weeds_fracture_3D.jl") | ||
x_test = randn(3 * num_eigenvectors + 2) #the input to the function should follow an N(0, I) distribution -- the eigenvectors and eigenvalues are embedded inside f | ||
@show f(x_test) #compute the pressure at the critical location | ||
@show extrema(grad_f(x_test)) #compute the gradient of the pressure at the critical location with respect to the eigenvector coefficients (then only show the largest/smallest values so it doesn't print a huge array) | ||
|
||
#Quick demo of how you could do Monte Carlo to get the distribution of pressures at the critical location -- hopefully the method Georg described can help us understand the tail distribution better | ||
numruns = 10 ^ 3 | ||
fs = Float64[] | ||
@time for i = 1:numruns | ||
x_test = randn(3 * num_eigenvectors + 2) | ||
push!(fs, f(x_test)) | ||
@show i | ||
end | ||
@show sum(fs) / length(fs) | ||
@show extrema(fs) | ||
|
||
fig, axs = PyPlot.subplots(1, 2) | ||
axs[1].hist(fs, bins=30) | ||
axs[1].set(xlabel="Pressure [MPa] at critical location", ylabel="Count (out of $(numruns))") | ||
axs[2].hist(log.(fs), bins=30) | ||
axs[2].set(xlabel="log(Pressure [MPa]) at critical location", ylabel="Count (out of $(numruns))") | ||
#display(fig); println() | ||
#fig.show() | ||
fig.savefig("distribution_of_pressure.png") | ||
PyPlot.close(fig) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,299 @@ | ||
import DPFEHM | ||
import GaussianRandomFields | ||
import PyPlot | ||
import Zygote | ||
|
||
plotstuff = false | ||
|
||
module FractureMaker | ||
mutable struct FractureTip | ||
i::Int | ||
j::Int | ||
k::Int | ||
di::Int | ||
dj::Int | ||
dk::Int | ||
alive::Bool | ||
end | ||
|
||
struct Material | ||
isfractured::Array{Float64, 3} | ||
end | ||
|
||
function Material(n, m,p) | ||
return Material(zeros(n, m,p)) | ||
end | ||
|
||
inside(m::Material, tip::FractureTip) = inside(m.isfractured, tip) | ||
|
||
function inside(A::Array{Float64, 3}, tip::FractureTip) | ||
if tip.di ==0 | ||
return tip.i <= size(A, 1) && tip.i > 0 && tip.j <= size(A, 2) && tip.j > 0 && tip.k <= size(A, 3) && tip.k > 0 && (tip.k +1) <= size(A, 2) && (tip.k + 1)> 0 && (tip.k -1) <= size(A, 2) && (tip.k - 1)> 0 && (tip.k +2) <= size(A, 2) && (tip.k +2)> 0 && (tip.k -2) <= size(A, 2) && (tip.k -2)> 0 | ||
else | ||
return tip.i <= size(A, 1) && tip.i > 0 && tip.j <= size(A, 2) && tip.j > 0 && tip.k <= size(A, 3) && tip.k > 0 && (tip.k +1) <= size(A, 2) && (tip.k + 1)> 0 && (tip.k -1) <= size(A, 2) && (tip.k - 1)> 0 && (tip.k +2) <= size(A, 2) && (tip.k +2)> 0 && (tip.k -2) <= size(A, 2) && (tip.k -2)> 0 | ||
end | ||
end | ||
|
||
function timestep!(m::Material, tips::Array{FractureTip}) | ||
for tip in tips | ||
if inside(m, tip) && tip.alive | ||
if tip.di ==0 | ||
m.isfractured[tip.i, tip.j, tip.k] = true | ||
m.isfractured[tip.i, tip.j, tip.k+1] = true | ||
m.isfractured[tip.i, tip.j, tip.k-1] = true | ||
m.isfractured[tip.i, tip.j, tip.k+2] = true | ||
m.isfractured[tip.i, tip.j, tip.k-2] = true | ||
else | ||
m.isfractured[tip.i, tip.j, tip.k] = true | ||
m.isfractured[tip.i, tip.j, tip.k+1] = true | ||
m.isfractured[tip.i, tip.j, tip.k-1] = true | ||
m.isfractured[tip.i, tip.j, tip.k+2] = true | ||
m.isfractured[tip.i, tip.j, tip.k-2] = true | ||
end | ||
end | ||
if tip.alive | ||
tip.i += tip.di | ||
tip.j += tip.dj | ||
tip.k += tip.dk | ||
end | ||
if inside(m, tip) && m.isfractured[tip.i, tip.j, tip.k] == true | ||
tip.alive = false | ||
end | ||
end | ||
end | ||
|
||
function simulate(mat::Material, tips, numtimesteps) | ||
for k = 1:numtimesteps | ||
timestep!(mat, tips) | ||
end | ||
return mat, tips | ||
end | ||
end # module FractureMaker | ||
|
||
# Set up the mesh | ||
ns = [101, 101, 50]#mesh size -- make it at least 100x200 to have the resolution for the fractures | ||
steadyhead = 0e0 #meters | ||
width = 1000 #meters | ||
heights = [200, 100, 200] | ||
mins = [0, 0, 0] #meters | ||
maxs = [width, width, sum(heights)] #meters | ||
coords, neighbors, areasoverlengths, volumes = DPFEHM.regulargrid3d(mins, maxs, ns) | ||
|
||
masks = [zeros(Bool, prod(ns)) for i = 1:3] | ||
for i = 1:size(coords, 2) | ||
if coords[3, i] > heights[1] + heights[2] | ||
masks[3][i] = true | ||
elseif coords[3, i] > heights[1] | ||
masks[2][i] = true | ||
else | ||
masks[1][i] = true | ||
end | ||
end | ||
masks = map(x->reshape(x, reverse(ns)...), masks) | ||
|
||
masksPlot = zeros(prod(ns),1) | ||
|
||
for i = 1:size(coords, 2) | ||
if coords[3, i] > heights[1] + heights[2] | ||
masksPlot[i] = 1 | ||
elseif coords[3, i] > heights[1] | ||
masksPlot[i] =2 | ||
else | ||
masksPlot[i] = 3 | ||
end | ||
end | ||
# Create a new figure for the 3D plot | ||
if plotstuff | ||
fig = PyPlot.figure() | ||
ax = fig[:add_subplot](111, projection="3d") | ||
scatter_plot = ax[:scatter](coords[1,:],coords[2,:],coords[3,:], c=masksPlot, cmap="viridis") | ||
display(fig) | ||
println() | ||
PyPlot.close(fig) | ||
end | ||
#setup the fracture network | ||
material = FractureMaker.Material(reverse(ns)...) #Logical array size equal to total number of cell | ||
|
||
true_ind = findall( masks[2]) | ||
true_ks=[] | ||
for i=1:length(true_ind) | ||
global true_ks=push!(true_ks,true_ind[i][1]) | ||
end | ||
true_ks=unique(true_ks) | ||
num_horizontal_fractures = sum(heights) ÷ 30 #put a horizontal fracture every 30 meters or so | ||
tips = Array{FractureMaker.FractureTip}(undef, 2 * num_horizontal_fractures) | ||
for frac = 1:num_horizontal_fractures | ||
i = rand(true_ks) | ||
j = rand(1:ns[2]) | ||
k = rand(1:ns[1]) | ||
tips[2 * frac - 1] = FractureMaker.FractureTip(i,j,k, 0, 1, 0, true) | ||
tips[2 * frac] = FractureMaker.FractureTip(i,j,k, 0, -1, 0, true) | ||
end | ||
FractureMaker.simulate(material, tips, 9 * ns[1] ÷ 20)#the time steps let the fracture grow to be at most a little less than the width of the domain | ||
horizontal_fracture_mask = copy(material.isfractured) .* masks[2] | ||
|
||
num_vertical_fractures = width ÷ 10#put a vertical fracture every 10 meters or so | ||
tips = Array{FractureMaker.FractureTip}(undef, 2 * num_vertical_fractures) | ||
for frac = 1:num_vertical_fractures | ||
i = rand(true_ks) | ||
j = rand(1:ns[2]) | ||
k = rand(1:ns[1]) | ||
tips[2*frac-1] = FractureMaker.FractureTip(i,j,k,1,0,0, true) | ||
tips[2*frac] = FractureMaker.FractureTip(i, j,k,-1,0,0, true) | ||
end | ||
FractureMaker.simulate(material, tips, ns[3] * heights[2] ÷ (3 * sum(heights))) | ||
vertical_fracture_mask = copy(material.isfractured) .* masks[2] - horizontal_fracture_mask | ||
|
||
horizontal_fracture_mask_plot=reshape(horizontal_fracture_mask,prod(ns)) | ||
vertical_fracture_mask_plot=reshape(vertical_fracture_mask,prod(ns)) | ||
|
||
if plotstuff | ||
fig = PyPlot.figure() | ||
ax = fig[:add_subplot](111, projection="3d") | ||
using Colors | ||
category_colors = [ | ||
[1, 0, 0, 0.0], | ||
[0, 1, 0, 1.0], | ||
] | ||
colors = [category_colors[c+1] for c in Int.(horizontal_fracture_mask_plot)] | ||
scatter_plot = ax[:scatter](coords[1,:],coords[2,:],coords[3,:], c=colors) | ||
display(fig) | ||
println() | ||
PyPlot.close(fig) | ||
|
||
|
||
category_colors = [ | ||
[0, 0, 0, 0.0], | ||
[1, 0, 0, 1.0], | ||
] | ||
fig = PyPlot.figure() | ||
ax = fig[:add_subplot](111, projection="3d") | ||
colors = [category_colors[c+1] for c in Int.(vertical_fracture_mask_plot)] | ||
scatter_plot = ax[:scatter](coords[1,:],coords[2,:],coords[3,:], c=colors) | ||
display(fig) | ||
println() | ||
PyPlot.close(fig) | ||
end | ||
# Set up the eigenvector parameterization of the geostatistical log-permeability field | ||
num_eigenvectors = 200 | ||
sigma = 1.0 | ||
lambda = 50 | ||
mean_log_conductivities = [log(1e-4), log(1e-8), log(1e-3)] #log(1e-4 [m/s]) -- similar to a pretty porous oil reservoir | ||
cov = GaussianRandomFields.CovarianceFunction(2, GaussianRandomFields.Matern(lambda, 1; σ=sigma)) | ||
x_pts = range(mins[1], maxs[1]; length=ns[1]) | ||
y_pts = range(mins[2], maxs[2]; length=ns[2]) | ||
grf = GaussianRandomFields.GaussianRandomField(cov, GaussianRandomFields.KarhunenLoeve(num_eigenvectors), x_pts, y_pts) | ||
parameterization = copy(grf.data.eigenfunc) | ||
sigmas = copy(grf.data.eigenval) | ||
|
||
|
||
logKs = zeros(reverse(ns)...) | ||
parameterization3D=zeros(ns[3],ns[1]*ns[2],num_eigenvectors) | ||
sigmas3D=zeros(ns[3],num_eigenvectors) | ||
logKs2d = GaussianRandomFields.sample(grf)'#generate a random realization of the log-conductivity field | ||
for i = 1:ns[3]#copy the 2d field to each of the 3d layers | ||
if i< true_ks[1] | ||
j=1 | ||
elseif i>true_ks[end] | ||
j=3 | ||
else | ||
j=2 | ||
end | ||
t=view(parameterization3D, i, :,:) | ||
t.=parameterization | ||
m=view(sigmas3D, i, :) | ||
m.=sigmas | ||
v = view(logKs, i, :, :) | ||
v .= mean_log_conductivities[j].+logKs2d | ||
end | ||
|
||
if plotstuff | ||
#plot the log-conductivity | ||
fig, ax = PyPlot.subplots() | ||
img = ax.imshow(logKs[:, 50, :], origin="lower") | ||
ax.title.set_text("Conductivity Field") | ||
fig.colorbar(img) | ||
display(fig) | ||
println() | ||
PyPlot.close(fig) | ||
end | ||
#play with these four numbers to make the fractures more or less likely to enable lots of flow from the lower reservoir to the upper reservoir | ||
horizontal_fracture_mean_conductivity = log(1e-10) | ||
vertical_fracture_mean_conductivity = log(1e-10) | ||
horizontal_fracture_sigma_conductivity = 10 | ||
vertical_fracture_sigma_conductivity = 10 | ||
function x2logKs(x) | ||
logKs = [log.(exp.(sum(masks[j][i,:,:]' .* reshape(parameterization3D[i,:,:] * (sigmas3D[i,:] .* x[(j - 1) * num_eigenvectors + 1:j * num_eigenvectors]) .+ mean_log_conductivities[j], ns[1:2]...) for j = 1:3)') + | ||
horizontal_fracture_mask[i,:,:] * exp(horizontal_fracture_mean_conductivity + horizontal_fracture_sigma_conductivity * x[end - 1]) + | ||
vertical_fracture_mask[i,:,:] * exp(vertical_fracture_mean_conductivity + vertical_fracture_sigma_conductivity * x[end])) for i in 1:ns[3]] | ||
logKs=cat(logKs..., dims=3) # concatenate along the first dimension | ||
return logKs = permutedims(logKs, (3, 2, 1)) | ||
end | ||
logKs = x2logKs(randn(3 * num_eigenvectors + 2)) | ||
if plotstuff | ||
for i=1:101 | ||
fig, axs = PyPlot.subplots(1, 1) | ||
im2=axs.imshow(logKs[:, i, :], origin="lower") | ||
display(fig); println() | ||
PyPlot.close(fig) | ||
@show i | ||
end | ||
end | ||
# Make the boundary conditions be dirichlet with a fixed head on the left and right boundaries with zero flux on the top and bottom | ||
dirichletnodes = Int[] | ||
dirichleths = zeros(size(coords, 2)) | ||
for i = 1:size(coords, 2) | ||
if coords[1, i] == width || coords[1, i] == 0 | ||
push!(dirichletnodes, i) | ||
dirichleths[i] = steadyhead | ||
end | ||
end | ||
# Set up the location of the injection and critical point | ||
function get_nodes_near(coords, obslocs) | ||
obsnodes = Array{Int}(undef, length(obslocs)) | ||
for i = 1:length(obslocs) | ||
obsnodes[i] = findmin(map(j->sum((obslocs[i] .- coords[:, j]) .^ 2), 1:size(coords, 2)))[2] | ||
end | ||
return obsnodes | ||
end | ||
|
||
injection_node, critical_point_node = get_nodes_near(coords, [[width / 2,width / 2, heights[1] / 2], [width / 2, width / 2, heights[1] + heights[2] + 0.5 * heights[3]]]) #put the critical point near (-80, -80) and the injection node near (80, 80) | ||
injection_nodes = [injection_node] | ||
|
||
# Set the Qs for the whole field to zero, then set it to the injection rate divided by the number of injection nodes at the injection nodes | ||
Qs = zeros(size(coords, 2)) | ||
Qinj = 0.031688 #injection rate [m^3/s] (1 MMT water/yr) | ||
Qs[injection_nodes] .= Qinj / length(injection_nodes) | ||
|
||
logKs2Ks_neighbors(Ks) = exp.(0.5 * (Ks[map(p->p[1], neighbors)] .+ Ks[map(p->p[2], neighbors)])) | ||
function solveforh(logKs) | ||
#@show length(logKs) | ||
@assert length(logKs) == length(Qs) | ||
Ks_neighbors = logKs2Ks_neighbors(logKs) | ||
result = DPFEHM.groundwater_steadystate(Ks_neighbors, neighbors, areasoverlengths, dirichletnodes, dirichleths, Qs; linear_solver=DPFEHM.cholesky_solver) | ||
return result | ||
end | ||
|
||
solveforheigs(x) = solveforh(x2logKs(x)) | ||
# Set up the functions that compute the pressure at the critical point and the gradient of the pressure at the critical point with respect to the eigenvector coefficients | ||
f(x) = solveforh(x2logKs(x))[critical_point_node]*9.807*997*1e-6 #convert from head (meters) to pressure (MPa) (for water 25 C) | ||
grad_f(x) = Zygote.gradient(f, x)[1] | ||
|
||
if plotstuff | ||
import Random | ||
Random.seed!(1) | ||
x_test = randn(3 * num_eigenvectors + 2) #the input to the function should follow an N(0, I) distribution -- the eigenvectors and eigenvalues are embedded inside f | ||
@show x_test[1:3] | ||
@show f(x_test) #compute the pressure at the critical location | ||
#compute the gradient of the pressure at the critical location with respect to the eigenvector coefficients (then only show the largest/smallest values so it doesn't print a huge array) | ||
@show extrema(grad_f(x_test)) | ||
P_mat=reshape(solveforh(x2logKs(x_test)), reverse(ns)...) | ||
fig, ax = PyPlot.subplots() | ||
img = ax.imshow(P_mat[:, :,50], origin="lower") | ||
ax.title.set_text("pressure Field") | ||
fig.colorbar(img) | ||
display(fig) | ||
println() | ||
PyPlot.close(fig) | ||
end | ||
|