Skip to content

Commit

Permalink
Merge pull request #58 from sintefmath/numerical-aquifers
Browse files Browse the repository at this point in the history
Support for numerical aquifers and cleanup of capillary pressure
  • Loading branch information
moyner authored Sep 22, 2024
2 parents 8ecc3cb + 72b7206 commit faf16d9
Show file tree
Hide file tree
Showing 15 changed files with 457 additions and 103 deletions.
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "JutulDarcy"
uuid = "82210473-ab04-4dce-b31b-11573c4f8e0a"
authors = ["Olav Møyner <[email protected]>"]
version = "0.2.30"
version = "0.2.31"

[deps]
Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
Expand Down Expand Up @@ -53,7 +53,7 @@ DelimitedFiles = "1.9.1"
DocStringExtensions = "0.9.3"
ForwardDiff = "0.10.30"
GLMakie = "0.10.0"
GeoEnergyIO = "1.1.6"
GeoEnergyIO = "1.1.9"
HYPRE = "1.4.0"
Jutul = "0.2.37"
Krylov = "0.9.1"
Expand Down
17 changes: 16 additions & 1 deletion docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,22 @@ DocTestSetup = quote
end
```

Documentation for [JutulDarcy.jl](https://github.com/sintefmath/JutulDarcy.jl). The documentation is currently limited to docstrings and a series of examples. The examples are sorted by complexity. We suggest you start with [Gravity segregation example](Gravity segregation example).
Documentation for [JutulDarcy.jl](https://github.com/sintefmath/JutulDarcy.jl). The documentation consists of a mixture of technical documentation, docstrings and examples. The examples are sorted by complexity. We suggest you start with [Gravity segregation example](Gravity segregation example).

To generate a folder that contains the examples locally, you can run the following code to create a folder `jutuldarcy_examples` in your current working directory:

```julia
using JutulDarcy
generate_jutuldarcy_examples()
```

Alternatively, a folder can be specified if you want the examples to be placed outside your present working directory:


```julia
using JutulDarcy
generate_jutuldarcy_examples("/home/username/")
```

JutulDarcy builds upon the general features found in [Jutul.jl](https://github.com/sintefmath/Jutul.jl). You may also find it useful to look at the [Jutul.jl documentation](https://sintefmath.github.io/Jutul.jl/dev/).

Expand Down
17 changes: 7 additions & 10 deletions examples/co2_brine_2d_vertical.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ bic = zeros(2, 2)

mixture = MultiComponentMixture([h2o, co2], A_ij = bic, names = ["H2O", "CO2"])
eos = GenericCubicEOS(mixture, PengRobinson())
#-
# ## Set up domain and wells
using Jutul, JutulDarcy, GLMakie
nx = 50
Expand All @@ -35,7 +34,6 @@ res = reservoir_domain(g, porosity = 0.3, permeability = K)
prod = setup_well(g, K, [(nx, ny, 1)], name = :Producer)
# Set up an injector in the opposite corner, perforated in bottom layer
inj = setup_well(g, K, [(1, 1, nz)], name = :Injector)
#-
# ## Define system and realize on grid
rhoLS = 844.23*kg/meter^3
rhoVS = 126.97*kg/meter^3
Expand All @@ -46,15 +44,15 @@ model, parameters = setup_reservoir_model(res, sys, wells = [inj, prod]);
push!(model[:Reservoir].output_variables, :Saturations)
kr = BrooksCoreyRelativePermeabilities(sys, 2.0, 0.0, 1.0)
model = replace_variables!(model, RelativePermeabilities = kr)
T0 = repeat([303.15*Kelvin], 1, nc)
T0 = fill(303.15*Kelvin, nc)
parameters[:Reservoir][:Temperature] = T0
state0 = setup_reservoir_state(model, Pressure = 50*bar, OverallMoleFractions = [1.0, 0.0]);

# ## Define schedule
# 5 year (5*365.24 days) simulation period
dt0 = repeat([1]*day, 26)
dt1 = repeat([10.0]*day, 180)
dt = append!(dt0, dt1)
dt0 = fill(1*day, 26)
dt1 = fill(10.0*day, 180)
dt = cat(dt0, dt1, dims = 1)
rate_target = TotalRateTarget(9.5066e-06*meter^3/sec)
I_ctrl = InjectorControl(rate_target, [0, 1], density = rhoVS)
bhp_target = BottomHolePressureTarget(50*bar)
Expand All @@ -78,18 +76,17 @@ function plot_vertical(x, t)
Colorbar(fig[1, 2], plot)
fig
end
#-

# ### Plot final CO2 mole fraction
plot_vertical(z, "CO2")
#-

# ### Plot final vapor saturation
sg = states[end][:Saturations][2, :]
plot_vertical(sg, "Vapor saturation")
#-

# ### Plot final pressure
p = states[end][:Pressure]
plot_vertical(p./bar, "Pressure [bar]")

#-
# ### Plot in interactive viewer
plot_reservoir(model, states, step = length(dt), key = :Saturations)
21 changes: 11 additions & 10 deletions examples/co2_sloped.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ Darcy, bar, kg, meter, day, yr = si_units(:darcy, :bar, :kilogram, :meter, :day,
# surface has a large impact on where the CO2 migrates.
cart_dims = (nx, 1, nz)
physical_dims = (1000.0, 1.0, 50.0)
mesh = UnstructuredMesh(CartesianMesh(cart_dims, physical_dims))
cart_mesh = CartesianMesh(cart_dims, physical_dims)
mesh = UnstructuredMesh(cart_mesh)

points = mesh.node_points
for (i, pt) in enumerate(points)
Expand Down Expand Up @@ -200,9 +201,9 @@ println("Boundary condition added to $(length(bc)) cells.")
# ## Plot the model
plot_reservoir(model)
# ## Set up schedule
# We set up 25 years of injection and 1000 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 in-situ conditions.
# 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.
nstep = 25
nstep_shut = 475
dt_inject = fill(365.0day, nstep)
Expand Down Expand Up @@ -239,10 +240,10 @@ wd, states, t = simulate_reservoir(state0, model, dt,
max_timestep = 90day
)
# ## Plot the CO2 mole fraction
# We plot log10 of the CO2 mole fraction. We use 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.
# 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.
using GLMakie
function plot_co2!(fig, ix, x, title = "")
ax = Axis3(fig[ix, 1],
Expand All @@ -251,15 +252,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)
plt = plot_cell_data!(ax, mesh, x, colormap = :seaborn_icefire_gradient, colorrange = (0.0, 0.1))
Colorbar(fig[ix, 2], plt)
end
fig = Figure(size = (900, 1200))
for (i, step) in enumerate([1, 5, nstep, 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, log10.(states[step][:OverallMoleFractions][2, :]), "log10 of CO2 mole fraction at report step $step/$(nstep+nstep_shut)")
plot_co2!(fig, i, states[step][:OverallMoleFractions][2, :], "CO2 mole fraction at report step $step/$(nstep+nstep_shut)")
end
end
fig
Expand Down
18 changes: 18 additions & 0 deletions src/deck_types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,15 @@ struct TableCompressiblePoreVolume{V, R} <: ScalarVariable
end
end

function Jutul.subvariable(p::TableCompressiblePoreVolume, map::FiniteVolumeGlobalMap)
c = map.cells
regions = Jutul.partition_variable_slice(p.regions, c)
return TableCompressiblePoreVolume(
p.tab,
regions = regions
)
end

struct ScalarPressureTable{V, R} <: ScalarVariable
tab::V
regions::R
Expand All @@ -630,6 +639,15 @@ struct ScalarPressureTable{V, R} <: ScalarVariable
end
end

function Jutul.subvariable(p::ScalarPressureTable, map::FiniteVolumeGlobalMap)
c = map.cells
regions = Jutul.partition_variable_slice(p.regions, c)
return ScalarPressureTable(
p.tab,
regions = regions
)
end

@jutul_secondary function update_variable!(pv, Φ::ScalarPressureTable, model, Pressure, ix)
@inbounds for i in ix
reg = region.regions, i)
Expand Down
21 changes: 17 additions & 4 deletions src/facility/wells/wells.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,21 @@ function common_well_setup(nr; dz = nothing, WI = nothing, gravity = gravity_con
end

"""
setup_well(D::DataDomain, reservoir_cells; skin = 0.0, Kh = nothing, radius = 0.1, dir = :z)
setup_well(D::DataDomain, reservoir_cells; skin = 0.0, Kh = nothing, radius = 0.1, dir = :z, name = :Well)
w = setup_well(D, 1, name = :MyWell) # Cell 1 in the grid
w = setup_well(D, (2, 5, 1), name = :MyWell) # Position (2, 5, 1) in logically structured mesh
w2 = setup_well(D, [1, 2, 3], name = :MyOtherWell)
Set up a well in `reservoir_cells` with given skin factor and radius. The order
of cells matter as it is treated as a trajectory.
The `name` keyword argument can be left defaulted if your model will only have a
single well (named `:Well`). It is highly recommended to provide this whenever a
well is set up.
`reservoir_cells` can be one of the following: A Vector of cells, a single cell,
a Vector of `(I, J, K)` Tuples or a single Tuple of the same type.
"""
function setup_well(D::DataDomain, reservoir_cells; cell_centers = D[:cell_centroids], kwarg...)
K = D[:permeability]
Expand Down Expand Up @@ -205,10 +216,12 @@ function Jutul.plot_primitives(mesh::MultiSegmentWell, plot_type; kwarg...)
end

"""
setup_vertical_well(D::DataDomain, i, j; <kwarg>)
setup_vertical_well(D::DataDomain, i, j; name = :MyWell, <kwarg>)
Set up a vertical well with a `DataDomain` input that represents the porous
medium / reservoir where the wells it to be placed.
Set up a vertical well with a [`DataDomain`](@ref) input that represents the porous
medium / reservoir where the wells it to be placed. See [`SimpleWell`](@ref),
[`MultiSegmentWell`](@ref) and [`setup_well`](@ref) for more details about possible keyword
arguments.
"""
function setup_vertical_well(D::DataDomain, i, j; cell_centers = D[:cell_centroids], kwarg...)
K = D[:permeability]
Expand Down
48 changes: 12 additions & 36 deletions src/init/init.jl
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ function equilibriate_state!(init, depths, model, sys, contacts, depth, datum_pr
return phase_density
end
# Find the reference phase. It is either liquid
ref_phase = find_reference_phase(model.system)
ref_phase = get_reference_phase_index(model.system)
pressures = determine_hydrostatic_pressures(depths, depth, zmin, zmax, contacts, datum_pressure, density_f, contacts_pc, ref_phase)
if nph > 1
relperm = model.secondary_variables[:RelativePermeabilities]
Expand Down Expand Up @@ -234,42 +234,21 @@ function equilibriate_state!(init, depths, model, sys, contacts, depth, datum_pr
return init
end

function find_reference_phase(system)
mphases = get_phases(system)
function find_phase(k)
ix = 0
for (i, ph) in enumerate(mphases)
if ph isa LiquidPhase
ix = i
break
end
end
return ix
end
l = find_phase(LiquidPhase)
a = find_phase(AqueousPhase)
if l > 0
phase = l
elseif a > 0
phase = a
else
# Last phase is water or something custom, send out 1.
phase = 1
end
return phase
end


function parse_state0_equil(model, datafile; normalize = :sum)
sys = model.system
d = model.data_domain

has_water = haskey(datafile["RUNSPEC"], "WATER")
has_oil = haskey(datafile["RUNSPEC"], "OIL")
has_gas = haskey(datafile["RUNSPEC"], "GAS")
if sys isa CompositionalSystem
has_oil = has_gas = true
else
has_oil = haskey(datafile["RUNSPEC"], "OIL")
has_gas = haskey(datafile["RUNSPEC"], "GAS")
end

is_co2 = haskey(datafile["RUNSPEC"], "JUTUL_CO2BRINE")
is_single_phase = (has_water + has_oil + has_gas) == 1

sys = model.system
d = model.data_domain
has_sat_reg = haskey(d, :satnum)
ncells = number_of_cells(d)
if has_sat_reg
Expand Down Expand Up @@ -392,9 +371,6 @@ function parse_state0_equil(model, datafile; normalize = :sum)
cap = cap[2:end]
end
ix = unique(i -> cap[i], 1:length(cap))
if i == 1 && get_phases(model.system)[1] isa AqueousPhase
@. cap *= -1
end
s = s[ix]
cap = cap[ix]
if length(s) == 1
Expand Down Expand Up @@ -446,11 +422,11 @@ function parse_state0_equil(model, datafile; normalize = :sum)
contacts_pc = (goc_pc, )
else
contacts = (woc, )
contacts_pc = (-woc_pc, )
contacts_pc = (woc_pc, )
end
else
contacts = (woc, goc)
contacts_pc = (-woc_pc, goc_pc)
contacts_pc = (woc_pc, goc_pc)
end

if disgas || vapoil
Expand Down
Loading

4 comments on commit faf16d9

@moyner
Copy link
Member Author

@moyner moyner commented on faf16d9 Sep 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/115687

Tip: Release Notes

Did you know you can add release notes too? Just add markdown formatted text underneath the comment after the text
"Release notes:" and it will be added to the registry PR, and if TagBot is installed it will also be added to the
release that TagBot creates. i.e.

@JuliaRegistrator register

Release notes:

## Breaking changes

- blah

To add them here just re-invoke and the PR will be updated.

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.2.31 -m "<description of version>" faf16d922c9d668af314fe1673cfb5dbfbd153b0
git push origin v0.2.31

@moyner
Copy link
Member Author

@moyner moyner commented on faf16d9 Sep 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register

Release notes:

Updates

  • Support for numerical aquifers
  • Better validation of static reservoir properties
  • Cleaned up capillary pressure definitions for two-phase scenarios
  • Fix generate_jutuldarcy_examples read-only issue
  • Improved docs

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request updated: JuliaRegistries/General/115687

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.2.31 -m "<description of version>" faf16d922c9d668af314fe1673cfb5dbfbd153b0
git push origin v0.2.31

Please sign in to comment.