Skip to content

Faults and Fractures

Overview

Faults and fractures are geological discontinuities that profoundly affect fluid flow in reservoirs. A fault is a surface along which rock layers have been displaced, often creating a barrier (or sometimes a conduit) to cross-fault flow. A fracture is a crack or opening in the rock that creates an enhanced permeability pathway. Both are modeled in BORES through transmissibility multipliers, following the same approach used by commercial simulators.

The transmissibility multiplier approach works by scaling the inter-block flow transmissibility across fault or fracture planes. A multiplier of 0.0001 means the fault reduces cross-fault flow to 0.01% of what the matrix permeability alone would allow (a nearly sealing fault). A multiplier of 10.0 means the fracture enhances flow to 10 times the matrix value. A multiplier of 1.0 has no effect (no fault or fracture).

This approach has several advantages. It does not require changes to the grid geometry, so wells, boundary conditions, and property distributions remain unaffected. The fault masks are computed once during model setup and apply minimal overhead at runtime. Multiple faults can be combined through vectorized operations, and the Numba JIT-compiled mask generation functions ensure fast setup even for large grids.


Factory Functions

BORES provides four factory functions for creating common fault and fracture configurations. These are the recommended entry points, as they handle geometry validation and mask generation automatically.

Vertical Sealing Faults

The most common fault type is a vertical plane that acts as a barrier to cross-fault flow:

from bores.fractures import vertical_sealing_fault, apply_fracture

# Vertical fault at x=25, spanning the entire grid
fault = vertical_sealing_fault(
    fault_id="main_fault",
    orientation="x",
    index=25,
    permeability_multiplier=1e-4,  # 99.99% sealing
)

model = apply_fracture(model=reservoir_model, fracture=fault)

The orientation parameter specifies which axis the fault plane is perpendicular to. An "x"-oriented fault creates a plane in the y-z direction at the specified x-index, blocking flow in the x-direction across that plane.

You can limit the fault extent using range parameters:

# Fault in upper layers only
shallow_fault = vertical_sealing_fault(
    fault_id="shallow_fault",
    orientation="y",
    index=40,
    z_range=(0, 15),         # Only in upper 16 layers
    permeability_multiplier=5e-5,
)

# Fault with limited lateral extent
partial_fault = vertical_sealing_fault(
    fault_id="fault_f3",
    orientation="x",
    index=50,
    permeability_multiplier=0.01,
    y_range=(0, 60),          # Limited lateral extent
    z_range=(10, 35),         # Offsetting layers 10-35
)

Inclined Faults

Non-vertical faults are modeled using a slope parameter that tilts the fault plane:

from bores.fractures import inclined_sealing_fault

# Fault dipping at 60 degrees (slope = tan(60) = 1.73)
dipping_fault = inclined_sealing_fault(
    fault_id="dipping_fault",
    orientation="y",          # Strikes in y-direction
    index=30,                 # Intersects at y=30
    slope=1.73,               # dz/dx (eastward dip)
    intercept=5.0,            # Fault at z=5 when x=0
    permeability_multiplier=1e-4,
)

The slope defines the fault plane equation. For y-oriented faults, the equation is \(z = \text{intercept} + \text{slope} \times x\). The fault mask marks grid cells that intersect this plane.

Damage Zone Faults

Major faults often have a zone of damaged rock with altered permeability and porosity:

from bores.fractures import damage_zone_fault

fault_with_damage = damage_zone_fault(
    fault_id="thrust_fault",
    orientation="x",
    cell_range=(48, 52),          # 5 cells wide
    permeability_multiplier=1e-5, # Very low cross-fault flow
    zone_permeability=10.0,       # Damaged rock: 10 mD (vs 100 mD matrix)
    zone_porosity=0.12,           # Reduced porosity (vs 0.20 matrix)
    z_range=(15, 45),
)

The cell_range parameter specifies the width of the damage zone in grid cells. The zone_permeability and zone_porosity values are applied to all cells within the zone, overriding the original matrix properties.

Conductive Fracture Networks

Natural or hydraulic fractures that enhance permeability are modeled with conductive_fracture_network:

from bores.fractures import conductive_fracture_network

fracture_swarm = conductive_fracture_network(
    fracture_id="fracture_corridor",
    orientation="y",
    cell_range=(15, 18),              # 3-cell-wide corridor
    fracture_permeability=5000.0,     # High perm: 5 Darcy
    fracture_porosity=0.01,           # Low storage (fractures)
    permeability_multiplier=10.0,     # 10x enhanced cross-corridor flow
    z_range=(20, 40),
)

Unlike sealing faults where the multiplier reduces flow, conductive fractures use multipliers greater than 1.0 to enhance cross-fracture flow. The fracture_permeability and fracture_porosity are applied to cells within the fracture corridor.


Applying Faults to a Model

Single Fault

from bores.fractures import apply_fracture

model = apply_fracture(model=reservoir_model, fracture=fault)

Multiple Faults

For compartmentalized reservoirs with multiple faults, use apply_fractures to apply them all at once:

from bores.fractures import apply_fractures, vertical_sealing_fault

faults = [
    vertical_sealing_fault("fault_1", "x", 25, permeability_multiplier=1e-4),
    vertical_sealing_fault("fault_2", "y", 35, permeability_multiplier=5e-4),
    vertical_sealing_fault("fault_3", "x", 60, permeability_multiplier=2e-4),
]

model = apply_fractures(reservoir_model, *faults)

apply_fractures handles the efficient combination of multiple fault masks using vectorized operations.


Horizontal Barriers

You can model horizontal barriers (shale layers, tight streaks, cemented zones) using the "z" orientation:

shale_barrier = vertical_sealing_fault(
    fault_id="shale_layer",
    orientation="z",          # Horizontal plane
    index=12,                 # At layer 12
    permeability_multiplier=1e-6,
    x_range=(0, 80),
    y_range=(0, 60),
)

This creates a horizontal barrier that reduces vertical flow across layer 12 to 0.0001% of the matrix transmissibility. This is useful for modeling interbedded shales, tight carbonate stringers, or any low-permeability layer that compartmentalizes the reservoir vertically.


Fracture Geometry

The FractureGeometry class defines the spatial extent and orientation of a fault or fracture. The factory functions create this automatically, but you can also build it directly:

from bores.fractures import FractureGeometry

# Vertical fault at x=25, limited extent
geom = FractureGeometry(
    orientation="x",
    x_range=(25, 25),         # Single cell plane
    y_range=(10, 40),         # Lateral extent
    z_range=(0, 20),          # Vertical extent
    slope=0.0,                # Vertical (no dip)
    intercept=0.0,
)

The range parameters use inclusive grid indices. Setting a range to None means the fault extends across the full grid dimension in that direction.


Validation

Before applying faults, you can validate their configuration against the grid shape:

from bores.fractures import validate_fracture

errors = validate_fracture(fracture=fault, grid_shape=(100, 80, 50))
if errors:
    for error in errors:
        print(f"Validation error: {error}")

Validation checks that all range indices are within the grid bounds and that the geometry is internally consistent (minimum index is less than or equal to maximum index, orientation has the required primary range).


Choosing Permeability Multipliers

Fault Type Multiplier Range Description
Completely sealing 1e-6 to 1e-5 No cross-fault flow
Highly sealing 1e-4 to 1e-3 Very little cross-fault flow
Partially sealing 0.01 to 0.1 Some cross-fault flow
Leaky fault 0.1 to 0.5 Significant cross-fault flow
No barrier 1.0 No effect on flow
Conductive fracture 1.0 to 100.0 Enhanced flow

In practice, fault transmissibility multipliers are calibrated through history matching. Start with estimates from geological interpretation (seismically mapped faults are often more sealing than sub-seismic fractures) and refine based on well test interference data, tracer studies, or production history.

Damage Zone Properties

For damage zone faults, typical property alterations are:

  • Permeability reduction: 10x to 100x relative to matrix
  • Porosity reduction: 20% to 40% of matrix values
  • Zone width: 1% to 10% of fault displacement