Skip to content

SPE10, model 2

Introduction   InputFile  

The SPE10 benchmark case is a standard benchmark case for reservoir simulators. The model is described in detail in the SPE10 benchmark page

This example demonstrates routines to set up the SPE10, model 2, case using premade functions in the JutulDarcy.SPE10 module. This model is often just called SPE10, since the first model of the benchmark is very small.

Set up the reservoir

We can set up the reservoir itself to have a look at the static properties.

julia
using JutulDarcy, GLMakie, HYPRE
reservoir = JutulDarcy.SPE10.setup_reservoir()
plot_reservoir(reservoir, key = :porosity)

Set up and run a simulation for the first layer

We can set up and run a full simulation for the first layer of the model. As we want the example to run quickly, we just pick the top layer. The function is set up to scale the default well rates to the smaller model, so you can adjust the number of layers without changing anything else.

julia
case = JutulDarcy.SPE10.setup_case(layers = 1:1)
Jutul case with 100 time-steps (5 years, 24 weeks, 5.787 days) and constant forces for all steps.

Model:

MultiModel with 7 models and 15 cross-terms. 26223 equations, 26223 degrees of freedom and 77841 parameters.

  models:
    1) Reservoir (26208x26208)
       ImmiscibleSystem with AqueousPhase, LiquidPhase
       ∈ MinimalTPFATopology (13104 cells, 25809 faces)
    2) I1 (2x2)
       ImmiscibleSystem with AqueousPhase, LiquidPhase
       ∈ SimpleWell [I1] (1 nodes, 0 segments, 1 perforations)
    3) P1 (2x2)
       ImmiscibleSystem with AqueousPhase, LiquidPhase
       ∈ SimpleWell [P1] (1 nodes, 0 segments, 1 perforations)
    4) P2 (2x2)
       ImmiscibleSystem with AqueousPhase, LiquidPhase
       ∈ SimpleWell [P2] (1 nodes, 0 segments, 1 perforations)
    5) P3 (2x2)
       ImmiscibleSystem with AqueousPhase, LiquidPhase
       ∈ SimpleWell [P3] (1 nodes, 0 segments, 1 perforations)
    6) P4 (2x2)
       ImmiscibleSystem with AqueousPhase, LiquidPhase
       ∈ SimpleWell [P4] (1 nodes, 0 segments, 1 perforations)
    7) Facility (5x5)
       JutulDarcy.PredictionMode()
       ∈ WellGroup([:I1, :P1, :P2, :P3, :P4], true, true)

  cross_terms:
    1) I1 <-> Reservoir (Eqs: mass_conservation <-> mass_conservation)
       JutulDarcy.ReservoirFromWellFlowCT
    2) P1 <-> Reservoir (Eqs: mass_conservation <-> mass_conservation)
       JutulDarcy.ReservoirFromWellFlowCT
    3) P2 <-> Reservoir (Eqs: mass_conservation <-> mass_conservation)
       JutulDarcy.ReservoirFromWellFlowCT
    4) P3 <-> Reservoir (Eqs: mass_conservation <-> mass_conservation)
       JutulDarcy.ReservoirFromWellFlowCT
    5) P4 <-> Reservoir (Eqs: mass_conservation <-> mass_conservation)
       JutulDarcy.ReservoirFromWellFlowCT
    6) I1  -> Facility (Eq: control_equation)
       JutulDarcy.FacilityFromWellFlowCT
    7) Facility  -> I1 (Eq: mass_conservation)
       JutulDarcy.WellFromFacilityFlowCT
    8) P1  -> Facility (Eq: control_equation)
       JutulDarcy.FacilityFromWellFlowCT
    9) Facility  -> P1 (Eq: mass_conservation)
       JutulDarcy.WellFromFacilityFlowCT
    10) P2  -> Facility (Eq: control_equation)
       JutulDarcy.FacilityFromWellFlowCT
    11) Facility  -> P2 (Eq: mass_conservation)
       JutulDarcy.WellFromFacilityFlowCT
    12) P3  -> Facility (Eq: control_equation)
       JutulDarcy.FacilityFromWellFlowCT
    13) Facility  -> P3 (Eq: mass_conservation)
       JutulDarcy.WellFromFacilityFlowCT
    14) P4  -> Facility (Eq: control_equation)
       JutulDarcy.FacilityFromWellFlowCT
    15) Facility  -> P4 (Eq: mass_conservation)
       JutulDarcy.WellFromFacilityFlowCT

Model storage will be optimized for runtime performance.

Plot the porosity

Note that some cells are removed due to very low porosity. The setup function has additional options to control this behavior if you would rather limit the minimum porosity than removing cells.

julia
plot_reservoir(case.model, key = :porosity)

Run the simulation

julia
ws, states = simulate_reservoir(case)
ReservoirSimResult with 100 entries:

  wells (5 present):
    :P1
    :P3
    :P4
    :I1
    :P2
    Results per well:
       :wrat => Vector{Float64} of size (100,)
       :Aqueous_mass_rate => Vector{Float64} of size (100,)
       :orat => Vector{Float64} of size (100,)
       :bhp => Vector{Float64} of size (100,)
       :mrat => Vector{Float64} of size (100,)
       :lrat => Vector{Float64} of size (100,)
       :mass_rate => Vector{Float64} of size (100,)
       :rate => Vector{Float64} of size (100,)
       :control => Vector{Symbol} of size (100,)
       :Liquid_mass_rate => Vector{Float64} of size (100,)
       :wcut => Vector{Float64} of size (100,)

  states (Vector with 100 entries, reservoir variables for each state)
    :Pressure => Vector{Float64} of size (13104,)
    :Saturations => Matrix{Float64} of size (2, 13104)
    :TotalMasses => Matrix{Float64} of size (2, 13104)

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

  result (extended states, reports)
     SimResult with 100 entries

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

  Completed at Nov. 26 2025 07:18 after 14 seconds, 647 milliseconds, 756 microseconds.

Plot the final saturation

julia
plot_reservoir(case.model, states, key = :Saturations, step = length(states))

Show the last layer

The first 35 layers correspond to the Tarbert formation and the latter 50 layers are the upper ness formation. We plot one of the last layers, showing a channelized, fluvial structure.

julia
reservoir_ness = JutulDarcy.SPE10.setup_reservoir(layers = 60)
plot_reservoir(reservoir_ness, key = :porosity)

Coarsen the model

The full model is quite large, with 1.1 million cells. We can coarsen the model to get a smaller model that is faster to simulate. The original model was intended as a benchmark for upscaling methods, even though such models are now quite easy to simulate when using parallel computing on CPU/GPU.

The default coarsening is quite simple (harmonic averages and sums), but quickly sets up a coarse model that can be used for testing.

julia
case_fine = JutulDarcy.SPE10.setup_case()
case_coarse = coarsen_reservoir_case(case_fine, (10, 22, 8))
Jutul case with 100 time-steps (5 years, 24 weeks, 5.787 days) and constant forces for all steps.

Model:

MultiModel with 7 models and 15 cross-terms. 4017 equations, 4017 degrees of freedom and 16073 parameters.

  models:
    1) Reservoir (4002x4002)
       ImmiscibleSystem with AqueousPhase, LiquidPhase
       ∈ MinimalTPFATopology (2001 cells, 5993 faces)
    2) I1 (2x2)
       ImmiscibleSystem with AqueousPhase, LiquidPhase
       ∈ SimpleWell [I1] (1 nodes, 0 segments, 8 perforations)
    3) P1 (2x2)
       ImmiscibleSystem with AqueousPhase, LiquidPhase
       ∈ SimpleWell [P1] (1 nodes, 0 segments, 8 perforations)
    4) P2 (2x2)
       ImmiscibleSystem with AqueousPhase, LiquidPhase
       ∈ SimpleWell [P2] (1 nodes, 0 segments, 8 perforations)
    5) P3 (2x2)
       ImmiscibleSystem with AqueousPhase, LiquidPhase
       ∈ SimpleWell [P3] (1 nodes, 0 segments, 8 perforations)
    6) P4 (2x2)
       ImmiscibleSystem with AqueousPhase, LiquidPhase
       ∈ SimpleWell [P4] (1 nodes, 0 segments, 8 perforations)
    7) Facility (5x5)
       JutulDarcy.PredictionMode()
       ∈ WellGroup([:I1, :P1, :P2, :P3, :P4], true, true)

  cross_terms:
    1) I1 <-> Reservoir (Eqs: mass_conservation <-> mass_conservation)
       JutulDarcy.ReservoirFromWellFlowCT
    2) P1 <-> Reservoir (Eqs: mass_conservation <-> mass_conservation)
       JutulDarcy.ReservoirFromWellFlowCT
    3) P2 <-> Reservoir (Eqs: mass_conservation <-> mass_conservation)
       JutulDarcy.ReservoirFromWellFlowCT
    4) P3 <-> Reservoir (Eqs: mass_conservation <-> mass_conservation)
       JutulDarcy.ReservoirFromWellFlowCT
    5) P4 <-> Reservoir (Eqs: mass_conservation <-> mass_conservation)
       JutulDarcy.ReservoirFromWellFlowCT
    6) I1  -> Facility (Eq: control_equation)
       JutulDarcy.FacilityFromWellFlowCT
    7) Facility  -> I1 (Eq: mass_conservation)
       JutulDarcy.WellFromFacilityFlowCT
    8) P1  -> Facility (Eq: control_equation)
       JutulDarcy.FacilityFromWellFlowCT
    9) Facility  -> P1 (Eq: mass_conservation)
       JutulDarcy.WellFromFacilityFlowCT
    10) P2  -> Facility (Eq: control_equation)
       JutulDarcy.FacilityFromWellFlowCT
    11) Facility  -> P2 (Eq: mass_conservation)
       JutulDarcy.WellFromFacilityFlowCT
    12) P3  -> Facility (Eq: control_equation)
       JutulDarcy.FacilityFromWellFlowCT
    13) Facility  -> P3 (Eq: mass_conservation)
       JutulDarcy.WellFromFacilityFlowCT
    14) P4  -> Facility (Eq: control_equation)
       JutulDarcy.FacilityFromWellFlowCT
    15) Facility  -> P4 (Eq: mass_conservation)
       JutulDarcy.WellFromFacilityFlowCT

Model storage will be optimized for runtime performance.

Simulate the coarse model

The now quite coarse model should run in less than a second.

julia
ws_coarse, states_coarse = simulate_reservoir(case_coarse);
plot_reservoir(case_coarse.model, states_coarse, key = :Saturations, step = length(states_coarse))

Conclusion

The SPE10, model 2, case is a standard benchmark case for reservoir simulators. JutulDarcy includes premade functions to set up and run this model, including wells, PVT, and the schedule from the original case. The model can be run as-is, or coarsened to a smaller size for testing and development.

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 140.04827739 seconds to complete.

This page was generated using Literate.jl.