diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 0dbb6e269..cf14b5b6a 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -10,10 +10,14 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' - name: Install dependencies run: | - sudo pip install cmake-format black + pip install cmake-format clang-format black - name: Clang Format run: ./script/clang-format --check diff --git a/lib/include/resdata/rd_file.hpp b/lib/include/resdata/rd_file.hpp index 9f073174a..d60e71dc6 100644 --- a/lib/include/resdata/rd_file.hpp +++ b/lib/include/resdata/rd_file.hpp @@ -16,9 +16,8 @@ extern "C" { #include #define RD_FILE_FLAGS_ENUM_DEFS \ - {.value = 1, .name = "RD_FILE_CLOSE_STREAM"}, { \ - .value = 2, .name = "RD_FILE_WRITABLE" \ - } + {.value = 1, .name = "RD_FILE_CLOSE_STREAM"}, \ + {.value = 2, .name = "RD_FILE_WRITABLE"} #define RD_FILE_FLAGS_ENUM_SIZE 2 typedef struct rd_file_struct rd_file_type; diff --git a/lib/include/resdata/rd_grid.hpp b/lib/include/resdata/rd_grid.hpp index fe15fdbf5..690fbcfbc 100644 --- a/lib/include/resdata/rd_grid.hpp +++ b/lib/include/resdata/rd_grid.hpp @@ -16,7 +16,7 @@ extern "C" { #endif #define RD_GRID_COORD_SIZE(nx, ny) (((nx) + 1) * ((ny) + 1) * 6) -#define RD_GRID_ZCORN_SIZE(nx, ny, nz) (((nx) * (ny) * (nz)*8)) +#define RD_GRID_ZCORN_SIZE(nx, ny, nz) (((nx) * (ny) * (nz) * 8)) #define RD_GRID_GLOBAL_GRID "Global" // used as key in hash tables over grids. #define RD_GRID_MAINGRID_LGR_NR 0 diff --git a/lib/include/resdata/rd_type.hpp b/lib/include/resdata/rd_type.hpp index fa6c7bcf4..57bbbd2b7 100644 --- a/lib/include/resdata/rd_type.hpp +++ b/lib/include/resdata/rd_type.hpp @@ -39,9 +39,8 @@ typedef enum { {.value = 2, .name = "RD_DOUBLE_TYPE"}, \ {.value = 3, .name = "RD_INT_TYPE"}, \ {.value = 4, .name = "RD_BOOL_TYPE"}, \ - {.value = 5, .name = "RD_MESS_TYPE"}, { \ - .value = 7, .name = "RD_STRING_TYPE" \ - } + {.value = 5, .name = "RD_MESS_TYPE"}, \ + {.value = 7, .name = "RD_STRING_TYPE"} /* Character data in restart format files comes as an array of fixed-length @@ -67,20 +66,15 @@ struct rd_type_struct { rd_data_type { RD_DOUBLE_TYPE, sizeof(double) } #define RD_BOOL \ rd_data_type { RD_BOOL_TYPE, sizeof(bool) } -#define RD_CHAR \ - rd_data_type { RD_CHAR_TYPE, RD_STRING8_LENGTH + 1 } -#define RD_MESS \ - rd_data_type { RD_MESS_TYPE, 0 } -#define RD_STRING(size) \ - rd_data_type { RD_STRING_TYPE, (size) + 1 } +#define RD_CHAR rd_data_type{RD_CHAR_TYPE, RD_STRING8_LENGTH + 1} +#define RD_MESS rd_data_type{RD_MESS_TYPE, 0} +#define RD_STRING(size) rd_data_type{RD_STRING_TYPE, (size) + 1} } #else #define RD_CHAR \ - (rd_data_type) { \ - .type = RD_CHAR_TYPE, .element_size = RD_STRING8_LENGTH + 1 \ - } + (rd_data_type){.type = RD_CHAR_TYPE, .element_size = RD_STRING8_LENGTH + 1} #define RD_INT \ (rd_data_type) { .type = RD_INT_TYPE, .element_size = sizeof(int) } #define RD_FLOAT \ @@ -89,10 +83,9 @@ struct rd_type_struct { (rd_data_type) { .type = RD_DOUBLE_TYPE, .element_size = sizeof(double) } #define RD_BOOL \ (rd_data_type) { .type = RD_BOOL_TYPE, .element_size = sizeof(bool) } -#define RD_MESS \ - (rd_data_type) { .type = RD_MESS_TYPE, .element_size = 0 } +#define RD_MESS (rd_data_type){.type = RD_MESS_TYPE, .element_size = 0} #define RD_STRING(size) \ - (rd_data_type) { .type = RD_STRING_TYPE, .element_size = (size) + 1 } + (rd_data_type){.type = RD_STRING_TYPE, .element_size = (size) + 1} #endif diff --git a/lib/include/resdata/rd_units.hpp b/lib/include/resdata/rd_units.hpp index 536bbcb05..1b4641024 100644 --- a/lib/include/resdata/rd_units.hpp +++ b/lib/include/resdata/rd_units.hpp @@ -6,8 +6,8 @@ extern "C" { #endif #define RD_UNITS_CUBIC(x) ((x) * (x) * (x)) -#define RD_UNITS_MILLI(x) ((x)*0.001) -#define RD_UNITS_MEGA(x) ((x)*1000000) +#define RD_UNITS_MILLI(x) ((x) * 0.001) +#define RD_UNITS_MEGA(x) ((x) * 1000000) #define RD_UNITS_LENGTH_INCH 0.0254 #define RD_UNITS_LENGTH_FEET 12 * RD_UNITS_LENGTH_INCH diff --git a/lib/include/resdata/rd_util.hpp b/lib/include/resdata/rd_util.hpp index 6fbc6fee6..870e5bd03 100644 --- a/lib/include/resdata/rd_util.hpp +++ b/lib/include/resdata/rd_util.hpp @@ -77,9 +77,8 @@ typedef enum { #define RD_PHASE_ENUM_DEFS \ {.value = 1, .name = "RD_OIL_PHASE"}, \ - {.value = 2, .name = "RD_GAS_PHASE"}, { \ - .value = 4, .name = "RD_WATER_PHASE" \ - } + {.value = 2, .name = "RD_GAS_PHASE"}, \ + {.value = 4, .name = "RD_WATER_PHASE"} #define RD_PHASE_ENUM_SIZE 3 typedef enum { diff --git a/lib/resdata/tests/rd_kw_fread.cpp b/lib/resdata/tests/rd_kw_fread.cpp index 55af4d2e1..c4a3d538a 100644 --- a/lib/resdata/tests/rd_kw_fread.cpp +++ b/lib/resdata/tests/rd_kw_fread.cpp @@ -76,7 +76,9 @@ void test_kw_io_charlength() { fortio_fclose(f); } - { test_assert_false(util_file_exists("TEST1")); } + { + test_assert_false(util_file_exists("TEST1")); + } { FILE *file = util_fopen("TEST2", "w"); diff --git a/lib/util/util.cpp b/lib/util/util.cpp index b720803aa..873e49a93 100644 --- a/lib/util/util.cpp +++ b/lib/util/util.cpp @@ -189,8 +189,8 @@ void util_endian_flip_vector(void *data, int element_size, int elements) { } #ifndef S_ISDIR -#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) -#define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif static bool EOL_CHAR(char c) { diff --git a/python/resdata/grid/faults/fault_block.py b/python/resdata/grid/faults/fault_block.py index a74a4c17d..787d61037 100644 --- a/python/resdata/grid/faults/fault_block.py +++ b/python/resdata/grid/faults/fault_block.py @@ -139,7 +139,7 @@ def contains_polyline(self, polyline): """ Will return true if at least one point from the polyline is inside the block. """ - edge_polyline = self.getEdgePolygon() + edge_polyline = self.get_edge_polygon() for p in polyline: if GeometryTools.pointInPolygon(p, edge_polyline): return True diff --git a/python/resdata/grid/faults/fault_block_layer.py b/python/resdata/grid/faults/fault_block_layer.py index 3176456a9..79be79bf3 100644 --- a/python/resdata/grid/faults/fault_block_layer.py +++ b/python/resdata/grid/faults/fault_block_layer.py @@ -199,7 +199,7 @@ def add_fault_link(self, fault1, fault2): def join_faults(self, fault1, fault2): if not fault1.intersectsFault(fault2, self.getK()): - layer = self.getGeoLayer() + layer = self.get_geo_layer() try: layer.addIJBarrier(Fault.joinFaults(fault1, fault2, self.getK())) except ValueError: diff --git a/python/resdata/grid/faults/fault_segments.py b/python/resdata/grid/faults/fault_segments.py index b5ff6e91a..90c328ce2 100644 --- a/python/resdata/grid/faults/fault_segments.py +++ b/python/resdata/grid/faults/fault_segments.py @@ -68,7 +68,7 @@ def __str__(self): return self.__segment_map.__str__() def verify(self): - for C, count in self.__count_map.iteritems(): + for C, count in self.__count_map.items(): if count > 0: d = self.__segment_map[C] if len(d) != count: @@ -135,7 +135,7 @@ def pop_next(self, segment): def print_content(self): for d in self.__segment_map.values(): - for C, S in d.iteritems(): + for C, S in d.items(): print(S) diff --git a/python/resdata/grid/faults/layer.py b/python/resdata/grid/faults/layer.py index eaf263f42..9e5f6e8da 100644 --- a/python/resdata/grid/faults/layer.py +++ b/python/resdata/grid/faults/layer.py @@ -179,8 +179,8 @@ def add_ij_barrier(self, ij_list): if len(ij_list) < 2: raise ValueError("Must have at least two (i,j) points") - nx = self.getNX() - ny = self.getNY() + nx = self.get_nx() + ny = self.get_ny() p1 = ij_list[0] i1, j1 = p1 for p2 in ij_list[1:]: diff --git a/python/tests/rd_tests/test_fault_blocks.py b/python/tests/rd_tests/test_fault_blocks.py index 09c648e46..895af8711 100644 --- a/python/tests/rd_tests/test_fault_blocks.py +++ b/python/tests/rd_tests/test_fault_blocks.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -from unittest import skipIf import cwrap +import pytest from resdata import ResDataType from resdata.resfile import ResdataKW @@ -147,6 +147,7 @@ def test_neighbours2(self): f.write("FAULTS\n") f.write("'FY' 1 4 4 4 1 1 'Y' /\n") f.write("'FX' 4 4 1 8 1 1 'X' /\n") + f.write("'FXX' 6 6 1 8 1 1 'X' /\n") f.write("/") faults = FaultCollection(grid, "faults.grdecl") @@ -185,6 +186,10 @@ def test_neighbours2(self): nb = b1.getNeighbours() self.assertEqual(len(nb), 0) + layer.join_faults(faults["FX"], faults["FY"]) + with pytest.raises(ValueError, match="Failed to join faults FXX and FY"): + layer.join_faults(faults["FXX"], faults["FY"]) + def test_neighbours3(self): nx = 8 ny = 8 @@ -286,6 +291,9 @@ def test_fault_block_layer(self): polyline = Polyline(init_points=[(1.0, 0.0), (2.0, 1.0)]) assert l1.contains_polyline(polyline) + polyline2 = Polyline(init_points=[(10.5, 1.0), (11, 5)]) + assert not l1.contains_polyline(polyline2) + with self.assertRaises(KeyError): l = layer.getBlock(66) @@ -417,6 +425,11 @@ def test_internal_blocks(self): faults = FaultCollection(grid, "faults.grdecl") layer.loadKeyword(kw) + faulty_kw = ResdataKW("SOIL", 10000, ResDataType.RD_INT) + with pytest.raises( + ValueError, match="The fault block keyword had wrong type/size" + ): + layer.load_keyword(faulty_kw) layer.addFaultBarrier(faults["FX"]) b1 = layer.getBlock(1) b2 = layer.getBlock(2) diff --git a/python/tests/rd_tests/test_faults.py b/python/tests/rd_tests/test_faults.py index 048e68054..f8a6ff3c6 100644 --- a/python/tests/rd_tests/test_faults.py +++ b/python/tests/rd_tests/test_faults.py @@ -12,6 +12,7 @@ FaultLine, FaultSegment, FaultBlockLayer, + SegmentMap, ) from resdata.util.test import TestAreaContext from resdata.geometry import Polyline, CPolyline @@ -127,6 +128,36 @@ def test_faultLine_center(self): self.assertEqual(len(fl), 2) self.assertEqual(fl.center(), (0.50, 0.50)) + def test_fault_segments(self): + S1 = FaultSegment(0, 10) + S2 = FaultSegment(10, 20) + S3 = FaultSegment(0, 10) + S4 = FaultSegment(10, 0) + S5 = FaultSegment(30, 40) + S6 = FaultSegment(50, 40) + assert S1 != S2 + assert S1 == S3 + assert S3 == S4 + assert S1.joins(S2) + assert S2.joins(S4) + assert S1.joins(S4) + assert not S2.joins(S5) + assert not S5.joins(S2) + assert S6.joins(S5) + + def test_segment_map(self): + S1 = FaultSegment(0, 10) + S2 = FaultSegment(10, 20) + S5 = FaultSegment(30, 40) + SM = SegmentMap() + SM.add_segment(S1) + SM.add_segment(S2) + assert len(SM) == 3 + SM.verify() + SM.add_segment(S5) + assert len(SM) == 5 + SM.print_content() + def test_faultLine(self): fl = FaultLine(self.grid, 10) S1 = FaultSegment(0, 10) diff --git a/python/tests/rd_tests/test_grdecl.py b/python/tests/rd_tests/test_grdecl.py index f9e7c733a..cd77ef7bc 100644 --- a/python/tests/rd_tests/test_grdecl.py +++ b/python/tests/rd_tests/test_grdecl.py @@ -1,10 +1,12 @@ +import pytest + from numpy import allclose import cwrap from resdata.resfile import ResdataKW -def test_eclkw_read_grdecl(tmp_path): +def test_resdatakw_read_grdecl(tmp_path): block_size = 10 num_blocks = 5 value = 0.15 @@ -17,6 +19,8 @@ def test_eclkw_read_grdecl(tmp_path): with cwrap.open(str(tmp_path / "test.grdecl")) as f: kw = ResdataKW.read_grdecl(f, "COORD") assert ResdataKW.fseek_grdecl(f, "COORD", True) + with pytest.raises(TypeError, match="Sorry keyword:TOOLONGAKW"): + ResdataKW.read_grdecl(f, "TOOLONGAKW") assert kw.get_name() == "COORD" assert len(kw.numpy_view()) == block_size * num_blocks diff --git a/python/tests/rd_tests/test_grid.py b/python/tests/rd_tests/test_grid.py index 4d8e734c1..8d1e6421e 100644 --- a/python/tests/rd_tests/test_grid.py +++ b/python/tests/rd_tests/test_grid.py @@ -2,9 +2,10 @@ import os.path from unittest import skip import itertools +import pytest import functools -from numpy import linspace, allclose +import numpy as np import cwrap from resdata.util.util import IntVector, DoubleVector @@ -260,6 +261,13 @@ def test_dims(self): self.assertEqual(grid.getDims(), (10, 20, 30, 6000)) + def test_global_index(self): + grid = GridGen.createRectangular((10, 20, 30), (1, 1, 1)) + with pytest.raises( + IndexError, match=r"Invalid value global_index:7000 Range: \[0,6000\)" + ): + grid.get_active_index(global_index=7000) + def test_load_column(self): column = DoubleVector(2 * 3 * 4) grid = GridGen.createRectangular((2, 3, 4), (1, 1, 1)) @@ -455,7 +463,18 @@ def test_create_3d_is_create_kw_inverse(self): numpy_3d = grid.create3D(kw1) kw2 = grid.create_kw(numpy_3d, "SWAT", False) self.assertEqual(kw2.name, "SWAT") - assert allclose(grid.create3D(kw2), numpy_3d) + assert np.allclose(grid.create3D(kw2), numpy_3d) + + def test_create_kw(self): + rng = np.random.default_rng() + nx = 10 + ny = 7 + nz = 5 + grid = GridGen.create_rectangular((nx, ny, nz), (1, 1, 1)) + array1 = rng.integers(0, 100, size=(nx, ny, nz), dtype=np.int32) + array2 = rng.normal(10, 2, size=(nx, ny, nz)) + _ = grid.create_kw(array1, "SWAT1", False) + _ = grid.create_kw(array2, "SWAT2", False) def test_create_3d_agrees_with_get_value(self): nx = 5 @@ -539,13 +558,13 @@ def test_unique_containment(self): (xmin, xmax), (ymin, ymax), (zmin, zmax) = getMinMaxValue(wgrid) - x_space = linspace( + x_space = np.linspace( xmin - 1, xmax + 1, int(xmax - xmin + 2) * steps_per_unit + 1 ) - y_space = linspace( + y_space = np.linspace( ymin - 1, ymax + 1, int(ymax - ymin + 2) * steps_per_unit + 1 ) - z_space = linspace( + z_space = np.linspace( zmin - 1, zmax + 1, int(zmax - zmin + 2) * steps_per_unit + 1 ) diff --git a/python/tests/rd_tests/test_rd_file.py b/python/tests/rd_tests/test_rd_file.py index c4f08d270..ca649439b 100644 --- a/python/tests/rd_tests/test_rd_file.py +++ b/python/tests/rd_tests/test_rd_file.py @@ -247,6 +247,9 @@ def test_block_view(self): view = rd_file.blockView2(None, "DATA2", 0) + with pytest.raises(KeyError, match="The keyword:FAULTY is not in file"): + rd_file.block_view2("HEADER", "FAULTY", 0) + def test_report_list(tmpdir): with tmpdir.as_cwd(): diff --git a/python/tests/rd_tests/test_rd_kw.py b/python/tests/rd_tests/test_rd_kw.py index 4ff7b17e8..7adb45cef 100644 --- a/python/tests/rd_tests/test_rd_kw.py +++ b/python/tests/rd_tests/test_rd_kw.py @@ -1,4 +1,7 @@ #!/usr/bin/env python + +import pytest + import random import warnings import cwrap @@ -622,6 +625,25 @@ def test_iadd(): assert list(kw1) == list(kw2) + with pytest.raises(TypeError, match="Type mismatch"): + kw1 += "a" + + +def test_imul(): + kw1 = ResdataKW("KW1", 5, ResDataType.RD_INT) + for i in range(len(kw1)): + kw1[i] = 1 + kw1 *= 10 + + assert list(kw1) == [10] * 5 + + with pytest.raises(TypeError, match="Type mismatch"): + kw1 *= 3.2 + + kw2 = ResdataKW("KW2", 5, ResDataType.RD_FLOAT) + with pytest.raises(TypeError, match="Only muliplication with scalar supported"): + kw2 *= "a" + def test_get_ptr_data(): assert ResdataKW("KW1", 10, ResDataType.RD_INT).get_data_ptr() diff --git a/python/tests/rd_tests/test_rd_util.py b/python/tests/rd_tests/test_rd_util.py index 7343abb10..39a41c790 100644 --- a/python/tests/rd_tests/test_rd_util.py +++ b/python/tests/rd_tests/test_rd_util.py @@ -13,7 +13,10 @@ def test_enums(self): def test_file_type(self): file_type, fmt, report = ResdataUtil.inspectExtension("CASE.X0078") - self.assertEqual(file_type, FileType.RESTART) + assert file_type == FileType.RESTART + file_type, fmt, report = ResdataUtil.inspectExtension("CASE.UNRST") + assert file_type == FileType.UNIFIED_RESTART + assert report == None def test_file_report_nr(self): report_nr = ResdataUtil.reportStep("CASE.X0080") diff --git a/python/tests/rd_tests/test_region.py b/python/tests/rd_tests/test_region.py index 2196d829b..840c70e74 100644 --- a/python/tests/rd_tests/test_region.py +++ b/python/tests/rd_tests/test_region.py @@ -24,6 +24,12 @@ def test_equal(self): with self.assertRaises(ValueError): region.select_equal(kw_float, 1) + with pytest.raises( + ValueError, + match="The select_equal method must have an integer valued keyword", + ): + region.deselect_equal(kw_float, 2) + def test_sum(self): grid = GridGenerator.create_rectangular((10, 10, 1), (1, 1, 1)) kw_mask = ResdataKW("INT", grid.getGlobalSize(), ResDataType.RD_INT) @@ -220,6 +226,13 @@ def poro(grid): ) +@pytest.fixture +def poro_int(grid): + return grid.create_kw( + np.ones((grid.nx, grid.ny, grid.nz), dtype=np.int32), "PORO", True + ) + + def test_select_in_range(empty_region, active_region, poro): empty_region.select_in_range(poro, 0.9, 1.1) assert empty_region == active_region @@ -464,6 +477,12 @@ def test_idiv_kw_full(full_region, poro): assert list(poro) == [1.0] * len(poro) +def test_idiv_kw_int(full_region, poro_int): + poro_int += 1 + full_region.idiv_kw(poro_int, 10) + assert list(poro_int) == [0] * len(poro_int) # ??? + + def test_mul_kw_full(full_region, poro): poro += 1.0 poro.mul(poro, mask=full_region)