diff --git a/ApplicationCode/GrpcInterface/CMakeLists.cmake b/ApplicationCode/GrpcInterface/CMakeLists.cmake index 65d97b6ed4..a67746c921 100644 --- a/ApplicationCode/GrpcInterface/CMakeLists.cmake +++ b/ApplicationCode/GrpcInterface/CMakeLists.cmake @@ -6,6 +6,7 @@ set ( SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RiaGrpcCallbacks.inl ${CMAKE_CURRENT_LIST_DIR}/RiaGrpcServiceInterface.h ${CMAKE_CURRENT_LIST_DIR}/RiaGrpcCaseService.h + ${CMAKE_CURRENT_LIST_DIR}/RiaGrpcSimulationWellService.h ${CMAKE_CURRENT_LIST_DIR}/RiaGrpcGridService.h ${CMAKE_CURRENT_LIST_DIR}/RiaGrpcProjectService.h ${CMAKE_CURRENT_LIST_DIR}/RiaGrpcCommandService.h @@ -18,6 +19,7 @@ set ( SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RiaGrpcServer.cpp ${CMAKE_CURRENT_LIST_DIR}/RiaGrpcServiceInterface.cpp ${CMAKE_CURRENT_LIST_DIR}/RiaGrpcCaseService.cpp + ${CMAKE_CURRENT_LIST_DIR}/RiaGrpcSimulationWellService.cpp ${CMAKE_CURRENT_LIST_DIR}/RiaGrpcGridService.cpp ${CMAKE_CURRENT_LIST_DIR}/RiaGrpcProjectService.cpp ${CMAKE_CURRENT_LIST_DIR}/RiaGrpcCommandService.cpp @@ -81,6 +83,7 @@ set(PROTO_FILES "Definitions" "PdmObject" "Case" + "SimulationWell" "Project" "Commands" "App" diff --git a/ApplicationCode/GrpcInterface/GrpcProtos/Case.proto b/ApplicationCode/GrpcInterface/GrpcProtos/Case.proto index 5bab5114c0..07e97db82f 100644 --- a/ApplicationCode/GrpcInterface/GrpcProtos/Case.proto +++ b/ApplicationCode/GrpcInterface/GrpcProtos/Case.proto @@ -1,6 +1,7 @@ syntax = "proto3"; import "PdmObject.proto"; +import "Definitions.proto"; package rips; @@ -60,12 +61,6 @@ message GridCount int32 count = 1; } -message Vec3i { - int32 i = 1; - int32 j = 2; - int32 k = 3; -} - message CellCount { int32 active_cell_count = 1; diff --git a/ApplicationCode/GrpcInterface/GrpcProtos/Definitions.proto b/ApplicationCode/GrpcInterface/GrpcProtos/Definitions.proto index dcdabb8310..5f6a2760e8 100644 --- a/ApplicationCode/GrpcInterface/GrpcProtos/Definitions.proto +++ b/ApplicationCode/GrpcInterface/GrpcProtos/Definitions.proto @@ -9,4 +9,11 @@ message Empty message ClientToServerStreamReply { int64 accepted_value_count = 1; -} \ No newline at end of file +} + +message Vec3i { + int32 i = 1; + int32 j = 2; + int32 k = 3; +} + diff --git a/ApplicationCode/GrpcInterface/GrpcProtos/Grid.proto b/ApplicationCode/GrpcInterface/GrpcProtos/Grid.proto index 36409e5bc2..aaea7f0b53 100644 --- a/ApplicationCode/GrpcInterface/GrpcProtos/Grid.proto +++ b/ApplicationCode/GrpcInterface/GrpcProtos/Grid.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package rips; +import "Definitions.proto"; import "Case.proto"; service Grid diff --git a/ApplicationCode/GrpcInterface/GrpcProtos/SimulationWell.proto b/ApplicationCode/GrpcInterface/GrpcProtos/SimulationWell.proto new file mode 100644 index 0000000000..65821e128a --- /dev/null +++ b/ApplicationCode/GrpcInterface/GrpcProtos/SimulationWell.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; + +import "Definitions.proto"; + +package rips; + +service SimulationWell +{ + rpc GetSimulationWellStatus(SimulationWellRequest) returns (SimulationWellStatus) {} + rpc GetSimulationWellCells(SimulationWellRequest) returns (SimulationWellCellInfoArray) {} +} + +message SimulationWellRequest { + int32 case_id = 1; + string well_name = 2; + int32 timestep = 3; +} + +message SimulationWellStatus +{ + string well_type = 1; + bool is_open = 2; +} + +message SimulationWellCellInfo { + Vec3i ijk = 1; + int32 grid_index = 2; + bool is_open = 3; + int32 branch_id = 4; + int32 segment_id = 5; +} + +message SimulationWellCellInfoArray +{ + repeated SimulationWellCellInfo data = 1; +} + diff --git a/ApplicationCode/GrpcInterface/Python/rips/PythonExamples/all_simulation_wells.py b/ApplicationCode/GrpcInterface/Python/rips/PythonExamples/all_simulation_wells.py new file mode 100644 index 0000000000..9dc1778553 --- /dev/null +++ b/ApplicationCode/GrpcInterface/Python/rips/PythonExamples/all_simulation_wells.py @@ -0,0 +1,27 @@ +################################################################################### +# This example will connect to ResInsight, retrieve a list of +# simulation wells and print info +################################################################################### + +# Import the ResInsight Processing Server Module +import rips + +# Connect to ResInsight +resinsight = rips.Instance.find() +if resinsight is not None: + # Get a list of all wells + cases = resinsight.project.cases() + + for case in cases: + print("Case id: " + str(case.case_id)) + print("Case name: " + case.name) + + timesteps = case.time_steps() + sim_wells = case.simulation_wells() + for sim_well in sim_wells: + print("Simulation well: " + sim_well.name) + + for (tidx, timestep) in enumerate(timesteps): + status = sim_well.status(tidx) + cells = sim_well.cells(tidx) + print("timestep: " + str(tidx) + " type: " + status.well_type + " open: " + str(status.is_open) + " cells:" + str(len(cells))) diff --git a/ApplicationCode/GrpcInterface/Python/rips/case.py b/ApplicationCode/GrpcInterface/Python/rips/case.py index ff729dc47b..e260947774 100644 --- a/ApplicationCode/GrpcInterface/Python/rips/case.py +++ b/ApplicationCode/GrpcInterface/Python/rips/case.py @@ -17,12 +17,12 @@ import rips.generated.PdmObject_pb2 as PdmObject_pb2 import rips.generated.Properties_pb2 as Properties_pb2 import rips.generated.Properties_pb2_grpc as Properties_pb2_grpc - from rips.grid import Grid from rips.pdmobject import PdmObject from rips.view import View from rips.contour_map import ContourMap, ContourMapType from rips.well_bore_stability_plot import WellBoreStabilityPlot, WbsParameters +from rips.simulation_well import SimulationWell class Case(PdmObject): """ResInsight case class @@ -808,3 +808,15 @@ class Case(PdmObject): res = self._execute_command(importFormationNames=Cmd.ImportFormationNamesRequest(formationFiles=formation_files, applyToCaseId=self.case_id)) + + def simulation_wells(self): + """Get a list of all simulation wells for a case + + Returns: + A list of rips SimulationWell objects + """ + pdm_objects = self.descendants("Well") + wells = [] + for pdm_object in pdm_objects: + wells.append(SimulationWell(pdm_object.get_value("WellName"), self.case_id, pdm_object)) + return wells diff --git a/ApplicationCode/GrpcInterface/Python/rips/simulation_well.py b/ApplicationCode/GrpcInterface/Python/rips/simulation_well.py new file mode 100644 index 0000000000..27603c286f --- /dev/null +++ b/ApplicationCode/GrpcInterface/Python/rips/simulation_well.py @@ -0,0 +1,39 @@ +""" +ResInsight SimulationWell +""" +import grpc + +import rips.generated.SimulationWell_pb2 as SimulationWell_pb2 +import rips.generated.SimulationWell_pb2_grpc as SimulationWell_pb2_grpc + +import rips.generated.Properties_pb2 as Properties_pb2 +import rips.generated.Properties_pb2_grpc as Properties_pb2_grpc + +import rips.generated.Commands_pb2 as Cmd + +from rips.pdmobject import PdmObject + +class SimulationWell(PdmObject): + """ResInsight simulation well class + + Attributes: + name(string): Name of the well. + + """ + def __init__(self, name, case_id, pdm_object): + PdmObject.__init__(self, pdm_object.pb2_object(), pdm_object.channel(), pdm_object.project()) + self._simulation_well_stub = SimulationWell_pb2_grpc.SimulationWellStub(pdm_object.channel()) + self.name = name + self.case_id = case_id + + def status(self, timestep): + sim_well_request = SimulationWell_pb2.SimulationWellRequest(case_id=self.case_id, + well_name=self.name, + timestep=timestep) + return self._simulation_well_stub.GetSimulationWellStatus(sim_well_request) + + def cells(self, timestep): + sim_well_request = SimulationWell_pb2.SimulationWellRequest(case_id=self.case_id, + well_name=self.name, + timestep=timestep) + return self._simulation_well_stub.GetSimulationWellCells(sim_well_request).data diff --git a/ApplicationCode/GrpcInterface/Python/rips/tests/test_simulation_wells.py b/ApplicationCode/GrpcInterface/Python/rips/tests/test_simulation_wells.py new file mode 100644 index 0000000000..e3ff4a123f --- /dev/null +++ b/ApplicationCode/GrpcInterface/Python/rips/tests/test_simulation_wells.py @@ -0,0 +1,49 @@ +import sys +import os + +sys.path.insert(1, os.path.join(sys.path[0], '../../')) +import rips + +import dataroot + +def test_10k(rips_instance, initialize_test): + case_path = dataroot.PATH + "/TEST10K_FLT_LGR_NNC/TEST10K_FLT_LGR_NNC.EGRID" + case = rips_instance.project.load_case(path=case_path) + assert(len(case.grids()) == 2) + cell_count_info = case.cell_count() + + sim_wells = case.simulation_wells() + assert(len(sim_wells) == 3) + + assert(sim_wells[0].name == "GI1") + assert(sim_wells[1].name == "GP1") + assert(sim_wells[2].name == "GP2") + + timesteps = case.time_steps() + + # On time step 0 all simulation wells are undefined + for sim_well in sim_wells: + status = sim_well.status(0) + assert(status.well_type == "NotDefined") + + # On time step 3 all wells are producing + for sim_well in sim_wells: + status = sim_well.status(3) + assert(status.well_type == "Producer") + + # On time step 0 all simulation wells have no cells + for sim_well in sim_wells: + cells = sim_well.cells(0) + assert(len(cells) == 0) + + # On the other time steps there should be cells + expected_cell_count = {} + expected_cell_count["GP1"] = 105 + expected_cell_count["GI1"] = 38 + expected_cell_count["GP2"] = 18 + for sim_well in sim_wells: + for (tidx, timestep) in enumerate(timesteps): + if (tidx > 0): + cells = sim_well.cells(tidx) + print("well: " + sim_well.name + " timestep: " + str(tidx) + " cells:" + str(len(cells))) + assert(len(cells) == expected_cell_count[sim_well.name]) diff --git a/ApplicationCode/GrpcInterface/RiaGrpcSimulationWellService.cpp b/ApplicationCode/GrpcInterface/RiaGrpcSimulationWellService.cpp new file mode 100644 index 0000000000..5694447abe --- /dev/null +++ b/ApplicationCode/GrpcInterface/RiaGrpcSimulationWellService.cpp @@ -0,0 +1,185 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2020- 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 +// for more details. +// +////////////////////////////////////////////////////////////////////////////////// +#include "RiaGrpcSimulationWellService.h" + +#include "RiaGrpcCallbacks.h" + +#include "RigEclipseCaseData.h" +#include "RigGridBase.h" +#include "RigSimWellData.h" + +#include "RimCase.h" +#include "RimEclipseCase.h" + +#include "cvfCollection.h" + +using namespace rips; + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +grpc::Status RiaGrpcSimulationWellService::GetSimulationWellStatus( grpc::ServerContext* context, + const rips::SimulationWellRequest* request, + rips::SimulationWellStatus* reply ) + +{ + RimEclipseCase* eclipseCase = dynamic_cast( findCase( request->case_id() ) ); + if ( !eclipseCase ) + { + return grpc::Status( grpc::NOT_FOUND, "Case not found" ); + } + + // First find the well result for the correct well + cvf::ref currentWellResult = findWellResult( eclipseCase, request->well_name() ); + if ( currentWellResult.isNull() ) + { + return grpc::Status( grpc::NOT_FOUND, "Well not found" ); + } + + size_t tsIdx = static_cast( request->timestep() ); + + QString wellType = "NotDefined"; + bool wellStatus = false; + if ( currentWellResult->hasWellResult( tsIdx ) ) + { + switch ( currentWellResult->wellResultFrame( tsIdx ).m_productionType ) + { + case RigWellResultFrame::PRODUCER: + wellType = "Producer"; + break; + case RigWellResultFrame::OIL_INJECTOR: + wellType = "OilInjector"; + break; + case RigWellResultFrame::WATER_INJECTOR: + wellType = "WaterInjector"; + break; + case RigWellResultFrame::GAS_INJECTOR: + wellType = "GasInjector"; + break; + } + + wellStatus = currentWellResult->wellResultFrame( tsIdx ).m_isOpen; + } + + reply->set_well_type( wellType.toStdString() ); + reply->set_is_open( wellStatus ); + + return grpc::Status::OK; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +grpc::Status RiaGrpcSimulationWellService::GetSimulationWellCells( grpc::ServerContext* context, + const rips::SimulationWellRequest* request, + rips::SimulationWellCellInfoArray* reply ) +{ + RimEclipseCase* eclipseCase = dynamic_cast( findCase( request->case_id() ) ); + if ( !eclipseCase ) + { + return grpc::Status( grpc::NOT_FOUND, "Case not found" ); + } + + // First find the well result for the correct well + cvf::ref currentWellResult = findWellResult( eclipseCase, request->well_name() ); + if ( currentWellResult.isNull() ) + { + return grpc::Status( grpc::NOT_FOUND, "Well not found" ); + } + + size_t tsIdx = static_cast( request->timestep() ); + if ( currentWellResult->hasWellResult( tsIdx ) ) + { + // Fetch results + const RigWellResultFrame& wellResFrame = currentWellResult->wellResultFrame( tsIdx ); + std::vector grids; + eclipseCase->eclipseCaseData()->allGrids( &grids ); + + for ( size_t bIdx = 0; bIdx < wellResFrame.m_wellResultBranches.size(); ++bIdx ) + { + const std::vector& branchResPoints = + wellResFrame.m_wellResultBranches[bIdx].m_branchResultPoints; + for ( size_t rpIdx = 0; rpIdx < branchResPoints.size(); ++rpIdx ) + { + const RigWellResultPoint& resPoint = branchResPoints[rpIdx]; + + if ( resPoint.isCell() ) + { + rips::SimulationWellCellInfo* cellInfo = reply->add_data(); + size_t i; + size_t j; + size_t k; + size_t gridIdx = resPoint.m_gridIndex; + grids[gridIdx]->ijkFromCellIndex( resPoint.m_gridCellIndex, &i, &j, &k ); + + Vec3i* ijk = new Vec3i; + ijk->set_i( i ); + ijk->set_j( j ); + ijk->set_k( k ); + + cellInfo->set_allocated_ijk( ijk ); + cellInfo->set_grid_index( gridIdx ); + cellInfo->set_is_open( resPoint.m_isOpen ); + cellInfo->set_branch_id( resPoint.m_ertBranchId ); + cellInfo->set_segment_id( resPoint.m_ertSegmentId ); + } + } + } + } + + return grpc::Status::OK; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +cvf::ref RiaGrpcSimulationWellService::findWellResult( const RimEclipseCase* eclipseCase, + const std::string& wellName ) +{ + const cvf::Collection& allWellRes = eclipseCase->eclipseCaseData()->wellResults(); + for ( size_t tsIdx = 0; tsIdx < allWellRes.size(); ++tsIdx ) + { + if ( allWellRes[tsIdx]->m_wellName.toStdString() == wellName ) + { + return allWellRes[tsIdx]; + } + } + + return cvf::ref(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RiaGrpcSimulationWellService::createCallbacks() +{ + typedef RiaGrpcSimulationWellService Self; + + return { + new RiaGrpcUnaryCallback( this, + &Self::GetSimulationWellStatus, + &Self::RequestGetSimulationWellStatus ), + new RiaGrpcUnaryCallback( this, + &Self::GetSimulationWellCells, + &Self::RequestGetSimulationWellCells ), + }; +} + +static bool RiaGrpcSimulationWellService_init = + RiaGrpcServiceFactory::instance()->registerCreator( + typeid( RiaGrpcSimulationWellService ).hash_code() ); diff --git a/ApplicationCode/GrpcInterface/RiaGrpcSimulationWellService.h b/ApplicationCode/GrpcInterface/RiaGrpcSimulationWellService.h new file mode 100644 index 0000000000..4bd89718d4 --- /dev/null +++ b/ApplicationCode/GrpcInterface/RiaGrpcSimulationWellService.h @@ -0,0 +1,60 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2020- 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 +// for more details. +// +////////////////////////////////////////////////////////////////////////////////// +#pragma once + +#include "SimulationWell.grpc.pb.h" + +#include "RiaGrpcServiceInterface.h" + +#include "cvfObject.h" + +#include +#include + +namespace rips +{ +class SimulationWellRequest; +class SimulationWellStatus; +} // namespace rips + +class RiaGrpcCallbackInterface; + +class RimEclipseCase; +class RigSimWellData; + +//================================================================================================== +// +// gRPC-service answering requests about grid information for a simulation wells +// +//================================================================================================== +class RiaGrpcSimulationWellService final : public rips::SimulationWell::AsyncService, public RiaGrpcServiceInterface +{ +public: + grpc::Status GetSimulationWellStatus( grpc::ServerContext* context, + const rips::SimulationWellRequest* request, + rips::SimulationWellStatus* reply ); + + grpc::Status GetSimulationWellCells( grpc::ServerContext* context, + const rips::SimulationWellRequest* request, + rips::SimulationWellCellInfoArray* reply ); + + std::vector createCallbacks() override; + +private: + static cvf::ref findWellResult( const RimEclipseCase* eclipseCase, const std::string& wellName ); +}; diff --git a/ApplicationCode/ProjectDataModel/RimSimWellInView.cpp b/ApplicationCode/ProjectDataModel/RimSimWellInView.cpp index 1752f16842..b27f326475 100644 --- a/ApplicationCode/ProjectDataModel/RimSimWellInView.cpp +++ b/ApplicationCode/ProjectDataModel/RimSimWellInView.cpp @@ -20,6 +20,8 @@ #include "RimSimWellInView.h" +#include "RicfCommandObject.h" + #include "RigActiveCellInfo.h" #include "RigCell.h" #include "RigEclipseCaseData.h" @@ -62,7 +64,7 @@ RimSimWellInView::RimSimWellInView() { CAF_PDM_InitObject( "Well", ":/Well.png", "", "" ); - CAF_PDM_InitFieldNoDefault( &name, "WellName", "Name", "", "", "" ); + RICF_InitFieldNoDefault( &name, "WellName", "Name", "", "", "" ); CAF_PDM_InitField( &showWell, "ShowWell", true, "Show well ", "", "", "" );