Skip to content

Norne: Real field black-oil model

The Norne model is a real field model. The model has been adapted so that the input file only contains features present in JutulDarcy, with the most notable omissions being removal of hysteresis and threshold pressures between equilibriation reqgions. For more details, see the OPM data webpage

julia
using Jutul, JutulDarcy, GLMakie, DelimitedFiles, HYPRE, GeoEnergyIO
norne_dir = GeoEnergyIO.test_input_file_path("NORNE_NOHYST")
data_pth = joinpath(norne_dir, "NORNE_NOHYST.DATA")
data = parse_data_file(data_pth)
case = setup_case_from_data_file(data);
 Downloading artifact: NORNE_NOHYST
F-4H completion: Removed COMPDAT as (36, 68, 17) is not active in processed mesh.
Rel. Perm. Scaling: Three-point scaling active.
Shutting D-1H: Well has no open perforations at step 137, shutting.
Initialization: Negative saturation in 215 cells for phase 2. Normalizing.
Transmissibility: Replaced 2 non-finite half-transmissibilities (out of 267694, 0.0%) with zero.
Transmissibility: Replaced 2 non-finite half-transmissibilities (out of 267694, 0.0%) with zero.

Unpack the case to see basic data structures

julia
model = case.model
parameters = case.parameters
forces = case.forces
dt = case.dt;

Plot the reservoir mesh, wells and faults

We compose a few different plotting calls together to make a plot that shows the outline of the mesh, the fault structures and the well trajectories.

julia
import Jutul: plot_mesh_edges!
reservoir = reservoir_domain(model)
mesh = physical_representation(reservoir)
wells = get_model_wells(model)
fig = Figure(size = (1200, 800))
ax = Axis3(fig[1, 1], zreversed = true)
plot_mesh_edges!(ax, mesh, alpha = 0.5)
for (k, w) in wells
    plot_well!(ax, mesh, w)
end
plot_faults!(ax, mesh, alpha = 0.5)
ax.azimuth[] = -3.0
ax.elevation[] = 0.5
fig

Plot the reservoir static properties in interactive viewer

julia
fig = plot_reservoir(model, key = :porosity)
ax = fig.current_axis[]
plot_faults!(ax, mesh, alpha = 0.5)
ax.azimuth[] = -3.0
ax.elevation[] = 0.5
fig

Simulate the model

julia
ws, states = simulate_reservoir(case, output_substates = true)
ReservoirSimResult with 467 entries:

  wells (36 present):
    :D-2H
    :F-2H
    :D-4H
    :B-1H
    :C-4H
    :F-1H
    :B-4AH
    :C-1H
    :B-2H
    :E-1H
    :B-4BH
    :D-3AH
    :D-3BH
    :C-3H
    :E-3H
    :K-3H
    :E-4AH
    :D-1H
    :B-1AH
    :E-3CH
    :E-4H
    :E-2H
    :E-2AH
    :C-4AH
    :B-1BH
    :C-2H
    :B-4DH
    :D-3H
    :E-3AH
    :D-4AH
    :B-3H
    :F-3H
    :E-3BH
    :D-1CH
    :F-4H
    :B-4H
    Results per well:
       :wrat => Vector{Float64} of size (467,)
       :Aqueous_mass_rate => Vector{Float64} of size (467,)
       :orat => Vector{Float64} of size (467,)
       :bhp => Vector{Float64} of size (467,)
       :gor => Vector{Float64} of size (467,)
       :lrat => Vector{Float64} of size (467,)
       :mass_rate => Vector{Float64} of size (467,)
       :rate => Vector{Float64} of size (467,)
       :Vapor_mass_rate => Vector{Float64} of size (467,)
       :control => Vector{Symbol} of size (467,)
       :Liquid_mass_rate => Vector{Float64} of size (467,)
       :wcut => Vector{Float64} of size (467,)
       :grat => Vector{Float64} of size (467,)

  states (Vector with 467 entries, reservoir variables for each state)
    :Rv => Vector{Float64} of size (44417,)
    :BlackOilUnknown => Vector{BlackOilX{Float64}} of size (44417,)
    :Saturations => Matrix{Float64} of size (3, 44417)
    :Pressure => Vector{Float64} of size (44417,)
    :Rs => Vector{Float64} of size (44417,)
    :ImmiscibleSaturation => Vector{Float64} of size (44417,)
    :TotalMasses => Matrix{Float64} of size (3, 44417)

  time (report time for each state)
     Vector{Float64} of length 467

  result (extended states, reports)
     SimResult with 247 entries

  extra
     Dict{Any, Any} with keys :simulator, :config

  Completed at Apr. 28 2025 9:04 after 9 minutes, 41 seconds, 669.2 milliseconds.

Plot the reservoir solution

julia
fig = plot_reservoir(model, states, step = 247, key = :Saturations)
ax = fig.current_axis[]
ax.azimuth[] = -3.0
ax.elevation[] = 0.5
fig

Load reference and set up plotting

julia
csv_path = joinpath(norne_dir, "REFERENCE.CSV")
data_ref, header = readdlm(csv_path, ',', header = true)
time_ref = data_ref[:, 1]
time_jutul = deepcopy(ws.time)
wells = deepcopy(ws.wells)
wnames = collect(keys(wells))
nw = length(wnames)
day = si_unit(:day)
cmap = :tableau_hue_circle

inj = Symbol[]
prod = Symbol[]
for (wellname, well) in pairs(wells)
    qts = well[:wrat] + well[:orat] + well[:grat]
    if sum(qts) > 0
        push!(inj, wellname)
    else
        push!(prod, wellname)
    end
end

function plot_well_comparison(response, well_names, reponse_name = "$response"; cumulative = false)
    fig = Figure(size = (1000, 400))
    if response == :bhp
        ys = 1/si_unit(:bar)
        yl = "Bottom hole pressure / Bar"
    elseif response == :wrat
        ys = si_unit(:day)
        if cumulative
            yl = "Cumulative water rate / m³"
        else
            yl = "Water rate / m³/day"
        end
    elseif response == :grat
        ys = si_unit(:day)/1e6
        if cumulative
            yl = "Cumulative gas rate / 10⁶ m³"
        else
            yl = "Gas rate / 10⁶ m³/day"
        end
    elseif response == :orat
        ys = si_unit(:day)/(1000*si_unit(:stb))
        if cumulative
            yl = "Cumulative oil rate / 10³ stb"
        else
            yl = "Oil rate / 10³ stb/day"
        end
    else
        error("$response not ready.")
    end
    welltypes = []
    ax = Axis(fig[1:4, 1], xlabel = "Time / days", ylabel = yl)
    i = 1
    linehandles = []
    linelabels = []
    for well_name in well_names
        well = wells[well_name]
        label_in_csv = "$well_name:$response"
        ref_pos = findfirst(x -> x == label_in_csv, vec(header))
        qoi = copy(well[response]).*ys
        qoi_ref = data_ref[:, ref_pos].*ys
        tot_rate = copy(well[:rate])
        grat_ref = data_ref[:, findfirst(x -> x == "$well_name:grat", vec(header))]
        orat_ref = data_ref[:, findfirst(x -> x == "$well_name:orat", vec(header))]
        wrat_ref = data_ref[:, findfirst(x -> x == "$well_name:wrat", vec(header))]
        tot_rate_ref = grat_ref + orat_ref + wrat_ref
        if cumulative
            @. qoi_ref[tot_rate_ref == 0] = 0
            @. qoi[tot_rate == 0] = 0
            qoi_ref = cumsum(qoi_ref.*diff([0, time_ref...]./day))
            qoi = cumsum(qoi.*diff([0, time_jutul...]./day))
        else
            @. qoi_ref[tot_rate_ref == 0] = NaN
            @. qoi[tot_rate == 0] = NaN
        end
        crange = (1, max(length(well_names), 2))
        lh = lines!(ax, time_jutul./day, abs.(qoi),
            color = i,
            colorrange = crange,
            label = "$well_name", colormap = cmap
        )
        push!(linehandles, lh)
        push!(linelabels, "$well_name")
        lines!(ax, time_ref./day, abs.(qoi_ref),
            color = i,
            colorrange = crange,
            linestyle = :dash,
            colormap = cmap
        )
        i += 1
    end
    l1 = LineElement(color = :black, linestyle = nothing)
    l2 = LineElement(color = :black, linestyle = :dash)

    Legend(fig[1:3, 2], linehandles, linelabels, nbanks = 3)
    Legend(fig[4, 2], [l1, l2], ["JutulDarcy.jl", "OPM Flow"])
    fig
end
plot_well_comparison (generic function with 2 methods)

Injector bhp

julia
plot_well_comparison(:bhp, inj, "Bottom hole pressure")

Gas injection rates

Rates

julia
plot_well_comparison(:grat, inj, "Gas surface injection rate")

Cumulative gas injection rates

julia
plot_well_comparison(:grat, inj, "Cumulative gas surface injection rate", cumulative = true)

Water injection rates

Rates

julia
plot_well_comparison(:wrat, inj, "Water surface injection rate")

Cumulative rates

julia
plot_well_comparison(:wrat, inj, "Cumulative water surface injection rate", cumulative = true)

Producer bhp

julia
plot_well_comparison(:bhp, prod, "Bottom hole pressure")

Oil production rates

Rates

julia
plot_well_comparison(:orat, prod, "Oil surface production rate")

Cumulative rates

julia
plot_well_comparison(:orat, prod, "Cumulative oil surface production rate", cumulative = true)

Gas production rates

Rates

julia
plot_well_comparison(:grat, prod, "Gas surface production rate")

Cumulative rates

julia
plot_well_comparison(:grat, prod, "Cumulative gas surface production rate", cumulative = true)

Water production rates

Rates

julia
plot_well_comparison(:wrat, prod, "Water surface production rate")

Cumulative rates

julia
plot_well_comparison(:wrat, prod, "Cumulative water surface production rate", cumulative = true)

Interactive plotting of field statistics

julia
plot_reservoir_measurables(case, ws, states, left = :fgpr, right = :pres)

Plot wells

julia
plot_well_results(ws)

Example on GitHub

If you would like to run this example yourself, it can be downloaded from the JutulDarcy.jl GitHub repository as a script, or as a Jupyter Notebook

This example took 655.3801298 seconds to complete.

This page was generated using Literate.jl.