0
# RFT and PLT Data Processing
1
2
Comprehensive processing of Repeat Formation Tester (RFT) and Production Logging Tool (PLT) data for pressure analysis, saturation profiles, and flow rate evaluation from reservoir simulation results.
3
4
## Capabilities
5
6
### RFT File Operations
7
8
Main interface for loading and processing RFT/PLT files containing well test and logging data.
9
10
```python { .api }
11
class ResdataRFTFile:
12
"""RFT/PLT file operations and data access."""
13
14
def __init__(self, filename: str):
15
"""
16
Load RFT/PLT file.
17
18
Args:
19
filename (str): Path to RFT file (.RFT)
20
"""
21
22
def get_header(self) -> dict:
23
"""Get file header information."""
24
25
def get_rft(self, well_name: str, date: datetime) -> ResdataRFT:
26
"""
27
Get RFT data for specific well and date.
28
29
Args:
30
well_name (str): Name of the well
31
date (datetime): Date of the RFT measurement
32
33
Returns:
34
ResdataRFT: RFT data container
35
"""
36
37
def get_rft_node(self, well_name: str, date: datetime):
38
"""Get RFT node for well and date."""
39
40
def get_well_time_rft(self, well_name: str, time: datetime) -> ResdataRFT:
41
"""Get RFT data for well at specific time."""
42
43
def get_dates(self, well_name: str) -> list:
44
"""
45
Get list of RFT dates for well.
46
47
Args:
48
well_name (str): Name of the well
49
50
Returns:
51
list: List of datetime objects
52
"""
53
54
def num_wells(self) -> int:
55
"""Get number of wells with RFT data."""
56
57
def size(self) -> int:
58
"""Get total number of RFT records."""
59
```
60
61
### RFT Data Container
62
63
Individual RFT measurement data with pressure, saturation, and depth information.
64
65
```python { .api }
66
class ResdataRFT:
67
"""Individual RFT/PLT data container and operations."""
68
69
def get_well_name(self) -> str:
70
"""Get well name."""
71
72
def get_date(self) -> datetime:
73
"""Get measurement date."""
74
75
def size(self) -> int:
76
"""Get number of measurement points."""
77
78
def get_pressure(self) -> numpy.ndarray:
79
"""
80
Get pressure measurements.
81
82
Returns:
83
numpy.ndarray: Pressure values in bar or psi
84
"""
85
86
def get_depth(self) -> numpy.ndarray:
87
"""
88
Get depth measurements.
89
90
Returns:
91
numpy.ndarray: Depth values in meters or feet
92
"""
93
94
def get_swat(self) -> numpy.ndarray:
95
"""
96
Get water saturation profile.
97
98
Returns:
99
numpy.ndarray: Water saturation values (0-1)
100
"""
101
102
def get_sgas(self) -> numpy.ndarray:
103
"""
104
Get gas saturation profile.
105
106
Returns:
107
numpy.ndarray: Gas saturation values (0-1)
108
"""
109
110
def get_ijk(self) -> list:
111
"""
112
Get grid indices for measurement points.
113
114
Returns:
115
list: List of (i, j, k) tuples
116
"""
117
118
def sort(self):
119
"""Sort data by depth."""
120
```
121
122
### RFT Cell Data
123
124
Individual cell-level RFT measurements with detailed pressure and saturation data.
125
126
```python { .api }
127
class ResdataRFTCell:
128
"""RFT cell data representation."""
129
130
def get_i(self) -> int:
131
"""Get I-index of cell."""
132
133
def get_j(self) -> int:
134
"""Get J-index of cell."""
135
136
def get_k(self) -> int:
137
"""Get K-index of cell."""
138
139
def get_depth(self) -> float:
140
"""Get cell depth."""
141
142
def get_pressure(self) -> float:
143
"""Get pressure measurement."""
144
145
def get_swat(self) -> float:
146
"""Get water saturation."""
147
148
def get_sgas(self) -> float:
149
"""Get gas saturation."""
150
151
def get_soil(self) -> float:
152
"""Get oil saturation."""
153
```
154
155
### PLT Cell Data
156
157
Production Logging Tool cell data extending RFT data with flow rate measurements.
158
159
```python { .api }
160
class ResdataPLTCell(ResdataRFTCell):
161
"""PLT cell data with flow rate measurements."""
162
163
def get_orat(self) -> float:
164
"""Get oil flow rate."""
165
166
def get_grat(self) -> float:
167
"""Get gas flow rate."""
168
169
def get_wrat(self) -> float:
170
"""Get water flow rate."""
171
172
def get_connection_start(self) -> float:
173
"""Get connection start depth."""
174
175
def get_flowrate(self) -> float:
176
"""Get total flow rate."""
177
178
def get_oil_flowrate(self) -> float:
179
"""Get oil flow rate (alias for get_orat)."""
180
181
def get_gas_flowrate(self) -> float:
182
"""Get gas flow rate (alias for get_grat)."""
183
184
def get_water_flowrate(self) -> float:
185
"""Get water flow rate (alias for get_wrat)."""
186
```
187
188
### Well Trajectory
189
190
Well trajectory information for spatial analysis and grid intersection calculations.
191
192
```python { .api }
193
class WellTrajectory:
194
"""Well trajectory data and spatial operations."""
195
196
def get_ijk(self) -> list:
197
"""Get grid indices along trajectory."""
198
199
def get_xyz(self) -> list:
200
"""Get world coordinates along trajectory."""
201
202
def intersect_grid(self, grid: Grid) -> list:
203
"""Get grid intersections for trajectory."""
204
205
def branch_range(self) -> tuple:
206
"""Get branch range for multi-lateral wells."""
207
```
208
209
## Usage Examples
210
211
### Basic RFT Analysis
212
213
```python
214
from resdata.rft import ResdataRFTFile
215
import matplotlib.pyplot as plt
216
import numpy as np
217
218
# Load RFT file
219
rft_file = ResdataRFTFile("SIMULATION.RFT")
220
221
print(f"Number of wells with RFT data: {rft_file.num_wells()}")
222
print(f"Total RFT records: {rft_file.size()}")
223
224
# Get RFT dates for a specific well
225
well_name = "PROD01"
226
rft_dates = rft_file.get_dates(well_name)
227
print(f"RFT dates for {well_name}: {len(rft_dates)} measurements")
228
229
# Analyze first RFT measurement
230
if rft_dates:
231
first_rft = rft_file.get_rft(well_name, rft_dates[0])
232
233
print(f"First RFT: {first_rft.get_date()}")
234
print(f"Measurement points: {first_rft.size()}")
235
236
# Get measurement data
237
depths = first_rft.get_depth()
238
pressures = first_rft.get_pressure()
239
water_sat = first_rft.get_swat()
240
241
print(f"Depth range: {depths.min():.1f} - {depths.max():.1f} m")
242
print(f"Pressure range: {pressures.min():.1f} - {pressures.max():.1f} bar")
243
print(f"Water saturation range: {water_sat.min():.3f} - {water_sat.max():.3f}")
244
```
245
246
### Pressure Profile Analysis
247
248
```python
249
from resdata.rft import ResdataRFTFile
250
import matplotlib.pyplot as plt
251
import numpy as np
252
253
# Load RFT data
254
rft_file = ResdataRFTFile("SIMULATION.RFT")
255
well_name = "PROD01"
256
rft_dates = rft_file.get_dates(well_name)
257
258
# Plot pressure profiles over time
259
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 8))
260
261
colors = plt.cm.viridis(np.linspace(0, 1, len(rft_dates)))
262
263
for i, date in enumerate(rft_dates[:5]): # First 5 measurements
264
rft = rft_file.get_rft(well_name, date)
265
266
depths = rft.get_depth()
267
pressures = rft.get_pressure()
268
water_sat = rft.get_swat()
269
270
# Sort by depth
271
sort_idx = np.argsort(depths)
272
depths_sorted = depths[sort_idx]
273
pressures_sorted = pressures[sort_idx]
274
water_sat_sorted = water_sat[sort_idx]
275
276
# Pressure profile
277
ax1.plot(pressures_sorted, depths_sorted, 'o-',
278
color=colors[i], label=f"{date.strftime('%Y-%m-%d')}")
279
280
# Water saturation profile
281
ax2.plot(water_sat_sorted, depths_sorted, 'o-',
282
color=colors[i], label=f"{date.strftime('%Y-%m-%d')}")
283
284
ax1.set_xlabel('Pressure (bar)')
285
ax1.set_ylabel('Depth (m)')
286
ax1.set_title(f'{well_name} - Pressure Profiles')
287
ax1.legend()
288
ax1.grid(True)
289
ax1.invert_yaxis() # Depth increases downward
290
291
ax2.set_xlabel('Water Saturation')
292
ax2.set_ylabel('Depth (m)')
293
ax2.set_title(f'{well_name} - Water Saturation Profiles')
294
ax2.legend()
295
ax2.grid(True)
296
ax2.invert_yaxis()
297
298
plt.tight_layout()
299
plt.show()
300
```
301
302
### RFT Time Series Analysis
303
304
```python
305
from resdata.rft import ResdataRFTFile
306
import numpy as np
307
import matplotlib.pyplot as plt
308
309
# Load RFT data
310
rft_file = ResdataRFTFile("SIMULATION.RFT")
311
well_name = "PROD01"
312
rft_dates = rft_file.get_dates(well_name)
313
314
# Analyze pressure evolution at a specific depth
315
target_depth = 2500.0 # Target depth in meters
316
tolerance = 50.0 # Depth tolerance
317
318
pressure_evolution = []
319
dates_with_data = []
320
water_sat_evolution = []
321
322
for date in rft_dates:
323
rft = rft_file.get_rft(well_name, date)
324
325
depths = rft.get_depth()
326
pressures = rft.get_pressure()
327
water_sat = rft.get_swat()
328
329
# Find measurements near target depth
330
depth_mask = np.abs(depths - target_depth) <= tolerance
331
332
if np.any(depth_mask):
333
# Use average if multiple points found
334
avg_pressure = np.mean(pressures[depth_mask])
335
avg_water_sat = np.mean(water_sat[depth_mask])
336
337
pressure_evolution.append(avg_pressure)
338
water_sat_evolution.append(avg_water_sat)
339
dates_with_data.append(date)
340
341
if pressure_evolution:
342
# Plot time evolution
343
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10))
344
345
# Pressure evolution
346
ax1.plot(dates_with_data, pressure_evolution, 'bo-', linewidth=2, markersize=6)
347
ax1.set_ylabel('Pressure (bar)')
348
ax1.set_title(f'{well_name} - Pressure Evolution at ~{target_depth:.0f}m depth')
349
ax1.grid(True)
350
351
# Water saturation evolution
352
ax2.plot(dates_with_data, water_sat_evolution, 'ro-', linewidth=2, markersize=6)
353
ax2.set_ylabel('Water Saturation')
354
ax2.set_xlabel('Date')
355
ax2.set_title(f'{well_name} - Water Saturation Evolution at ~{target_depth:.0f}m depth')
356
ax2.grid(True)
357
358
plt.tight_layout()
359
plt.show()
360
361
# Statistics
362
pressure_decline = pressure_evolution[0] - pressure_evolution[-1]
363
water_sat_increase = water_sat_evolution[-1] - water_sat_evolution[0]
364
365
print(f"Pressure decline: {pressure_decline:.1f} bar")
366
print(f"Water saturation increase: {water_sat_increase:.3f}")
367
```
368
369
### PLT Flow Analysis
370
371
```python
372
from resdata.rft import ResdataRFTFile, ResdataPLTCell
373
374
# Load RFT file (assuming it contains PLT data)
375
rft_file = ResdataRFTFile("SIMULATION.RFT")
376
well_name = "PROD01"
377
rft_dates = rft_file.get_dates(well_name)
378
379
# Analyze PLT data if available
380
for date in rft_dates:
381
rft = rft_file.get_rft(well_name, date)
382
383
print(f"\nPLT Analysis for {well_name} on {date.strftime('%Y-%m-%d')}:")
384
385
# Get cell-level data
386
for i in range(rft.size()):
387
# Note: This is a simplified example
388
# In practice, you'd need to access individual cell data
389
# through the RFT interface
390
depths = rft.get_depth()
391
392
if i < len(depths):
393
depth = depths[i]
394
print(f" Depth {depth:.1f}m:")
395
396
# In a real implementation, you'd access PLT-specific data
397
# This example shows the expected interface
398
```
399
400
### Grid Integration
401
402
```python
403
from resdata.rft import ResdataRFTFile, WellTrajectory
404
from resdata.grid import Grid
405
406
# Load grid and RFT data
407
grid = Grid("SIMULATION.EGRID")
408
rft_file = ResdataRFTFile("SIMULATION.RFT")
409
410
well_name = "PROD01"
411
rft_dates = rft_file.get_dates(well_name)
412
413
if rft_dates:
414
rft = rft_file.get_rft(well_name, rft_dates[0])
415
416
# Get grid indices for RFT measurements
417
ijk_indices = rft.get_ijk()
418
depths = rft.get_depth()
419
pressures = rft.get_pressure()
420
421
print(f"RFT measurements in grid cells:")
422
for idx, (i, j, k) in enumerate(ijk_indices):
423
if idx < len(depths) and idx < len(pressures):
424
# Get cell properties from grid
425
if grid.cell_valid(i, j, k):
426
cell_depth = grid.depth(i, j, k)
427
cell_volume = grid.get_cell_volume(i, j, k)
428
429
print(f" Cell ({i},{j},{k}): RFT depth={depths[idx]:.1f}m, "
430
f"grid depth={cell_depth:.1f}m, pressure={pressures[idx]:.1f}bar")
431
```
432
433
## Types
434
435
```python { .api }
436
# RFT measurement data arrays
437
PressureProfile = numpy.ndarray # Pressure measurements
438
DepthProfile = numpy.ndarray # Depth measurements
439
SaturationProfile = numpy.ndarray # Saturation measurements
440
FlowRateProfile = numpy.ndarray # Flow rate measurements
441
442
# Grid cell indices
443
CellIndices = tuple[int, int, int] # (i, j, k)
444
CellIndicesList = list[CellIndices]
445
446
# Measurement metadata
447
RFTMetadata = dict[str, any] # RFT header information
448
```