Skip to content

Commit 8988c7e

Browse files
committed
#13313 Python: Add support for setting geometry on an existing case
1 parent 937d9e0 commit 8988c7e

File tree

6 files changed

+322
-0
lines changed

6 files changed

+322
-0
lines changed

ApplicationLibCode/ProjectDataModel/RimCornerPointCase.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,35 @@ std::expected<RimCornerPointCase*, QString> RimCornerPointCase::createFromCoordi
108108
return cornerPointCase;
109109
}
110110

111+
//--------------------------------------------------------------------------------------------------
112+
///
113+
//--------------------------------------------------------------------------------------------------
114+
std::expected<void, QString> RimCornerPointCase::replaceGridFromCoordinatesArray( RimCornerPointCase& cornerPointCase,
115+
const int nx,
116+
const int ny,
117+
const int nz,
118+
const std::vector<float>& coord,
119+
const std::vector<float>& zcorn,
120+
const std::vector<float>& actnum )
121+
{
122+
// cornerPointCase.
123+
// cornerPointCase.closeReservoirCase();
124+
125+
RigActiveCellInfo* activeCellInfo = cornerPointCase.eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL );
126+
CVF_ASSERT( activeCellInfo );
127+
activeCellInfo->clear();
128+
129+
RigActiveCellInfo* fractureActiveCellInfo =
130+
cornerPointCase.eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL );
131+
CVF_ASSERT( fractureActiveCellInfo );
132+
fractureActiveCellInfo->clear();
133+
134+
buildGrid( *cornerPointCase.eclipseCaseData(), nx, ny, nz, coord, zcorn, actnum );
135+
cornerPointCase.computeCachedData();
136+
137+
return {};
138+
}
139+
111140
//--------------------------------------------------------------------------------------------------
112141
///
113142
//--------------------------------------------------------------------------------------------------

ApplicationLibCode/ProjectDataModel/RimCornerPointCase.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ class RimCornerPointCase : public RimEclipseCase
5050
const std::vector<float>& zcorn,
5151
const std::vector<float>& actnum );
5252

53+
static std::expected<void, QString> replaceGridFromCoordinatesArray( RimCornerPointCase& cornerPointCase,
54+
const int nx,
55+
const int ny,
56+
const int nz,
57+
const std::vector<float>& coord,
58+
const std::vector<float>& zcorn,
59+
const std::vector<float>& actnum );
60+
5361
protected:
5462
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
5563

ApplicationLibCode/ProjectDataModelCommands/RimcEclipseCase.cpp

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "RiaApplication.h"
2222
#include "RiaGuiApplication.h"
2323
#include "RiaKeyValueStoreUtil.h"
24+
#include "RiaViewRedrawScheduler.h"
2425

2526
#include "RigActiveCellInfo.h"
2627
#include "RigCaseCellResultsData.h"
@@ -32,9 +33,13 @@
3233
#include "RigResultAccessor.h"
3334
#include "RigResultAccessorFactory.h"
3435

36+
#include "Rim3dView.h"
3537
#include "RimCase.h"
38+
#include "RimCornerPointCase.h"
3639
#include "RimEclipseCase.h"
40+
#include "RimEclipseView.h"
3741
#include "RimProject.h"
42+
#include "RimReloadCaseTools.h"
3843

3944
#include "cafPdmFieldScriptingCapability.h"
4045

@@ -254,3 +259,93 @@ std::expected<caf::PdmObjectHandle*, QString> RimcEclipseCase_exportCornerPointG
254259

255260
return nullptr;
256261
}
262+
263+
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimEclipseCase, RimcEclipseCase_replaceCornerPointGridInternal, "replace_corner_point_grid_internal" );
264+
265+
//--------------------------------------------------------------------------------------------------
266+
///
267+
//--------------------------------------------------------------------------------------------------
268+
RimcEclipseCase_replaceCornerPointGridInternal::RimcEclipseCase_replaceCornerPointGridInternal( caf::PdmObjectHandle* self )
269+
: caf::PdmVoidObjectMethod( self )
270+
{
271+
CAF_PDM_InitObject( "Replace Corner Point Grid Internal", "", "", "Replace Corner Point Grid Internal" );
272+
273+
CAF_PDM_InitScriptableFieldNoDefault( &m_nx, "Nx", "" );
274+
CAF_PDM_InitScriptableFieldNoDefault( &m_ny, "Ny", "" );
275+
CAF_PDM_InitScriptableFieldNoDefault( &m_nz, "Nz", "" );
276+
CAF_PDM_InitScriptableFieldNoDefault( &m_coordKey, "CoordKey", "" );
277+
CAF_PDM_InitScriptableFieldNoDefault( &m_zcornKey, "ZcornKey", "" );
278+
CAF_PDM_InitScriptableFieldNoDefault( &m_actnumKey, "ActnumKey", "" );
279+
}
280+
281+
//--------------------------------------------------------------------------------------------------
282+
///
283+
//--------------------------------------------------------------------------------------------------
284+
std::expected<caf::PdmObjectHandle*, QString> RimcEclipseCase_replaceCornerPointGridInternal::execute()
285+
{
286+
auto eclipseCase = self<RimEclipseCase>();
287+
if ( !eclipseCase )
288+
{
289+
return std::unexpected( "Unable to get Eclipse case." );
290+
}
291+
292+
int nx = m_nx();
293+
int ny = m_ny();
294+
int nz = m_nz();
295+
if ( nx <= 0 || ny <= 0 || nz <= 0 )
296+
{
297+
return std::unexpected( "Invalid grid dimensions. nx, ny and nz must be positive." );
298+
}
299+
300+
auto keyValueStore = RiaApplication::instance()->keyValueStore();
301+
302+
std::vector<float> coord = RiaKeyValueStoreUtil::convertToFloatVector( keyValueStore->get( m_coordKey().toStdString() ) );
303+
std::vector<float> zcorn = RiaKeyValueStoreUtil::convertToFloatVector( keyValueStore->get( m_zcornKey().toStdString() ) );
304+
std::vector<float> actnum = RiaKeyValueStoreUtil::convertToFloatVector( keyValueStore->get( m_actnumKey().toStdString() ) );
305+
306+
if ( coord.empty() || zcorn.empty() || actnum.empty() )
307+
{
308+
return std::unexpected( "Found unexpected empty coord, zcorn or actnum array." );
309+
}
310+
311+
RimCornerPointCase* cornerPointCase = dynamic_cast<RimCornerPointCase*>( eclipseCase );
312+
if ( !cornerPointCase )
313+
{
314+
return std::unexpected( "Grid is not a corner point case!!!!!" );
315+
}
316+
317+
// Create a new corner point grid from the provided geometry
318+
auto result = RimCornerPointCase::replaceGridFromCoordinatesArray( *cornerPointCase, nx, ny, nz, coord, zcorn, actnum );
319+
if ( !result.has_value() )
320+
{
321+
return std::unexpected( "Failed to replace grid" );
322+
}
323+
324+
// Update all 3D views to reflect the new grid
325+
// We must completely reinitialize the views because the grid size has changed
326+
RiaViewRedrawScheduler::instance()->blockUpdate( true );
327+
328+
std::vector<Rim3dView*> views = eclipseCase->views();
329+
for ( Rim3dView* view : views )
330+
{
331+
if ( RimEclipseView* eclView = dynamic_cast<RimEclipseView*>( view ) )
332+
{
333+
// Force complete regeneration by calling loadDataAndUpdate
334+
// This will reinitialize all geometry managers with the new grid size
335+
eclView->loadDataAndUpdate();
336+
}
337+
}
338+
339+
RiaViewRedrawScheduler::instance()->clearViewsScheduledForUpdate();
340+
RiaViewRedrawScheduler::instance()->blockUpdate( false );
341+
342+
// Update connected editors
343+
eclipseCase->updateConnectedEditors();
344+
345+
// Clean up key-value store
346+
keyValueStore->remove( m_coordKey().toStdString() );
347+
keyValueStore->remove( m_zcornKey().toStdString() );
348+
keyValueStore->remove( m_actnumKey().toStdString() );
349+
350+
return nullptr;
351+
}

ApplicationLibCode/ProjectDataModelCommands/RimcEclipseCase.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,24 @@ class RimcEclipseCase_exportCornerPointGridInternal : public caf::PdmVoidObjectM
8080
caf::PdmField<QString> m_coordKey;
8181
caf::PdmField<QString> m_actnumKey;
8282
};
83+
84+
//==================================================================================================
85+
///
86+
//==================================================================================================
87+
class RimcEclipseCase_replaceCornerPointGridInternal : public caf::PdmVoidObjectMethod
88+
{
89+
CAF_PDM_HEADER_INIT;
90+
91+
public:
92+
RimcEclipseCase_replaceCornerPointGridInternal( caf::PdmObjectHandle* self );
93+
94+
std::expected<caf::PdmObjectHandle*, QString> execute() override;
95+
96+
private:
97+
caf::PdmField<int> m_nx;
98+
caf::PdmField<int> m_ny;
99+
caf::PdmField<int> m_nz;
100+
caf::PdmField<QString> m_coordKey;
101+
caf::PdmField<QString> m_zcornKey;
102+
caf::PdmField<QString> m_actnumKey;
103+
};

GrpcInterface/Python/rips/case.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,3 +1435,56 @@ def export_corner_point_grid(
14351435
return (zcorn, coord, actnum, nx, ny, nz)
14361436
else:
14371437
return ([], [], [], 0, 0, 0)
1438+
1439+
1440+
@add_method(Case)
1441+
def replace_corner_point_grid(
1442+
self,
1443+
nx: int,
1444+
ny: int,
1445+
nz: int,
1446+
coord: List[float],
1447+
zcorn: List[float],
1448+
actnum: List[int],
1449+
):
1450+
"""Replace the current case grid with new corner point grid geometry
1451+
1452+
This method modifies the geometry of an existing case by replacing it with
1453+
new grid parameters. All existing properties will be cleared.
1454+
1455+
Arguments:
1456+
nx(int): Number of cells in x direction
1457+
ny(int): Number of cells in y direction
1458+
nz(int): Number of cells in z direction
1459+
coord(list[float]): Coordinate lines as COORD keyword in Eclipse.
1460+
Each coordinate line is defined by two points (top and bottom).
1461+
Size: (nx+1) * (ny+1) * 2 * 3. Points are ordered as in Eclipse.
1462+
zcorn(list[float]): Corner depths as defined by the Eclipse keyword ZCORN.
1463+
Size: nx * ny * nz * 8
1464+
actnum(list[int]): Active cell info: cells with values > 0 are active.
1465+
Size: nx * ny * nz
1466+
"""
1467+
# Generate unique keys for three arrays
1468+
coord_key = "{}_{}".format(uuid.uuid4(), "coord")
1469+
zcorn_key = "{}_{}".format(uuid.uuid4(), "zcorn")
1470+
actnum_key = "{}_{}".format(uuid.uuid4(), "actnum")
1471+
1472+
# Get project to access key-value store
1473+
project = self.ancestor(rips.project.Project)
1474+
if not project:
1475+
raise RuntimeError("Unable to get project from case")
1476+
1477+
# Store arrays in key-value store
1478+
project.set_key_values(coord_key, coord)
1479+
project.set_key_values(zcorn_key, zcorn)
1480+
project.set_key_values(actnum_key, actnum)
1481+
1482+
# Call internal C++ method to replace the grid
1483+
self.replace_corner_point_grid_internal(
1484+
nx=nx,
1485+
ny=ny,
1486+
nz=nz,
1487+
coord_key=coord_key,
1488+
zcorn_key=zcorn_key,
1489+
actnum_key=actnum_key,
1490+
)

GrpcInterface/Python/rips/tests/test_export_corner_point_grid.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,119 @@ def test_export_corner_point_grid_roundtrip(rips_instance, initialize_test):
172172

173173
max_diff = max(diff_x, diff_y, diff_z)
174174
assert max_diff < 1e-3
175+
176+
177+
def test_replace_corner_point_grid_basic(rips_instance, initialize_test):
178+
"""Test the replace_corner_point_grid method by creating a grid and replacing it with a larger one"""
179+
180+
# Create initial grid with 2x2x2 cells
181+
nx_init, ny_init, nz_init = 2, 2, 2
182+
183+
# Generate simple coordinate data for initial grid
184+
coord_init = []
185+
for j in range(ny_init + 1):
186+
for i in range(nx_init + 1):
187+
x = i * 100.0
188+
y = j * 100.0
189+
# Top point
190+
coord_init.extend([x, y, 1000.0])
191+
# Bottom point
192+
coord_init.extend([x, y, 1100.0])
193+
194+
# Generate simple ZCORN data for initial grid
195+
zcorn_init = []
196+
for k in range(nz_init):
197+
for j in range(ny_init):
198+
for i in range(nx_init):
199+
base_depth = 1000.0 + k * 100.0
200+
# 8 corner depths for each cell
201+
zcorn_init.extend(
202+
[
203+
base_depth,
204+
base_depth,
205+
base_depth,
206+
base_depth,
207+
base_depth + 100.0,
208+
base_depth + 100.0,
209+
base_depth + 100.0,
210+
base_depth + 100.0,
211+
]
212+
)
213+
214+
# Generate ACTNUM for initial grid (all cells active)
215+
actnum_init = [1] * (nx_init * ny_init * nz_init)
216+
217+
# Create the initial grid case
218+
case = rips_instance.project.create_corner_point_grid(
219+
"InitialGrid", nx_init, ny_init, nz_init, coord_init, zcorn_init, actnum_init
220+
)
221+
222+
# Verify initial grid dimensions
223+
initial_cell_count = case.cell_count()
224+
assert initial_cell_count.reservoir_cell_count == nx_init * ny_init * nz_init
225+
226+
# Now create replacement grid with more cells (3x3x3)
227+
nx_new, ny_new, nz_new = 3, 3, 3
228+
229+
# Generate coordinate data for new grid
230+
coord_new = []
231+
for j in range(ny_new + 1):
232+
for i in range(nx_new + 1):
233+
x = i * 100.0
234+
y = j * 100.0
235+
# Top point
236+
coord_new.extend([x, y, 1000.0])
237+
# Bottom point
238+
coord_new.extend([x, y, 1200.0])
239+
240+
# Generate ZCORN data for new grid
241+
zcorn_new = []
242+
for k in range(nz_new):
243+
for j in range(ny_new):
244+
for i in range(nx_new):
245+
base_depth = 1000.0 + k * 100.0
246+
# 8 corner depths for each cell
247+
zcorn_new.extend(
248+
[
249+
base_depth,
250+
base_depth,
251+
base_depth,
252+
base_depth,
253+
base_depth + 100.0,
254+
base_depth + 100.0,
255+
base_depth + 100.0,
256+
base_depth + 100.0,
257+
]
258+
)
259+
260+
# Generate ACTNUM for new grid (all cells active)
261+
actnum_new = [1] * (nx_new * ny_new * nz_new)
262+
263+
# Replace the grid geometry
264+
case.replace_corner_point_grid(
265+
nx_new, ny_new, nz_new, coord_new, zcorn_new, actnum_new
266+
)
267+
268+
# Verify that the grid now has the new dimensions
269+
new_cell_count = case.cell_count()
270+
assert new_cell_count.reservoir_cell_count == nx_new * ny_new * nz_new
271+
assert new_cell_count.reservoir_cell_count == 27 # 3x3x3
272+
273+
# Verify the grid has more cells than before
274+
assert new_cell_count.reservoir_cell_count > initial_cell_count.reservoir_cell_count
275+
276+
# Export the replaced grid and verify dimensions
277+
exported_zcorn, exported_coord, exported_actnum, export_nx, export_ny, export_nz = (
278+
case.export_corner_point_grid()
279+
)
280+
281+
# Verify dimensions match
282+
assert export_nx == nx_new
283+
assert export_ny == ny_new
284+
assert export_nz == nz_new
285+
286+
# Verify array sizes
287+
print(exported_actnum)
288+
assert len(exported_actnum) == nx_new * ny_new * nz_new
289+
# All cells should be active
290+
assert sum(1 for x in exported_actnum if x > 0) == len(actnum_new)

0 commit comments

Comments
 (0)