Add python api for creating corner point grid.

This commit is contained in:
Kristian Bendiksen
2025-05-07 12:07:31 +02:00
parent 1bc9925954
commit e5dc280ce0
16 changed files with 1002 additions and 14 deletions
@@ -21,6 +21,7 @@
#include "Cloud/RiaOsduConnector.h"
#include "Cloud/RiaSumoConnector.h"
#include "Cloud/RiaSumoDefines.h"
#include "KeyValueStore/RiaKeyValueStore.h"
#include "RiaOsduDefines.h"
#include "RiaBaseDefs.h"
@@ -178,6 +179,8 @@ RiaApplication::RiaApplication()
m_commandRouter = std::make_unique<RimCommandRouter>();
m_osduConnector = nullptr;
m_sumoConnector = nullptr;
m_keyValueStore = std::make_unique<RiaKeyValueStore<char>>();
}
//--------------------------------------------------------------------------------------------------
@@ -1758,3 +1761,11 @@ void RiaApplication::initializeDataLoadController()
dataLoadController->registerDataLoader( RimWellLogFile::classKeywordStatic(), wellLogKeyword, std::make_unique<RimWellLogFileDataLoader>() );
dataLoadController->registerDataLoader( RimOsduWellLog::classKeywordStatic(), wellLogKeyword, std::make_unique<RimOsduWellLogDataLoader>() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaKeyValueStore<char>* RiaApplication::keyValueStore() const
{
return m_keyValueStore.get();
}
@@ -19,6 +19,7 @@
//////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "KeyValueStore/RiaKeyValueStore.h"
#include "RiaDefines.h"
#include "cafPdmPointer.h"
@@ -199,6 +200,8 @@ public:
RiaOsduConnector* makeOsduConnector();
RiaSumoConnector* makeSumoConnector();
RiaKeyValueStore<char>* keyValueStore() const;
protected:
// Protected implementation specific overrides
virtual void invokeProcessEvents( QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents ) = 0;
@@ -237,6 +240,8 @@ protected:
QPointer<RiaSocketServer> m_socketServer;
std::unique_ptr<caf::UiProcess> m_workerProcess;
std::unique_ptr<RiaKeyValueStore<char>> m_keyValueStore;
// Execute for all settings
std::list<int> m_scriptCaseIds;
int m_currentScriptCaseId;
@@ -1,9 +1,6 @@
set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RiaKeyValueStore.h
)
set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RiaKeyValueStore.h)
set(SOURCE_GROUP_SOURCE_FILES
)
set(SOURCE_GROUP_SOURCE_FILES)
list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})
@@ -56,14 +56,6 @@ public:
static bool hasGridData( const QString& filename );
private:
static void interpretSplitenzData( int nz,
float zoffset,
float zscale,
const std::vector<char>& splitenz,
const std::vector<float>& zdata,
std::vector<float>& zcornsv );
static size_t computeActiveCellMatrixIndex( std::vector<int>& activeCells );
static cvf::Vec3d getCorner( const RigMainGrid& grid,
@@ -74,6 +66,14 @@ private:
const cvf::Vec3d& offset,
const cvf::Vec3d& scale );
private:
static void interpretSplitenzData( int nz,
float zoffset,
float zscale,
const std::vector<char>& splitenz,
const std::vector<float>& zdata,
std::vector<float>& zcornsv );
static double interpolate( const cvf::Vec3d& top, const cvf::Vec3d& bottom, double z, int idx );
static std::vector<double>
@@ -124,7 +124,8 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RimCameraPosition.h
${CMAKE_CURRENT_LIST_DIR}/RimWellTargetMapping.h
${CMAKE_CURRENT_LIST_DIR}/RimRegularGridCase.h
${CMAKE_CURRENT_LIST_DIR}/RimGeometrySelectionItem.cpp
${CMAKE_CURRENT_LIST_DIR}/RimGeometrySelectionItem.h
${CMAKE_CURRENT_LIST_DIR}/RimCornerPointCase.h
)
set(SOURCE_GROUP_SOURCE_FILES
@@ -250,6 +251,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RimWellTargetMapping.cpp
${CMAKE_CURRENT_LIST_DIR}/RimRegularGridCase.cpp
${CMAKE_CURRENT_LIST_DIR}/RimGeometrySelectionItem.cpp
${CMAKE_CURRENT_LIST_DIR}/RimCornerPointCase.cpp
)
if(RESINSIGHT_USE_QT_CHARTS)
@@ -0,0 +1,329 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025- Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RimCornerPointCase.h"
#include "RiaApplication.h"
#include "RiaDefines.h"
#include "RiaLogging.h"
#include "RifInputPropertyLoader.h"
#include "RifRoffFileTools.h"
#include "RigActiveCellInfo.h"
#include "RigCaseCellResultsData.h"
#include "RigEclipseCaseData.h"
#include "RigMainGrid.h"
#include "RimEclipseInputProperty.h"
#include "RimReservoirCellResultsStorage.h"
#include "cafPdmObjectScriptingCapability.h"
#include "cafProgressInfo.h"
#include <QDir>
#include <QFileInfo>
#include <chrono>
#ifdef USE_OPENMP
#include <omp.h>
#endif
using namespace std::chrono;
CAF_PDM_SOURCE_INIT( RimCornerPointCase, "RimCornerPointCase" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimCornerPointCase::RimCornerPointCase()
: RimEclipseCase()
{
CAF_PDM_InitScriptableObject( "RimCornerPointCase", ":/EclipseInput48x48.png" );
setReservoirData( new RigEclipseCaseData( this ) );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimCornerPointCase::~RimCornerPointCase()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimCornerPointCase::openEclipseGridFile()
{
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<RimCornerPointCase*, QString> RimCornerPointCase::createFromCoordinatesArray( const int nx,
const int ny,
const int nz,
const std::vector<float>& coord,
const std::vector<float>& zcorn,
const std::vector<float>& actnum )
{
CAF_ASSERT( nx > 0 );
CAF_ASSERT( ny > 0 );
CAF_ASSERT( nz > 0 );
size_t ncoord = ( nx + 1 ) * ( ny + 1 ) * 2 * 3;
size_t nzcorn = nx * ny * nz * 8;
size_t ntot = nx * ny * nz;
if ( coord.size() != ncoord )
return { nullptr, QString( "Wrong size of coord array. Expected %1, but got %2" ).arg( ncoord ).arg( coord.size() ) };
if ( zcorn.size() != nzcorn )
return { nullptr, QString( "Wrong size of zcorn array. Expected %1, but got %2" ).arg( nzcorn ).arg( zcorn.size() ) };
if ( actnum.size() != ntot )
return { nullptr, QString( "Wrong size of actnum array. Expected %1, but got %2" ).arg( ntot ).arg( actnum.size() ) };
auto cornerPointCase = new RimCornerPointCase;
buildGrid( *cornerPointCase->eclipseCaseData(), nx, ny, nz, coord, zcorn, actnum );
cornerPointCase->ensureFaultDataIsComputed();
return { cornerPointCase, "" };
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::array<cvf::Vec3d, 8> RimCornerPointCase::getCorners( const RigMainGrid& grid,
const std::vector<float>& coord,
const std::vector<float>& zcorn,
const size_t cellIdx,
const cvf::Vec3d& offset,
const cvf::Vec3d& scale )
{
size_t i;
size_t j;
size_t k;
grid.ijkFromCellIndex( cellIdx, &i, &j, &k );
const size_t nx = grid.cellCountI();
const size_t ny = grid.cellCountJ();
// Get depths from zcorn
std::array<size_t, 8> zind;
zind[0] = ( k * nx * ny * 8 + j * nx * 4 + i * 2 );
zind[1] = ( zind[0] + 1 );
zind[2] = ( zind[0] + nx * 2 );
zind[3] = ( zind[2] + 1 );
for ( size_t n = 0; n < 4; n++ )
zind[n + 4] = ( zind[n] + nx * ny * 4 );
std::array<cvf::Vec3d, 8> corners;
for ( size_t n = 0; n < 8; n++ )
corners[n].z() = zcorn[zind[n]];
// calculate indices for grid pillars in COORD arrray
std::array<size_t, 4> pind;
pind[0] = j * ( nx + 1 ) * 6 + i * 6;
pind[1] = pind[0] + 6;
pind[2] = pind[0] + ( nx + 1 ) * 6;
pind[3] = pind[2] + 6;
for ( size_t n = 0; n < 4; n++ )
{
const double zt = coord[pind[n] + 2];
const double zb = coord[pind[n] + 5];
const double xt = coord[pind[n]];
const double yt = coord[pind[n] + 1];
const double xb = coord[pind[n] + 3];
const double yb = coord[pind[n] + 4];
const double diffZ = zt - zb;
if ( diffZ == 0.0 )
{
corners[n].x() = xt;
corners[n + 4].x() = xt;
corners[n].y() = yt;
corners[n + 4].y() = yt;
}
else
{
corners[n].x() = xt + ( xb - xt ) / diffZ * ( zt - corners[n].z() );
corners[n + 4].x() = xt + ( xb - xt ) / diffZ * ( zt - corners[n + 4].z() );
corners[n].y() = yt + ( yb - yt ) / diffZ * ( zt - corners[n].z() );
corners[n + 4].y() = yt + ( yb - yt ) / diffZ * ( zt - corners[n + 4].z() );
}
}
return corners;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCornerPointCase::buildGrid( RigEclipseCaseData& eclipseCaseData,
const int nx,
const int ny,
const int nz,
const std::vector<float>& coord,
const std::vector<float>& zcorn,
const std::vector<float>& actnum )
{
auto startTime = high_resolution_clock::now();
RigActiveCellInfo* activeCellInfo = eclipseCaseData.activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL );
CVF_ASSERT( activeCellInfo );
RigActiveCellInfo* fractureActiveCellInfo = eclipseCaseData.activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL );
CVF_ASSERT( fractureActiveCellInfo );
RigMainGrid* mainGrid = eclipseCaseData.mainGrid();
CVF_ASSERT( mainGrid );
cvf::Vec3st gridPointDim( nx + 1, ny + 1, nz + 1 );
mainGrid->setGridPointDimensions( gridPointDim );
mainGrid->setGridName( "Main grid" );
size_t totalCellCount = nx * ny * nz;
activeCellInfo->setGridCount( 1 );
fractureActiveCellInfo->setGridCount( 1 );
activeCellInfo->setReservoirCellCount( totalCellCount );
fractureActiveCellInfo->setReservoirCellCount( totalCellCount );
// Reserve room for the cells and nodes and fill them with data
mainGrid->reservoirCells().reserve( totalCellCount );
mainGrid->nodes().reserve( 8 * totalCellCount );
int progTicks = 100;
caf::ProgressInfo progInfo( progTicks, "" );
int cellCount = static_cast<int>( totalCellCount );
size_t cellStartIndex = mainGrid->reservoirCells().size();
size_t nodeStartIndex = mainGrid->nodes().size();
RigCell defaultCell;
defaultCell.setHostGrid( mainGrid );
mainGrid->reservoirCells().resize( cellStartIndex + cellCount, defaultCell );
mainGrid->nodes().resize( nodeStartIndex + static_cast<size_t>( cellCount ) * 8, cvf::Vec3d( 0, 0, 0 ) );
const size_t cellMappingECLRi[8] = { 0, 1, 3, 2, 4, 5, 7, 6 };
cvf::Vec3d offset( 0.0, 0.0, 0.0 );
cvf::Vec3d scale( 1.0, 1.0, 1.0 );
// Convert actnum to integer
std::vector<int> activeCells( nx * ny * nz, 0 );
CAF_ASSERT( activeCells.size() == actnum.size() );
for ( size_t i = 0; i < activeCells.size(); i++ )
{
activeCells[i] = static_cast<int>( actnum[i] > 0.0 );
}
// Precompute the active cell matrix index
size_t numActiveCells = RifRoffFileTools::computeActiveCellMatrixIndex( activeCells );
// Loop over cells and fill them with data
#pragma omp for
for ( int gridLocalCellIndex = 0; gridLocalCellIndex < cellCount; ++gridLocalCellIndex )
{
RigCell& cell = mainGrid->cell( cellStartIndex + gridLocalCellIndex );
cell.setGridLocalCellIndex( gridLocalCellIndex );
// Active cell index
int matrixActiveIndex = activeCells[gridLocalCellIndex];
if ( matrixActiveIndex != -1 )
{
activeCellInfo->setCellResultIndex( cellStartIndex + gridLocalCellIndex, matrixActiveIndex );
}
cell.setParentCellIndex( cvf::UNDEFINED_SIZE_T );
std::array<cvf::Vec3d, 8> corners = getCorners( *mainGrid, coord, zcorn, gridLocalCellIndex, offset, scale );
// Corner coordinates
for ( int cIdx = 0; cIdx < 8; ++cIdx )
{
double* point = mainGrid->nodes()[nodeStartIndex + (size_t)gridLocalCellIndex * 8 + cellMappingECLRi[cIdx]].ptr();
point[0] = corners[cIdx].x();
point[1] = corners[cIdx].y();
point[2] = -corners[cIdx].z();
cell.cornerIndices()[cIdx] = nodeStartIndex + (size_t)gridLocalCellIndex * 8 + cIdx;
}
// Mark inactive long pyramid looking cells as invalid
cell.setInvalid( cell.isLongPyramidCell() );
}
activeCellInfo->setGridActiveCellCounts( 0, numActiveCells );
fractureActiveCellInfo->setGridActiveCellCounts( 0, 0 );
mainGrid->initAllSubGridsParentGridPointer();
activeCellInfo->computeDerivedData();
fractureActiveCellInfo->computeDerivedData();
auto endTime = high_resolution_clock::now();
auto totalDuration = duration_cast<milliseconds>( endTime - startTime );
RiaLogging::info( QString( "Total: %1 ms" ).arg( totalDuration.count() ) );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCornerPointCase::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
uiOrdering.add( &m_caseUserDescription );
uiOrdering.add( &m_displayNameOption );
uiOrdering.add( &m_caseId );
uiOrdering.add( &m_caseFileName );
auto group = uiOrdering.addNewGroup( "Case Options" );
group->add( &m_activeFormationNames );
group->add( &m_flipXAxis );
group->add( &m_flipYAxis );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimCornerPointCase::locationOnDisc() const
{
if ( gridFileName().isEmpty() ) return QString();
QFileInfo fi( gridFileName() );
return fi.absolutePath();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimCornerPointCase::importAsciiInputProperties( const QStringList& fileNames )
{
return true;
}
@@ -0,0 +1,68 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025- Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "RimEclipseCase.h"
#include "cafPdmObject.h"
//==================================================================================================
//
//
//
//==================================================================================================
class RimCornerPointCase : public RimEclipseCase
{
CAF_PDM_HEADER_INIT;
public:
RimCornerPointCase();
~RimCornerPointCase() override;
bool openEclipseGridFile() override;
bool importAsciiInputProperties( const QStringList& fileNames ) override;
QString locationOnDisc() const override;
static std::pair<RimCornerPointCase*, QString> createFromCoordinatesArray( const int nx,
const int ny,
const int nz,
const std::vector<float>& coord,
const std::vector<float>& zcorn,
const std::vector<float>& actnum );
protected:
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
static void buildGrid( RigEclipseCaseData& eclipseCaseData,
const int nx,
const int ny,
const int nz,
const std::vector<float>& coord,
const std::vector<float>& zcorn,
const std::vector<float>& actnum );
static std::array<cvf::Vec3d, 8> getCorners( const RigMainGrid& grid,
const std::vector<float>& coord,
const std::vector<float>& zcorn,
const size_t cellIdx,
const cvf::Vec3d& offset,
const cvf::Vec3d& scale );
};
@@ -20,14 +20,22 @@
#include "RiaApplication.h"
#include "RiaGuiApplication.h"
#include "RiaLogging.h"
#include "RicImportSummaryCasesFeature.h"
#include "RimCornerPointCase.h"
#include "RimEclipseCaseCollection.h"
#include "RimEclipseCellColors.h"
#include "RimEclipseView.h"
#include "RimFileSummaryCase.h"
#include "RimMainPlotCollection.h"
#include "RimOilField.h"
#include "RimProject.h"
#include "RimSummaryCase.h"
#include "RimSurfaceCollection.h"
#include "RiuMainWindow.h"
#include "RiuPlotMainWindow.h"
#include "cafPdmFieldScriptingCapability.h"
@@ -220,3 +228,150 @@ bool RimProject_surfaceFolder::isNullptrValidResult() const
{
return true;
}
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimProject, RimProject_createGridFromKeyValues, "createGridFromKeyValues" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimProject_createGridFromKeyValues::RimProject_createGridFromKeyValues( caf::PdmObjectHandle* self )
: caf::PdmObjectMethod( self )
{
CAF_PDM_InitObject( "Create grid from key values", "", "", "Create Grid From Key Values" );
CAF_PDM_InitScriptableFieldNoDefault( &m_name, "Name", "" );
CAF_PDM_InitScriptableFieldNoDefault( &m_nx, "Nx", "" );
CAF_PDM_InitScriptableFieldNoDefault( &m_ny, "Ny", "" );
CAF_PDM_InitScriptableFieldNoDefault( &m_nz, "Nz", "" );
CAF_PDM_InitScriptableFieldNoDefault( &m_coordKey, "CoordKey", "" );
CAF_PDM_InitScriptableFieldNoDefault( &m_zcornKey, "ZcornKey", "" );
CAF_PDM_InitScriptableFieldNoDefault( &m_actnumKey, "ActnumKey", "" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::PdmObjectHandle* RimProject_createGridFromKeyValues::execute()
{
RiaLogging::info( "Creating grid from key values" );
QString name = m_name();
if ( name.isEmpty() )
{
RiaLogging::error( "Empty name not allowed" );
return nullptr;
}
int nx = m_nx();
int ny = m_ny();
int nz = m_nz();
RiaLogging::info( QString( "Grid dimensions: [%1 %2 %3]" ).arg( nx ).arg( ny ).arg( nz ) );
RiaLogging::info( QString( "Coord: %1" ).arg( m_coordKey() ) );
RiaLogging::info( QString( "Zcorn: %1" ).arg( m_zcornKey() ) );
RiaLogging::info( QString( "Actnum: %1" ).arg( m_actnumKey() ) );
auto keyValueStore = RiaApplication::instance()->keyValueStore();
auto convertToFloatVector = []( const std::optional<std::vector<char>>& input ) -> std::vector<float>
{
if ( input && !input->empty() )
{
// Ensure the byte vector size is a multiple of sizeof(float)
if ( input->size() % sizeof( float ) != 0 )
{
return {};
}
// Calculate how many floats we'll have
size_t float_count = input->size() / sizeof( float );
// Create a vector of floats with the appropriate size
std::vector<float> float_vec( float_count );
// Copy the binary data from the byte vector to the float vector
std::memcpy( float_vec.data(), input->data(), input->size() );
return float_vec;
}
return {};
};
std::vector<float> coord = convertToFloatVector( keyValueStore->get( m_coordKey().toStdString() ) );
std::vector<float> zcorn = convertToFloatVector( keyValueStore->get( m_zcornKey().toStdString() ) );
std::vector<float> actnum = convertToFloatVector( keyValueStore->get( m_actnumKey().toStdString() ) );
if ( coord.empty() || zcorn.empty() || actnum.empty() )
{
RiaLogging::error( "Found unexcepted empty coord, zcorn or actnum array." );
return nullptr;
}
auto [grid, errorMessage] = RimCornerPointCase::createFromCoordinatesArray( nx, ny, nz, coord, zcorn, actnum );
if ( grid )
{
RimProject* project = RimProject::current();
if ( !project ) return nullptr;
grid->setCustomCaseName( name );
project->assignCaseIdToCase( grid );
RimEclipseCaseCollection* analysisModels = project->activeOilField() ? project->activeOilField()->analysisModels() : nullptr;
if ( !analysisModels ) return nullptr;
analysisModels->cases.push_back( grid );
RimMainPlotCollection::current()->ensureDefaultFlowPlotsAreCreated();
RimEclipseView* riv = grid->createAndAddReservoirView();
riv->loadDataAndUpdate();
if ( !riv->cellResult()->hasResult() )
{
riv->cellResult()->setResultVariable( RiaResultNames::undefinedResultName() );
}
analysisModels->updateConnectedEditors();
if ( RiaGuiApplication::isRunning() )
{
if ( RiuMainWindow::instance() ) RiuMainWindow::instance()->selectAsCurrentItem( riv->cellResult() );
}
}
else
{
RiaLogging::error( QString( "Creating corner point grid failed: %1" ).arg( errorMessage ) );
}
keyValueStore->remove( m_coordKey().toStdString() );
keyValueStore->remove( m_zcornKey().toStdString() );
keyValueStore->remove( m_actnumKey().toStdString() );
return grid;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimProject_createGridFromKeyValues::resultIsPersistent() const
{
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::unique_ptr<caf::PdmObjectHandle> RimProject_createGridFromKeyValues::defaultResult() const
{
return std::unique_ptr<caf::PdmObjectHandle>( new RimCornerPointCase );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimProject_createGridFromKeyValues::isNullptrValidResult() const
{
return true;
}
@@ -82,3 +82,28 @@ public:
private:
caf::PdmField<QString> m_folderName;
};
//==================================================================================================
///
//==================================================================================================
class RimProject_createGridFromKeyValues : public caf::PdmObjectMethod
{
CAF_PDM_HEADER_INIT;
public:
RimProject_createGridFromKeyValues( caf::PdmObjectHandle* self );
caf::PdmObjectHandle* execute() override;
bool resultIsPersistent() const override;
std::unique_ptr<PdmObjectHandle> defaultResult() const override;
bool isNullptrValidResult() const override;
private:
caf::PdmField<QString> m_name;
caf::PdmField<int> m_nx;
caf::PdmField<int> m_ny;
caf::PdmField<int> m_nz;
caf::PdmField<QString> m_coordKey;
caf::PdmField<QString> m_zcornKey;
caf::PdmField<QString> m_actnumKey;
};
@@ -123,6 +123,7 @@ protected: // only for use by file readers and internal services. TODO: replace
friend class RiaGrpcCaseService;
friend class RiaActiveCellInfoStateHandler;
friend class RicCreateTemporaryLgrFeature;
friend class RimCornerPointCase;
std::vector<RigCell>& reservoirCells();
const std::vector<RigCell>& reservoirCells() const;
+2
View File
@@ -33,6 +33,7 @@ set(SOURCE_GROUP_HEADER_FILES
RiaGrpcCommandService.h
RiaGrpcAppService.h
RiaGrpcPropertiesService.h
RiaGrpcKeyValueStoreService.h
RiaGrpcNNCPropertiesService.h
RiaGrpcPdmObjectService.h
RiaGrpcApplicationInterface.h
@@ -49,6 +50,7 @@ set(SOURCE_GROUP_SOURCE_FILES
RiaGrpcCommandService.cpp
RiaGrpcAppService.cpp
RiaGrpcPropertiesService.cpp
RiaGrpcKeyValueStoreService.cpp
RiaGrpcNNCPropertiesService.cpp
RiaGrpcPdmObjectService.cpp
RiaGrpcApplicationInterface.cpp)
@@ -0,0 +1,25 @@
syntax = "proto3";
package rips;
import "Definitions.proto";
service KeyValueStore {
rpc SetValue(stream KeyValueStoreInputChunk)
returns (ClientToServerStreamReply) {}
}
message KeyValueStoreInputChunk {
oneof KeyValueStoreChunkType {
KeyValueInputParameters parameters = 1;
KeyValueStoreChunk values = 2;
}
}
message KeyValueInputParameters {
// Name needs to be sent in the first message
string name = 1;
int64 num_elements = 2;
}
message KeyValueStoreChunk { repeated float values = 1; }
@@ -0,0 +1,41 @@
######################################################################
# This script creates a corner point grid from a Eclipse coord, zcorn
# and actnum configuration.
######################################################################
import rips
import xtgeo
from xtgeo.grid3d._egrid import EGrid, RockModel
from xtgeo.io._file import FileFormat, FileWrapper
import numpy as np
grid_filepath = "/home/resinsight/testdata/01_drogon_ahm/realization-0/iter-0/eclipse/model/DROGON-0.EGRID"
name = "DROGON-0 from python"
grid = EGrid.from_file(grid_filepath, fileformat=FileFormat.EGRID)
print("Grid: ", grid)
print("Grid type: ", type(grid))
print("coord: ", grid.coord.shape, grid.coord.dtype)
print("zcorn:", grid.zcorn.shape, grid.zcorn.dtype)
print("actnum: ", grid.actnum.shape, grid.actnum.dtype)
resinsight = rips.Instance.find()
project = resinsight.project
coord = np.ascontiguousarray(grid.coord, dtype=np.float32)
zcorn = np.ascontiguousarray(grid.zcorn, dtype=np.float32)
actnum = np.ascontiguousarray(grid.actnum, dtype=np.int32)
print("coordsv: ", len(coord), type(coord[0]))
print("zcornsv: ", len(zcorn), type(zcorn[0]))
print("actnumsv: ", len(actnum), type(actnum[0]))
print("Grid dimensions: ", grid.dimensions)
nx = grid.dimensions.ncol
ny = grid.dimensions.nrow
nz = grid.dimensions.nlay
project.create_corner_point_grid(name, nx, ny, nz, coord, zcorn, actnum)
+97
View File
@@ -3,8 +3,10 @@
"""
The ResInsight project module
"""
import builtins
import grpc
import uuid
from .case import Case
from .gridcasegroup import GridCaseGroup
@@ -16,6 +18,9 @@ import Commands_pb2
from Definitions_pb2 import Empty
import Project_pb2_grpc
import Project_pb2
import KeyValueStore_pb2_grpc
import KeyValueStore_pb2
import PdmObject_pb2
from .resinsight_classes import Project, PlotWindow, WellPath, SummaryCase, Reservoir
@@ -25,6 +30,12 @@ from typing import Optional, List
@add_method(Project)
def __custom_init__(self, pb2_object, channel: grpc.Channel) -> None:
self._project_stub = Project_pb2_grpc.ProjectStub(self._channel)
self.__key_value_store_stub = KeyValueStore_pb2_grpc.KeyValueStoreStub(
self._channel
)
# Public properties
self.chunk_size = 8160
@add_static_method(Project)
@@ -424,3 +435,89 @@ def import_formation_names(self, formation_files=None):
formationFiles=formation_files, applyToCaseId=-1
)
)
@add_method(Project)
def create_corner_point_grid(
self,
name: str,
nx: int,
ny: int,
nz: int,
coord: List[float],
zcorn: List[float],
actnum: List[int],
):
"""Creates a corner point grid from given parameters.
Arguments:
name(str): Name of the grid.
nx(int): Number of cells in x direction
ny(int): Number of cells in y direction
nz(int): Number of cells in z direction
coord(list[float]): Coordinate lines as COORD keyword in Eclipse.
Each coordinate line is defined by two points (top and bottom).
Size: (nx+1) * (ny+1) * 2 * 3. Points are ordered as in Eclipse.
zcorn(list[float]): Corner depths as defined by the Eclipse keyword ZCORN.
Size: nx * ny * nz * 8
actnum(list[int]): Active cell info: cells with values > 0 are active.
Size: nx * ny * nz
"""
# Generate unique keys for three arrays
coord_key = "{}_{}".format(uuid.uuid4(), "coord")
zcorn_key = "{}_{}".format(uuid.uuid4(), "zcorn")
actnum_key = "{}_{}".format(uuid.uuid4(), "actnum")
self.set_key_values(coord_key, coord)
self.set_key_values(zcorn_key, zcorn)
self.set_key_values(actnum_key, actnum)
return self.create_grid_from_key_values(
name=name,
nx=nx,
ny=ny,
nz=nz,
coord_key=coord_key,
zcorn_key=zcorn_key,
actnum_key=actnum_key,
)
@add_method(Project)
def set_key_values(self, key, values):
"""Sets values for a given key in the key-value store.
Arguments:
key(str): The key (should be unique).
values(list): a list of double precision floating point numbers
"""
request_iterator = self.__generate_key_value_store_input_chunks(values, key)
reply = self.__key_value_store_stub.SetValue(request_iterator)
if reply.accepted_value_count < len(values):
raise IndexError
@add_method(Project)
def __generate_key_value_store_input_chunks(self, array, name):
index = -1
while index < len(array):
chunk = KeyValueStore_pb2.KeyValueStoreInputChunk()
if index == -1:
parameters = KeyValueStore_pb2.KeyValueInputParameters(
name=name, num_elements=len(array)
)
chunk.parameters.CopyFrom(parameters)
index += 1
else:
actual_chunk_size = min(len(array) - index + 1, self.chunk_size)
chunk.values.CopyFrom(
KeyValueStore_pb2.KeyValueStoreChunk(
values=array[index : index + actual_chunk_size]
)
)
index += actual_chunk_size
yield chunk
# Final empty message to signal completion
chunk = KeyValueStore_pb2.KeyValueStoreInputChunk()
yield chunk
@@ -0,0 +1,188 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025- Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
//////////////////////////////////////////////////////////////////////////////////
#include "RiaGrpcKeyValueStoreService.h"
#include "RiaApplication.h"
#include "RiaGrpcCallbacks.h"
#include "RiaGrpcCaseService.h"
#include "RiaLogging.h"
#include "cafAssert.h"
using namespace rips;
#define NUM_CONCURRENT_CLIENT_TO_SERVER_STREAMS 10
class RiaKeyValueStoreStateHandler
{
using Status = grpc::Status;
public:
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaKeyValueStoreStateHandler( bool clientStreamer = false )
: m_streamedValueCount( 0u )
, m_cellCount( 0u )
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t totalValueCount() const { return m_cellCount; }
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t streamedValueCount() const { return m_streamedValueCount; }
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
Status init( const std::string& name, size_t numElements )
{
RiaLogging::debug(
QString( "Initializing stream: %1 size: %2" ).arg( QString::fromStdString( name ) ).arg( numElements ) );
m_name = name;
m_cellCount = numElements;
return grpc::Status::OK;
}
//--------------------------------------------------------------------------------------------------
/// Client streamers need to be initialised with the encapsulated parameters
//--------------------------------------------------------------------------------------------------
Status init( const KeyValueStoreInputChunk* chunk )
{
CAF_ASSERT( chunk );
if ( chunk->has_parameters() )
{
return init( chunk->parameters().name(), chunk->parameters().num_elements() );
}
return grpc::Status( grpc::INVALID_ARGUMENT, "Need to have parameters in first message" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
Status receiveStreamRequest( const KeyValueStoreInputChunk* request, ClientToServerStreamReply* reply )
{
CAF_ASSERT( request );
CAF_ASSERT( reply );
if ( request->has_values() )
{
auto values = request->values().values();
if ( !values.empty() )
{
size_t currentCellIdx = m_streamedValueCount;
m_streamedValueCount += values.size();
if ( !values.empty() )
{
m_data.insert( m_data.end(), values.begin(), values.end() );
}
if ( m_streamedValueCount > m_cellCount )
{
return grpc::Status( grpc::OUT_OF_RANGE, "Attempting to write out of bounds" );
}
RiaLogging::debug( QString( "Received stream request. Start index: %1. Size after request: %2" )
.arg( currentCellIdx )
.arg( m_data.size() ) );
reply->set_accepted_value_count( static_cast<int64_t>( m_data.size() ) );
return Status::OK;
}
}
return Status::OK;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void finish()
{
RiaLogging::debug(
QString( "Stream finished: name=%1 size=%2" ).arg( QString::fromStdString( m_name ) ).arg( m_data.size() ) );
if ( !m_name.empty() && !m_data.empty() )
{
auto convertFromFloatVectorToBytes = []( const std::vector<float>& float_vec ) -> std::vector<char>
{
if ( float_vec.empty() )
{
return {};
}
// Calculate the total size needed for the byte array
size_t size_in_bytes = float_vec.size() * sizeof( float );
// Create a vector of bytes with the appropriate size
std::vector<char> byte_vec( size_in_bytes );
// Copy the binary data from the float vector to the byte vector
std::memcpy( byte_vec.data(), float_vec.data(), size_in_bytes );
return byte_vec;
};
RiaApplication::instance()->keyValueStore()->set( m_name, convertFromFloatVectorToBytes( m_data ) );
}
}
protected:
size_t m_streamedValueCount;
size_t m_cellCount;
std::string m_name;
std::vector<float> m_data;
};
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
grpc::Status RiaGrpcKeyValueStoreService::SetValue( grpc::ServerContext* context,
const rips::KeyValueStoreInputChunk* request,
rips::ClientToServerStreamReply* reply,
RiaKeyValueStoreStateHandler* stateHandler )
{
return stateHandler->receiveStreamRequest( request, reply );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RiaGrpcCallbackInterface*> RiaGrpcKeyValueStoreService::createCallbacks()
{
typedef RiaGrpcKeyValueStoreService Self;
std::vector<RiaGrpcCallbackInterface*> callbacks;
callbacks = {
new RiaGrpcClientToServerStreamCallback<Self,
KeyValueStoreInputChunk,
ClientToServerStreamReply,
RiaKeyValueStoreStateHandler>( this,
&Self::SetValue,
&Self::RequestSetValue,
new RiaKeyValueStoreStateHandler( true ) ) };
return callbacks;
}
static bool RiaGrpcKeyValueStoreService_init =
RiaGrpcServiceFactory::instance()->registerCreator<RiaGrpcKeyValueStoreService>(
typeid( RiaGrpcKeyValueStoreService ).hash_code() );
@@ -0,0 +1,42 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025- Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
//////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "RiaGrpcServiceInterface.h"
#include "KeyValueStore.grpc.pb.h"
#include <grpcpp/grpcpp.h>
class RiaKeyValueStoreStateHandler;
//==================================================================================================
//
// gRPC-service answering requests about property information for a given case and time step
//
//==================================================================================================
class RiaGrpcKeyValueStoreService final : public rips::KeyValueStore::AsyncService, public RiaGrpcServiceInterface
{
public:
grpc::Status SetValue( grpc::ServerContext* context,
const rips::KeyValueStoreInputChunk* chunk,
rips::ClientToServerStreamReply* reply,
RiaKeyValueStoreStateHandler* stateHandler );
std::vector<RiaGrpcCallbackInterface*> createCallbacks() override;
};