Skip to content

Component Fugitive

MB edited this page Oct 27, 2025 · 1 revision

Field.get_component_fugitive() Implementation Analysis

Overview

This document provides a comprehensive technical analysis of the Field.get_component_fugitive() method in OPGEE, which calculates fugitive emission loss rates for oil and gas production equipment using Jeff's component fugitive model. The method implements sophisticated matrix operations on productivity-stratified loss data to estimate emissions from downhole pumps, separation equipment, and crude oil storage.

Location: opgee/core/field.py:Field.get_component_fugitive()
Return: (process_loss_rate: pd.Series, loss_mat_gas_ave_df: pd.DataFrame)

Key Discovery: Productivity-Stratified Loss Data Structure

The most critical aspect of this implementation is understanding that the loss matrix CSV files contain productivity-stratified data where repeated column headers represent different data for different productivity tranches:

  • Gas Wells: 140 columns = 10 productivity tranches × 14 equipment components
  • Oil Wells: 120 columns = 10 productivity tranches × 12 equipment components
  • Data Pattern: Loss rates systematically decrease as productivity increases (higher production wells have lower per-unit losses)

All columns are actively used - there are no duplicates or legacy artifacts.

Input Dependencies

Field Attributes Used (11 total)

The method relies on these key field characteristics:

Attribute Purpose Typical Value
GOR Gas-oil ratio for well classification 1822.7
GOR_cutoff Threshold for gas vs oil well treatment 1000
oil_prod Oil production rate affecting productivity Variable
gas_lifting, GLIR Gas lifting flag and injection ratio Boolean, ratio
gas_flooding, flood_gas_type, GFIR, frac_CO2_breakthrough CO2 flooding parameters Various
num_prod_wells Number of production wells Count
frac_wells_with_plunger, frac_wells_with_non_plunger Plunger lift system distribution Fractions

Data Tables Structure

Productivity Reference Tables

Both productivity-gas.csv and productivity-oil.csv contain 10 rows × 9 columns:

Column Description Example Values
Tranche Productivity bin identifier 1-10
Bin low Lower productivity bound 0, 1, 5, 10, 20, 50, 100, 500, 1000, 10000
Bin high Upper productivity bound 1, 5, 10, 20, 50, 100, 500, 1000, 10000, 1×10²⁰
Normalized rate Productivity normalization factor 0.003314 to 161.068
Frac total gas Fraction of total gas from this tranche 0.000295 to 0.414513

These tables define 10 productivity bins ranging from very low productivity (0-1 Mscf/well/day) to extremely high productivity (10,000+ Mscf/well/day).

Loss Matrix Tables: The Sophisticated 3D Data Structure

loss-matrix-gas.csv (10,000 rows × 141 columns)

  • Column 0: "Tranche" (metadata, excluded from calculations)
  • Columns 1-140: Productivity-stratified loss data organized as:
    [Tranche 1: Well, Header, Heater, Separator, Meter, Tanks-Leaks, Tank thief hatch, Recip Comp, Dehydrator, Chem Inj Pump, Pneum Controllers, Flash factor, LU-plunger, LU-non plunger]
    [Tranche 2: Well.1, Header.1, Heater.1, ..., LU-non plunger.1]
    ...
    [Tranche 10: Well.9, Header.9, Heater.9, ..., LU-non plunger.9]
    

Example Loss Rate Pattern (Well component):

  • Tranche 1 (low productivity): 0.067326
  • Tranche 5 (medium productivity): 0.007881
  • Tranche 10 (high productivity): 0.000015

loss-matrix-oil.csv (10,002 rows × 121 columns)

  • Column 0: "Names" (metadata, excluded from calculations)
  • Columns 1-120: Same pattern but 12 components per tranche (excludes LU-plunger, LU-non plunger)

Each row represents one Monte Carlo iteration (10,000+ iterations total) for uncertainty quantification.

Detailed Algorithm Analysis

Stage 1: Field Productivity Calculation

productivity = oil_rate * (GOR + gas_lifting * GLIR)
if gas_flooding and flood_gas_type == "CO2":
    productivity += oil_rate * GFIR * frac_CO2_breakthrough

# Convert to per-well basis
productivity_per_well = productivity / num_prod_wells  # → kscf/day

This calculates the total field gas productivity, accounting for:

  • Natural gas-oil ratio
  • Gas lifting operations
  • CO2 breakthrough in gas flooding scenarios

Stage 2: Field Productivity DataFrame Construction

DataFrame: field_productivity

  • Shape: (10 rows × 4 columns)
  • Index: [1, 2, 3, ..., 10] (productivity tranches from reference tables)
Column Description Data Source
Assignment Productivity tranche assignment (populated in Stage 3) Calculated
col_shift Unused placeholder NaN
Mean gas rate (Mscf/well/day) Normalized rate × per-well productivity productivity_gas/oil["Normalized rate"]
Frac total gas Fraction of total gas from this tranche productivity_gas/oil["Frac total gas"]

GOR-based Table Selection: If GOR > GOR_cutoff, uses gas productivity table; otherwise uses oil productivity table.

Stage 3: Productivity-to-Tranche Assignment

The comp_fugitive_productivity() helper function maps field productivity to appropriate bins:

def comp_fugitive_productivity(prod_mat_gas, mean):
    result = prod_mat_gas[
        (prod_mat_gas["Bin low"] < mean) & (prod_mat_gas["Bin high"] >= mean)
    ].index.values.astype(int)[0]
    return result

Process: For each of the 10 tranches, find which productivity bin the field's calculated productivity falls into. Result: field_productivity["Assignment"] contains tranche numbers (1-10) indicating which loss data to use.

Stage 4: The Critical Matrix Reshape Operation

This is the most sophisticated part of the algorithm, where ALL 140/120 columns are utilized.

Step 4a: Monte Carlo Averaging

loss_mat_ave = loss_mat.mean(axis=0).values  # Average across 10,000 iterations
  • Gas Result: (140,) array containing averaged loss rates for all tranches and components
  • Oil Result: (120,) array containing averaged loss rates for all tranches and components

Step 4b: Strategic Reshape to Access Tranche Data

loss_mat_ave = loss_mat_ave.reshape(len(tranch), len(cols))
# Gas: (140,) → (10, 14) matrix
# Oil: (120,) → (10, 12) matrix

This reshape operation transforms the linear array into a productivity-organized matrix:

Row 0 (Tranche 1): [Well_loss_T1, Header_loss_T1, ..., LU-non_plunger_T1]
Row 1 (Tranche 2): [Well_loss_T2, Header_loss_T2, ..., LU-non_plunger_T2]
...
Row 9 (Tranche 10): [Well_loss_T10, Header_loss_T10, ..., LU-non_plunger_T10]

DataFrame: df (Productivity-Stratified Loss Matrix)

  • Shape: (10 rows × 14/12 columns)
  • Index: [0, 1, 2, ..., 9] (0-indexed tranches for array access)
  • Columns: Component names ['Well', 'Header', 'Separator', 'Meter', 'Tanks-leaks', 'Tank-thief hatch', 'Recip Comp', 'Dehydrator', 'Chem Inj Pump', 'Pneum Controllers', 'Flash factor', 'LU-plunger', 'LU-no plunger']
  • Values: Tranche-specific loss rates showing clear decreasing pattern with higher productivity

Additional DataFrame: loss_mat_gas_ave_df (debugging output)

  • Same data as df but with meaningful index labels (productivity bin boundaries)
  • Used for analysis and validation of gas well loss patterns

Stage 5: Loss Rate Lookup by Productivity Assignment

df = field_productivity.apply(
    lambda row: self.comp_fugitive_loss(df, row["Assignment"]), axis=1
)

The comp_fugitive_loss() helper function performs the lookup:

def comp_fugitive_loss(loss_mat_ave, assignment):
    return loss_mat_ave.iloc[assignment - 1, :]  # Convert 1-indexed to 0-indexed

Data Transformation:

  • Input: df with tranche-organized loss rates (10 rows of different productivity levels)
  • Process: For each field tranche, select the appropriate productivity-specific loss rates
  • Index Handling: Critical assignment - 1 conversion (assignments are 1-indexed, DataFrame is 0-indexed)
  • Output: df where each row now contains the loss rates specific to that tranche's productivity assignment

Example: If field productivity assigns Tranche 3 to productivity bin 6, then row 2 (0-indexed) of the result will contain the loss rates from row 5 (0-indexed) of the original matrix.

Stage 6: Field-Weighted Aggregation

comp_fugitive = df.T.dot(field_productivity["Frac total gas"])

Matrix Operation Details:

  • df.T: Transpose to (14 rows × 10 columns) - components become rows, tranches become columns
  • Dot Product: Matrix multiplication with (10,) "Frac total gas" vector
  • Mathematical Purpose: Weight each component's loss rate by the field's gas production distribution across tranches

Result Series: comp_fugitive

  • Index: Component names (14 for gas, 12 for oil)
  • Values: Field-wide weighted average loss rate for each component
  • Units: Dimensionless fractions (maintained through Pint units)

Stage 7: Final Processing & Aggregation

Flash Factor Correction

comp_fugitive["Flash factor"] /= 0.51  # Rutherford et al. 2021 correction factor

This corrects for the fraction of wells controlled in the study data (only 51% had controls).

Plunger Lift Weighted Average (Gas Wells Only)

if GOR > GOR_cutoff:  # Gas wells
    comp_fugitive["LU-plunger-norm"] = (
        comp_fugitive["LU-plunger"] * frac_wells_with_plunger +
        comp_fugitive["LU-no plunger"] * frac_wells_with_non_plunger
    )
    # Remove individual plunger components
    comp_fugitive.drop(["LU-plunger", "LU-no plunger"], inplace=True)

Process-Category Aggregation

The final step aggregates component-level losses into process-level categories:

separation_loss_rate = comp_fugitive["Separator"]
tank_loss_rate = comp_fugitive["Flash factor"] 
pump_loss_rate = comp_fugitive.drop(["Separator", "Flash factor"]).sum()

Equipment Component Mapping:

  • Separation: Separator equipment only
  • CrudeOilStorage: Flash factor (tank breathing and working losses)
  • DownholePump: All remaining components (Well, Header, Heater, Meter, Tanks-leaks, Tank-thief hatch, Recip Comp, Dehydrator, Chem Inj Pump, Pneum Controllers, plus normalized plunger lift for gas wells)

Stage 8: Return Values

Primary Return: process_loss_rate

process_loss_rate = pd.Series({
    "Separation": separation_loss_rate,
    "CrudeOilStorage": tank_loss_rate, 
    "DownholePump": pump_loss_rate
}, dtype="pint[frac]")
  • Type: pd.Series with Pint fraction units
  • Purpose: Provides aggregated loss rates for major process categories used elsewhere in OPGEE

Secondary Return: loss_mat_gas_ave_df

  • Purpose: Analysis and debugging of gas well loss patterns
  • Contains: The reshaped gas loss matrix (10×14) with meaningful productivity bin labels as index
  • Usage: Allows inspection of how loss rates vary across productivity spectrum

Critical Technical Insights

The Productivity-Stratified Data Architecture

The CSV file structure represents a sophisticated 3D data organization:

  • Dimension 1: Monte Carlo iterations (rows)
  • Dimension 2: Productivity tranches (column groups)
  • Dimension 3: Equipment components (columns within groups)

The reshape operation efficiently converts this flattened representation into a usable matrix format.

Loss Rate Physics

The data reveals important physical relationships:

  • Higher productivity wells have systematically lower per-unit loss rates
  • Example: Well component losses range from 0.067326 (low productivity) to 0.000015 (high productivity)
  • Physical basis: More productive wells typically have better infrastructure and maintenance

Index Alignment Strategy

The algorithm carefully manages multiple indexing schemes:

  • CSV Structure: 1-indexed column groups representing tranches
  • Pandas DataFrames: 0-indexed for array operations
  • Assignment Logic: 1-indexed assignments requiring assignment - 1 conversion
  • Final Output: Maintains physical meaning through appropriate index labels

Monte Carlo Integration

  • 10,000+ iterations provide robust uncertainty quantification
  • Pre-averaged data improves computational efficiency during field-level calculations
  • Statistical robustness supports risk assessment and sensitivity analysis

Matrix Operation Efficiency

The reshape-and-lookup approach enables:

  • Fast productivity-aware calculations without complex conditional logic
  • Vectorized operations for computational efficiency
  • Clean separation between data structure and algorithm logic

Performance & Design Rationale

Data Structure Optimization

  • Compact CSV storage with repeated headers minimizes file size
  • Pre-computed averages eliminate need for real-time Monte Carlo calculations
  • Matrix operations leverage NumPy/Pandas efficiency

Field-Level Aggregation Strategy

  • Productivity weighting recognizes heterogeneity in field characteristics
  • Component-level detail supports detailed emissions accounting
  • Process-level outputs integrate with broader OPGEE architecture

Extensibility Considerations

  • Tranche-based organization easily accommodates additional productivity bins
  • Component modularity supports equipment-specific analyses
  • Clear data flow facilitates maintenance and updates

Usage Context in OPGEE

This method is typically called during field-level emissions calculations to estimate fugitive losses from production equipment. The results feed into:

  • Process-specific emissions accounting
  • Field-level carbon intensity calculations
  • Uncertainty propagation through Monte Carlo analyses
  • Equipment-level optimization studies

The productivity-stratified approach ensures that emissions estimates appropriately reflect the field's specific production characteristics and equipment distributions.

Clone this wiki locally