Configuration¶
Overview¶
The Config class holds every parameter that controls how a BORES simulation runs. It specifies the timer (time stepping behavior), the rock-fluid tables (relative permeability and capillary pressure models), the wells, the numerical scheme, the solvers, the preconditioners, the convergence tolerances, and all the physical constraints that keep the simulation stable and accurate.
Config is a frozen (immutable) attrs class. Once you create a Config, its fields cannot be changed in place. To modify a configuration, you create a new one using the copy() or with_updates() methods. This immutability prevents accidental modification of simulation parameters during a run and makes configurations safe to pass between functions without defensive copying.
Every simulation in BORES requires a Config. You pass it (along with a reservoir model) to bores.run() to start the simulation. The Config is the single point of control for all numerical behavior. If two simulations use different schemes, solvers, or convergence criteria, those differences are captured entirely in their respective Config objects.
Creating a Config¶
At minimum, a Config requires a Timer and a RockFluidTables:
import bores
rock_fluid_tables = bores.RockFluidTables(
relative_permeability_table=bores.BrooksCoreyThreePhaseRelPermModel(
irreducible_water_saturation=0.25,
residual_oil_saturation_water=0.30,
residual_oil_saturation_gas=0.15,
residual_gas_saturation=0.05,
water_exponent=2.5,
oil_exponent=2.0,
gas_exponent=2.0,
),
capillary_pressure_table=bores.BrooksCoreyCapillaryPressureModel(
irreducible_water_saturation=0.25,
residual_oil_saturation_water=0.30,
residual_oil_saturation_gas=0.15,
residual_gas_saturation=0.05,
),
)
config = bores.Config(
timer=bores.Timer(
initial_step_size=bores.Time(days=1),
max_step_size=bores.Time(days=10),
min_step_size=bores.Time(hours=1),
simulation_time=bores.Time(years=3),
),
rock_fluid_tables=rock_fluid_tables,
)
This creates a valid configuration with all defaults. The IMPES scheme, BiCGSTAB solver with ILU preconditioning, and standard convergence tolerances are used automatically.
Adding Wells¶
Most simulations include wells. Pass them through the wells parameter:
wells = bores.wells_(injectors=[injector], producers=[producer])
config = bores.Config(
timer=bores.Timer(
initial_step_size=bores.Time(days=1),
max_step_size=bores.Time(days=10),
min_step_size=bores.Time(hours=1),
simulation_time=bores.Time(years=3),
),
rock_fluid_tables=rock_fluid_tables,
wells=wells,
)
Adding Well Schedules¶
For simulations with time-varying well controls, use well_schedules:
config = bores.Config(
timer=timer,
rock_fluid_tables=rock_fluid_tables,
well_schedules=schedules,
)
When well_schedules is provided, the simulator automatically switches well controls at the scheduled times. See the Well Scheduling page for details.
Adding Boundary Conditions¶
Boundary conditions (aquifer support, constant pressure boundaries, etc.) are specified through the boundary_conditions parameter:
config = bores.Config(
timer=timer,
rock_fluid_tables=rock_fluid_tables,
wells=wells,
boundary_conditions=boundary_conditions,
)
All Config Parameters¶
Required Parameters¶
| Parameter | Type | Description |
|---|---|---|
timer | Timer | Time stepping manager (initial/max/min step sizes, simulation time) |
rock_fluid_tables | RockFluidTables | Relative permeability and capillary pressure models |
Optional Model Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
wells | Wells | None | Well configuration (injectors and producers) |
well_schedules | WellSchedules | None | Dynamic well control schedules |
boundary_conditions | BoundaryConditions | None | Boundary conditions (aquifers, constant pressure, etc.) |
pvt_tables | PVTTables | None | Tabulated PVT properties (alternative to correlations) |
constants | Constants | Default | Physical and conversion constants |
Numerical Scheme¶
| Parameter | Type | Default | Description |
|---|---|---|---|
scheme | str | "impes" | Evolution scheme: "impes", "explicit", or "implicit" |
use_pseudo_pressure | bool | True | Use pseudo-pressure formulation for gas |
See Schemes for detailed information on each evolution scheme.
Solver Configuration¶
| Parameter | Type | Default | Description |
|---|---|---|---|
pressure_solver | str or list | "bicgstab" | Solver(s) for the pressure equation |
saturation_solver | str or list | "bicgstab" | Solver(s) for the saturation equation |
pressure_preconditioner | str or None | "ilu" | Preconditioner for pressure solvers |
saturation_preconditioner | str or None | "ilu" | Preconditioner for saturation solvers |
pressure_convergence_tolerance | float | 1e-6 | Relative convergence tolerance for pressure |
saturation_convergence_tolerance | float | 1e-4 | Relative convergence tolerance for saturation |
max_iterations | int | 250 | Maximum solver iterations per step (capped at 500) |
See Solvers and Preconditioners for details.
Time Step Controls¶
| Parameter | Type | Default | Description |
|---|---|---|---|
saturation_cfl_threshold | float | 0.7 | Maximum saturation CFL number |
pressure_cfl_threshold | float | 0.9 | Maximum pressure CFL number |
max_oil_saturation_change | float | 0.2 | Maximum oil saturation change per step |
max_water_saturation_change | float | 0.2 | Maximum water saturation change per step |
max_gas_saturation_change | float | 0.1 | Maximum gas saturation change per step |
max_pressure_change | float | 500.0 | Maximum pressure change per step (psi) |
Gas Saturation Change Limits
The default max_gas_saturation_change of 0.1 is intentionally lenient. Gas saturation can change rapidly during solution gas liberation or gas injection, and tightening this limit forces very small timesteps that slow the simulation significantly without meaningful accuracy gains. Only lower this value when you specifically need fine resolution of gas saturation evolution, such as detailed gas coning studies or near-critical fluid behavior. For most simulations, leave it at the default or increase it further.
See Time Step Control for guidance on adjusting these.
Physical Controls¶
| Parameter | Type | Default | Description |
|---|---|---|---|
capillary_strength_factor | float | 1.0 | Scale factor for capillary effects (0 to 1) |
disable_capillary_effects | bool | False | Completely disable capillary pressure |
disable_structural_dip | bool | False | Disable gravity/structural dip effects |
miscibility_model | str | "immiscible" | Miscibility model: "immiscible" or "todd_longstaff" |
freeze_saturation_pressure | bool | False | Keep bubble point pressure constant |
Fluid Mobility¶
| Parameter | Type | Default | Description |
|---|---|---|---|
relative_mobility_range | RelativeMobilityRange | See below | Min/max relative mobility per phase |
total_compressibility_range | Range | (1e-24, 1e-2) | Min/max total compressibility |
phase_appearance_tolerance | float | 1e-6 | Saturation below which a phase is absent |
The default relative mobility ranges are:
- Oil: \(10^{-12}\) to \(10^{6}\)
- Water: \(10^{-12}\) to \(10^{6}\)
- Gas: \(10^{-12}\) to \(10^{6}\)
These ranges prevent division by zero and numerical overflow in mobility calculations. You rarely need to change them.
Hysteresis¶
| Parameter | Type | Default | Description |
|---|---|---|---|
residual_oil_drainage_ratio_water_flood | float | 0.6 | Oil drainage residual ratio (waterflood) |
residual_oil_drainage_ratio_gas_flood | float | 0.6 | Oil drainage residual ratio (gas flood) |
residual_gas_drainage_ratio | float | 0.5 | Gas drainage residual ratio |
Output and Logging¶
| Parameter | Type | Default | Description |
|---|---|---|---|
output_frequency | int | 1 | Yield a state every N steps |
log_interval | int | 5 | Log progress every N steps |
warn_well_anomalies | bool | True | Warn about anomalous well flow rates |
Modifying a Config¶
Since Config is immutable, you cannot modify fields directly. Use copy() or with_updates() to create modified versions:
copy()¶
# Create a new config with a different scheme
implicit_config = config.copy(scheme="implicit")
# Multiple changes at once
tuned_config = config.copy(
scheme="implicit",
pressure_solver="gmres",
pressure_preconditioner="amg",
max_iterations=400,
)
with_updates()¶
with_updates() works the same way as copy() but validates that all provided keys are valid Config attributes:
# This works
updated = config.with_updates(scheme="implicit")
# This raises AttributeError because "schemee" is not a valid field
updated = config.with_updates(schemee="implicit") # AttributeError
Use with_updates() when you want protection against typos in parameter names. Use copy() when you prefer the shorter name and are confident in the parameter names.
Freeze Saturation Pressure¶
The freeze_saturation_pressure flag controls whether the oil bubble point pressure (Pb) is recomputed at each time step or held constant at its initial value.
# Keep Pb constant (standard black-oil assumption)
config = bores.Config(
timer=timer,
rock_fluid_tables=rock_fluid_tables,
wells=wells,
freeze_saturation_pressure=True,
)
When freeze_saturation_pressure=True, the following properties are computed using the initial bubble point pressure rather than a dynamically updated value:
- Bubble point pressure (Pb) itself
- Solution gas-oil ratio (Rs)
- Oil formation volume factor (Bo)
- Oil compressibility (Co)
- Oil viscosity (indirectly through Rs)
- Oil density (indirectly through Rs and Bo)
This is appropriate for natural depletion and waterflooding where oil composition remains constant. Set it to False (the default) for miscible injection or any process where dissolved gas content changes significantly during the simulation.
Capillary Strength Factor¶
The capillary_strength_factor scales capillary pressure effects without changing the capillary pressure model itself. It ranges from 0.0 (no capillary effects) to 1.0 (full capillary effects).
# Reduce capillary effects by 50% for numerical stability
config = bores.Config(
timer=timer,
rock_fluid_tables=rock_fluid_tables,
wells=wells,
capillary_strength_factor=0.5,
)
Capillary gradients can become numerically dominant in fine meshes or at sharp saturation fronts, causing oscillations or overshoot. Reducing the capillary strength factor damps these effects without removing them entirely. This is a common technique for improving convergence in difficult models while preserving the qualitative influence of capillary pressure on fluid distribution.
Setting disable_capillary_effects=True is equivalent to capillary_strength_factor=0.0 but is more explicit in intent.
Example Configurations¶
Simple Depletion Study¶
config = bores.Config(
timer=bores.Timer(
initial_step_size=bores.Time(days=2),
max_step_size=bores.Time(days=30),
min_step_size=bores.Time(days=1),
simulation_time=bores.Time(years=10),
),
rock_fluid_tables=rock_fluid_tables,
wells=wells,
freeze_saturation_pressure=True,
)
High-Resolution Waterflood¶
config = bores.Config(
timer=bores.Timer(
initial_step_size=bores.Time(days=0.5),
max_step_size=bores.Time(days=5),
min_step_size=bores.Time(hours=1),
simulation_time=bores.Time(years=5),
),
rock_fluid_tables=rock_fluid_tables,
wells=wells,
pressure_solver="gmres",
pressure_preconditioner="amg",
max_water_saturation_change=0.15,
max_pressure_change=50.0,
)
Miscible Gas Injection¶
config = bores.Config(
timer=bores.Timer(
initial_step_size=bores.Time(hours=6),
max_step_size=bores.Time(days=3),
min_step_size=bores.Time(minutes=30),
simulation_time=bores.Time(years=3),
),
rock_fluid_tables=rock_fluid_tables,
wells=wells,
miscibility_model="todd_longstaff",
freeze_saturation_pressure=False,
max_pressure_change=75.0,
)