0
# Grid Operations
1
2
Comprehensive 3D grid processing and analysis capabilities for reservoir simulation grids. Supports corner-point grids, structured grids, Local Grid Refinement (LGR), and advanced region selection operations.
3
4
## Capabilities
5
6
### Grid Structure and Analysis
7
8
Main grid class providing access to 3D reservoir grid structure, cell properties, and spatial operations.
9
10
```python { .api }
11
class Grid:
12
"""3D reservoir grid operations and analysis."""
13
14
def __init__(self, filename: str):
15
"""
16
Load grid from GRID or EGRID file.
17
18
Args:
19
filename (str): Path to grid file (.GRID or .EGRID)
20
"""
21
22
def save_EGRID(self, filename: str):
23
"""Save grid in EGRID format."""
24
25
def get_name(self) -> str:
26
"""Get grid name."""
27
28
def get_cell_dims(self) -> tuple:
29
"""
30
Get grid dimensions.
31
32
Returns:
33
tuple: (nx, ny, nz) grid dimensions
34
"""
35
36
def get_cell_volume(self, i: int, j: int, k: int) -> float:
37
"""
38
Get cell volume.
39
40
Args:
41
i, j, k (int): Cell indices
42
43
Returns:
44
float: Cell volume in cubic meters
45
"""
46
47
def get_cell_corner(self, i: int, j: int, k: int, corner: int) -> tuple:
48
"""
49
Get cell corner coordinates.
50
51
Args:
52
i, j, k (int): Cell indices
53
corner (int): Corner index (0-7)
54
55
Returns:
56
tuple: (x, y, z) coordinates
57
"""
58
59
def active_index(self, i: int, j: int, k: int) -> int:
60
"""
61
Get active index for cell.
62
63
Args:
64
i, j, k (int): Cell indices
65
66
Returns:
67
int: Active index or -1 if inactive
68
"""
69
70
def global_index(self, i: int, j: int, k: int) -> int:
71
"""Get global index for cell."""
72
73
def get_xyz(self, i: int, j: int, k: int) -> tuple:
74
"""
75
Get cell center coordinates.
76
77
Args:
78
i, j, k (int): Cell indices
79
80
Returns:
81
tuple: (x, y, z) center coordinates
82
"""
83
84
def get_corner_xyz(self, corner: int) -> tuple:
85
"""Get corner coordinates for all cells."""
86
87
def grid_value(self, x: float, y: float, z: float) -> tuple:
88
"""
89
Find cell containing point.
90
91
Args:
92
x, y, z (float): World coordinates
93
94
Returns:
95
tuple: (i, j, k) indices or None if outside
96
"""
97
98
def depth(self, i: int, j: int, k: int) -> float:
99
"""Get cell depth (negative Z)."""
100
101
def cell_invalid(self, i: int, j: int, k: int) -> bool:
102
"""Check if cell is invalid."""
103
104
def cell_valid(self, i: int, j: int, k: int) -> bool:
105
"""Check if cell is valid."""
106
107
def get_active_index(self, i: int, j: int, k: int) -> int:
108
"""Get active index (raises exception if inactive)."""
109
110
def try_get_global_index(self, i: int, j: int, k: int) -> int:
111
"""Try to get global index, returns -1 if invalid."""
112
113
def get_global_index(self, i: int, j: int, k: int) -> int:
114
"""Get global index (raises exception if invalid)."""
115
116
def get_ijk(self, global_index: int) -> tuple:
117
"""
118
Get IJK indices from global index.
119
120
Args:
121
global_index (int): Global cell index
122
123
Returns:
124
tuple: (i, j, k) indices
125
"""
126
127
def get_xyz_from_ijk(self, i: int, j: int, k: int) -> tuple:
128
"""Get XYZ coordinates from IJK indices."""
129
130
def distance(self, i1: int, j1: int, k1: int, i2: int, j2: int, k2: int) -> float:
131
"""Calculate distance between two cells."""
132
133
def num_active(self) -> int:
134
"""Get number of active cells."""
135
136
def get_num_active(self) -> int:
137
"""Get number of active cells (alias)."""
138
139
def get_num_lgr(self) -> int:
140
"""Get number of Local Grid Refinements."""
141
142
def wells(self) -> list:
143
"""Get list of wells intersecting the grid."""
144
145
def grid_attach(self, lgr_grid):
146
"""Attach Local Grid Refinement."""
147
148
def grid_get_lgr(self, lgr_name: str):
149
"""Get Local Grid Refinement by name."""
150
```
151
152
### Individual Cell Operations
153
154
Access to individual cell properties and metadata.
155
156
```python { .api }
157
class Cell:
158
"""Individual grid cell representation."""
159
160
@property
161
def active(self) -> bool:
162
"""Check if cell is active."""
163
164
@property
165
def i(self) -> int:
166
"""I-index of cell."""
167
168
@property
169
def j(self) -> int:
170
"""J-index of cell."""
171
172
@property
173
def k(self) -> int:
174
"""K-index of cell."""
175
176
@property
177
def global_index(self) -> int:
178
"""Global index of cell."""
179
180
@property
181
def volume(self) -> float:
182
"""Volume of cell."""
183
184
@property
185
def dimensions(self) -> tuple:
186
"""Cell dimensions (dx, dy, dz)."""
187
```
188
189
### Region Selection and Masking
190
191
Advanced region selection capabilities for creating masks based on various criteria.
192
193
```python { .api }
194
class ResdataRegion:
195
"""Region selection and masking operations."""
196
197
def __init__(self, grid: Grid, preselect: bool = True):
198
"""
199
Create region selector.
200
201
Args:
202
grid (Grid): Grid to operate on
203
preselect (bool): Whether to preselect all cells
204
"""
205
206
def select_equal(self, kw: ResdataKW, value: float):
207
"""Select cells where keyword equals value."""
208
209
def select_less(self, kw: ResdataKW, value: float):
210
"""Select cells where keyword is less than value."""
211
212
def select_more(self, kw: ResdataKW, value: float):
213
"""Select cells where keyword is greater than value."""
214
215
def select_in_range(self, kw: ResdataKW, min_value: float, max_value: float):
216
"""Select cells where keyword is in range."""
217
218
def select_active(self):
219
"""Select only active cells."""
220
221
def select_inactive(self):
222
"""Select only inactive cells."""
223
224
def select_small(self, kw: ResdataKW, num_cells: int):
225
"""Select cells with smallest keyword values."""
226
227
def select_large(self, kw: ResdataKW, num_cells: int):
228
"""Select cells with largest keyword values."""
229
230
def select_thin(self, kw: ResdataKW, num_cells: int):
231
"""Select thinnest cells based on keyword."""
232
233
def select_thick(self, kw: ResdataKW, num_cells: int):
234
"""Select thickest cells based on keyword."""
235
236
def select_box(self, i1: int, i2: int, j1: int, j2: int, k1: int, k2: int):
237
"""
238
Select cells in box region.
239
240
Args:
241
i1, i2 (int): I-index range (inclusive)
242
j1, j2 (int): J-index range (inclusive)
243
k1, k2 (int): K-index range (inclusive)
244
"""
245
246
def select_islice(self, i1: int, i2: int):
247
"""Select I-slice of cells."""
248
249
def select_jslice(self, j1: int, j2: int):
250
"""Select J-slice of cells."""
251
252
def select_kslice(self, k1: int, k2: int):
253
"""Select K-slice of cells."""
254
255
def invert(self):
256
"""Invert current selection."""
257
258
def copy(self) -> ResdataRegion:
259
"""Create copy of region."""
260
261
def reset(self):
262
"""Reset selection to all cells."""
263
```
264
265
### Grid Generation Utilities
266
267
Utilities for creating synthetic grids for testing and modeling.
268
269
```python { .api }
270
class GridGenerator:
271
"""Grid generation utilities."""
272
273
@classmethod
274
def create_rectangular(cls, nx: int, ny: int, nz: int,
275
dx: float, dy: float, dz: float) -> Grid:
276
"""
277
Create rectangular grid.
278
279
Args:
280
nx, ny, nz (int): Grid dimensions
281
dx, dy, dz (float): Cell sizes
282
283
Returns:
284
Grid: Generated rectangular grid
285
"""
286
287
@classmethod
288
def create_GRDECL(cls, nx: int, ny: int, nz: int,
289
coord: list, zcorn: list, actnum: list = None) -> Grid:
290
"""
291
Create grid from GRDECL format data.
292
293
Args:
294
nx, ny, nz (int): Grid dimensions
295
coord (list): Coordinate data
296
zcorn (list): Z-corner data
297
actnum (list, optional): Active cell flags
298
299
Returns:
300
Grid: Generated grid
301
"""
302
```
303
304
## Usage Examples
305
306
### Basic Grid Operations
307
308
```python
309
from resdata.grid import Grid
310
311
# Load grid
312
grid = Grid("SIMULATION.EGRID")
313
314
# Get basic information
315
nx, ny, nz = grid.get_cell_dims()
316
print(f"Grid dimensions: {nx} x {ny} x {nz}")
317
print(f"Total cells: {nx * ny * nz}")
318
print(f"Active cells: {grid.num_active()}")
319
320
# Get cell properties
321
i, j, k = 10, 15, 5 # Cell indices
322
if grid.cell_valid(i, j, k):
323
volume = grid.get_cell_volume(i, j, k)
324
x, y, z = grid.get_xyz(i, j, k)
325
depth = grid.depth(i, j, k)
326
327
print(f"Cell ({i},{j},{k}):")
328
print(f" Volume: {volume:.2f} m³")
329
print(f" Center: ({x:.1f}, {y:.1f}, {z:.1f})")
330
print(f" Depth: {depth:.1f} m")
331
```
332
333
### Region Selection
334
335
```python
336
from resdata.grid import Grid, ResdataRegion
337
from resdata.resfile import ResdataFile
338
339
# Load grid and data
340
grid = Grid("SIMULATION.EGRID")
341
restart = ResdataFile("SIMULATION.UNRST")
342
343
# Get keyword data
344
porosity = restart.get_kw("PORO")
345
pressure = restart.get_kw("PRESSURE")
346
347
# Create region selector
348
region = ResdataRegion(grid, preselect=True)
349
350
# Select high porosity cells
351
region.select_more(porosity, 0.2) # Porosity > 20%
352
353
# Further refine: high pressure in high porosity zone
354
region.select_more(pressure, 200.0) # Pressure > 200 bar
355
356
# Select a specific layer
357
layer_region = ResdataRegion(grid, preselect=False)
358
layer_region.select_kslice(10, 10) # Just layer 10
359
360
print(f"High porosity, high pressure cells: {region.active_size()}")
361
print(f"Layer 10 cells: {layer_region.active_size()}")
362
```
363
364
### Working with Cell Data
365
366
```python
367
from resdata.grid import Grid
368
from resdata.resfile import ResdataFile
369
import numpy as np
370
371
# Load data
372
grid = Grid("SIMULATION.EGRID")
373
restart = ResdataFile("SIMULATION.UNRST")
374
375
# Get pressure data
376
pressure_kw = restart.get_kw("PRESSURE")
377
pressure_data = pressure_kw.numpy_copy()
378
379
# Create arrays for analysis
380
nx, ny, nz = grid.get_cell_dims()
381
avg_pressure_by_layer = np.zeros(nz)
382
383
# Calculate average pressure by layer
384
for k in range(nz):
385
layer_pressures = []
386
for j in range(ny):
387
for i in range(nx):
388
if grid.cell_valid(i, j, k):
389
active_idx = grid.active_index(i, j, k)
390
if active_idx >= 0:
391
layer_pressures.append(pressure_data[active_idx])
392
393
if layer_pressures:
394
avg_pressure_by_layer[k] = np.mean(layer_pressures)
395
396
print("Average pressure by layer:")
397
for k, avg_p in enumerate(avg_pressure_by_layer):
398
if avg_p > 0:
399
print(f" Layer {k+1}: {avg_p:.1f} bar")
400
```
401
402
### Grid Generation
403
404
```python
405
from resdata.grid import GridGenerator
406
407
# Create simple rectangular grid
408
grid = GridGenerator.create_rectangular(
409
nx=10, ny=10, nz=5, # 10x10x5 cells
410
dx=100.0, dy=100.0, dz=10.0 # 100m x 100m x 10m cells
411
)
412
413
print(f"Generated grid: {grid.get_cell_dims()}")
414
print(f"Active cells: {grid.num_active()}")
415
416
# Check cell properties
417
volume = grid.get_cell_volume(5, 5, 2) # Middle cell
418
x, y, z = grid.get_xyz(5, 5, 2)
419
print(f"Cell (5,5,2): volume={volume:.0f} m³, center=({x:.0f},{y:.0f},{z:.0f})")
420
```
421
422
### Finding Cells by Location
423
424
```python
425
from resdata.grid import Grid
426
427
grid = Grid("SIMULATION.EGRID")
428
429
# Find cell containing a point
430
x, y, z = 1000.0, 2000.0, -1500.0 # World coordinates
431
cell_ijk = grid.grid_value(x, y, z)
432
433
if cell_ijk:
434
i, j, k = cell_ijk
435
print(f"Point ({x}, {y}, {z}) is in cell ({i}, {j}, {k})")
436
437
# Get cell properties
438
if grid.cell_valid(i, j, k):
439
volume = grid.get_cell_volume(i, j, k)
440
center_x, center_y, center_z = grid.get_xyz(i, j, k)
441
print(f"Cell center: ({center_x:.1f}, {center_y:.1f}, {center_z:.1f})")
442
print(f"Cell volume: {volume:.2f} m³")
443
else:
444
print(f"Point ({x}, {y}, {z}) is outside the grid")
445
```
446
447
## Types
448
449
```python { .api }
450
# Grid dimensions
451
GridDimensions = tuple[int, int, int] # (nx, ny, nz)
452
453
# Cell coordinates
454
CellIndices = tuple[int, int, int] # (i, j, k)
455
WorldCoordinates = tuple[float, float, float] # (x, y, z)
456
457
# Cell properties
458
CellCorners = tuple[tuple[float, float, float], ...] # 8 corner coordinates
459
```