High-level API

Setup

Meshes

Reservoir

JutulDarcy.reservoir_domainFunction
reservoir_domain(g; permeability = convert_to_si(0.1, :darcy), porosity = 0.1, kwarg...)

Set up a DataDomain instance for given mesh or other representation g. permeability and porosity are then added to the domain. If scalars are passed, they are expanded to cover all cells. Arrays are asserted to match all cells. Permeability is either one value per cell (diagonal scalar), one value per dimension given in each row (for a diagonal tensor) or a vector that represents a compact full tensor representation (6 elements in 3D, 3 in 2D).

Default data and their values

NameExplanationUnitDefault
permeabilityRock ability to conduct fluid flow$m^2$100 mD
porosityRock void fraction open to flow (0 to 1)-0.3
rock_densityMass density of rock$kg^3/m^3$2000.0
rock_heat_capacitySpecific heat capacity of rock$J/(kg K)$900.0
rock_thermal_conductivityHeat conductivity of rock$W/m K$3.0
fluid_thermal_conductivityHeat conductivity of fluid phases$W/m K$0.6
component_heat_capacitySpecific heat capacity of fluid components$J/(kg K)$4184.0

Note that the default values are taken to be roughly those of water for fluid phases and sandstone for those of rock. Choice of values can severely impact your simulation results - take care to check the values that your physical system makes use of!

source
reservoir_domain(m::Union{SimulationModel, MultiModel})

Get reservoir domain embedded in model.

source
reservoir_domain(case::JutulCase)

Get reservoir domain from a reservoir simulation case.

source

Wells

JutulDarcy.setup_wellFunction
setup_well(D::DataDomain, reservoir_cells; skin = 0.0, Kh = nothing, radius = 0.1, dir = :z)

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.

source
JutulDarcy.setup_vertical_wellFunction
setup_vertical_well(D::DataDomain, i, j; <kwarg>)

Set up a vertical well with a DataDomain input that represents the porous medium / reservoir where the wells it to be placed.

source
setup_vertical_well(g, K, i, j; heel = 1, toe = grid_dims_ijk(g)[3], kwarg...)

Set up a vertical well for given grid g and permeability K at logical indices i, j perforating all cells starting at k-logical index heel to toe.

source

Model

JutulDarcy.setup_reservoir_modelFunction
setup_reservoir_model(reservoir, system; wells = [], <keyword arguments>)
setup_reservoir_model(reservoir, system; wells = [], context = DefaultContext(), reservoir_context = nothing, backend = :csc, <keyword arguments>)

Set up a reservoir MultiModel for a given reservoir DataDomain typically set up from reservoir_domain and an optional vector of wells that are created using setup_vertical_well and setup_well.

The routine automatically sets up a facility and couples the wells with the reservoir and that facility.

source

Initial state

JutulDarcy.setup_reservoir_stateFunction
setup_reservoir_state(model, <keyword arguments>)
# Ex: For immiscible two-phase
setup_reservoir_state(model, Pressure = 1e5, Saturations = [0.2, 0.8])

Convenience constructor that initializes a state for a MultiModel set up using setup_reservoir_model. The main convenience over setup_state is only the reservoir initialization values need be provided: wells are automatically initialized from the connected reservoir cells.

As an alternative to passing keyword arguments, a Dict{Symbol, Any} instance can be sent in as a second, non-keyword argument.

source

Why is initialization needed?

Simple initialization

Hydrostatic equilibriation

Simulation

JutulDarcy.simulate_reservoirFunction
simulate_reservoir(state0, model, dt;
    parameters = setup_parameters(model),
    restart = false,
    forces = setup_forces(model),
    kwarg...
)
simulate_reservoir(case;
    kwarg...
)

Convenience function for simulating a reservoir model. This function internally calls setup_reservoir_simulator, simulates the problem and returns a ReservoirSimResult. Keyword arguments are passed onto setup_reservoir_simulator and are documented in that function.

You can optionally unpack this result into the most typical desired outputs:

wellsols, states = simulate_reservoir(...)

where wellsols contains the well results and states the reservoir results (pressure, saturations and so on, in each cell of the reservoir domain).

Examples

You can restart/resume simulations by both providing the output_path argument and the restart argument:

# Automatically restart from last solved step and returning the outputs if the simulation was already solved.
result = simulate_reservoir(state0, model, dt, output_path = "/some/path", restart = true)

# Restart from step 5
result = simulate_reservoir(state0, model, dt, output_path = "/some/path", restart = 5)

# Start from the beginning (default)
result = simulate_reservoir(state0, model, dt, output_path = "/some/path", restart = false)
source
JutulDarcy.setup_reservoir_simulatorFunction
setup_reservoir_simulator(models, initializer, parameters = nothing; <keyword arguments>)

Arguments

  • models: either a single model or a Dict with the key :Reservoir for multimodels
  • initializer: used to setup state0, must be compatible with model
  • parameters: initialized parameters, must be compatible with model if provided

Keyword arguments

  • split_wells: Add facility model to each well (needed for domain decomposition and MPI solves)
  • assemble_wells_together: Option to split wells into multiple sparse matrices (false argument experimental)
  • specialize=false: use deep specialization of storage for faster execution, but significantly more compile time

Additional keyword arguments are documented in the version of this function that uses JutulCase as the input.

source
setup_reservoir_simulator(case::JutulCase; <keyword arguments>)

Keyword arguments

Linear solver options

  • linear_solver=:bicgstab: iterative solver to use (provided model supports it). Typical options are :bicgstab or :gmres Can alternatively pass a linear solver instance.
  • precond=:cpr: preconditioner for iterative solver: Either :cpr or :ilu0.
  • rtol=1e-3: relative tolerance for linear solver

Timestepping options

  • initial_dt=3600*24.0: initial time-step in seconds (one day by default)
  • target_ds=Inf: target saturation change over a timestep used by timestepper.
  • target_its=8: target number of nonlinear iterations per time step
  • offset_its=1: dampening parameter for time step selector where larger values lead to more pessimistic estimates.
  • timesteps=:auto: Set to :auto to use automatic timestepping, :none for no autoamtic timestepping (i.e. try to solve exact report steps)

Convergence criterions

  • tol_cnv=1e-3: maximum allowable point-wise error (volume-balance)
  • tol_mb=1e-7: maximum alllowable integrated error (mass-balance)
  • tol_cnv_well=10*tol_cnv: maximum allowable point-wise error for well node (volume-balance)
  • tol_mb_well=1e4*tol_mb: maximum alllowable integrated error for well node (mass-balance)

Inherited keyword arguments

Additional keyword arguments come from the base Jutul simulation framework. We list a few of the most relevant entries here for convenience:

  • info_level =0: Output level. Set to 0 for minimal output, -1 for no output and 1 or more for increasing verbosity.
  • output_path: Path to write output to.

Additional keyword arguments are passed onto simulator_config.

source