0
# File Operations
1
2
Comprehensive file I/O capabilities for reservoir simulation formats including binary Fortran files, keyword data containers, and specialized file readers. ResData supports RESTART, INIT, RFT, Summary, and GRID files in both unified and non-unified, formatted and unformatted variants.
3
4
## Capabilities
5
6
### Binary Fortran File I/O
7
8
Low-level binary Fortran file operations providing the foundation for reading and writing reservoir simulation data files.
9
10
```python { .api }
11
class FortIO:
12
"""Binary Fortran file I/O operations."""
13
14
# Constants
15
READ_MODE: str
16
WRITE_MODE: str
17
READ_AND_WRITE_MODE: str
18
APPEND_MODE: str
19
20
def __init__(self, filename: str, mode: str = READ_MODE):
21
"""
22
Open a Fortran binary file.
23
24
Args:
25
filename (str): Path to the file
26
mode (str): File mode (READ_MODE, WRITE_MODE, etc.)
27
"""
28
29
def close(self):
30
"""Close the file."""
31
32
def get_position(self) -> int:
33
"""Get current file position."""
34
35
def seek(self, position: int):
36
"""Seek to position in file."""
37
38
def truncate(self, position: int):
39
"""Truncate file at position."""
40
41
def filename(self) -> str:
42
"""Get filename."""
43
44
def is_fortran_file(self) -> bool:
45
"""Check if file is a valid Fortran binary file."""
46
47
def free(self):
48
"""Free resources."""
49
50
def openFortIO(filename: str, mode: str = FortIO.READ_MODE):
51
"""
52
Context manager for FortIO operations.
53
54
Args:
55
filename (str): Path to the file
56
mode (str): File mode
57
58
Returns:
59
FortIO: File handle
60
61
Example:
62
with openFortIO("data.bin") as f:
63
# Use f for file operations
64
position = f.get_position()
65
"""
66
```
67
68
### Keyword Data Container
69
70
Container for simulation data keywords with comprehensive data manipulation and arithmetic operations.
71
72
```python { .api }
73
class ResdataKW:
74
"""Container for keyword data with operations."""
75
76
def __init__(self, name: str, data_type: ResDataType, size: int):
77
"""
78
Create a new keyword.
79
80
Args:
81
name (str): Keyword name (max 8 characters)
82
data_type (ResDataType): Data type
83
size (int): Number of elements
84
"""
85
86
def copy(self) -> ResdataKW:
87
"""Create a complete copy."""
88
89
def slice_copy(self, start: int, stop: int) -> ResdataKW:
90
"""Create copy of slice."""
91
92
def sub_copy(self, index_list: list) -> ResdataKW:
93
"""Create copy with selected indices."""
94
95
@classmethod
96
def read_grdecl(cls, file_handle, kw_name: str) -> ResdataKW:
97
"""Read GRDECL format keyword from file."""
98
99
def fread(self, fortio: FortIO):
100
"""Read data from Fortran binary file."""
101
102
def name(self) -> str:
103
"""Get keyword name."""
104
105
def set_name(self, name: str):
106
"""Set keyword name."""
107
108
def resize(self, new_size: int):
109
"""Resize the keyword data array."""
110
111
def get_min_max(self) -> tuple:
112
"""Get (min, max) values."""
113
114
def get_min(self) -> float:
115
"""Get minimum value."""
116
117
def get_max(self) -> float:
118
"""Get maximum value."""
119
120
def type(self) -> ResDataType:
121
"""Get data type."""
122
123
def data_type(self) -> ResDataType:
124
"""Get data type (alias for type())."""
125
126
def header(self) -> tuple:
127
"""Get (name, size, type) header information."""
128
129
def array(self) -> numpy.ndarray:
130
"""Get numpy array view of data."""
131
132
def numpy_view(self) -> numpy.ndarray:
133
"""Get numpy array view (read-only)."""
134
135
def numpy_copy(self) -> numpy.ndarray:
136
"""Get numpy array copy."""
137
138
def fwrite(self, fortio: FortIO):
139
"""Write to Fortran binary file."""
140
141
def write_grdecl(self, file_handle):
142
"""Write in GRDECL format."""
143
144
def fprintf_data(self, file_handle, format_str: str):
145
"""Write formatted data to file."""
146
147
def create_actnum(self) -> ResdataKW:
148
"""Create ACTNUM keyword from this keyword."""
149
150
def fix_uninitialized(self, actnum: ResdataKW):
151
"""Fix uninitialized values using ACTNUM."""
152
153
def first_different(self, other: ResdataKW, offset: int = 0) -> int:
154
"""Find first different element index."""
155
156
def scatter_copy(self, target: ResdataKW, mapping: list):
157
"""Scatter copy using index mapping."""
158
159
def safe_div(self, other: ResdataKW) -> ResdataKW:
160
"""Safe division avoiding division by zero."""
161
162
# Arithmetic operations
163
def add(self, other: ResdataKW):
164
"""In-place addition."""
165
166
def sub(self, other: ResdataKW):
167
"""In-place subtraction."""
168
169
def mul(self, other: ResdataKW):
170
"""In-place multiplication."""
171
172
def div(self, other: ResdataKW):
173
"""In-place division."""
174
175
def assign(self, other: ResdataKW):
176
"""Assign values from another keyword."""
177
178
def apply(self, func):
179
"""Apply function to all elements."""
180
181
def sum(self) -> float:
182
"""Sum all elements."""
183
184
def isqrt(self):
185
"""In-place inverse square root."""
186
187
def add_squared(self, other: ResdataKW):
188
"""Add squared values of other keyword."""
189
190
def equal(self, other: ResdataKW) -> bool:
191
"""Check exact equality."""
192
193
def equal_numeric(self, other: ResdataKW, epsilon: float = 1e-6) -> bool:
194
"""Check numeric equality within tolerance."""
195
```
196
197
### Main File Reader
198
199
High-level file reader for restart format files with keyword selection and filtering capabilities.
200
201
```python { .api }
202
class ResdataFile:
203
"""Main reader for restart format files."""
204
205
def __init__(self, filename: str, flags: int = 0):
206
"""
207
Open a restart format file.
208
209
Args:
210
filename (str): Path to the file
211
flags (int): Optional flags for file opening
212
"""
213
214
def close(self):
215
"""Close the file."""
216
217
def save_kw(self, kw: ResdataKW):
218
"""Save keyword to file."""
219
220
def select_matching_kw(self, pattern: str) -> ResdataFileView:
221
"""Select keywords matching pattern."""
222
223
def select_from_report(self, report_step: int) -> ResdataFileView:
224
"""Select data from specific report step."""
225
226
def select_restart_section(self, report_step: int, occurrence: int = 0) -> ResdataFileView:
227
"""Select restart section."""
228
229
def select_global(self) -> ResdataFileView:
230
"""Select global keywords."""
231
232
def restart_get_kw(self, kw_name: str, report_step: int, occurrence: int = 0) -> ResdataKW:
233
"""Get keyword from restart step."""
234
235
def has_kw(self, kw_name: str) -> bool:
236
"""Check if keyword exists."""
237
238
def num_named_kw(self, kw_name: str) -> int:
239
"""Count occurrences of named keyword."""
240
241
def iget_named_kw(self, kw_name: str, index: int) -> ResdataKW:
242
"""Get named keyword by index."""
243
244
def get_kw(self, kw_name: str, index: int = 0) -> ResdataKW:
245
"""Get keyword by name and index."""
246
247
def replace_kw(self, kw: ResdataKW, kw_name: str, index: int):
248
"""Replace keyword in file."""
249
250
def fwrite_kw(self, kw_name: str, fortio: FortIO, index: int = 0):
251
"""Write keyword to Fortran file."""
252
253
def get_header(self) -> dict:
254
"""Get file header information."""
255
256
def wells(self) -> list:
257
"""Get list of well names."""
258
259
def groups(self) -> list:
260
"""Get list of group names."""
261
262
def openResdataFile(filename: str):
263
"""
264
Context manager for ResdataFile.
265
266
Args:
267
filename (str): Path to the file
268
269
Returns:
270
ResdataFile: File handle
271
272
Example:
273
with openResdataFile("CASE.UNRST") as f:
274
pressure = f.get_kw("PRESSURE")
275
"""
276
```
277
278
### File View Operations
279
280
Filtered view of file data enabling efficient data access and manipulation.
281
282
```python { .api }
283
class ResdataFileView:
284
"""Filtered view into ResdataFile."""
285
286
def get_kw(self, kw_name: str, index: int = 0) -> ResdataKW:
287
"""Get keyword from view."""
288
289
def has_kw(self, kw_name: str) -> bool:
290
"""Check if keyword exists in view."""
291
292
def size(self) -> int:
293
"""Get number of keywords in view."""
294
295
def restart_get_kw(self, kw_name: str, report_step: int) -> ResdataKW:
296
"""Get restart keyword from view."""
297
```
298
299
### 3D Data Operations
300
301
Specialized operations for 3D grid-based data including layered access and spatial operations.
302
303
```python { .api }
304
class Resdata3DKW(ResdataKW):
305
"""3D keyword data operations."""
306
307
def get_xyz(self, grid: Grid) -> tuple:
308
"""Get XYZ coordinates for grid."""
309
310
def get_ijk(self, grid: Grid, global_index: int) -> tuple:
311
"""Get IJK indices from global index."""
312
313
class Resdata3DFile(ResdataFile):
314
"""3D file operations."""
315
316
def get_3d_kw(self, kw_name: str) -> Resdata3DKW:
317
"""Get 3D keyword."""
318
319
class ResdataInitFile(Resdata3DFile):
320
"""INIT file operations."""
321
322
def get_porv(self) -> ResdataKW:
323
"""Get pore volume data."""
324
325
def get_tranx(self) -> ResdataKW:
326
"""Get X-direction transmissibility."""
327
328
def get_trany(self) -> ResdataKW:
329
"""Get Y-direction transmissibility."""
330
331
def get_tranz(self) -> ResdataKW:
332
"""Get Z-direction transmissibility."""
333
334
class ResdataRestartFile(Resdata3DFile):
335
"""Restart file operations."""
336
337
def unified_read(self, report_step: int) -> dict:
338
"""Read unified restart data for step."""
339
340
def time_map(self) -> list:
341
"""Get time mapping for restart steps."""
342
```
343
344
## Usage Examples
345
346
### Reading Restart Files
347
348
```python
349
from resdata.resfile import ResdataFile
350
351
# Open restart file
352
restart = ResdataFile("SIMULATION.UNRST")
353
354
# Get keywords
355
pressure = restart.get_kw("PRESSURE", 0) # First occurrence
356
swat = restart.get_kw("SWAT", 0)
357
358
# Access data as numpy arrays
359
pressure_data = pressure.numpy_copy()
360
water_sat = swat.numpy_copy()
361
362
print(f"Pressure range: {pressure_data.min():.2f} - {pressure_data.max():.2f}")
363
print(f"Water saturation range: {water_sat.min():.3f} - {water_sat.max():.3f}")
364
365
restart.close()
366
```
367
368
### Keyword Arithmetic
369
370
```python
371
from resdata.resfile import ResdataFile, ResdataKW
372
373
restart = ResdataFile("SIMULATION.UNRST")
374
375
# Get oil and water saturations
376
soil = restart.get_kw("SOIL")
377
swat = restart.get_kw("SWAT")
378
379
# Calculate gas saturation (Sg = 1 - So - Sw)
380
sgas = soil.copy()
381
sgas.add(swat) # sgas = soil + swat
382
sgas.mul(-1.0) # sgas = -(soil + swat)
383
sgas.add(1.0) # sgas = 1 - (soil + swat)
384
385
sgas_data = sgas.numpy_copy()
386
print(f"Gas saturation range: {sgas_data.min():.3f} - {sgas_data.max():.3f}")
387
```
388
389
### Working with Binary Files
390
391
```python
392
from resdata.resfile import FortIO, ResdataKW, ResDataType
393
394
# Create and write data
395
with openFortIO("output.bin", FortIO.WRITE_MODE) as f:
396
# Create keyword
397
pressure_kw = ResdataKW("PRESSURE", ResDataType.RD_FLOAT, 1000)
398
399
# Set values
400
pressure_array = pressure_kw.numpy_view()
401
pressure_array[:] = 250.0 # Set all to 250 bar
402
403
# Write to file
404
pressure_kw.fwrite(f)
405
406
# Read data back
407
with openFortIO("output.bin", FortIO.READ_MODE) as f:
408
read_kw = ResdataKW("PRESSURE", ResDataType.RD_FLOAT, 1000)
409
read_kw.fread(f)
410
411
data = read_kw.numpy_copy()
412
print(f"Read {len(data)} pressure values, first value: {data[0]}")
413
```
414
415
## Types
416
417
```python { .api }
418
# Data types
419
class ResDataType:
420
RD_INT: ResDataType # Integer type
421
RD_FLOAT: ResDataType # Single precision float
422
RD_DOUBLE: ResDataType # Double precision float
423
RD_BOOL: ResDataType # Boolean type
424
RD_MESS: ResDataType # Message type
425
RD_CHAR: ResDataType # Character type
426
427
@classmethod
428
def RD_STRING(cls, elem_size: int) -> ResDataType:
429
"""String type with specified element size."""
430
431
def is_int(self) -> bool: ...
432
def is_float(self) -> bool: ...
433
def is_double(self) -> bool: ...
434
def is_bool(self) -> bool: ...
435
def is_char(self) -> bool: ...
436
def is_string(self) -> bool: ...
437
def is_numeric(self) -> bool: ...
438
def is_equal(self, other: ResDataType) -> bool: ...
439
440
# Enumerations
441
ResdataTypeEnum = Literal[
442
"RD_CHAR_TYPE", "RD_FLOAT_TYPE", "RD_DOUBLE_TYPE",
443
"RD_INT_TYPE", "RD_BOOL_TYPE", "RD_MESS_TYPE", "RD_STRING_TYPE"
444
]
445
446
FileType = Literal[
447
"OTHER", "RESTART", "UNIFIED_RESTART", "SUMMARY",
448
"UNIFIED_SUMMARY", "SUMMARY_HEADER", "GRID", "EGRID", "INIT", "RFT", "DATA"
449
]
450
451
FileMode = Literal["DEFAULT", "CLOSE_STREAM", "WRITABLE"]
452
```