0
# Utilities
1
2
Comprehensive utility classes and functions supporting core ResData functionality including vector operations, time handling, random number generation, data type management, and testing utilities.
3
4
## Capabilities
5
6
### Vector Operations
7
8
Type-specific vector classes providing high-performance array operations with seamless NumPy integration.
9
10
```python { .api }
11
class IntVector:
12
"""Integer vector operations with dynamic resizing."""
13
14
def __init__(self, initial_size: int = 0, default_value: int = 0):
15
"""
16
Create integer vector.
17
18
Args:
19
initial_size (int): Initial size
20
default_value (int): Default value for new elements
21
"""
22
23
def append(self, value: int):
24
"""Append value to vector."""
25
26
def pop(self) -> int:
27
"""Remove and return last element."""
28
29
def get_max(self) -> int:
30
"""Get maximum value."""
31
32
def get_min(self) -> int:
33
"""Get minimum value."""
34
35
def get_max_index(self) -> int:
36
"""Get index of maximum value."""
37
38
def get_min_index(self) -> int:
39
"""Get index of minimum value."""
40
41
def shift(self, offset: int):
42
"""Shift all values by offset."""
43
44
def scale(self, factor: int):
45
"""Scale all values by factor."""
46
47
def sum(self) -> int:
48
"""Sum all elements."""
49
50
def sort(self):
51
"""Sort in ascending order."""
52
53
def rsort(self):
54
"""Sort in descending order."""
55
56
def safe_get(self, index: int) -> int:
57
"""Get value with bounds checking."""
58
59
def set_default(self, default_value: int):
60
"""Set default value for new elements."""
61
62
def numpy_copy(self) -> numpy.ndarray:
63
"""Get NumPy array copy."""
64
65
class DoubleVector:
66
"""Double precision vector operations."""
67
68
def __init__(self, initial_size: int = 0, default_value: float = 0.0):
69
"""Create double vector."""
70
71
def append(self, value: float):
72
"""Append value to vector."""
73
74
def pop(self) -> float:
75
"""Remove and return last element."""
76
77
def get_max(self) -> float:
78
"""Get maximum value."""
79
80
def get_min(self) -> float:
81
"""Get minimum value."""
82
83
def get_max_index(self) -> int:
84
"""Get index of maximum value."""
85
86
def get_min_index(self) -> int:
87
"""Get index of minimum value."""
88
89
def shift(self, offset: float):
90
"""Shift all values by offset."""
91
92
def scale(self, factor: float):
93
"""Scale all values by factor."""
94
95
def sum(self) -> float:
96
"""Sum all elements."""
97
98
def sort(self):
99
"""Sort in ascending order."""
100
101
def rsort(self):
102
"""Sort in descending order."""
103
104
def safe_get(self, index: int) -> float:
105
"""Get value with bounds checking."""
106
107
def set_default(self, default_value: float):
108
"""Set default value for new elements."""
109
110
def numpy_copy(self) -> numpy.ndarray:
111
"""Get NumPy array copy."""
112
113
class BoolVector:
114
"""Boolean vector operations."""
115
116
def __init__(self, initial_size: int = 0, default_value: bool = False):
117
"""Create boolean vector."""
118
119
def append(self, value: bool):
120
"""Append value to vector."""
121
122
def pop(self) -> bool:
123
"""Remove and return last element."""
124
125
def count_true(self) -> int:
126
"""Count True values."""
127
128
def count_false(self) -> int:
129
"""Count False values."""
130
131
def all_true(self) -> bool:
132
"""Check if all values are True."""
133
134
def any_true(self) -> bool:
135
"""Check if any value is True."""
136
137
def safe_get(self, index: int) -> bool:
138
"""Get value with bounds checking."""
139
140
def set_default(self, default_value: bool):
141
"""Set default value for new elements."""
142
143
def numpy_copy(self) -> numpy.ndarray:
144
"""Get NumPy array copy."""
145
```
146
147
### Time Vector Operations
148
149
Specialized vector for time-based data with date/time functionality.
150
151
```python { .api }
152
class TimeVector:
153
"""Time vector operations for datetime data."""
154
155
def __init__(self):
156
"""Create time vector."""
157
158
def append(self, time: datetime):
159
"""Append datetime to vector."""
160
161
def get_data_ptr(self) -> list:
162
"""Get internal data pointer."""
163
164
def active_list(self) -> list:
165
"""Get list of active time points."""
166
167
def select_matching(self, pattern: str) -> BoolVector:
168
"""Select times matching pattern."""
169
170
def get_active_mask(self) -> BoolVector:
171
"""Get mask of active time points."""
172
```
173
174
### String Operations
175
176
String list container with search and manipulation capabilities.
177
178
```python { .api }
179
class StringList:
180
"""String list operations and management."""
181
182
def __init__(self):
183
"""Create empty string list."""
184
185
def append(self, string: str):
186
"""Append string to list."""
187
188
def get_last(self) -> str:
189
"""Get last string."""
190
191
def pop(self) -> str:
192
"""Remove and return last string."""
193
194
def sort(self):
195
"""Sort strings alphabetically."""
196
197
def contains(self, string: str) -> bool:
198
"""Check if string is in list."""
199
200
def find_first(self, string: str) -> int:
201
"""Find first occurrence index."""
202
203
def size(self) -> int:
204
"""Get number of strings."""
205
```
206
207
### Hash Table Operations
208
209
Hash table implementations for different data types with key-value storage.
210
211
```python { .api }
212
class Hash:
213
"""Generic hash table operations."""
214
215
def __init__(self):
216
"""Create empty hash table."""
217
218
def insert(self, key: str, value):
219
"""Insert key-value pair."""
220
221
def get(self, key: str):
222
"""Get value by key."""
223
224
def has_key(self, key: str) -> bool:
225
"""Check if key exists."""
226
227
def keys(self) -> list:
228
"""Get list of keys."""
229
230
def values(self) -> list:
231
"""Get list of values."""
232
233
def clear(self):
234
"""Clear all entries."""
235
236
def size(self) -> int:
237
"""Get number of entries."""
238
239
class StringHash(Hash):
240
"""String-specific hash table."""
241
242
def insert(self, key: str, value: str):
243
"""Insert string key-value pair."""
244
245
def get(self, key: str) -> str:
246
"""Get string value by key."""
247
248
class IntegerHash(Hash):
249
"""Integer-specific hash table."""
250
251
def insert(self, key: str, value: int):
252
"""Insert integer key-value pair."""
253
254
def get(self, key: str) -> int:
255
"""Get integer value by key."""
256
257
class DoubleHash(Hash):
258
"""Double-specific hash table."""
259
260
def insert(self, key: str, value: float):
261
"""Insert double key-value pair."""
262
263
def get(self, key: str) -> float:
264
"""Get double value by key."""
265
```
266
267
### Time Handling
268
269
C-based time operations providing high-performance datetime functionality.
270
271
```python { .api }
272
class CTime:
273
"""C-based time handling and operations."""
274
275
def __init__(self, time_input=None):
276
"""
277
Create CTime object.
278
279
Args:
280
time_input: datetime, timestamp, or None for current time
281
"""
282
283
def datetime(self) -> datetime:
284
"""Convert to Python datetime."""
285
286
def ctime(self) -> str:
287
"""Get C-style time string."""
288
289
def stripped_copy(self) -> CTime:
290
"""Create copy with stripped time components."""
291
292
def set_date(self, year: int, month: int, day: int):
293
"""Set date components."""
294
295
def date(self) -> tuple:
296
"""Get (year, month, day) tuple."""
297
298
def year(self) -> int:
299
"""Get year."""
300
301
def month(self) -> int:
302
"""Get month."""
303
304
def mday(self) -> int:
305
"""Get day of month."""
306
```
307
308
### Permutation Operations
309
310
Permutation vector for sorting and reordering operations.
311
312
```python { .api }
313
class PermutationVector:
314
"""Permutation operations for data reordering."""
315
316
def __init__(self, size: int):
317
"""Create permutation vector."""
318
319
def sort_vector(self, data_vector):
320
"""Sort vector using permutation."""
321
322
def permute_vector(self, data_vector):
323
"""Apply permutation to vector."""
324
```
325
326
### Random Number Generation
327
328
High-quality random number generation with multiple algorithms.
329
330
```python { .api }
331
class RandomNumberGenerator:
332
"""Random number generation with configurable algorithms."""
333
334
def __init__(self, algorithm: RngAlgTypeEnum = RngAlgTypeEnum.MZRAN,
335
init_mode: RngInitModeEnum = RngInitModeEnum.INIT_DEFAULT):
336
"""
337
Create random number generator.
338
339
Args:
340
algorithm: RNG algorithm type
341
init_mode: Initialization mode
342
"""
343
344
def get_double(self) -> float:
345
"""Get random double [0, 1)."""
346
347
def get_int(self, max_value: int) -> int:
348
"""Get random integer [0, max_value)."""
349
350
def seed(self, seed_value: int):
351
"""Set random seed."""
352
353
def state_size(self) -> int:
354
"""Get state size in bytes."""
355
```
356
357
### Lookup Tables
358
359
Lookup table operations for interpolation and data mapping.
360
361
```python { .api }
362
class LookupTable:
363
"""Lookup table for interpolation and mapping."""
364
365
def __init__(self):
366
"""Create empty lookup table."""
367
368
def append(self, x: float, y: float):
369
"""Add (x, y) point to table."""
370
371
def get_value(self, x: float) -> float:
372
"""Get interpolated value at x."""
373
374
def has_key(self, x: float) -> bool:
375
"""Check if exact key exists."""
376
377
def size(self) -> int:
378
"""Get number of points."""
379
380
def get_min_arg(self) -> float:
381
"""Get minimum x value."""
382
383
def get_max_arg(self) -> float:
384
"""Get maximum x value."""
385
```
386
387
### Version Information
388
389
Version and build information access.
390
391
```python { .api }
392
class ResdataVersion:
393
"""ResData version and build information."""
394
395
@staticmethod
396
def versionString() -> str:
397
"""Get version string."""
398
399
@staticmethod
400
def versionTuple() -> tuple:
401
"""Get version tuple (major, minor, patch)."""
402
403
@staticmethod
404
def isDevelVersion() -> bool:
405
"""Check if development version."""
406
407
@staticmethod
408
def getGitCommit() -> str:
409
"""Get git commit hash."""
410
411
@staticmethod
412
def getBuildTime() -> str:
413
"""Get build timestamp."""
414
```
415
416
### Context Management
417
418
Context managers for working directory and other state management.
419
420
```python { .api }
421
class CWDContext:
422
"""Context manager for working directory changes."""
423
424
def __init__(self, path: str):
425
"""
426
Create working directory context.
427
428
Args:
429
path (str): Target directory path
430
"""
431
432
def __enter__(self):
433
"""Enter context and change directory."""
434
return self
435
436
def __exit__(self, exc_type, exc_val, exc_tb):
437
"""Exit context and restore directory."""
438
```
439
440
## Usage Examples
441
442
### Vector Operations
443
444
```python
445
from resdata.util import IntVector, DoubleVector, BoolVector
446
import numpy as np
447
448
# Create and populate integer vector
449
int_vec = IntVector()
450
for i in range(10):
451
int_vec.append(i * i) # Square numbers
452
453
print(f"Integer vector size: {int_vec.size()}")
454
print(f"Max value: {int_vec.get_max()}")
455
print(f"Min value: {int_vec.get_min()}")
456
print(f"Sum: {int_vec.sum()}")
457
458
# Convert to NumPy for advanced operations
459
int_array = int_vec.numpy_copy()
460
print(f"NumPy array: {int_array}")
461
print(f"Mean: {np.mean(int_array):.2f}")
462
463
# Double vector operations
464
double_vec = DoubleVector(5, 1.0) # Size 5, default value 1.0
465
double_vec.scale(2.5) # Scale all values
466
double_vec.shift(10.0) # Add offset
467
468
# Boolean vector for masking
469
bool_vec = BoolVector()
470
for val in int_array:
471
bool_vec.append(val > 25) # Values greater than 25
472
473
print(f"Values > 25: {bool_vec.count_true()}/{bool_vec.size()}")
474
```
475
476
### Hash Table Usage
477
478
```python
479
from resdata.util import StringHash, DoubleHash
480
481
# String mapping
482
well_types = StringHash()
483
well_types.insert("PROD01", "Producer")
484
well_types.insert("INJ01", "Water Injector")
485
well_types.insert("INJ02", "Gas Injector")
486
487
print("Well Types:")
488
for key in well_types.keys():
489
print(f" {key}: {well_types.get(key)}")
490
491
# Numerical properties
492
well_rates = DoubleHash()
493
well_rates.insert("PROD01", 150.5)
494
well_rates.insert("PROD02", 89.2)
495
well_rates.insert("PROD03", 234.7)
496
497
# Find maximum rate
498
max_rate = 0.0
499
max_well = ""
500
for key in well_rates.keys():
501
rate = well_rates.get(key)
502
if rate > max_rate:
503
max_rate = rate
504
max_well = key
505
506
print(f"Highest rate: {max_well} at {max_rate} m³/day")
507
```
508
509
### Time Operations
510
511
```python
512
from resdata.util import CTime, TimeVector
513
from datetime import datetime, timedelta
514
515
# Create time objects
516
start_time = CTime(datetime(2020, 1, 1))
517
current_time = CTime() # Current time
518
519
print(f"Start date: {start_time.datetime()}")
520
print(f"Current time: {current_time.datetime()}")
521
522
# Work with time components
523
year = start_time.year()
524
month = start_time.month()
525
day = start_time.mday()
526
print(f"Start date components: {year}-{month:02d}-{day:02d}")
527
528
# Time vector for series data
529
time_series = TimeVector()
530
base_date = datetime(2020, 1, 1)
531
for i in range(12):
532
month_date = base_date + timedelta(days=30*i)
533
time_series.append(month_date)
534
535
print(f"Time series length: {len(time_series.active_list())}")
536
```
537
538
### Lookup Table Operations
539
540
```python
541
from resdata.util import LookupTable
542
import numpy as np
543
544
# Create pressure-depth relationship
545
pressure_depth = LookupTable()
546
547
# Add data points (depth in meters, pressure in bar)
548
depths = [0, 500, 1000, 1500, 2000, 2500, 3000]
549
pressures = [1, 51, 101, 151, 201, 251, 301] # Hydrostatic + formation pressure
550
551
for depth, pressure in zip(depths, pressures):
552
pressure_depth.append(depth, pressure)
553
554
print(f"Lookup table size: {pressure_depth.size()}")
555
print(f"Depth range: {pressure_depth.get_min_arg():.0f} - {pressure_depth.get_max_arg():.0f} m")
556
557
# Interpolate pressures
558
test_depths = [750, 1250, 2750]
559
for depth in test_depths:
560
pressure = pressure_depth.get_value(depth)
561
print(f"Pressure at {depth}m: {pressure:.1f} bar")
562
```
563
564
### Random Number Generation
565
566
```python
567
from resdata.util import RandomNumberGenerator, RngAlgTypeEnum
568
import numpy as np
569
570
# Create random number generator
571
rng = RandomNumberGenerator(RngAlgTypeEnum.MZRAN)
572
rng.seed(42) # Set seed for reproducibility
573
574
# Generate random data
575
random_doubles = [rng.get_double() for _ in range(1000)]
576
random_ints = [rng.get_int(100) for _ in range(1000)]
577
578
print(f"Random doubles: min={min(random_doubles):.3f}, max={max(random_doubles):.3f}")
579
print(f"Random ints: min={min(random_ints)}, max={max(random_ints)}")
580
581
# Statistical analysis
582
doubles_array = np.array(random_doubles)
583
print(f"Mean: {np.mean(doubles_array):.3f}")
584
print(f"Std dev: {np.std(doubles_array):.3f}")
585
586
# Generate normally distributed values (Box-Muller transform)
587
normal_values = []
588
for i in range(0, 1000, 2):
589
u1 = rng.get_double()
590
u2 = rng.get_double()
591
592
# Box-Muller transform
593
z1 = np.sqrt(-2 * np.log(u1)) * np.cos(2 * np.pi * u2)
594
z2 = np.sqrt(-2 * np.log(u1)) * np.sin(2 * np.pi * u2)
595
596
normal_values.extend([z1, z2])
597
598
normal_array = np.array(normal_values[:1000])
599
print(f"Normal distribution: mean={np.mean(normal_array):.3f}, std={np.std(normal_array):.3f}")
600
```
601
602
### Working Directory Context
603
604
```python
605
from resdata.util import CWDContext
606
import os
607
608
# Get current directory
609
original_dir = os.getcwd()
610
print(f"Original directory: {original_dir}")
611
612
# Use context manager to change directory
613
target_dir = "/tmp"
614
with CWDContext(target_dir):
615
current_dir = os.getcwd()
616
print(f"Inside context: {current_dir}")
617
618
# Do work in target directory
619
# Files created here will be in target_dir
620
621
# Back to original directory
622
final_dir = os.getcwd()
623
print(f"After context: {final_dir}")
624
assert final_dir == original_dir, "Directory not restored!"
625
```
626
627
### Version Information
628
629
```python
630
from resdata.util import ResdataVersion
631
632
# Get version information
633
version_str = ResdataVersion.versionString()
634
version_tuple = ResdataVersion.versionTuple()
635
is_devel = ResdataVersion.isDevelVersion()
636
git_commit = ResdataVersion.getGitCommit()
637
build_time = ResdataVersion.getBuildTime()
638
639
print(f"ResData Version: {version_str}")
640
print(f"Version tuple: {version_tuple}")
641
print(f"Development version: {is_devel}")
642
print(f"Git commit: {git_commit}")
643
print(f"Build time: {build_time}")
644
645
# Version comparison
646
major, minor, patch = version_tuple
647
if major >= 5 and minor >= 1:
648
print("Using ResData 5.1+ features")
649
else:
650
print("Consider upgrading ResData for latest features")
651
```
652
653
## Types
654
655
```python { .api }
656
# Random number generator enums
657
RngAlgTypeEnum = Literal[
658
"MZRAN", # Marsaglia-Zaman random number generator
659
"RANLUX", # RANLUX generator
660
"TAUS" # Tausworthe generator
661
]
662
663
RngInitModeEnum = Literal[
664
"INIT_DEFAULT", # Default initialization
665
"INIT_CLOCK", # Initialize from system clock
666
"INIT_DEV_RANDOM" # Initialize from /dev/random
667
]
668
669
# Version tuple type
670
VersionTuple = tuple[int, int, int] # (major, minor, patch)
671
672
# Hash table key-value types
673
HashKeyValue = dict[str, any] # Generic hash contents
674
StringHashContents = dict[str, str]
675
IntegerHashContents = dict[str, int]
676
DoubleHashContents = dict[str, float]
677
```