From c03633230e23f199a25368d89df347c77cf985ad Mon Sep 17 00:00:00 2001 From: Kristian Bendiksen Date: Wed, 26 Feb 2020 00:21:19 +0100 Subject: [PATCH] #4477 Add python method to set NNC connection values. Equivalent to SetNNCProperty in Octave. --- .../GrpcProtos/NNCProperties.proto | 19 ++ .../GrpcInterface/Python/rips/case.py | 46 +++ .../Python/rips/tests/test_nnc_properties.py | 22 ++ .../RiaGrpcNNCPropertiesService.cpp | 277 ++++++++++++++++-- .../RiaGrpcNNCPropertiesService.h | 36 +++ 5 files changed, 383 insertions(+), 17 deletions(-) diff --git a/ApplicationCode/GrpcInterface/GrpcProtos/NNCProperties.proto b/ApplicationCode/GrpcInterface/GrpcProtos/NNCProperties.proto index 3abbe18ce8..8fa510c220 100644 --- a/ApplicationCode/GrpcInterface/GrpcProtos/NNCProperties.proto +++ b/ApplicationCode/GrpcInterface/GrpcProtos/NNCProperties.proto @@ -10,6 +10,7 @@ service NNCProperties rpc GetAvailableNNCProperties(CaseRequest) returns (AvailableNNCProperties) {} rpc GetNNCConnections(CaseRequest) returns (stream NNCConnections) {} rpc GetNNCValues(NNCValuesRequest) returns (stream NNCValues) {} + rpc SetNNCValues(stream NNCValuesChunk) returns (ClientToServerStreamReply) {} } enum NNCPropertyType @@ -55,3 +56,21 @@ message NNCValues { repeated double values = 1; } + +message NNCValuesInputRequest +{ + int32 case_id = 1; + string property_name = 2; + PorosityModelType porosity_model = 3; + int32 time_step = 4; +} + +message NNCValuesChunk +{ + oneof ChunkType + { + // Params needs to be sent in the first message + NNCValuesInputRequest params = 1; + NNCValues values = 2; + } +} diff --git a/ApplicationCode/GrpcInterface/Python/rips/case.py b/ApplicationCode/GrpcInterface/Python/rips/case.py index e4ac6e94b4..69e1ca6f98 100644 --- a/ApplicationCode/GrpcInterface/Python/rips/case.py +++ b/ApplicationCode/GrpcInterface/Python/rips/case.py @@ -1097,3 +1097,49 @@ def nnc_connections_generated_values(self, property_name, time_step): """ generator = self.nnc_connections_generated_values_async(property_name, time_step) return self.__nnc_values_generator_to_list(generator) + +@add_method(Case) +def __generate_nnc_property_input_chunks(self, array, parameters): + index = -1 + while index < len(array): + chunk = NNCProperties_pb2.NNCValuesChunk() + if index is -1: + chunk.params.CopyFrom(parameters) + index += 1 + else: + actual_chunk_size = min(len(array) - index + 1, self.chunk_size) + chunk.values.CopyFrom( + NNCProperties_pb2.NNCValues(values=array[index:index + actual_chunk_size])) + index += actual_chunk_size + + yield chunk + # Final empty message to signal completion + chunk = NNCProperties_pb2.NNCValuesChunk() + yield chunk + +@add_method(Case) +def set_nnc_connections_values( + self, + values, + property_name, + time_step, + porosity_model="MATRIX_MODEL"): + """Set nnc connection values for all connections.. + + Arguments: + values(list): a list of double precision floating point numbers + property_name(str): name of an Eclipse property + time_step(int): the time step for which to get the property for + porosity_model(str): string enum. See available() + """ + porosity_model_enum = Case_pb2.PorosityModelType.Value(porosity_model) + request = NNCProperties_pb2.NNCValuesInputRequest( + case_id=self.id, + property_name=property_name, + time_step=time_step, + porosity_model=porosity_model_enum, + ) + request_iterator = self.__generate_nnc_property_input_chunks(values, request) + reply = self.__nnc_properties_stub.SetNNCValues(request_iterator) + if reply.accepted_value_count < len(values): + raise IndexError diff --git a/ApplicationCode/GrpcInterface/Python/rips/tests/test_nnc_properties.py b/ApplicationCode/GrpcInterface/Python/rips/tests/test_nnc_properties.py index f3ebd59112..814bde5c3f 100644 --- a/ApplicationCode/GrpcInterface/Python/rips/tests/test_nnc_properties.py +++ b/ApplicationCode/GrpcInterface/Python/rips/tests/test_nnc_properties.py @@ -46,6 +46,28 @@ def test_10kSync(rips_instance, initialize_test): for a in allen_vals: assert(isinstance(a, float)) + # Generate some data + new_data = [] + for (c, _) in enumerate(nnc_connections): + new_data.append(float(c)) + + property_name = "NEW_PROP" + case.set_nnc_connections_values(new_data, property_name, 0) + new_prop_vals = case.nnc_connections_generated_values(property_name, 0) + assert(len(new_prop_vals) == len(new_data)) + for i in range(0, len(new_data)): + assert(new_data[i] == new_prop_vals[i]) + + # Set some other data for second time step + for i in range(0, len(new_data)): + new_data[i] = new_data[i] * 2.0 + + case.set_nnc_connections_values(new_data, property_name, 1) + new_prop_vals = case.nnc_connections_generated_values(property_name, 1) + assert(len(new_prop_vals) == len(nnc_connections)) + for i in range(0, len(new_data)): + assert(new_data[i] == new_prop_vals[i]) + def test_non_existing_dynamic_values(rips_instance, initialize_test): casePath = dataroot.PATH + "/TEST10K_FLT_LGR_NNC/TEST10K_FLT_LGR_NNC.EGRID" case = rips_instance.project.load_case(path=casePath) diff --git a/ApplicationCode/GrpcInterface/RiaGrpcNNCPropertiesService.cpp b/ApplicationCode/GrpcInterface/RiaGrpcNNCPropertiesService.cpp index 6d27f478b9..3cf17e6b37 100644 --- a/ApplicationCode/GrpcInterface/RiaGrpcNNCPropertiesService.cpp +++ b/ApplicationCode/GrpcInterface/RiaGrpcNNCPropertiesService.cpp @@ -25,7 +25,14 @@ #include "RigEclipseResultAddress.h" #include "RigEclipseResultInfo.h" #include "RigMainGrid.h" + #include "RimEclipseCase.h" +#include "RimEclipseCellColors.h" +#include "RimEclipseInputCase.h" +#include "RimEclipseInputProperty.h" +#include "RimEclipseInputPropertyCollection.h" +#include "RimEclipseView.h" +#include "RimIntersectionCollection.h" using namespace rips; @@ -150,6 +157,9 @@ grpc::Status RiaNNCValuesStateHandler::init( const rips::NNCValuesRequest* reque return grpc::Status::OK; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- const std::vector* getScalarResultByName( const RigNNCData* nncData, RigNNCData::NNCResultType resultType, const QString& propertyName, @@ -254,6 +264,230 @@ grpc::Status RiaGrpcNNCPropertiesService::GetAvailableNNCProperties( grpc::Serve return grpc::Status( grpc::NOT_FOUND, "No such case" ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +static bool scalarResultExistsOrCreate( RigCaseCellResultsData* results, QString propertyName ) +{ + RigEclipseResultAddress resAddr( RiaDefines::GENERATED, propertyName ); + + if ( !results->ensureKnownResultLoaded( resAddr ) ) + { + results->createResultEntry( resAddr, true ); + } + + std::vector>* scalarResultFrames = results->modifiableCellScalarResultTimesteps( resAddr ); + size_t timeStepCount = results->maxTimeStepCount(); + scalarResultFrames->resize( timeStepCount ); + + return true; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +static bool createIJKCellResults( RigCaseCellResultsData* results, QString propertyName ) +{ + bool ok; + ok = scalarResultExistsOrCreate( results, QString( "%1IJK" ).arg( propertyName ) ); + if ( !ok ) return false; + ok = scalarResultExistsOrCreate( results, QString( "%1I" ).arg( propertyName ) ); + if ( !ok ) return false; + ok = scalarResultExistsOrCreate( results, QString( "%1J" ).arg( propertyName ) ); + if ( !ok ) return false; + ok = scalarResultExistsOrCreate( results, QString( "%1K" ).arg( propertyName ) ); + + return ok; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RiaNNCInputValuesStateHandler::RiaNNCInputValuesStateHandler( bool ) + : m_eclipseCase( nullptr ) + , m_streamedValueCount( 0u ) + , m_cellCount( 0u ) + , m_timeStep( 0u ) +{ +} + +std::vector* getOrCreateConnectionScalarResultByName( RigNNCData* nncData, const QString propertyName, int timeStep ) +{ + std::vector* resultsToAdd = nncData->generatedConnectionScalarResultByName( propertyName, timeStep ); + if ( resultsToAdd ) + { + return resultsToAdd; + } + else + { + nncData->makeGeneratedConnectionScalarResult( propertyName, timeStep + 1 ); + return nncData->generatedConnectionScalarResultByName( propertyName, timeStep ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +grpc::Status RiaNNCInputValuesStateHandler::init( const NNCValuesInputRequest* request ) +{ + int caseId = request->case_id(); + m_eclipseCase = dynamic_cast( RiaGrpcServiceInterface::findCase( caseId ) ); + + if ( m_eclipseCase && m_eclipseCase->eclipseCaseData() && m_eclipseCase->eclipseCaseData()->mainGrid() ) + { + auto caseData = m_eclipseCase->eclipseCaseData(); + auto m_porosityModel = static_cast( request->porosity_model() ); + auto resultData = caseData->results( m_porosityModel ); + m_timeStep = request->time_step(); + m_propertyName = QString::fromStdString( request->property_name() ); + + RigNNCData* nncData = m_eclipseCase->eclipseCaseData()->mainGrid()->nncData(); + std::vector* resultsToAdd = getOrCreateConnectionScalarResultByName( nncData, m_propertyName, m_timeStep ); + if ( !resultsToAdd ) + { + return grpc::Status( grpc::NOT_FOUND, "No results for scalar results found." ); + } + + if ( !m_eclipseCase->results( m_porosityModel ) ) + { + return grpc::Status( grpc::NOT_FOUND, "No results for porosity model." ); + } + + bool ok = createIJKCellResults( m_eclipseCase->results( m_porosityModel ), m_propertyName ); + if ( !ok ) + { + return grpc::Status( grpc::NOT_FOUND, "Could not find the property results." ); + } + + RigEclipseResultAddress resAddr( QString( "%1IJK" ).arg( m_propertyName ) ); + m_eclipseCase->results( m_porosityModel )->ensureKnownResultLoaded( resAddr ); + nncData->setEclResultAddress( m_propertyName, resAddr ); + + m_cellCount = caseData->mainGrid()->nncData()->connections().size(); + + resultsToAdd->resize( m_cellCount, HUGE_VAL ); + + return Status::OK; + } + + return grpc::Status( grpc::NOT_FOUND, "Couldn't find an Eclipse case matching the case Id" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +grpc::Status RiaNNCInputValuesStateHandler::init( const rips::NNCValuesChunk* chunk ) +{ + if ( chunk->has_params() ) + { + return init( &( chunk->params() ) ); + } + return grpc::Status( grpc::INVALID_ARGUMENT, "Need to have PropertyRequest parameters in first message" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +grpc::Status RiaNNCInputValuesStateHandler::receiveStreamRequest( const NNCValuesChunk* request, + ClientToServerStreamReply* reply ) +{ + if ( request->has_values() ) + { + auto values = request->values().values(); + if ( !values.empty() ) + { + RigNNCData* nncData = m_eclipseCase->eclipseCaseData()->mainGrid()->nncData(); + + std::vector>* resultsToAdd = + nncData->generatedConnectionScalarResultByName( m_propertyName ); + + size_t currentCellIdx = m_streamedValueCount; + m_streamedValueCount += values.size(); + + for ( int i = 0; i < values.size() && currentCellIdx < m_cellCount; ++i, ++currentCellIdx ) + { + resultsToAdd->at( m_timeStep )[currentCellIdx] = values[i]; + } + + if ( m_streamedValueCount > m_cellCount ) + { + return grpc::Status( grpc::OUT_OF_RANGE, "Attempting to write out of bounds" ); + } + reply->set_accepted_value_count( static_cast( currentCellIdx ) ); + return Status::OK; + } + } + return Status::OK; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +size_t RiaNNCInputValuesStateHandler::cellCount() const +{ + return m_cellCount; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +size_t RiaNNCInputValuesStateHandler::streamedValueCount() const +{ + return m_streamedValueCount; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiaNNCInputValuesStateHandler::finish() +{ + if ( m_eclipseCase != nullptr ) + { + // Create a new input property if we have an input reservoir + RimEclipseInputCase* inputRes = dynamic_cast( m_eclipseCase ); + if ( inputRes ) + { + RimEclipseInputProperty* inputProperty = + inputRes->inputPropertyCollection()->findInputProperty( m_propertyName ); + if ( !inputProperty ) + { + inputProperty = new RimEclipseInputProperty; + inputProperty->resultName = m_propertyName; + inputProperty->eclipseKeyword = ""; + inputProperty->fileName = QString( "" ); + inputRes->inputPropertyCollection()->inputProperties.push_back( inputProperty ); + inputRes->inputPropertyCollection()->updateConnectedEditors(); + } + inputProperty->resolvedState = RimEclipseInputProperty::RESOLVED_NOT_SAVED; + } + + for ( size_t i = 0; i < m_eclipseCase->reservoirViews.size(); ++i ) + { + if ( m_eclipseCase->reservoirViews[i] ) + { + // As new result might have been introduced, update all editors connected + m_eclipseCase->reservoirViews[i]->cellResult()->updateConnectedEditors(); + + // It is usually not needed to create new display model, but if any derived geometry based on + // generated data (from Octave) a full display model rebuild is required + m_eclipseCase->reservoirViews[i]->scheduleCreateDisplayModelAndRedraw(); + m_eclipseCase->reservoirViews[i]->intersectionCollection()->scheduleCreateDisplayModelAndRedraw2dIntersectionViews(); + } + } + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +grpc::Status RiaGrpcNNCPropertiesService::SetNNCValues( grpc::ServerContext* context, + const rips::NNCValuesChunk* chunk, + rips::ClientToServerStreamReply* reply, + RiaNNCInputValuesStateHandler* stateHandler ) +{ + return stateHandler->receiveStreamRequest( chunk, reply ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -262,23 +496,32 @@ std::vector RiaGrpcNNCPropertiesService::createCallba typedef RiaGrpcNNCPropertiesService Self; std::vector callbacks; - callbacks = {new RiaGrpcUnaryCallback( this, - &Self::GetAvailableNNCProperties, - &Self::RequestGetAvailableNNCProperties ), - new RiaGrpcServerToClientStreamCallback( this, - &Self::GetNNCConnections, - &Self::RequestGetNNCConnections, - new RiaNNCConnectionsStateHandler ), - new RiaGrpcServerToClientStreamCallback( this, - &Self::GetNNCValues, - &Self::RequestGetNNCValues, - new RiaNNCValuesStateHandler )}; + callbacks = + {new RiaGrpcUnaryCallback( this, + &Self::GetAvailableNNCProperties, + &Self::RequestGetAvailableNNCProperties ), + new RiaGrpcServerToClientStreamCallback( this, + &Self::GetNNCConnections, + &Self::RequestGetNNCConnections, + new RiaNNCConnectionsStateHandler ), + new RiaGrpcServerToClientStreamCallback( this, + &Self::GetNNCValues, + &Self::RequestGetNNCValues, + new RiaNNCValuesStateHandler ), + + new RiaGrpcClientToServerStreamCallback( this, + &Self::SetNNCValues, + &Self::RequestSetNNCValues, + new RiaNNCInputValuesStateHandler( true ) )}; return callbacks; } diff --git a/ApplicationCode/GrpcInterface/RiaGrpcNNCPropertiesService.h b/ApplicationCode/GrpcInterface/RiaGrpcNNCPropertiesService.h index a43dd86776..ebdab76e00 100644 --- a/ApplicationCode/GrpcInterface/RiaGrpcNNCPropertiesService.h +++ b/ApplicationCode/GrpcInterface/RiaGrpcNNCPropertiesService.h @@ -23,6 +23,8 @@ #include #include +#include + class RimEclipseCase; //================================================================================================== @@ -65,6 +67,36 @@ protected: size_t m_currentIdx; }; +//================================================================================================== +// +// State handler for client-to-server streaming of NNC values +// +//================================================================================================== + +class RiaNNCInputValuesStateHandler +{ +public: + typedef grpc::Status Status; + +public: + RiaNNCInputValuesStateHandler( bool t = true ); + grpc::Status init( const rips::NNCValuesChunk* request ); + grpc::Status init( const rips::NNCValuesInputRequest* request ); + grpc::Status receiveStreamRequest( const rips::NNCValuesChunk* request, rips::ClientToServerStreamReply* reply ); + + size_t cellCount() const; + size_t streamedValueCount() const; + void finish(); + +protected: + const rips::NNCValuesInputRequest* m_request; + RimEclipseCase* m_eclipseCase; + size_t m_cellCount; + size_t m_streamedValueCount; + size_t m_timeStep; + QString m_propertyName; +}; + //================================================================================================== // // gRPC-service answering requests about NNC property information for a given case and time step @@ -84,6 +116,10 @@ public: const rips::NNCValuesRequest* request, rips::NNCValues* reply, RiaNNCValuesStateHandler* stateHandler ); + grpc::Status SetNNCValues( grpc::ServerContext* context, + const rips::NNCValuesChunk* chunk, + rips::ClientToServerStreamReply* reply, + RiaNNCInputValuesStateHandler* stateHandler ); std::vector createCallbacks() override; };