Skip to the content.

The neqsim.blackoil package supports pressure-dependent black-oil PVT tables, three-component black-oil flashes, conversion from compositional equations of state (EOS), and Eclipse or CMG table export.

Package overview

Class Purpose
BlackOilPVTTable Stores pressure-indexed PVT records and interpolates between them
BlackOilFlash Splits standard oil, gas, and water totals at one pressure
BlackOilFlashResult Reports standard totals, reservoir volumes, densities, viscosities, and PVT values
BlackOilConverter Generates a table and SystemBlackOil from a compositional fluid
SystemBlackOil Wraps a PVT table as a NeqSim thermodynamic system
EclipseEOSExporter Writes Eclipse-compatible black-oil tables
CMGEOSExporter Writes CMG IMEX, GEM, or STARS tables

The table uses one consistent pressure unit, normally bar. Recommended property units are:

Property Unit
Rs, Rv Sm³/Sm³
Bo, Bg, Bw Rm³/Sm³
mu_o, mu_g, mu_w Pa·s
Standard-condition densities kg/m³

The model does not apply Standing, Vasquez-Beggs, or another empirical correlation internally. A manually supplied table contains the user’s input data; BlackOilConverter obtains its table from EOS flash calculations.

Create and interpolate a PVT table

Each record has the constructor order (pressure, Rs, Bo, mu_o, Bg, mu_g, Rv, Bw, mu_w).

import java.util.Arrays;
import java.util.List;
import neqsim.blackoil.BlackOilPVTTable;
import neqsim.blackoil.BlackOilPVTTable.Record;

List<Record> records = Arrays.asList(
    new Record(100.0, 100.0, 1.30, 1.2e-3, 8.0e-3, 1.6e-5,
        0.0, 1.01, 1.0e-3),
    new Record(200.0, 140.0, 1.40, 9.0e-4, 5.0e-3, 1.8e-5,
        0.0, 1.02, 9.0e-4),
    new Record(300.0, 160.0, 1.45, 8.0e-4, 3.0e-3, 2.0e-5,
        0.0, 1.03, 8.0e-4));

double bubblePointPressure = 250.0; // same pressure unit as the records
BlackOilPVTTable pvt = new BlackOilPVTTable(
    records, bubblePointPressure);

double pressure = 175.0;
double rs = pvt.Rs(pressure);
double bo = pvt.Bo(pressure);
double bg = pvt.Bg(pressure);
double oilViscosity = pvt.mu_o(pressure);
double gasViscosity = pvt.mu_g(pressure);

Interpolation is linear between adjacent records and clamps to the nearest record outside the supplied pressure range. RsEffective(pressure) returns Rs at the bubble point for pressures above the bubble point.

Flash standard totals

For a table with vaporized-oil ratio Rv, the flash solves the standard gas and oil split below. It uses RsEffective(pressure), so solution GOR is clamped to the bubble-point value above the bubble point.

\(G_f^{sc} = \frac{G_t^{sc} - R_s^{eff} O_t^{sc}}{1 - R_s^{eff} R_v}\) \(O_l^{sc} = O_t^{sc} - R_v G_f^{sc}\) \(V_o = B_o O_l^{sc},\qquad V_g = B_g G_f^{sc},\qquad V_w = B_w W^{sc}\)

When Gtot_std <= RsEffective(pressure) * Otot_std, BlackOilFlash reports no free gas and retains the supplied stock-tank oil total. Computed free-gas and liquid-oil totals are also bounded at zero, preventing negative phase volumes. For the common Rv = 0 case above the gas threshold, free gas equals total gas minus dissolved gas.

import neqsim.blackoil.BlackOilFlash;
import neqsim.blackoil.BlackOilFlashResult;

double oilDensitySc = 850.0;   // kg/m³
double gasDensitySc = 0.85;    // kg/m³
double waterDensitySc = 1000.0; // kg/m³

BlackOilFlash flash = new BlackOilFlash(
    pvt, oilDensitySc, gasDensitySc, waterDensitySc);

BlackOilFlashResult result = flash.flash(
    200.0,     // pressure, same unit as the table
    373.15,    // K; the table represents this reference temperature
    1000.0,    // total stock-tank oil, Sm³
    150000.0,  // total standard gas, Sm³
    500.0);    // standard water, Sm³

double oilVolume = result.V_o;       // reservoir m³
double gasVolume = result.V_g;       // reservoir m³
double waterVolume = result.V_w;     // reservoir m³
double oilDensity = result.rho_o;    // kg/m³
double oilViscosity = result.mu_o;   // Pa·s
double freeGasSc = result.Gf_std;    // Sm³

BlackOilFlash treats temperature as metadata because one table represents one reference temperature. Build separate tables when temperature dependence is material.

Convert a compositional fluid

BlackOilConverter exposes a static convert method. The returned Result contains the generated table, standard-condition densities, the detected bubble point, and a configured SystemBlackOil.

import neqsim.blackoil.BlackOilConverter;
import neqsim.thermo.system.SystemInterface;
import neqsim.thermo.system.SystemPrEos;

SystemInterface oil = new SystemPrEos(373.15, 300.0);
oil.addComponent("nitrogen", 0.005);
oil.addComponent("CO2", 0.010);
oil.addComponent("methane", 0.350);
oil.addComponent("ethane", 0.070);
oil.addComponent("propane", 0.065);
oil.addComponent("i-butane", 0.025);
oil.addComponent("n-butane", 0.040);
oil.addComponent("i-pentane", 0.020);
oil.addComponent("n-pentane", 0.025);
oil.addComponent("n-hexane", 0.050);
oil.addComponent("n-heptane", 0.080);
oil.addComponent("n-octane", 0.080);
oil.addComponent("n-nonane", 0.060);
oil.addComponent("nC10", 0.120);
oil.setMixingRule("classic");
oil.useVolumeCorrection(true);
oil.setMultiPhaseCheck(true);

double[] pressureGrid = {
    25.0, 50.0, 100.0, 150.0, 200.0, 250.0, 300.0
};

BlackOilConverter.Result converted = BlackOilConverter.convert(
    oil,
    373.15,      // table reference temperature, K
    pressureGrid,
    1.01325,     // standard pressure, bar
    288.15);     // standard temperature, K

double bubblePoint = converted.bubblePoint;
double oilDensitySc = converted.rho_o_sc;
double gasDensitySc = converted.rho_g_sc;
double boAt100Bar = converted.pvt.Bo(100.0);

The converter sorts the pressure grid. Supply at least two points that cover the intended operating range. The recent phase-detection and property initialization fixes ensure NeqSim oil phases and finite oil, gas, and water viscosities are handled; callers should still reject non-finite values before using a generated deck.

Export tables

Use the current simulator-specific exporters. There is no BlackOilTableExporter class.

import neqsim.blackoil.io.CMGEOSExporter;
import neqsim.blackoil.io.EclipseEOSExporter;

double waterDensitySc = 1000.0; // kg/m³; dry example has no aqueous phase

String eclipseDeck = EclipseEOSExporter.toString(
    converted.pvt,
    converted.rho_o_sc,
    converted.rho_g_sc,
    waterDensitySc);

String cmgDeck = CMGEOSExporter.toString(
    converted.pvt,
    converted.rho_o_sc,
    converted.rho_g_sc,
    waterDensitySc);

To write files, call EclipseEOSExporter.toFile(...) or CMGEOSExporter.toFile(...) with a java.nio.file.Path. Both exporters also provide ExportConfig overloads for pressure grids, unit systems, comments, and simulator-specific options. Validate generated files in the target simulator before production use.

Engineering checks