or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

file-operations.mdgeometry-operations.mdgravimetry-subsidence.mdgrid-operations.mdindex.mdrft-plt-data.mdsummary-analysis.mdutilities.mdwell-data.md

rft-plt-data.mddocs/

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

```