Skip to main content

Reservoir simulation with JutulDarcy in Python.

Project description

PyJutulDarcy

Python wrapper for JutulDarcy.jl, a fully differentiable reservoir simulator written in Julia. Key features:

  • Immiscible, black-oil and compositional multiphase flow
  • Geothermal simulation and simulation of CO2 sequestration
  • Can read standard input files and corner-point grids, or make your own

This package facilitates automatic installation of JutulDarcy from Python, as well as a minimal interface that allows fast simulation of .DATA files in pure Python. For more details about JutulDarcy.jl, please see the Julia Documentation. If you want to run MPI or CUDA accelerated simulations we recommend working either in Julia or the standalone compiled version.

The package also provides access to all the functions of the Julia version under jutuldarcy.jl.JutulDarcy, jutuldarcy.jl.GeoEnergyIO and jutuldarcy.jl.Jutul. These functions are directly wrapped using JuliaCall. For more details, see the JuliaCall Documentation on converting of types.

Installation

The package can be installed with pip:

pip install jutuldarcy

On first time usage of the package JuliaCall will automatically install Julia and manage all dependency packages.

Activating plotting

There is highly experimental support for 3D and 2D visualization. To enable, you can either add GLMakie to your environment manually, or run the following:

import jutuldarcy as jd
jd.install_plotting()

Note that this requires that you are running in an environment that supports plotting (OpenGL capable, i.e. not at a SSH remote without forwarding).

Examples

Copies of these examples can be found in the examples directory.

A minimal example: Running a benchmark file

import jutuldarcy as jd
# Load SPE9 dataset to disk
pth = jd.test_file_path("SPE9", "SPE9.DATA")
# Simulate the model and convert to Python dicts
res = jd.simulate_data_file(pth, convert = True)
# Get field quantities and plot
import matplotlib.pyplot as plt
fopr = res["FIELD"]["FOPR"]
days = res["DAYS"]
plt.plot(days, fopr)
plt.ylabel("Field oil production")
plt.xlabel("Days")
plt.show()
pyplot_fopr

Here, res is a standard dict containing the following fields:

  • "FIELD": Field quantities (average pressure, total water injection, etc) as numpy arrays.
  • "WELLS": Well quantities (bottom hole pressures, injection rates, production rates, etc)
  • "STATES": Reservoir states for all active cells, given as a list with a dict for each timestep. For example, res["STATES"][10]["Rs"] will give you an array of the solution gas-oil-ratio at step 10.
  • "DAYS": Array of the number of days elapsed for each step.

Optionally, if the keyword argument convert to simulate_data_file is set to False or left defaulted, the "full" output as seen in Julia will be returned, where it is possible to get access to grid geometry, model parameters, and so on.

Setting up a simulation without input file

This example is a port of the first JutulDarcy.jl documentation example. If you want to understand a bit more about what is going on, please refer to that page.

Note that only a subset of the full set of routines are available directly in the wrapper. PRs that add more wrapping functionality is welcome.

import jutuldarcy as jd
import numpy as np
# Grab some unit conversion factors
day = jd.si_unit("day")
Darcy = jd.si_unit("darcy")
kg = jd.si_unit("kilogram")
meter = jd.si_unit("meter")
bar = jd.si_unit("bar")
# Set up mesh
nx = ny = 25
nz = 10
cart_dims = (nx, ny, nz)
physical_dims = (1000.0*meter, 1000.0*meter, 100.0*meter)
g = jd.CartesianMesh(cart_dims, physical_dims)
# Convert to unstructured representation
g = jd.UnstructuredMesh(g)
domain = jd.reservoir_domain(g, permeability = 0.3*Darcy, porosity = 0.2)
Injector = jd.setup_vertical_well(domain, 1, 1, name = "Injector")
Producer = jd.setup_well(domain, (nx, ny, 1), name = "Producer")
# Show the properties in the domain
phases = (jd.LiquidPhase(), jd.VaporPhase())
rhoLS = 1000.0*kg/meter**3
rhoGS = 100.0*kg/meter**3
reference_densities = [rhoLS, rhoGS]
sys = jd.ImmiscibleSystem(phases, reference_densities = reference_densities)
model, parameters = jd.setup_reservoir_model(domain, sys, wells = [Injector, Producer])
# Replace dynamic functions with custom ones
c = np.array([1e-6/bar, 1e-4/bar])
density = jd.jl.ConstantCompressibilityDensities(
    p_ref = 100*bar,
    density_ref = reference_densities,
    compressibility = c
)
kr = jd.jl.BrooksCoreyRelativePermeabilities(sys, np.array([2.0, 3.0]))
jd.replace_variables(model, PhaseMassDensities = density, RelativePermeabilities = kr)
# Set the initial conditions
state0 = jd.setup_reservoir_state(model,
    Pressure = 120*bar,
    Saturations = np.array([1.0, 0.0])
)
# Set up reporting steps
nstep = 25
dt = np.repeat(365.0*day, nstep)
# Set up timestepping and well controls
pv = jd.pore_volume(model, parameters)
inj_rate = 1.5*np.sum(pv)/sum(dt)
I_ctrl = jd.setup_injector_control(inj_rate, "rate", np.array([0.0, 1.0]), density = rhoGS)
P_ctrl = jd.setup_producer_control(100*bar, "bhp")
controls = dict(Injector = I_ctrl, Producer = P_ctrl)
forces = jd.setup_reservoir_forces(model, control = controls)
result = jd.simulate_reservoir(state0, model, dt, parameters = parameters, forces = forces)

Converting to Python dict

We can convert the result into a standard dict with numpy array types:

case = jd.setup_jutul_case(state0, model, dt, forces, parameters)
res_py = jd.convert_to_pydict(result, case, units = "field")

Plotting results (experimental)

We can also plot the results if the plotting has been installed:

plt = jd.plot_reservoir(model, result.states)
jd.make_interactive()
front

Paper and citing

The main paper describing JutulDarcy.jl is JutulDarcy.jl - a Fully Differentiable High-Performance Reservoir Simulator Based on Automatic Differentiation, available as open access from Springer:

@article{jutuldarcy,
  title={JutulDarcy.jl - a fully differentiable high-performance reservoir simulator based on automatic differentiation},
  author={M{\o}yner, Olav},
  journal={Computational Geosciences},
  volume={29},
  number={30},
  year={2025},
  publisher={Springer}
}

Contributing

Contributions that expose additional functionality is welcome.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jutuldarcy-1.2.0.tar.gz (15.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jutuldarcy-1.2.0-py3-none-any.whl (13.3 kB view details)

Uploaded Python 3

File details

Details for the file jutuldarcy-1.2.0.tar.gz.

File metadata

  • Download URL: jutuldarcy-1.2.0.tar.gz
  • Upload date:
  • Size: 15.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for jutuldarcy-1.2.0.tar.gz
Algorithm Hash digest
SHA256 d41486e56dd06131aee2d1031948d65f37f9d4168601b5682f69d747cfa74234
MD5 53ba4e392e5ae5cb833c137000ef8895
BLAKE2b-256 28566f00f0fe3de77ae355fa48c5fc789005a03556d04ceda696ea3f0e6d99a0

See more details on using hashes here.

File details

Details for the file jutuldarcy-1.2.0-py3-none-any.whl.

File metadata

  • Download URL: jutuldarcy-1.2.0-py3-none-any.whl
  • Upload date:
  • Size: 13.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for jutuldarcy-1.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1f6804d8e2d67d57a930647fc23cbd03638927ad7f4c8aa92e45a9d659949908
MD5 c37918202d90e62b017a127678116252
BLAKE2b-256 6c55da777e596b03ba18b587be50c006cb9c539d1224aa6b9ef06f07af35d688

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page