xtgeo 4.8.0__cp313-cp313-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of xtgeo might be problematic. Click here for more details.

Files changed (117) hide show
  1. cxtgeo.py +582 -0
  2. cxtgeoPYTHON_wrap.c +20938 -0
  3. xtgeo/__init__.py +246 -0
  4. xtgeo/_cxtgeo.cp313-win_amd64.pyd +0 -0
  5. xtgeo/_internal.cp313-win_amd64.pyd +0 -0
  6. xtgeo/common/__init__.py +19 -0
  7. xtgeo/common/_angles.py +29 -0
  8. xtgeo/common/_xyz_enum.py +50 -0
  9. xtgeo/common/calc.py +396 -0
  10. xtgeo/common/constants.py +30 -0
  11. xtgeo/common/exceptions.py +42 -0
  12. xtgeo/common/log.py +93 -0
  13. xtgeo/common/sys.py +166 -0
  14. xtgeo/common/types.py +18 -0
  15. xtgeo/common/version.py +21 -0
  16. xtgeo/common/xtgeo_dialog.py +604 -0
  17. xtgeo/cube/__init__.py +9 -0
  18. xtgeo/cube/_cube_export.py +214 -0
  19. xtgeo/cube/_cube_import.py +532 -0
  20. xtgeo/cube/_cube_roxapi.py +180 -0
  21. xtgeo/cube/_cube_utils.py +287 -0
  22. xtgeo/cube/_cube_window_attributes.py +340 -0
  23. xtgeo/cube/cube1.py +1023 -0
  24. xtgeo/grid3d/__init__.py +15 -0
  25. xtgeo/grid3d/_ecl_grid.py +774 -0
  26. xtgeo/grid3d/_ecl_inte_head.py +148 -0
  27. xtgeo/grid3d/_ecl_logi_head.py +71 -0
  28. xtgeo/grid3d/_ecl_output_file.py +81 -0
  29. xtgeo/grid3d/_egrid.py +1004 -0
  30. xtgeo/grid3d/_find_gridprop_in_eclrun.py +625 -0
  31. xtgeo/grid3d/_grdecl_format.py +266 -0
  32. xtgeo/grid3d/_grdecl_grid.py +388 -0
  33. xtgeo/grid3d/_grid3d.py +29 -0
  34. xtgeo/grid3d/_grid3d_fence.py +181 -0
  35. xtgeo/grid3d/_grid3d_utils.py +228 -0
  36. xtgeo/grid3d/_grid_boundary.py +76 -0
  37. xtgeo/grid3d/_grid_etc1.py +1566 -0
  38. xtgeo/grid3d/_grid_export.py +221 -0
  39. xtgeo/grid3d/_grid_hybrid.py +66 -0
  40. xtgeo/grid3d/_grid_import.py +79 -0
  41. xtgeo/grid3d/_grid_import_ecl.py +101 -0
  42. xtgeo/grid3d/_grid_import_roff.py +135 -0
  43. xtgeo/grid3d/_grid_import_xtgcpgeom.py +375 -0
  44. xtgeo/grid3d/_grid_refine.py +125 -0
  45. xtgeo/grid3d/_grid_roxapi.py +292 -0
  46. xtgeo/grid3d/_grid_wellzone.py +165 -0
  47. xtgeo/grid3d/_gridprop_export.py +178 -0
  48. xtgeo/grid3d/_gridprop_import_eclrun.py +164 -0
  49. xtgeo/grid3d/_gridprop_import_grdecl.py +130 -0
  50. xtgeo/grid3d/_gridprop_import_roff.py +52 -0
  51. xtgeo/grid3d/_gridprop_import_xtgcpprop.py +168 -0
  52. xtgeo/grid3d/_gridprop_lowlevel.py +171 -0
  53. xtgeo/grid3d/_gridprop_op1.py +174 -0
  54. xtgeo/grid3d/_gridprop_roxapi.py +239 -0
  55. xtgeo/grid3d/_gridprop_value_init.py +140 -0
  56. xtgeo/grid3d/_gridprops_import_eclrun.py +344 -0
  57. xtgeo/grid3d/_gridprops_import_roff.py +83 -0
  58. xtgeo/grid3d/_roff_grid.py +469 -0
  59. xtgeo/grid3d/_roff_parameter.py +303 -0
  60. xtgeo/grid3d/grid.py +2537 -0
  61. xtgeo/grid3d/grid_properties.py +699 -0
  62. xtgeo/grid3d/grid_property.py +1341 -0
  63. xtgeo/grid3d/types.py +15 -0
  64. xtgeo/io/__init__.py +1 -0
  65. xtgeo/io/_file.py +592 -0
  66. xtgeo/metadata/__init__.py +17 -0
  67. xtgeo/metadata/metadata.py +431 -0
  68. xtgeo/roxutils/__init__.py +7 -0
  69. xtgeo/roxutils/_roxar_loader.py +54 -0
  70. xtgeo/roxutils/_roxutils_etc.py +122 -0
  71. xtgeo/roxutils/roxutils.py +207 -0
  72. xtgeo/surface/__init__.py +18 -0
  73. xtgeo/surface/_regsurf_boundary.py +26 -0
  74. xtgeo/surface/_regsurf_cube.py +210 -0
  75. xtgeo/surface/_regsurf_cube_window.py +391 -0
  76. xtgeo/surface/_regsurf_cube_window_v2.py +297 -0
  77. xtgeo/surface/_regsurf_cube_window_v3.py +360 -0
  78. xtgeo/surface/_regsurf_export.py +388 -0
  79. xtgeo/surface/_regsurf_grid3d.py +271 -0
  80. xtgeo/surface/_regsurf_gridding.py +347 -0
  81. xtgeo/surface/_regsurf_ijxyz_parser.py +278 -0
  82. xtgeo/surface/_regsurf_import.py +347 -0
  83. xtgeo/surface/_regsurf_lowlevel.py +122 -0
  84. xtgeo/surface/_regsurf_oper.py +631 -0
  85. xtgeo/surface/_regsurf_roxapi.py +241 -0
  86. xtgeo/surface/_regsurf_utils.py +81 -0
  87. xtgeo/surface/_surfs_import.py +43 -0
  88. xtgeo/surface/_zmap_parser.py +138 -0
  89. xtgeo/surface/regular_surface.py +2967 -0
  90. xtgeo/surface/surfaces.py +276 -0
  91. xtgeo/well/__init__.py +24 -0
  92. xtgeo/well/_blockedwell_roxapi.py +221 -0
  93. xtgeo/well/_blockedwells_roxapi.py +68 -0
  94. xtgeo/well/_well_aux.py +30 -0
  95. xtgeo/well/_well_io.py +327 -0
  96. xtgeo/well/_well_oper.py +574 -0
  97. xtgeo/well/_well_roxapi.py +304 -0
  98. xtgeo/well/_wellmarkers.py +486 -0
  99. xtgeo/well/_wells_utils.py +158 -0
  100. xtgeo/well/blocked_well.py +216 -0
  101. xtgeo/well/blocked_wells.py +122 -0
  102. xtgeo/well/well1.py +1514 -0
  103. xtgeo/well/wells.py +211 -0
  104. xtgeo/xyz/__init__.py +6 -0
  105. xtgeo/xyz/_polygons_oper.py +272 -0
  106. xtgeo/xyz/_xyz.py +741 -0
  107. xtgeo/xyz/_xyz_data.py +646 -0
  108. xtgeo/xyz/_xyz_io.py +490 -0
  109. xtgeo/xyz/_xyz_lowlevel.py +42 -0
  110. xtgeo/xyz/_xyz_oper.py +613 -0
  111. xtgeo/xyz/_xyz_roxapi.py +766 -0
  112. xtgeo/xyz/points.py +681 -0
  113. xtgeo/xyz/polygons.py +811 -0
  114. xtgeo-4.8.0.dist-info/METADATA +145 -0
  115. xtgeo-4.8.0.dist-info/RECORD +117 -0
  116. xtgeo-4.8.0.dist-info/WHEEL +5 -0
  117. xtgeo-4.8.0.dist-info/licenses/LICENSE.md +165 -0
@@ -0,0 +1,164 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, Literal
4
+
5
+ import resfo
6
+
7
+ from xtgeo.io._file import FileFormat
8
+
9
+ from ._find_gridprop_in_eclrun import (
10
+ find_gridprop_from_init_file,
11
+ find_gridprops_from_restart_file,
12
+ )
13
+
14
+ if TYPE_CHECKING:
15
+ from xtgeo.io._file import FileWrapper
16
+
17
+ from .grid import Grid
18
+
19
+
20
+ def decorate_name(
21
+ name: str, dual_porosity: bool, fracture: bool, date: int | None = None
22
+ ) -> str:
23
+ """Decorate a property name with date and matrix/fracture.
24
+
25
+ >>> decorate_name('PORO', True, False, 19991231)
26
+ 'POROM_19991231'
27
+ """
28
+ decorated_name = name
29
+ if dual_porosity:
30
+ if fracture:
31
+ decorated_name += "F"
32
+ else:
33
+ decorated_name += "M"
34
+
35
+ if date is not None:
36
+ decorated_name += "_" + str(date)
37
+ return decorated_name
38
+
39
+
40
+ def import_gridprop_from_init(
41
+ pfile: FileWrapper, name: str, grid: Grid, fracture: bool = False
42
+ ) -> dict[str, Any]:
43
+ """Import one parameter with the given name from an init file.
44
+
45
+ Args:
46
+ pfile: The init file.
47
+ name: The name of the parmaeter
48
+ grid: The grid used by the simulator to produce the init file.
49
+ fracture: If a dual porosity module, indicates that the fracture
50
+ (as apposed to the matrix) grid property should be imported.
51
+
52
+ Raises:
53
+ ValueError: If the parameter does not exist in the file.
54
+
55
+ Returns:
56
+ GridProperty parameter dictionary.
57
+ """
58
+ init_props = find_gridprop_from_init_file(pfile.file, [name], grid, fracture)
59
+ if len(init_props) != 1:
60
+ raise ValueError(f"Could not find property {name} in {pfile}")
61
+ init_props[0]["name"] = decorate_name(
62
+ init_props[0]["name"], grid.dualporo, fracture
63
+ )
64
+ return init_props[0]
65
+
66
+
67
+ def sanitize_date(
68
+ date: int | str | Literal["first", "last"],
69
+ ) -> list[int] | Literal["first", "last"]:
70
+ """
71
+ Converts dateformats of the form 'YYYY-MM-DD', 'YYYYMMDD' or YYYYMMDD to
72
+ list of integers of the form [YYYYMMDD] (ie. suitible for find_gridprops
73
+ functions), but lets the special literals 'first' and 'last' remain
74
+ unchanged.
75
+
76
+ >>> sanitize_date('first')
77
+ 'first'
78
+ >>> sanitize_date('last')
79
+ 'last'
80
+ >>> sanitize_date('2020-01-01')
81
+ [20200101]
82
+ >>> sanitize_date('20200101')
83
+ [20200101]
84
+ >>> sanitize_date(20200101)
85
+ [20200101]
86
+ """
87
+ if isinstance(date, int):
88
+ return [date]
89
+ if date not in ("first", "last"):
90
+ try:
91
+ if (
92
+ isinstance(date, str)
93
+ and len(date) == 10
94
+ and date[4] == "-"
95
+ and date[7] == "-"
96
+ ):
97
+ date = date.replace("-", "")
98
+ return [int(date)]
99
+ except ValueError as err:
100
+ raise ValueError(
101
+ "valid dates are either of the "
102
+ "form 'YYYY-MM-DD', 'YYYYMMDD' or 'first'/'last' "
103
+ f"got {date}"
104
+ ) from err
105
+ # Satisfy mypy that we're not returning a str
106
+ return "first" if date == "first" else "last"
107
+
108
+
109
+ def sanitize_fformat(fformat: FileFormat) -> resfo.Format:
110
+ """Converts 'unrst' and 'funrst' to the corresponding resfo.Format.
111
+
112
+ >>> sanitize_fformat('unrst')
113
+ <Format.UNFORMATTED: 2>
114
+ >>> sanitize_fformat('funrst')
115
+ <Format.FORMATTED: 1>
116
+ """
117
+ if fformat == FileFormat.UNRST:
118
+ return resfo.Format.UNFORMATTED
119
+ if fformat == FileFormat.FUNRST:
120
+ return resfo.Format.FORMATTED
121
+ raise ValueError(f"fformat must be either 'unrst' or 'funrst' got {fformat}")
122
+
123
+
124
+ def import_gridprop_from_restart(
125
+ pfile: FileWrapper,
126
+ name: str,
127
+ grid: Grid,
128
+ date: int | str | Literal["first", "last"],
129
+ fracture: bool = False,
130
+ fformat: FileFormat = FileFormat.UNRST,
131
+ ) -> dict[str, Any]:
132
+ """Import one parameter for the given name and date in a restart file.
133
+
134
+ Args:
135
+ pfile: The restart file.
136
+ name: The name of the parmaeter
137
+ date: xtgeo style date (e.g. int(19990101) or "YYYYMMDD"), also
138
+ accepts "YYYY-MM-DD". "last" and "first" can be given for
139
+ last or first date in the file
140
+ grid: The grid used by the simulator to produce the restart file.
141
+ fracture: If a dual porosity module, indicates that the fracture
142
+ (as apposed to the matrix) grid property should be imported.
143
+ Raises:
144
+ ValueError: If the parameter does not exist in the file.
145
+
146
+ Returns:
147
+ GridProperty parameter dictionary.
148
+ """
149
+ restart_props = find_gridprops_from_restart_file(
150
+ pfile.file,
151
+ [name],
152
+ sanitize_date(date),
153
+ grid,
154
+ fracture,
155
+ sanitize_fformat(fformat),
156
+ )
157
+ if len(restart_props) == 0:
158
+ raise ValueError(f"Could not find property {name} for {date} in {pfile.file}")
159
+ if len(restart_props) > 1:
160
+ raise ValueError(f"Ambiguous property {name} for {date} in {pfile.file}")
161
+ restart_props[0]["name"] = decorate_name(
162
+ restart_props[0]["name"], grid.dualporo, fracture, restart_props[0]["date"]
163
+ )
164
+ return restart_props[0]
@@ -0,0 +1,130 @@
1
+ """Importing grid props from GRDECL, ascii or binary"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ import numpy as np
8
+ import numpy.ma as ma
9
+ import resfo
10
+
11
+ from xtgeo.common import null_logger
12
+ from xtgeo.common.exceptions import KeywordNotFoundError
13
+
14
+ from ._grdecl_format import match_keyword, open_grdecl
15
+
16
+ logger = null_logger(__name__)
17
+
18
+ if TYPE_CHECKING:
19
+ from xtgeo.common.types import FileLike
20
+ from xtgeo.io._file import FileWrapper
21
+
22
+ from .grid import Grid
23
+
24
+
25
+ def import_bgrdecl_prop(pfile: FileWrapper, name: str, grid: Grid) -> dict[str, Any]:
26
+ """Import prop for binary files with GRDECL layout.
27
+
28
+ Args:
29
+ pfile (_XTgeoCFile): xtgeo file instance
30
+ name (str): Name of parameter.
31
+ grid (Grid()): XTGeo Grid instance.
32
+
33
+ Raises:
34
+ KeywordNotFoundError: Cannot find property...
35
+
36
+ Returns:
37
+ GridProperty parameter dictionary.
38
+ """
39
+ result: dict[str, Any] = {}
40
+ result["ncol"] = grid.ncol
41
+ result["nrow"] = grid.nrow
42
+ result["nlay"] = grid.nlay
43
+ result["name"] = name
44
+ result["filesrc"] = pfile
45
+
46
+ for entry in resfo.lazy_read(pfile.file):
47
+ if match_keyword(entry.read_keyword(), name):
48
+ values = entry.read_array()
49
+ result["discrete"] = np.issubdtype(values.dtype, np.integer)
50
+ if result["discrete"]:
51
+ uniq = np.unique(values).tolist()
52
+ codes = dict(zip(uniq, uniq))
53
+ codes = {key: str(val) for key, val in codes.items()} # val: strings
54
+ result["codes"] = codes
55
+ values = values.astype(np.int32)
56
+ result["roxar_dtype"] = np.uint16
57
+ else:
58
+ values = values.astype(np.float64)
59
+ result["codes"] = {}
60
+ result["roxar_dtype"] = np.float32
61
+ result["values"] = ma.masked_where(
62
+ grid.get_actnum().values < 1, values.reshape(grid.dimensions, order="F")
63
+ )
64
+ return result
65
+
66
+ raise KeywordNotFoundError(f"Cannot find property name {name} in file {pfile.name}")
67
+
68
+
69
+ def read_grdecl_3d_property(
70
+ filename: FileLike,
71
+ keyword: str,
72
+ dimensions: tuple[int, int, int],
73
+ dtype: type[float] | type[int] = float,
74
+ ) -> np.ndarray:
75
+ """
76
+ Read a 3d grid property from a grdecl file, see open_grdecl for description
77
+ of format.
78
+
79
+ Args:
80
+ filename (pathlib.Path or str): File in grdecl format.
81
+ keyword (str): The keyword of the property in the file
82
+ dimensions ((int,int,int)): Triple of the size of grid.
83
+ dtype (function): The datatype to be read, ie., float.
84
+
85
+ Returns:
86
+ numpy array with given dimensions and data type read
87
+ from the grdecl file.
88
+
89
+ Raises:
90
+ KeywordNotFoundError: If keyword is not found in the file.
91
+ """
92
+ with open_grdecl(filename, keywords=[], simple_keywords=[keyword]) as kw_generator:
93
+ try:
94
+ _, result = next(kw_generator)
95
+ except StopIteration as si:
96
+ raise KeywordNotFoundError(
97
+ f"Cannot import {keyword}, not present in file {filename}?"
98
+ ) from si
99
+
100
+ # The values are stored in F order in the grdecl file
101
+ f_order_values = np.array([dtype(v) for v in result])
102
+ return np.ascontiguousarray(f_order_values.reshape(dimensions, order="F"))
103
+
104
+
105
+ def import_grdecl_prop(pfile: FileWrapper, name: str, grid: Grid) -> dict[str, Any]:
106
+ """Import prop for ascii files with GRDECL layout.
107
+
108
+ Args:
109
+ pfile (_XTgeoCFile): xtgeo file instance
110
+ name (str): Name of parameter.
111
+ grid (Grid()): XTGeo Grid instance.
112
+
113
+ Raises:
114
+ KeywordNotFoundError: Cannot find property...
115
+
116
+ Returns:
117
+ GridProperty parameter dictionary.
118
+ """
119
+ result: dict[str, Any] = {}
120
+ result["ncol"] = grid.ncol
121
+ result["nrow"] = grid.nrow
122
+ result["nlay"] = grid.nlay
123
+ result["name"] = name
124
+ result["filesrc"] = pfile
125
+ actnumv = grid.get_actnum().values
126
+
127
+ result["values"] = ma.masked_where(
128
+ actnumv == 0, read_grdecl_3d_property(pfile.file, name, grid.dimensions, float)
129
+ )
130
+ return result
@@ -0,0 +1,52 @@
1
+ """Importing grid props from ROFF, binary"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ import numpy as np
8
+
9
+ from xtgeo.common import null_logger
10
+
11
+ from ._roff_parameter import RoffParameter
12
+
13
+ if TYPE_CHECKING:
14
+ from xtgeo.grid3d import Grid
15
+ from xtgeo.io._file import FileWrapper
16
+
17
+ logger = null_logger(__name__)
18
+
19
+
20
+ def import_roff(
21
+ pfile: FileWrapper,
22
+ name: str | None = None,
23
+ grid: Grid | None = None,
24
+ ) -> dict[str, Any]:
25
+ """Import ROFF format"""
26
+ result: dict[str, Any] = {}
27
+ roff_param = RoffParameter.from_file(pfile._file, name)
28
+ result["codes"] = roff_param.xtgeo_codes()
29
+ result["name"] = roff_param.name
30
+ result["ncol"] = int(roff_param.nx)
31
+ result["nrow"] = int(roff_param.ny)
32
+ result["nlay"] = int(roff_param.nz)
33
+ result["discrete"] = roff_param.is_discrete
34
+ result["values"] = roff_param.xtgeo_values()
35
+
36
+ if grid is not None and (actnum := grid.get_actnum()):
37
+ result["values"] = np.ma.masked_where(
38
+ actnum.values < 1,
39
+ result["values"],
40
+ )
41
+
42
+ roff_val = roff_param.values
43
+ if isinstance(roff_val, bytes) or np.issubdtype(roff_val.dtype, np.uint8):
44
+ result["roxar_dtype"] = np.uint8
45
+ elif np.issubdtype(roff_val.dtype, np.integer):
46
+ result["roxar_dtype"] = np.uint16
47
+ elif np.issubdtype(roff_val.dtype, np.floating):
48
+ result["roxar_dtype"] = np.float32
49
+ else:
50
+ raise ValueError(f"Could not deduce roxar type of {roff_val.dtype}")
51
+
52
+ return result
@@ -0,0 +1,168 @@
1
+ """GridProperty import function of xtgcpprop format."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from contextlib import contextmanager
7
+ from io import BytesIO, StringIO
8
+ from pathlib import Path
9
+ from struct import unpack
10
+ from typing import TYPE_CHECKING, Any, Generator
11
+
12
+ import numpy as np
13
+
14
+ import xtgeo.common.sys as xsys
15
+ from xtgeo.common import null_logger
16
+ from xtgeo.common.constants import UNDEF, UNDEF_INT
17
+ from xtgeo.metadata.metadata import MetaDataCPProperty
18
+
19
+ logger = null_logger(__name__)
20
+
21
+ if TYPE_CHECKING:
22
+ from collections.abc import Sequence
23
+
24
+ from numpy.typing import DTypeLike
25
+
26
+ from xtgeo.io._file import FileWrapper
27
+
28
+
29
+ @contextmanager
30
+ def _read_from_stream(
31
+ stream: BytesIO | StringIO,
32
+ size: int | None,
33
+ seek: int | None,
34
+ ) -> Generator[bytes, None, None]:
35
+ """Helper function to read from a stream with optional seeking."""
36
+ was_at = stream.tell()
37
+ if seek is not None:
38
+ stream.seek(seek)
39
+ try:
40
+ data = stream.read(size)
41
+ yield data if isinstance(data, bytes) else data.encode()
42
+ finally:
43
+ stream.seek(was_at)
44
+
45
+
46
+ @contextmanager
47
+ def _read_filelike(
48
+ filelike: Path | BytesIO | StringIO,
49
+ size: int | None = None,
50
+ seek: int | None = None,
51
+ ) -> Generator[bytes, None, None]:
52
+ """Context manager for reading a specified number of bytes from a file-like object.
53
+
54
+ Accepts either a Path, BytesIO, or StringIO object as input. The function reads
55
+ up to 'offset' bytes from the file-like object and yields these bytes.
56
+
57
+ For BytesIO and StringIO, the read operation preserves the original
58
+ file cursor position.
59
+ """
60
+
61
+ if isinstance(filelike, Path):
62
+ with filelike.open("rb") as f:
63
+ if seek is not None:
64
+ f.seek(seek)
65
+ yield f.read(size)
66
+ elif isinstance(filelike, (BytesIO, StringIO)):
67
+ with _read_from_stream(stream=filelike, size=size, seek=seek) as f:
68
+ yield f
69
+ else:
70
+ raise TypeError("Filelike must be one of: Path, BytesIO or StringIO.")
71
+
72
+
73
+ def import_xtgcpprop(
74
+ mfile: FileWrapper,
75
+ ijrange: Sequence[int] | None = None,
76
+ zerobased: bool = False,
77
+ ) -> dict[str, Any]:
78
+ """Using pure python for experimental xtgcpprop import.
79
+
80
+ Args:
81
+ mfile (FileWrapper): Input file reference
82
+ ijrange (list-like): List or tuple with 4 members [i_from, i_to, j_from, j_to]
83
+ where cell indices are zero based (starts with 0)
84
+ zerobased (bool): If ijrange basis is zero or one.
85
+
86
+ """
87
+ offset = 36
88
+
89
+ with _read_filelike(mfile.file, size=offset) as header:
90
+ # unpack header
91
+ swap, magic, nbyte, ncol, nrow, nlay = unpack("= i i i q q q", header)
92
+
93
+ if swap != 1 or magic not in (1351, 1352):
94
+ raise ValueError("Invalid file format (wrong swap id or magic number).")
95
+
96
+ if magic == 1351:
97
+ dtype: DTypeLike = np.float32 if nbyte == 4 else np.float64
98
+ else:
99
+ dtype = f"int{nbyte * 8}"
100
+
101
+ narr = ncol * nrow * nlay
102
+
103
+ ncolnew = nrownew = 0
104
+
105
+ if ijrange:
106
+ vals, ncolnew, nrownew = _import_xtgcpprop_partial(
107
+ mfile, nbyte, dtype, offset, ijrange, zerobased, ncol, nrow, nlay
108
+ )
109
+
110
+ else:
111
+ vals = xsys.npfromfile(mfile.file, dtype=dtype, count=narr, offset=offset)
112
+
113
+ # read metadata which will be at position offet + nfloat*narr +13
114
+ with _read_filelike(
115
+ mfile.file,
116
+ seek=offset + nbyte * narr + 13,
117
+ ) as _meta:
118
+ meta = json.loads(_meta, object_pairs_hook=dict)
119
+
120
+ req = meta["_required_"]
121
+
122
+ result = {att: req[att] for att in MetaDataCPProperty.REQUIRED}
123
+
124
+ if ijrange:
125
+ result["ncol"] = ncolnew
126
+ result["nrow"] = nrownew
127
+
128
+ result["values"] = np.ma.masked_equal(
129
+ vals.reshape((result["ncol"], result["nrow"], result["nlay"])),
130
+ UNDEF_INT if result["discrete"] else UNDEF,
131
+ )
132
+ return result
133
+
134
+
135
+ def _import_xtgcpprop_partial(
136
+ mfile: FileWrapper,
137
+ nbyte: int,
138
+ dtype: DTypeLike,
139
+ offset: int,
140
+ ijrange: Sequence[int],
141
+ zerobased: bool,
142
+ ncol: int,
143
+ nrow: int,
144
+ nlay: int,
145
+ ) -> tuple[np.ndarray, int, int]:
146
+ """Partial import of a property."""
147
+ i1, i2, j1, j2 = ijrange
148
+ if not zerobased:
149
+ i1 -= 1
150
+ i2 -= 1
151
+ j1 -= 1
152
+ j2 -= 1
153
+
154
+ ncolnew = i2 - i1 + 1
155
+ nrownew = j2 - j1 + 1
156
+
157
+ if ncolnew < 1 or ncolnew > ncol or nrownew < 1 or nrownew > nrow:
158
+ raise ValueError("The ijrange spesification is invalid.")
159
+
160
+ vals = np.zeros(ncolnew * nrownew * nlay, dtype=dtype)
161
+
162
+ for newnum, inum in enumerate(range(i1, i2 + 1)):
163
+ newpos = offset + (inum * nrow * nlay + j1 * nlay) * nbyte
164
+ ncount = nrownew * nlay
165
+ xvals = xsys.npfromfile(mfile.file, dtype=dtype, count=ncount, offset=newpos)
166
+ vals[newnum * ncount : newnum * ncount + ncount] = xvals
167
+
168
+ return vals, ncolnew, nrownew
@@ -0,0 +1,171 @@
1
+ """GridProperty (not GridProperies) low level functions"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Literal
6
+
7
+ import numpy as np
8
+ import numpy.ma as ma
9
+
10
+ from xtgeo import _cxtgeo
11
+ from xtgeo.common import null_logger
12
+ from xtgeo.common.constants import UNDEF, UNDEF_INT
13
+
14
+ logger = null_logger(__name__)
15
+
16
+ if TYPE_CHECKING:
17
+ from ctypes import Array as cArray
18
+
19
+ from numpy.typing import DTypeLike
20
+
21
+ from xtgeo.grid3d import Grid, GridProperty
22
+
23
+
24
+ def f2c_order(obj: Grid | GridProperty, values1d: np.ndarray) -> np.ndarray:
25
+ """Convert values1d from Fortran to C order, obj can be a Grid() or GridProperty()
26
+ instance
27
+ """
28
+ val = np.reshape(values1d, (obj.ncol, obj.nrow, obj.nlay), order="F")
29
+ val = np.asanyarray(val, order="C")
30
+ return val.ravel()
31
+
32
+
33
+ def c2f_order(obj: Grid | GridProperty, values1d: np.ndarray) -> np.ndarray:
34
+ """Convert values1d from C to F order, obj can be a Grid() or GridProperty()
35
+ instance
36
+ """
37
+ val = np.reshape(values1d, (obj.ncol, obj.nrow, obj.nlay), order="C")
38
+ val = np.asanyarray(val, order="F")
39
+ return val.ravel(order="F")
40
+
41
+
42
+ def update_values_from_carray(
43
+ self: GridProperty,
44
+ carray: cArray,
45
+ dtype: DTypeLike,
46
+ delete: bool = False,
47
+ ) -> None:
48
+ """Transfer values from SWIG 1D carray to numpy, 3D array"""
49
+
50
+ logger.debug("Update numpy from C array values")
51
+
52
+ nv = self.ntotal
53
+
54
+ self._isdiscrete = False
55
+
56
+ if dtype == np.float64:
57
+ logger.info("Entering conversion to numpy (float64) ...")
58
+ values1d = _cxtgeo.swig_carr_to_numpy_1d(nv, carray)
59
+ else:
60
+ logger.info("Entering conversion to numpy (int32) ...")
61
+ values1d = _cxtgeo.swig_carr_to_numpy_i1d(nv, carray)
62
+ self._isdiscrete = True
63
+
64
+ values = np.reshape(values1d, (self._ncol, self._nrow, self._nlay), order="F")
65
+
66
+ # make into C order as this is standard Python order...
67
+ values = np.asanyarray(values, order="C")
68
+
69
+ # make it float64 or whatever(?) and mask it
70
+ self.values = values # type: ignore
71
+ self.mask_undef()
72
+
73
+ # optionally delete the C array if needed
74
+ if delete:
75
+ delete_carray(self, carray)
76
+
77
+
78
+ def update_carray(
79
+ self: GridProperty,
80
+ undef: int | float | None = None,
81
+ discrete: bool | None = None,
82
+ dtype: DTypeLike = None,
83
+ order: Literal["C", "F", "A", "K"] = "F",
84
+ ) -> cArray:
85
+ """Copy (update) values from numpy to SWIG, 1D array, returns a pointer
86
+ to SWIG C array. If discrete is defined as True or False, force
87
+ the SWIG array to be of that kind.
88
+
89
+ Note that dtype will "override" current datatype if set. The resulting
90
+ carray will be in Fortran order, unless order is specified as 'C'
91
+ """
92
+
93
+ dstatus = self._isdiscrete
94
+ if discrete is not None:
95
+ dstatus = bool(discrete)
96
+
97
+ if undef is None:
98
+ undef = UNDEF
99
+ if dstatus:
100
+ undef = UNDEF_INT
101
+
102
+ logger.debug("Entering conversion from numpy to C array ...")
103
+
104
+ values = self.values.copy()
105
+
106
+ if not dtype:
107
+ values = values.astype(np.int32) if dstatus else values.astype(np.float64)
108
+ else:
109
+ values = values.astype(dtype)
110
+
111
+ values = ma.filled(values, undef)
112
+ values = np.asfortranarray(values)
113
+
114
+ if order == "F":
115
+ values = np.asfortranarray(values)
116
+
117
+ values1d = np.ravel(values, order=order)
118
+
119
+ if values1d.dtype == "float64" and dstatus and not dtype:
120
+ values1d = values1d.astype("int32")
121
+ logger.debug("Casting has been done")
122
+
123
+ if values1d.dtype == "float64":
124
+ logger.debug("Convert to carray (double)")
125
+ carray = _cxtgeo.new_doublearray(self.ntotal)
126
+ _cxtgeo.swig_numpy_to_carr_1d(values1d, carray)
127
+ elif values1d.dtype == "float32":
128
+ logger.debug("Convert to carray (float)")
129
+ carray = _cxtgeo.new_floatarray(self.ntotal)
130
+ _cxtgeo.swig_numpy_to_carr_f1d(values1d, carray)
131
+ elif values1d.dtype == "int32":
132
+ logger.debug("Convert to carray (int32)")
133
+ carray = _cxtgeo.new_intarray(self.ntotal)
134
+ _cxtgeo.swig_numpy_to_carr_i1d(values1d, carray)
135
+ else:
136
+ raise RuntimeError(f"Unsupported dtype, probable bug in {__name__}")
137
+ return carray
138
+
139
+
140
+ def delete_carray(self: GridProperty, carray: cArray) -> None:
141
+ """Delete carray SWIG C pointer, return carray as None"""
142
+
143
+ logger.debug("Enter delete carray values method for %d", id(self))
144
+ if carray is None:
145
+ return
146
+
147
+ if "int" in str(carray):
148
+ _cxtgeo.delete_intarray(carray)
149
+ return
150
+ if "float" in str(carray):
151
+ _cxtgeo.delete_floatarray(carray)
152
+ return
153
+ if "double" in str(carray):
154
+ _cxtgeo.delete_doublearray(carray)
155
+ return
156
+
157
+ raise RuntimeError("BUG?")
158
+
159
+
160
+ def check_shape_ok(self: GridProperty, values: np.ndarray) -> bool:
161
+ """Check if chape of values is OK"""
162
+ if values.shape == (self._ncol, self._nrow, self._nlay):
163
+ return True
164
+ logger.error(
165
+ "Wrong shape: Dimens of values %s %s %svs %s %s %s",
166
+ *values.shape,
167
+ self._ncol,
168
+ self._nrow,
169
+ self._nlay,
170
+ )
171
+ return False