Skip to content

Commit

Permalink
Merge pull request #62 from sintefmath/salinity
Browse files Browse the repository at this point in the history
Support for salts in CO2 model, new property codes for CO2 and brine based on work by Lluis Salo
  • Loading branch information
moyner committed Sep 24, 2024
2 parents 6cb6304 + bd90978 commit f7c7ced
Show file tree
Hide file tree
Showing 9 changed files with 927 additions and 34 deletions.
12 changes: 7 additions & 5 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
name = "JutulDarcy"
uuid = "82210473-ab04-4dce-b31b-11573c4f8e0a"
authors = ["Olav Møyner <[email protected]>"]
version = "0.2.31"
version = "0.2.32"

[deps]
Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
AlgebraicMultigrid = "2169fc97-5a83-5252-b627-83903c6c433c"
Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab"
Expand All @@ -21,6 +21,7 @@ MAT = "23992714-dd62-5051-b70f-ba57cb901cac"
MultiComponentFlash = "35e5bd01-9722-4017-9deb-64a5d32478ff"
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588"
Polynomials = "f27b6e38-b328-58d1-80ce-0feddd5e7a45"
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca"
Expand All @@ -35,8 +36,8 @@ Tullio = "bc48ee85-29a4-5162-ae0b-a64e1601d4bc"
[weakdeps]
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
HYPRE = "b5ffcf37-a2bd-41ab-a3da-4bd9bc8ad771"
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195"
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
PartitionedArrays = "5a9dfac6-5c52-46f7-8278-5e2210713be9"

[extensions]
Expand All @@ -45,15 +46,15 @@ JutulDarcyMakieExt = "Makie"
JutulDarcyPartitionedArraysExt = ["PartitionedArrays", "MPI", "HYPRE"]

[compat]
Artifacts = "1"
AlgebraicMultigrid = "0.5.1, 0.6.0"
Artifacts = "1"
DataStructures = "0.18.13"
Dates = "1"
DelimitedFiles = "1.6"
DocStringExtensions = "0.9.3"
ForwardDiff = "0.10.30"
GLMakie = "0.10.0"
GeoEnergyIO = "1.1.9"
GeoEnergyIO = "1.1.10"
HYPRE = "1.4.0"
Jutul = "0.2.37"
Krylov = "0.9.1"
Expand All @@ -65,6 +66,7 @@ MultiComponentFlash = "1.1.15"
OrderedCollections = "1.6.2"
PartitionedArrays = "0.3.2"
Polyester = "0.6.11, 0.7.3"
Polynomials = "3, 4"
PrecompileTools = "1.0.1"
ProgressMeter = "1.7.2"
SparseArrays = "1"
Expand Down
63 changes: 40 additions & 23 deletions examples/co2_sloped.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Darcy, bar, kg, meter, day, yr = si_units(:darcy, :bar, :kilogram, :meter, :day,
cart_dims = (nx, 1, nz)
physical_dims = (1000.0, 1.0, 50.0)
cart_mesh = CartesianMesh(cart_dims, physical_dims)
mesh = UnstructuredMesh(cart_mesh)
mesh = UnstructuredMesh(cart_mesh, z_is_depth = true)

points = mesh.node_points
for (i, pt) in enumerate(points)
Expand All @@ -42,14 +42,14 @@ end;
# intersected by the trajectory.
import Jutul: find_enclosing_cells, plot_mesh_edges
trajectory = [
745.0 0.5 45; # First point
760.0 0.5 70; # Second point
810.0 0.5 100.0 # Third point
645.0 0.5 75; # First point
660.0 0.5 85; # Second point
710.0 0.5 100.0 # Third point
]

wc = find_enclosing_cells(mesh, trajectory)

fig, ax, plt = plot_mesh_edges(mesh, z_is_depth = true)
fig, ax, plt = plot_mesh_edges(mesh)
plot_mesh!(ax, mesh, cells = wc, transparency = true, alpha = 0.4)
# View from the side
ax.azimuth[] = 1.5*π
Expand Down Expand Up @@ -88,7 +88,8 @@ for cell in 1:nc
region[cell] = reg
end

plot_cell_data(mesh, poro)
fig, ax, plt = plot_cell_data(mesh, poro)
fig
# ## Set up simulation model
# We set up a domain and a single injector. We pass the special :co2brine
# argument in place of the system to the reservoir model setup routine. This
Expand All @@ -98,16 +99,26 @@ plot_cell_data(mesh, poro)
# Note that this model by default is isothermal, but we still need to specify a
# temperature when setting up the model. This is because the properties of CO2
# strongly depend on temperature, even when thermal transport is not solved.
#
# The model also accounts for a constant, reservoir-wide salinity. We input mole
# fractions of salts in the brine so that the solubilities, densities and
# viscosities for brine cells are corrected in the property model.

domain = reservoir_domain(mesh, permeability = perm, porosity = poro, temperature = convert_to_si(30.0, :Celsius))
domain = reservoir_domain(mesh, permeability = perm, porosity = poro, temperature = convert_to_si(60.0, :Celsius))
Injector = setup_well(domain, wc, name = :Injector, simple_well = true)

if use_immiscible
physics = :immiscible
else
physics = :kvalue
end
model = setup_reservoir_model(domain, :co2brine, wells = Injector, extra_out = false, co2_physics = physics);
model = setup_reservoir_model(domain, :co2brine,
wells = Injector,
extra_out = false,
salt_names = ["NaCl", "KCl", "CaSO4", "CaCl2", "MgSO4", "MgCl2"],
salt_mole_fractions = [0.01, 0.005, 0.005, 0.001, 0.0002, 1e-5],
co2_physics = physics
);
# ## Customize model by adding relative permeability with hysteresis
# We define three relative permeability functions: kro(so) for the brine/liquid
# phase and krg(g) for both drainage and imbibition. Here we limit the
Expand All @@ -121,10 +132,10 @@ so = range(0, 1, 10)
krog_t = so.^2
krog = PhaseRelativePermeability(so, krog_t, label = :og)

# Higher resolution for second table
# Higher resolution for second table:
sg = range(0, 1, 50)

# Evaluate Brooks-Corey to generate tables
# Evaluate Brooks-Corey to generate tables:
tab_krg_drain = brooks_corey_relperm.(sg, n = 2, residual = 0.1)
tab_krg_imb = brooks_corey_relperm.(sg, n = 3, residual = 0.25)

Expand Down Expand Up @@ -169,7 +180,9 @@ nc = number_of_cells(mesh)
p0 = zeros(nc)
depth = domain[:cell_centroids][3, :]
g = Jutul.gravity_constant
@. p0 = 200bar + depth*g*1000.0
@. p0 = 160bar + depth*g*1000.0
fig, ax, plt = plot_cell_data(mesh, p0)
fig
# Set up initial state and parameters
if use_immiscible
state0 = setup_reservoir_state(model,
Expand Down Expand Up @@ -202,17 +215,17 @@ println("Boundary condition added to $(length(bc)) cells.")
plot_reservoir(model)
# ## Set up schedule
# We set up 25 years of injection and 475 years of migration where the well is
# shut. The density of the injector is set to 900 kg/m^3, which is roughly the
# density of CO2 at these high-pressure in-situ conditions.
# shut. The density of the injector is set to 630 kg/m^3, which is roughly the
# density of CO2 at the in-situ conditions.
nstep = 25
nstep_shut = 475
dt_inject = fill(365.0day, nstep)
pv = pore_volume(model, parameters)
inj_rate = 0.05*sum(pv)/sum(dt_inject)
inj_rate = 0.075*sum(pv)/sum(dt_inject)

rate_target = TotalRateTarget(inj_rate)
I_ctrl = InjectorControl(rate_target, [0.0, 1.0],
density = 900.0,
density = 630.0,
)
# Set up forces for use in injection
controls = Dict(:Injector => I_ctrl)
Expand Down Expand Up @@ -240,10 +253,14 @@ wd, states, t = simulate_reservoir(state0, model, dt,
max_timestep = 90day
)
# ## Plot the CO2 mole fraction
# We plot the overall CO2 mole fraction. We scale the color range to account for
# the fact that the mole fraction in cells made up of only the aqueous phase is
# much smaller than that of cells with only the gaseous phase, where there is
# almost just CO2.
# We plot the overall CO2 mole fraction. We scale the color range to log10 to
# account for the fact that the mole fraction in cells made up of only the
# aqueous phase is much smaller than that of cells with only the gaseous phase,
# where there is almost just CO2.
#
# The aquifer gives some degree of passive flow through the domain, ensuring
# that much of the dissolved CO2 will leave the reservoir by the end of the
# injection period.
using GLMakie
function plot_co2!(fig, ix, x, title = "")
ax = Axis3(fig[ix, 1],
Expand All @@ -252,15 +269,15 @@ function plot_co2!(fig, ix, x, title = "")
elevation = 0.05,
aspect = (1.0, 1.0, 0.3),
title = title)
plt = plot_cell_data!(ax, mesh, x, colormap = :seaborn_icefire_gradient, colorrange = (0.0, 0.1))
plt = plot_cell_data!(ax, mesh, x, colormap = :seaborn_icefire_gradient)
Colorbar(fig[ix, 2], plt)
end
fig = Figure(size = (900, 1200))
for (i, step) in enumerate([1, 5, nstep, nstep+nstep_shut])
for (i, step) in enumerate([5, nstep, nstep + Int(floor(nstep_shut/2)), nstep+nstep_shut])
if use_immiscible
plot_co2!(fig, i, states[step][:Saturations][2, :], "CO2 plume saturation at report step $step/$(nstep+nstep_shut)")
else
plot_co2!(fig, i, states[step][:OverallMoleFractions][2, :], "CO2 mole fraction at report step $step/$(nstep+nstep_shut)")
plot_co2!(fig, i, log10.(states[step][:OverallMoleFractions][2, :]), "log10 of CO2 mole fraction at report step $step/$(nstep+nstep_shut)")
end
end
fig
Expand Down Expand Up @@ -347,7 +364,7 @@ lines(t./yr, p_avg,
# show the ellipsoid, the mass of CO2 in that region for a specific step, and
# the time series of the CO2 in the same region.

stepno = 100
stepno = 30
co2_mass_in_region = map(
state -> sum(state[:TotalMasses][2, is_inside])/1e3,
states
Expand Down
4 changes: 3 additions & 1 deletion src/CO2Properties/CO2Properties.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
module CO2Properties
using Jutul, JutulDarcy, StaticArrays, MultiComponentFlash, DelimitedFiles, Artifacts, LazyArtifacts
using Jutul, JutulDarcy, StaticArrays, MultiComponentFlash, DelimitedFiles, Artifacts, LazyArtifacts, Polynomials

include("kvalues.jl")
include("props.jl")
include("reading.jl")
include("setup.jl")
include("generation.jl")

function JutulDarcy.setup_reservoir_model(reservoir::DataDomain, ::Val{:co2brine}; kwarg...)
return setup_reservoir_model_co2_brine(reservoir; kwarg...)
Expand Down
Loading

0 comments on commit f7c7ced

Please sign in to comment.