Add support for more MSW tables in Python

* Use caf::VecIjk0
* Adds MSW tables to the gRPC interface
* Adds unified completion data retrieval
* Add unite tests for MSW data
This commit is contained in:
Magne Sjaastad authored and GitHub committed 2025-11-17 11:54:02 +01:00
1 parent 81c0d688c5
commit 7e299c4841
17 files changed
+1493 -113

No files matched your search

+2
View File
@@ -38,6 +38,7 @@ set(SOURCE_GROUP_HEADER_FILES
RiaGrpcPdmObjectService.h
RiaGrpcApplicationInterface.h
RiaGrpcWellPathService.h
RiaWellPathDataToGrpcConverter.h
)
set(SOURCE_GROUP_SOURCE_FILES
@@ -56,6 +57,7 @@ set(SOURCE_GROUP_SOURCE_FILES
RiaGrpcPdmObjectService.cpp
RiaGrpcApplicationInterface.cpp
RiaGrpcWellPathService.cpp
RiaWellPathDataToGrpcConverter.cpp
)
# Find Protobuf installation Looks for protobuf-config.cmake file installed by
@@ -7,6 +7,8 @@ package rips;
service WellPath {
rpc GetCompletionData(SimulatorTableRequest)
returns (SimulatorTableData) {}
rpc GetCompletionDataUnified(SimulatorTableUnifiedRequest)
returns (SimulatorTableData) {}
}
message SimulatorTableRequest {
@@ -14,6 +16,11 @@ message SimulatorTableRequest {
int32 case_id = 2;
}
message SimulatorTableUnifiedRequest {
repeated string wellpath_names = 1;
int32 case_id = 2;
}
message SimulatorCompdatEntry {
string well_name = 1;
int32 grid_i = 2;
@@ -49,8 +56,87 @@ message SimulatorWelspecsEntry {
optional int32 fip_region = 13;
}
message SimulatorWelsegsHeaderEntry {
string well_name = 1;
double top_depth = 2;
double top_length = 3;
optional double wellbore_volume = 4;
string info_type = 5;
optional string pressure_omponents = 6;
optional string flow_model = 7;
}
message SimulatorWelsegsRowEntry {
int32 segment_1 = 1;
int32 segment_2 = 2;
int32 branch = 3;
int32 join_segment = 4;
double length = 5;
double depth = 6;
optional double diameter = 7;
optional double roughness = 8;
optional string description = 9;
}
message SimulatorWelsegsEntry {
SimulatorWelsegsHeaderEntry header = 1;
repeated SimulatorWelsegsRowEntry row = 2;
}
message SimulatorCompsegsEntry {
int32 i = 1;
int32 j = 2;
int32 k = 3;
int32 branch = 4;
double distance_start = 5;
double distance_end = 6;
optional string grid_name = 7;
}
message SimulatorWsegvalvEntry {
string well_name = 1;
int32 segment_number = 2;
double cv = 3;
double area = 4;
optional double extra_length = 5;
optional double pipe_d = 6;
optional double roughness = 7;
optional double pipe_a = 8;
optional string status = 9;
optional double max_a = 10;
}
message SimulatorWsegaicdEntry {
string well_name = 1;
int32 segment_1 = 2;
int32 segment_2 = 3;
double strength = 4;
optional double length = 5;
optional double density_cali = 6;
optional double viscosity_cali = 7;
optional double critical_value = 8;
optional double width_trans = 9;
optional double max_visc_ratio = 10;
optional int32 method_scaling_factor = 11;
double max_abs_rate = 12;
double flow_rate_exponent = 13;
double visc_exponent = 14;
optional string status = 15;
optional double oil_flow_fraction = 16;
optional double water_flow_fraction = 17;
optional double gas_flow_fraction = 18;
optional double oil_visc_fraction = 19;
optional double water_visc_fraction = 20;
optional double gas_visc_fraction = 21;
optional string description = 22;
}
message SimulatorTableData {
repeated SimulatorCompdatEntry compdat = 1;
repeated SimulatorWelspecsEntry welspecs = 2;
repeated SimulatorWelsegsEntry welsegs = 3;
repeated SimulatorCompsegsEntry compsegs = 4;
repeated SimulatorWsegvalvEntry wsegvalv = 5;
repeated SimulatorWsegaicdEntry wsegaicd = 6;
}
@@ -0,0 +1,232 @@
import sys
import os
import tempfile
sys.path.insert(1, os.path.join(sys.path[0], "../../"))
import dataroot
def test_msw_completion_data_perf_lateral(rips_instance, initialize_test):
"""Test MSW completion data retrieval from perf_lateral.rsp project.
This test loads a project with Multi-Segment Well (MSW) configuration
and verifies that completion data can be retrieved including MSW-specific
tables (WELSEGS, COMPSEGS).
"""
project_path = dataroot.PATH + "/msw-export/project-files/perf_lateral.rsp"
project = rips_instance.project.open(path=project_path)
# Get the case
cases = project.cases()
assert len(cases) > 0, "Project should contain at least one case"
case = cases[0]
# Get well paths
well_paths = project.well_paths()
assert len(well_paths) >= 2, "Project should contain at least 2 well paths"
# Find the well paths by name
well_y1 = None
well_y2 = None
for well in well_paths:
if well.name == "Well-1 Y1":
well_y1 = well
elif well.name == "Well-1 Y2":
well_y2 = well
assert well_y1 is not None, "Well-1 Y1 should exist in project"
assert well_y2 is not None, "Well-1 Y2 should exist in project"
# Test completion data for Well-1 Y1
completion_data_y1 = well_y1.completion_data(case.id)
# Verify WELSPECS data
welspecs = completion_data_y1.welspecs
assert len(welspecs) > 0, "Should have WELSPECS data for Well-1 Y1"
welspec = welspecs[0]
assert welspec.well_name == "Well-1", "Well name should be Well-1"
assert welspec.group_name == "PROD_N", "Group name should be PROD_N"
assert welspec.phase == "OIL", "Phase should be OIL"
# Verify COMPDAT data (perforation intervals)
compdat = completion_data_y1.compdat
assert len(compdat) > 0, "Should have COMPDAT data for Well-1 Y1"
# Check that we have MD information in COMPDAT
has_md_data = False
for comp in compdat:
if comp.HasField("start_md") and comp.HasField("end_md"):
has_md_data = True
assert comp.start_md > 0, "Start MD should be positive"
assert comp.end_md > comp.start_md, "End MD should be greater than start MD"
break
assert has_md_data, "Should have MD (measured depth) data in COMPDAT"
# Verify MSW-specific data: WELSEGS
welsegs = completion_data_y1.welsegs
assert len(welsegs) > 0, "Should have WELSEGS data for MSW well Well-1 Y1"
# Check WELSEGS header (first entry)
welsegs_entry = welsegs[0]
assert (
welsegs_entry.header.well_name == "Well-1"
), "WELSEGS well name should be Well-1"
assert (
welsegs_entry.header.top_depth > 0
), "Should have positive top depth in WELSEGS"
assert welsegs_entry.header.top_length >= 0, "Should have top length in WELSEGS"
assert welsegs_entry.header.info_type in [
"ABS",
"INC",
], "Should have valid info type"
# Check WELSEGS rows (segment definitions)
assert len(welsegs_entry.row) > 0, "Should have segment rows in WELSEGS"
for row in welsegs_entry.row:
assert row.segment_1 > 0, "Segment 1 should be positive"
assert row.segment_2 > 0, "Segment 2 should be positive"
# Verify MSW-specific data: COMPSEGS
compsegs = completion_data_y1.compsegs
assert len(compsegs) > 0, "Should have COMPSEGS data for MSW well Well-1 Y1"
# Check COMPSEGS entries
for compseg in compsegs:
assert compseg.i > 0, "Grid I should be positive"
assert compseg.j > 0, "Grid J should be positive"
assert compseg.k > 0, "Grid K should be positive"
assert compseg.branch >= 0, "Branch should be non-negative"
def test_msw_completion_data_unified(rips_instance, initialize_test):
"""Test unified MSW completion data retrieval for multiple wells.
This test verifies that completion data can be retrieved for multiple
MSW wells in a single call using the completion_data_unified method.
"""
project_path = dataroot.PATH + "/msw-export/project-files/perf_lateral.rsp"
project = rips_instance.project.open(path=project_path)
# Get the case
cases = project.cases()
assert len(cases) > 0, "Project should contain at least one case"
case = cases[0]
# Get well path collection
well_path_collection = project.well_path_collection()
# Get unified completion data for all wells
unified_data = well_path_collection.completion_data_unified(case_id=case.id)
# Verify WELSPECS
assert len(unified_data.welspecs) > 0, "Should have WELSPECS in unified data"
# Verify COMPDAT
assert len(unified_data.compdat) > 0, "Should have COMPDAT in unified data"
# Verify WELSEGS (MSW-specific)
assert len(unified_data.welsegs) > 0, "Should have WELSEGS in unified MSW data"
# Count wells in WELSEGS
well_names_in_welsegs = set()
for welsegs_entry in unified_data.welsegs:
well_names_in_welsegs.add(welsegs_entry.header.well_name)
assert (
len(well_names_in_welsegs) >= 1
), "Should have at least one well in WELSEGS data"
# Verify COMPSEGS (MSW-specific)
assert len(unified_data.compsegs) > 0, "Should have COMPSEGS in unified MSW data"
# Check that COMPSEGS has valid grid coordinates
for compseg in unified_data.compsegs:
assert compseg.i > 0, "Grid I should be positive"
assert compseg.j > 0, "Grid J should be positive"
assert compseg.k > 0, "Grid K should be positive"
def test_msw_export(rips_instance, initialize_test):
"""Test MSW export functionality.
This test verifies that MSW data can be exported to file using the
export_msw method on a case.
"""
project_path = dataroot.PATH + "/msw-export/project-files/perf_lateral.rsp"
project = rips_instance.project.open(path=project_path)
# Get the case
cases = project.cases()
assert len(cases) > 0, "Project should contain at least one case"
case = cases[0]
# Get well paths
well_paths = project.well_paths()
well_y1 = None
for well in well_paths:
if well.name == "Well-1 Y1":
well_y1 = well
break
assert well_y1 is not None, "Well-1 Y1 should exist in project"
# Set export folder
export_folder = tempfile.gettempdir()
rips_instance.set_export_folder(export_type="COMPLETIONS", path=export_folder)
# Export MSW data
case.export_msw(well_path=well_y1.name)
# Note: The actual file creation validation would require checking
# the export folder, which may be platform-dependent and require
# additional file system checks
def test_msw_settings_read(rips_instance, initialize_test):
"""Test reading MSW settings from project.
This test verifies that MSW settings configured in the project
can be read through the Python API.
"""
project_path = dataroot.PATH + "/msw-export/project-files/perf_lateral.rsp"
project = rips_instance.project.open(path=project_path)
# Get well paths
well_paths = project.well_paths()
well_y1 = None
for well in well_paths:
if well.name == "Well-1 Y1":
well_y1 = well
break
assert well_y1 is not None, "Well-1 Y1 should exist in project"
# Get MSW settings
msw_settings = well_y1.msw_settings()
# Verify MSW settings values from the project file
# According to the project analysis, the liner diameter is 0.152m
assert msw_settings.liner_diameter == 0.152, "Liner diameter should be 0.152m"
assert msw_settings.roughness_factor == 1e-05, "Roughness factor should be 1e-05"
# Check pressure drop setting
assert msw_settings.pressure_drop == "HF-", "Pressure drop should be HF-"
# Check max segment length setting
assert msw_settings.max_segment_length == 200.0, "Max segment length should be 200m"
# Verify completion settings
completion_settings = well_y1.completion_settings()
assert (
completion_settings.well_name_for_export == "Well-1"
), "Well name for export should be Well-1"
assert (
completion_settings.group_name_for_export == "PROD_N"
), "Group name should be PROD_N"
assert completion_settings.well_type_for_export == "OIL", "Well type should be OIL"
assert (
completion_settings.allow_well_cross_flow
), "Allow well cross flow should be True"
@@ -1,11 +1,24 @@
import uuid
from typing import List
import grpc
from typing import List, Optional, Union, cast
import SimulatorTables_pb2
import SimulatorTables_pb2_grpc
import PdmObject_pb2
from .pdmobject import add_method
from .resinsight_classes import WellPathCollection
from .resinsight_classes import WellPathCollection, WellPath
from .project import Project
@add_method(WellPathCollection)
def __custom_init__(
self: WellPathCollection, pb2_object: PdmObject_pb2.PdmObject, channel: grpc.Channel
) -> None:
self.__well_path_stub = SimulatorTables_pb2_grpc.WellPathStub(channel)
@add_method(WellPathCollection)
def import_well_path_from_points(
self: WellPathCollection, name: str, coordinates: List[List[float]]
@@ -82,3 +95,61 @@ def import_well_path_from_points(
project.remove_key_values(x_key)
project.remove_key_values(y_key)
project.remove_key_values(z_key)
@add_method(WellPathCollection)
def completion_data_unified(
self: WellPathCollection,
wells: Optional[Union[List[WellPath], List[str]]] = None,
case_id: Optional[int] = None,
) -> SimulatorTables_pb2.SimulatorTableData:
"""Get unified completion data for multiple wells.
This method merges completion data (COMPDAT, WELSPECS, WELSEGS, COMPSEGS,
WSEGVALV, WSEGAICD) from multiple wells into a single response.
Arguments:
wells: List of WellPath objects or well names. If None, uses all wells in collection.
case_id: ID of the case to use. If None, tries to determine from context.
Returns:
SimulatorTableData containing merged completion data from all specified wells
Raises:
ValueError: If case_id cannot be determined or wells list is invalid
RuntimeError: If unable to access required data
"""
# Determine case_id if not provided
if case_id is None:
project = self.ancestor(Project)
if not project:
raise RuntimeError("Could not find parent project")
# Try to get case_id from the first available case
cases = project.cases()
if not cases:
raise RuntimeError("No cases available in project")
case_id = cases[0].case_id
# Handle wells parameter
if wells is None:
# Get all wells in collection
well_names = [wp.name for wp in self.well_paths()]
elif all(isinstance(w, str) for w in wells):
# List of well names
well_names = cast(List[str], wells)
elif all(hasattr(w, "name") for w in wells):
# List of WellPath objects
well_names = [w.name for w in wells if hasattr(w, "name")]
else:
raise ValueError(
"wells parameter must be a list of WellPath objects or strings"
)
if not well_names:
raise ValueError("No wells specified or found in collection")
sim_tab_req = SimulatorTables_pb2.SimulatorTableUnifiedRequest(
wellpath_names=well_names, case_id=case_id
)
return self.__well_path_stub.GetCompletionDataUnified(sim_tab_req)
+91 -71
View File
@@ -15,13 +15,17 @@
// for more details.
//
//////////////////////////////////////////////////////////////////////////////////
#include "RiaGrpcWellPathService.h"
#include "RiaGrpcCallbacks.h"
#include "RiaGrpcHelper.h"
#include "RiaWellPathDataToGrpcConverter.h"
#include "../Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.h"
#include "Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.h"
#include "Commands/CompletionExportCommands/RicWellPathExportMswTableData.h"
#include "CompletionsMsw/RigMswTableData.h"
#include "RigEclipseCaseData.h"
#include "RigGridBase.h"
#include "RigMainGrid.h"
@@ -64,14 +68,37 @@ grpc::Status RiaGrpcWellPathService::GetCompletionData( grpc::ServerContext*
for ( const auto& cd : compdata )
{
SimulatorCompdatEntry* grpcData = reply->add_compdat();
RiaGrpcWellPathService::copyCompdatToGrpc( cd, grpcData );
RiaWellPathDataToGrpcConverter::copyCompdatToGrpc( cd, grpcData );
}
auto ijPos = RicWellPathExportCompletionDataFeatureImpl::wellPathUpperGridIntersectionIJ( eclipseCase, wellPath );
auto compSettings = wellPath->completionSettings();
SimulatorWelspecsEntry* grpcData = reply->add_welspecs();
RiaGrpcWellPathService::copyWelspecsToGrpc( compSettings, grpcData, eclipseCase, ijPos.second.x(), ijPos.second.y() );
RiaWellPathDataToGrpcConverter::copyWelspecsToGrpc( compSettings,
grpcData,
eclipseCase,
ijPos.second.x(),
ijPos.second.y() );
// Multisegment wells
int timeStep = 0;
auto mswDataContainer = RicWellPathExportMswTableData::extractSingleWellMswData( eclipseCase, wellPath, timeStep );
if ( mswDataContainer.has_value() )
{
auto tables = mswDataContainer.value();
if ( tables.hasWelsegsData() )
{
SimulatorWelsegsEntry* grpcSegData = reply->add_welsegs();
RiaWellPathDataToGrpcConverter::copyWelsegsToGrpc( tables, grpcSegData );
}
RiaWellPathDataToGrpcConverter::copyCompsegsToGrpc( tables, reply );
RiaWellPathDataToGrpcConverter::copyWsegvalvToGrpc( tables, reply );
RiaWellPathDataToGrpcConverter::copyWsegaicdToGrpc( tables, reply );
}
return grpc::Status::OK;
}
@@ -79,81 +106,71 @@ grpc::Status RiaGrpcWellPathService::GetCompletionData( grpc::ServerContext*
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGrpcWellPathService::copyWelspecsToGrpc( const RimWellPathCompletionSettings* compSettings,
rips::SimulatorWelspecsEntry* grpcData,
RimEclipseCase* eclipseCase,
int gridI,
int gridJ )
grpc::Status RiaGrpcWellPathService::GetCompletionDataUnified( grpc::ServerContext* context,
const rips::SimulatorTableUnifiedRequest* request,
rips::SimulatorTableData* reply )
{
grpcData->set_well_name( compSettings->wellNameForExport().toStdString() );
grpcData->set_group_name( compSettings->groupNameForExport().toStdString() );
RimEclipseCase* eclipseCase = dynamic_cast<RimEclipseCase*>( RiaGrpcHelper::findCase( request->case_id() ) );
if ( !eclipseCase )
{
return grpc::Status( grpc::NOT_FOUND, "Case not found" );
}
// Convert to 1-based indexing
grpcData->set_grid_i( gridI + 1 );
grpcData->set_grid_j( gridJ + 1 );
auto proj = RimProject::current();
if ( !proj )
{
return grpc::Status( grpc::INTERNAL, "No current project" );
}
if ( compSettings->referenceDepth().has_value() )
// Process each well and merge all data into the single reply
for ( const std::string& wellPathName : request->wellpath_names() )
{
grpcData->set_bhp_depth( compSettings->referenceDepth().value() );
}
grpcData->set_phase( compSettings->wellTypeNameForExport().toStdString() );
if ( compSettings->drainageRadius().has_value() )
{
grpcData->set_drainage_radius( compSettings->drainageRadius().value() );
}
grpcData->set_inflow_equation( compSettings->gasInflowEquationForExport().toStdString() );
grpcData->set_auto_shut_in( compSettings->automaticWellShutInForExport().toStdString() );
grpcData->set_cross_flow( compSettings->allowWellCrossFlowForExport().toStdString() );
grpcData->set_pvt_num( compSettings->wellBoreFluidPVT() );
grpcData->set_hydrostatic_density_calc( compSettings->hydrostaticDensityForExport().toStdString() );
grpcData->set_fip_region( compSettings->fluidInPlaceRegion() );
}
RimWellPath* wellPath = proj->wellPathByName( QString::fromStdString( wellPathName ) );
if ( !wellPath )
{
// Continue processing other wells instead of failing the entire request
continue;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGrpcWellPathService::copyCompdatToGrpc( const RigCompletionData& inputData, rips::SimulatorCompdatEntry* compDat )
{
compDat->set_comment( inputData.metaDataString().toStdString() );
compDat->set_well_name( inputData.wellName().toStdString() );
// Add completion data for this well
auto compdata = RicWellPathExportCompletionDataFeatureImpl::completionDataForWellPath( wellPath, eclipseCase );
for ( const auto& cd : compdata )
{
SimulatorCompdatEntry* grpcData = reply->add_compdat();
RiaWellPathDataToGrpcConverter::copyCompdatToGrpc( cd, grpcData );
}
// Convert to 1-based indexing
compDat->set_grid_i( inputData.completionDataGridCell().localCellIndexI() + 1 );
compDat->set_grid_j( inputData.completionDataGridCell().localCellIndexJ() + 1 );
compDat->set_upper_k( inputData.completionDataGridCell().localCellIndexK() + 1 );
compDat->set_lower_k( inputData.completionDataGridCell().localCellIndexK() + 1 );
// Add welspecs data for this well
auto ijPos = RicWellPathExportCompletionDataFeatureImpl::wellPathUpperGridIntersectionIJ( eclipseCase, wellPath );
auto compSettings = wellPath->completionSettings();
compDat->set_open_shut_flag( "OPEN" );
if ( inputData.saturation() != inputData.defaultValue() )
{
compDat->set_saturation( inputData.saturation() );
}
if ( inputData.transmissibility() != inputData.defaultValue() )
{
compDat->set_transmissibility( inputData.transmissibility() );
}
if ( inputData.diameter() != inputData.defaultValue() )
{
compDat->set_diameter( inputData.diameter() );
}
if ( inputData.kh() != inputData.defaultValue() )
{
compDat->set_kh( inputData.kh() );
}
if ( inputData.skinFactor() != inputData.defaultValue() )
{
compDat->set_skin_factor( inputData.skinFactor() );
}
if ( inputData.dFactor() != inputData.defaultValue() )
{
compDat->set_d_factor( inputData.dFactor() );
}
compDat->set_direction( inputData.directionStringXYZ().toStdString() );
if ( inputData.startMD().has_value() )
{
compDat->set_start_md( inputData.startMD().value() );
compDat->set_end_md( inputData.endMD().value() );
SimulatorWelspecsEntry* grpcData = reply->add_welspecs();
RiaWellPathDataToGrpcConverter::copyWelspecsToGrpc( compSettings,
grpcData,
eclipseCase,
ijPos.second.x(),
ijPos.second.y() );
// Add multisegment well data for this well
int timeStep = 0;
auto mswDataContainer = RicWellPathExportMswTableData::extractSingleWellMswData( eclipseCase, wellPath, timeStep );
if ( mswDataContainer.has_value() )
{
auto tables = mswDataContainer.value();
if ( tables.hasWelsegsData() )
{
SimulatorWelsegsEntry* grpcSegData = reply->add_welsegs();
RiaWellPathDataToGrpcConverter::copyWelsegsToGrpc( tables, grpcSegData );
}
RiaWellPathDataToGrpcConverter::copyCompsegsToGrpc( tables, reply );
RiaWellPathDataToGrpcConverter::copyWsegvalvToGrpc( tables, reply );
RiaWellPathDataToGrpcConverter::copyWsegaicdToGrpc( tables, reply );
}
}
return grpc::Status::OK;
}
//--------------------------------------------------------------------------------------------------
@@ -165,7 +182,10 @@ std::vector<RiaGrpcCallbackInterface*> RiaGrpcWellPathService::createCallbacks()
return { new RiaGrpcUnaryCallback<Self, SimulatorTableRequest, SimulatorTableData>( this,
&Self::GetCompletionData,
&Self::RequestGetCompletionData ) };
&Self::RequestGetCompletionData ),
new RiaGrpcUnaryCallback<Self, SimulatorTableUnifiedRequest, SimulatorTableData>( this,
&Self::GetCompletionDataUnified,
&Self::RequestGetCompletionDataUnified ) };
}
static bool RiaGrpcWellPathService_init = RiaGrpcServiceFactory::instance()->registerCreator<RiaGrpcWellPathService>(
+6 -8
View File
@@ -29,6 +29,7 @@ namespace rips
class SimulationWellRequest;
class SimulationWellStatus;
class SimulatorWelspecsEntry;
class SimulatorWelsegsEntry;
} // namespace rips
class RiaGrpcCallbackInterface;
@@ -36,6 +37,7 @@ class RiaGrpcCallbackInterface;
class RimEclipseCase;
class RimWellPathCompletionSettings;
class RigCompletionData;
class RigMswTableData;
//==================================================================================================
//
@@ -49,13 +51,9 @@ public:
const ::rips::SimulatorTableRequest* request,
::rips::SimulatorTableData* response ) override;
std::vector<RiaGrpcCallbackInterface*> createCallbacks() override;
grpc::Status GetCompletionDataUnified( ::grpc::ServerContext* context,
const ::rips::SimulatorTableUnifiedRequest* request,
::rips::SimulatorTableData* response ) override;
private:
static void copyCompdatToGrpc( const RigCompletionData& inputData, rips::SimulatorCompdatEntry* compDat );
static void copyWelspecsToGrpc( const RimWellPathCompletionSettings* compSettings,
rips::SimulatorWelspecsEntry* grpcData,
RimEclipseCase* eclipseCase,
int gridI,
int gridJ );
std::vector<RiaGrpcCallbackInterface*> createCallbacks() override;
};
@@ -0,0 +1,314 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RiaWellPathDataToGrpcConverter.h"
#include "CompletionsMsw/RigMswTableData.h"
#include "RigCompletionData.h"
#include "RimEclipseCase.h"
#include "RimWellPathCompletionSettings.h"
#include <QString>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaWellPathDataToGrpcConverter::copyCompdatToGrpc( const RigCompletionData& inputData,
rips::SimulatorCompdatEntry* compDat )
{
compDat->set_comment( inputData.metaDataString().toStdString() );
compDat->set_well_name( inputData.wellName().toStdString() );
// Convert to 1-based indexing
compDat->set_grid_i( inputData.completionDataGridCell().localCellIndexI() + 1 );
compDat->set_grid_j( inputData.completionDataGridCell().localCellIndexJ() + 1 );
compDat->set_upper_k( inputData.completionDataGridCell().localCellIndexK() + 1 );
compDat->set_lower_k( inputData.completionDataGridCell().localCellIndexK() + 1 );
compDat->set_open_shut_flag( "OPEN" );
if ( inputData.saturation() != inputData.defaultValue() )
{
compDat->set_saturation( inputData.saturation() );
}
if ( inputData.transmissibility() != inputData.defaultValue() )
{
compDat->set_transmissibility( inputData.transmissibility() );
}
if ( inputData.diameter() != inputData.defaultValue() )
{
compDat->set_diameter( inputData.diameter() );
}
if ( inputData.kh() != inputData.defaultValue() )
{
compDat->set_kh( inputData.kh() );
}
if ( inputData.skinFactor() != inputData.defaultValue() )
{
compDat->set_skin_factor( inputData.skinFactor() );
}
if ( inputData.dFactor() != inputData.defaultValue() )
{
compDat->set_d_factor( inputData.dFactor() );
}
compDat->set_direction( inputData.directionStringXYZ().toStdString() );
if ( inputData.startMD().has_value() )
{
compDat->set_start_md( inputData.startMD().value() );
compDat->set_end_md( inputData.endMD().value() );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaWellPathDataToGrpcConverter::copyWelspecsToGrpc( const RimWellPathCompletionSettings* compSettings,
rips::SimulatorWelspecsEntry* grpcData,
RimEclipseCase* eclipseCase,
int gridI,
int gridJ )
{
grpcData->set_well_name( compSettings->wellNameForExport().toStdString() );
grpcData->set_group_name( compSettings->groupNameForExport().toStdString() );
// Convert to 1-based indexing
grpcData->set_grid_i( gridI + 1 );
grpcData->set_grid_j( gridJ + 1 );
if ( compSettings->referenceDepth().has_value() )
{
grpcData->set_bhp_depth( compSettings->referenceDepth().value() );
}
grpcData->set_phase( compSettings->wellTypeNameForExport().toStdString() );
if ( compSettings->drainageRadius().has_value() )
{
grpcData->set_drainage_radius( compSettings->drainageRadius().value() );
}
grpcData->set_inflow_equation( compSettings->gasInflowEquationForExport().toStdString() );
grpcData->set_auto_shut_in( compSettings->automaticWellShutInForExport().toStdString() );
grpcData->set_cross_flow( compSettings->allowWellCrossFlowForExport().toStdString() );
grpcData->set_pvt_num( compSettings->wellBoreFluidPVT() );
grpcData->set_hydrostatic_density_calc( compSettings->hydrostaticDensityForExport().toStdString() );
grpcData->set_fip_region( compSettings->fluidInPlaceRegion() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaWellPathDataToGrpcConverter::copyWelsegsToGrpc( const RigMswTableData& mswTableData,
rips::SimulatorWelsegsEntry* grpcData )
{
if ( !mswTableData.hasWelsegsData() ) return;
auto* headerEntry = grpcData->mutable_header();
auto welsegsHeader = mswTableData.welsegsHeader();
headerEntry->set_well_name( welsegsHeader.well );
headerEntry->set_top_depth( welsegsHeader.topDepth );
headerEntry->set_top_length( welsegsHeader.topLength );
if ( welsegsHeader.wellboreVolume.has_value() )
{
headerEntry->set_wellbore_volume( welsegsHeader.wellboreVolume.value() );
}
headerEntry->set_info_type( welsegsHeader.infoType );
if ( welsegsHeader.pressureComponents.has_value() )
{
headerEntry->set_pressure_omponents( welsegsHeader.pressureComponents.value() );
}
if ( welsegsHeader.flowModel.has_value() )
{
headerEntry->set_flow_model( welsegsHeader.flowModel.value() );
}
for ( const auto& welsegsRow : mswTableData.welsegsData() )
{
auto* rowEntry = grpcData->add_row();
rowEntry->set_segment_1( welsegsRow.segment1 );
rowEntry->set_segment_2( welsegsRow.segment2 );
rowEntry->set_branch( welsegsRow.branch );
rowEntry->set_join_segment( welsegsRow.joinSegment );
rowEntry->set_length( welsegsRow.length );
rowEntry->set_depth( welsegsRow.depth );
if ( welsegsRow.diameter.has_value() )
{
rowEntry->set_diameter( welsegsRow.diameter.value() );
}
if ( welsegsRow.roughness.has_value() )
{
rowEntry->set_roughness( welsegsRow.roughness.value() );
}
if ( !welsegsRow.description.empty() )
{
rowEntry->set_description( welsegsRow.description );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaWellPathDataToGrpcConverter::copyCompsegsToGrpc( const RigMswTableData& mswTableData,
rips::SimulatorTableData* reply )
{
if ( !mswTableData.hasCompsegsData() ) return;
for ( const auto& compsegsRow : mswTableData.compsegsData() )
{
auto* grpcCompData = reply->add_compsegs();
// 1-based indexing for cell coordinates
grpcCompData->set_i( compsegsRow.i );
grpcCompData->set_j( compsegsRow.j );
grpcCompData->set_k( compsegsRow.k );
grpcCompData->set_branch( compsegsRow.branch );
grpcCompData->set_distance_start( compsegsRow.distanceStart );
grpcCompData->set_distance_end( compsegsRow.distanceEnd );
if ( !compsegsRow.gridName.empty() )
{
grpcCompData->set_grid_name( compsegsRow.gridName );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaWellPathDataToGrpcConverter::copyWsegvalvToGrpc( const RigMswTableData& mswTableData,
rips::SimulatorTableData* reply )
{
if ( !mswTableData.hasWsegvalvData() ) return;
for ( const auto& wsegvalvRow : mswTableData.wsegvalvData() )
{
auto* grpcValvData = reply->add_wsegvalv();
grpcValvData->set_well_name( wsegvalvRow.well );
grpcValvData->set_segment_number( wsegvalvRow.segmentNumber );
grpcValvData->set_cv( wsegvalvRow.cv );
grpcValvData->set_area( wsegvalvRow.area );
if ( wsegvalvRow.extraLength.has_value() )
{
grpcValvData->set_extra_length( wsegvalvRow.extraLength.value() );
}
if ( wsegvalvRow.pipeD.has_value() )
{
grpcValvData->set_pipe_d( wsegvalvRow.pipeD.value() );
}
if ( wsegvalvRow.roughness.has_value() )
{
grpcValvData->set_roughness( wsegvalvRow.roughness.value() );
}
if ( wsegvalvRow.pipeA.has_value() )
{
grpcValvData->set_pipe_a( wsegvalvRow.pipeA.value() );
}
if ( wsegvalvRow.status.has_value() )
{
grpcValvData->set_status( wsegvalvRow.status.value() );
}
if ( wsegvalvRow.maxA.has_value() )
{
grpcValvData->set_max_a( wsegvalvRow.maxA.value() );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaWellPathDataToGrpcConverter::copyWsegaicdToGrpc( const RigMswTableData& mswTableData,
rips::SimulatorTableData* reply )
{
if ( !mswTableData.hasWsegaicdData() ) return;
for ( const auto& wsegaicdRow : mswTableData.wsegaicdData() )
{
auto* grpcAicdData = reply->add_wsegaicd();
grpcAicdData->set_well_name( wsegaicdRow.well );
grpcAicdData->set_segment_1( wsegaicdRow.segment1 );
grpcAicdData->set_segment_2( wsegaicdRow.segment2 );
grpcAicdData->set_strength( wsegaicdRow.strength );
grpcAicdData->set_max_abs_rate( wsegaicdRow.maxAbsRate );
grpcAicdData->set_flow_rate_exponent( wsegaicdRow.flowRateExponent );
grpcAicdData->set_visc_exponent( wsegaicdRow.viscExponent );
if ( wsegaicdRow.length.has_value() )
{
grpcAicdData->set_length( wsegaicdRow.length.value() );
}
if ( wsegaicdRow.densityCali.has_value() )
{
grpcAicdData->set_density_cali( wsegaicdRow.densityCali.value() );
}
if ( wsegaicdRow.viscosityCali.has_value() )
{
grpcAicdData->set_viscosity_cali( wsegaicdRow.viscosityCali.value() );
}
if ( wsegaicdRow.criticalValue.has_value() )
{
grpcAicdData->set_critical_value( wsegaicdRow.criticalValue.value() );
}
if ( wsegaicdRow.widthTrans.has_value() )
{
grpcAicdData->set_width_trans( wsegaicdRow.widthTrans.value() );
}
if ( wsegaicdRow.maxViscRatio.has_value() )
{
grpcAicdData->set_max_visc_ratio( wsegaicdRow.maxViscRatio.value() );
}
if ( wsegaicdRow.methodScalingFactor.has_value() )
{
grpcAicdData->set_method_scaling_factor( wsegaicdRow.methodScalingFactor.value() );
}
if ( wsegaicdRow.status.has_value() )
{
grpcAicdData->set_status( wsegaicdRow.status.value() );
}
if ( wsegaicdRow.oilFlowFraction.has_value() )
{
grpcAicdData->set_oil_flow_fraction( wsegaicdRow.oilFlowFraction.value() );
}
if ( wsegaicdRow.waterFlowFraction.has_value() )
{
grpcAicdData->set_water_flow_fraction( wsegaicdRow.waterFlowFraction.value() );
}
if ( wsegaicdRow.gasFlowFraction.has_value() )
{
grpcAicdData->set_gas_flow_fraction( wsegaicdRow.gasFlowFraction.value() );
}
if ( wsegaicdRow.oilViscFraction.has_value() )
{
grpcAicdData->set_oil_visc_fraction( wsegaicdRow.oilViscFraction.value() );
}
if ( wsegaicdRow.waterViscFraction.has_value() )
{
grpcAicdData->set_water_visc_fraction( wsegaicdRow.waterViscFraction.value() );
}
if ( wsegaicdRow.gasViscFraction.has_value() )
{
grpcAicdData->set_gas_visc_fraction( wsegaicdRow.gasViscFraction.value() );
}
if ( !wsegaicdRow.description.empty() )
{
grpcAicdData->set_description( wsegaicdRow.description );
}
}
}
@@ -0,0 +1,48 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "SimulatorTables.pb.h"
class RigCompletionData;
class RimWellPathCompletionSettings;
class RimEclipseCase;
class RigMswTableData;
//==================================================================================================
///
/// Helper class for converting well path data to gRPC protobuf messages
///
//==================================================================================================
class RiaWellPathDataToGrpcConverter
{
public:
static void copyCompdatToGrpc( const RigCompletionData& inputData, rips::SimulatorCompdatEntry* compDat );
static void copyWelspecsToGrpc( const RimWellPathCompletionSettings* compSettings,
rips::SimulatorWelspecsEntry* grpcData,
RimEclipseCase* eclipseCase,
int gridI,
int gridJ );
static void copyWelsegsToGrpc( const RigMswTableData& mswTableData, rips::SimulatorWelsegsEntry* grpcData );
static void copyCompsegsToGrpc( const RigMswTableData& mswTableData, rips::SimulatorTableData* reply );
static void copyWsegvalvToGrpc( const RigMswTableData& mswTableData, rips::SimulatorTableData* reply );
static void copyWsegaicdToGrpc( const RigMswTableData& mswTableData, rips::SimulatorTableData* reply );
};