diff --git a/ApplicationExeCode/Resources/Play.svg b/ApplicationExeCode/Resources/Play.svg
new file mode 100644
index 00000000000..1f701af18d4
--- /dev/null
+++ b/ApplicationExeCode/Resources/Play.svg
@@ -0,0 +1,8 @@
+
+
+
diff --git a/ApplicationExeCode/Resources/ResInsight.qrc b/ApplicationExeCode/Resources/ResInsight.qrc
index 7458e4a2eb5..5a827a61d2d 100644
--- a/ApplicationExeCode/Resources/ResInsight.qrc
+++ b/ApplicationExeCode/Resources/ResInsight.qrc
@@ -295,7 +295,9 @@
arrow-swap.svg
inspect.svg
pin.svg
- pinned.svg
+ Play.svg
+ gear_icon_16x16.png
+ pinned.svg
pinned-remove.svg
Select.svg
NavigationProperty.svg
diff --git a/ApplicationExeCode/Resources/gear_icon_16x16.png b/ApplicationExeCode/Resources/gear_icon_16x16.png
new file mode 100644
index 00000000000..a1cf07e94b0
Binary files /dev/null and b/ApplicationExeCode/Resources/gear_icon_16x16.png differ
diff --git a/ApplicationLibCode/Application/Tools/CMakeLists_files.cmake b/ApplicationLibCode/Application/Tools/CMakeLists_files.cmake
index e9eac644664..c01b30167e2 100644
--- a/ApplicationLibCode/Application/Tools/CMakeLists_files.cmake
+++ b/ApplicationLibCode/Application/Tools/CMakeLists_files.cmake
@@ -29,7 +29,6 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RiaCurveDataTools.h
${CMAKE_CURRENT_LIST_DIR}/RiaWellLogCurveMerger.h
${CMAKE_CURRENT_LIST_DIR}/RiaTimeHistoryCurveResampler.h
- ${CMAKE_CURRENT_LIST_DIR}/RiaStatisticsTools.h
${CMAKE_CURRENT_LIST_DIR}/RiaOffshoreSphericalCoords.h
${CMAKE_CURRENT_LIST_DIR}/RiaWeightedMeanCalculator.h
${CMAKE_CURRENT_LIST_DIR}/RiaMedianCalculator.h
@@ -91,7 +90,6 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RiaCurveDataTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RiaWellLogCurveMerger.cpp
${CMAKE_CURRENT_LIST_DIR}/RiaTimeHistoryCurveResampler.cpp
- ${CMAKE_CURRENT_LIST_DIR}/RiaStatisticsTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RiaWeightedGeometricMeanCalculator.cpp
${CMAKE_CURRENT_LIST_DIR}/RiaWeightedHarmonicMeanCalculator.cpp
${CMAKE_CURRENT_LIST_DIR}/RiaOptionItemFactory.cpp
diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/RicMswValveAccumulators.cpp b/ApplicationLibCode/Commands/CompletionExportCommands/RicMswValveAccumulators.cpp
index e34ced036e5..01fc03130ed 100644
--- a/ApplicationLibCode/Commands/CompletionExportCommands/RicMswValveAccumulators.cpp
+++ b/ApplicationLibCode/Commands/CompletionExportCommands/RicMswValveAccumulators.cpp
@@ -17,7 +17,7 @@
/////////////////////////////////////////////////////////////////////////////////
#include "RicMswValveAccumulators.h"
-#include "RiaStatisticsTools.h"
+#include "RigStatisticsTools.h"
#include "RicMswCompletions.h"
@@ -110,7 +110,7 @@ bool RicMswAICDAccumulator::accumulateValveParameters( const RimWellPathValve* w
std::array values = params->doubleValues();
for ( size_t i = 0; i < (size_t)AICD_NUM_PARAMS; ++i )
{
- if ( RiaStatisticsTools::isValidNumber( values[i] ) )
+ if ( RigStatisticsTools::isValidNumber( values[i] ) )
{
m_meanCalculators[i].addValueAndWeight( values[i], overlapLength );
}
diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.cpp b/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.cpp
index 9781bea6f7d..b95e4eeb28f 100644
--- a/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.cpp
+++ b/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.cpp
@@ -23,8 +23,8 @@
#include "RiaFractureDefines.h"
#include "RiaLogging.h"
#include "RiaPreferencesSystem.h"
-#include "RiaStatisticsTools.h"
#include "RiaWeightedMeanCalculator.h"
+#include "RigStatisticsTools.h"
#include "ExportCommands/RicExportLgrFeature.h"
#include "RicExportCompletionDataSettingsUi.h"
@@ -493,7 +493,7 @@ RigCompletionData RicWellPathExportCompletionDataFeatureImpl::combineEclipseCell
RiaWeightedMeanCalculator skinFactorCalculator;
auto isValidTransmissibility = []( double transmissibility )
- { return RiaStatisticsTools::isValidNumber( transmissibility ) && transmissibility >= 0.0; };
+ { return RigStatisticsTools::isValidNumber( transmissibility ) && transmissibility >= 0.0; };
auto startMD = completions[0].startMD();
auto endMD = completions[0].endMD();
diff --git a/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelFeature.cpp b/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelFeature.cpp
index 40b96f56727..39122635a03 100644
--- a/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelFeature.cpp
+++ b/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelFeature.cpp
@@ -357,9 +357,9 @@ cvf::ref
cvf::ref visibility = new cvf::UByteArray( totalCellCount );
visibility->setAll( false );
- for ( size_t activeCellIdx : activeReservoirCells )
+ for ( auto activeCellIdx : activeReservoirCells )
{
- visibility->set( activeCellIdx, true );
+ visibility->set( activeCellIdx.value(), true );
}
return visibility;
}
diff --git a/ApplicationLibCode/Commands/JobCommands/CMakeLists_files.cmake b/ApplicationLibCode/Commands/JobCommands/CMakeLists_files.cmake
index bc24e0c7e5e..69f434e612d 100644
--- a/ApplicationLibCode/Commands/JobCommands/CMakeLists_files.cmake
+++ b/ApplicationLibCode/Commands/JobCommands/CMakeLists_files.cmake
@@ -1,11 +1,13 @@
set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RicRunJobFeature.h
${CMAKE_CURRENT_LIST_DIR}/RicNewOpmFlowJobFeature.h
+ ${CMAKE_CURRENT_LIST_DIR}/RicDuplicateJobFeature.h
)
set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RicRunJobFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicNewOpmFlowJobFeature.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/RicDuplicateJobFeature.cpp
)
list(APPEND COMMAND_CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})
diff --git a/ApplicationLibCode/Commands/JobCommands/RicDuplicateJobFeature.cpp b/ApplicationLibCode/Commands/JobCommands/RicDuplicateJobFeature.cpp
new file mode 100644
index 00000000000..c75f2e004b1
--- /dev/null
+++ b/ApplicationLibCode/Commands/JobCommands/RicDuplicateJobFeature.cpp
@@ -0,0 +1,73 @@
+/////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+// for more details.
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+#include "RicDuplicateJobFeature.h"
+
+#include "RicNewOpmFlowJobFeature.h"
+
+#include "Jobs/RimJobCollection.h"
+#include "Jobs/RimOpmFlowJob.h"
+#include "RimTools.h"
+
+#include "Riu3DMainWindowTools.h"
+
+#include "cafSelectionManager.h"
+
+#include
+#include
+#include
+
+CAF_CMD_SOURCE_INIT( RicDuplicateJobFeature, "RicDuplicateJobFeature" );
+
+//--------------------------------------------------------------------------------------------------
+///
+//--------------------------------------------------------------------------------------------------
+void RicDuplicateJobFeature::onActionTriggered( bool isChecked )
+{
+ if ( auto job = dynamic_cast( caf::SelectionManager::instance()->selectedItem() ) )
+ {
+ QString defaultDir = job->mainWorkingDirectory();
+ QFileInfo fi( defaultDir );
+ defaultDir = fi.dir().absolutePath();
+
+ QString workDir = RicNewOpmFlowJobFeature::workingFolder( defaultDir );
+ if ( workDir.isEmpty() ) return;
+
+ if ( auto copiedJob = job->copyObject() )
+ {
+ copiedJob->setWorkingDirectory( workDir );
+ copiedJob->setName( job->name() + " (copy)" );
+
+ auto jobColl = RimTools::jobCollection();
+ jobColl->addNewJob( copiedJob );
+
+ copiedJob->resolveReferencesRecursively();
+
+ Riu3DMainWindowTools::selectAsCurrentItem( copiedJob );
+ }
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+///
+//--------------------------------------------------------------------------------------------------
+void RicDuplicateJobFeature::setupActionLook( QAction* actionToSetup )
+{
+ actionToSetup->setIcon( QIcon( ":/Copy.svg" ) );
+ actionToSetup->setText( "Duplicate..." );
+}
diff --git a/ApplicationLibCode/Commands/JobCommands/RicDuplicateJobFeature.h b/ApplicationLibCode/Commands/JobCommands/RicDuplicateJobFeature.h
new file mode 100644
index 00000000000..7701d71a04c
--- /dev/null
+++ b/ApplicationLibCode/Commands/JobCommands/RicDuplicateJobFeature.h
@@ -0,0 +1,33 @@
+/////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+// for more details.
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+#pragma once
+
+#include "cafCmdFeature.h"
+
+//==================================================================================================
+///
+//==================================================================================================
+class RicDuplicateJobFeature : public caf::CmdFeature
+{
+ CAF_CMD_HEADER_INIT;
+
+protected:
+ void onActionTriggered( bool isChecked ) override;
+ void setupActionLook( QAction* actionToSetup ) override;
+};
diff --git a/ApplicationLibCode/Commands/JobCommands/RicNewOpmFlowJobFeature.cpp b/ApplicationLibCode/Commands/JobCommands/RicNewOpmFlowJobFeature.cpp
index fc4cf17e24c..a012f603168 100644
--- a/ApplicationLibCode/Commands/JobCommands/RicNewOpmFlowJobFeature.cpp
+++ b/ApplicationLibCode/Commands/JobCommands/RicNewOpmFlowJobFeature.cpp
@@ -96,12 +96,13 @@ void RicNewOpmFlowJobFeature::setupActionLook( QAction* actionToSetup )
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
-QString RicNewOpmFlowJobFeature::workingFolder()
+QString RicNewOpmFlowJobFeature::workingFolder( QString defaultDir )
{
// get base directory for our work, should be a new, empty folder somewhere
const QString defaultDirName = "OPM_FLOW_MODELING";
- QString defaultDir = RiaApplication::instance()->lastUsedDialogDirectoryWithFallbackToProjectFolder( defaultDirName );
- QString baseDir =
+ if ( defaultDir.isEmpty() )
+ defaultDir = RiaApplication::instance()->lastUsedDialogDirectoryWithFallbackToProjectFolder( defaultDirName );
+ QString baseDir =
RiuFileDialogTools::getExistingDirectory( Riu3DMainWindowTools::mainWindowWidget(), "Select Simulation Output Directory", defaultDir );
if ( baseDir.isNull() || baseDir.isEmpty() ) return "";
RiaApplication::instance()->setLastUsedDialogDirectory( defaultDirName, baseDir );
diff --git a/ApplicationLibCode/Commands/JobCommands/RicNewOpmFlowJobFeature.h b/ApplicationLibCode/Commands/JobCommands/RicNewOpmFlowJobFeature.h
index d9a02166647..44962bb6f29 100644
--- a/ApplicationLibCode/Commands/JobCommands/RicNewOpmFlowJobFeature.h
+++ b/ApplicationLibCode/Commands/JobCommands/RicNewOpmFlowJobFeature.h
@@ -27,12 +27,12 @@ class RicNewOpmFlowJobFeature : public caf::CmdFeature
{
CAF_CMD_HEADER_INIT;
+public:
+ static QString workingFolder( QString defaultDir = "" );
+ static QString inputDataFile();
+
protected:
bool isCommandEnabled() const override;
void onActionTriggered( bool isChecked ) override;
void setupActionLook( QAction* actionToSetup ) override;
-
-private:
- static QString workingFolder();
- static QString inputDataFile();
};
diff --git a/ApplicationLibCode/Commands/JobCommands/RicRunJobFeature.cpp b/ApplicationLibCode/Commands/JobCommands/RicRunJobFeature.cpp
index 578a416a9d7..77f47227bba 100644
--- a/ApplicationLibCode/Commands/JobCommands/RicRunJobFeature.cpp
+++ b/ApplicationLibCode/Commands/JobCommands/RicRunJobFeature.cpp
@@ -39,8 +39,8 @@ void RicRunJobFeature::onActionTriggered( bool isChecked )
//--------------------------------------------------------------------------------------------------
void RicRunJobFeature::setupActionLook( QAction* actionToSetup )
{
- actionToSetup->setIcon( QIcon( ":/gear.png" ) );
- actionToSetup->setText( "Run Job..." );
+ actionToSetup->setIcon( QIcon( ":/Play.svg" ) );
+ actionToSetup->setText( "Run..." );
}
//--------------------------------------------------------------------------------------------------
diff --git a/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.cpp b/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.cpp
index c4b83ee9eb4..3f860363695 100644
--- a/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.cpp
+++ b/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.cpp
@@ -20,6 +20,8 @@
#include "RifOpmDeckTools.h"
+#include "RigEclipseResultTools.h"
+
#include "opm/common/utility/TimeService.hpp"
#include "opm/input/eclipse/Deck/Deck.hpp"
#include "opm/input/eclipse/Deck/FileDeck.hpp"
@@ -27,6 +29,7 @@
#include "opm/input/eclipse/Parser/InputErrorAction.hpp"
#include "opm/input/eclipse/Parser/ParseContext.hpp"
#include "opm/input/eclipse/Parser/Parser.hpp"
+#include "opm/input/eclipse/Parser/ParserKeywords/B.hpp"
#include "opm/input/eclipse/Parser/ParserKeywords/C.hpp"
#include "opm/input/eclipse/Parser/ParserKeywords/D.hpp"
#include "opm/input/eclipse/Parser/ParserKeywords/E.hpp"
@@ -37,6 +40,8 @@
#include "opm/input/eclipse/Parser/ParserKeywords/S.hpp"
#include "opm/input/eclipse/Parser/ParserKeywords/W.hpp"
+#include "cvfStructGrid.h"
+
#include
#include
#include
@@ -629,6 +634,7 @@ bool RifOpmFlowDeckFile::appendDateKeywords( const std::vector& dat
newRec.addItem( RifOpmDeckTools::item( Opm::ParserKeywords::DATES::DAY::itemName, lt->tm_mday ) );
newRec.addItem( RifOpmDeckTools::item( Opm::ParserKeywords::DATES::MONTH::itemName, month ) );
newRec.addItem( RifOpmDeckTools::item( Opm::ParserKeywords::DATES::YEAR::itemName, lt->tm_year + 1900 ) );
+ newRec.addItem( RifOpmDeckTools::defaultItem( Opm::ParserKeywords::DATES::TIME::itemName ) );
newKw.addRecord( std::move( newRec ) );
@@ -861,6 +867,132 @@ bool RifOpmFlowDeckFile::addOperaterKeyword( std::string section,
return true;
}
+//--------------------------------------------------------------------------------------------------
+///
+//--------------------------------------------------------------------------------------------------
+bool RifOpmFlowDeckFile::addBcconKeyword( std::string section, const std::vector& borderCellFaces )
+{
+ if ( m_fileDeck.get() == nullptr ) return false;
+
+ if ( borderCellFaces.empty() ) return true; // Nothing to add
+
+ // Find insertion point within the section
+ auto insertPos = internal::findSectionInsertionPoint( m_fileDeck, section );
+ if ( !insertPos.has_value() )
+ {
+ return false; // Section not found
+ }
+
+ // Helper lambda to convert FaceType to string
+ auto faceTypeToString = []( cvf::StructGridInterface::FaceType faceType ) -> std::string
+ {
+ switch ( faceType )
+ {
+ case cvf::StructGridInterface::POS_I:
+ return "X";
+ case cvf::StructGridInterface::NEG_I:
+ return "X-";
+ case cvf::StructGridInterface::POS_J:
+ return "Y";
+ case cvf::StructGridInterface::NEG_J:
+ return "Y-";
+ case cvf::StructGridInterface::POS_K:
+ return "Z";
+ case cvf::StructGridInterface::NEG_K:
+ return "Z-";
+ default:
+ return "";
+ }
+ };
+
+ // Create the BCCON keyword using OPM's BCCON parser keyword
+ using B = Opm::ParserKeywords::BCCON;
+
+ Opm::DeckKeyword bcconKw( ( Opm::ParserKeywords::BCCON() ) );
+
+ for ( const auto& borderFace : borderCellFaces )
+ {
+ // Convert from 0-based to 1-based Eclipse indexing
+ int i1 = static_cast( borderFace.ijk[0] ) + 1;
+ int j1 = static_cast( borderFace.ijk[1] ) + 1;
+ int k1 = static_cast( borderFace.ijk[2] ) + 1;
+
+ std::string faceStr = faceTypeToString( borderFace.faceType );
+
+ // Create items for the record using proper BCCON item names
+ std::vector recordItems;
+
+ recordItems.push_back( RifOpmDeckTools::item( B::INDEX::itemName, borderFace.boundaryCondition ) );
+ recordItems.push_back( RifOpmDeckTools::item( B::I1::itemName, i1 ) );
+ recordItems.push_back( RifOpmDeckTools::item( B::I2::itemName, i1 ) ); // Same as i1 for single cell
+ recordItems.push_back( RifOpmDeckTools::item( B::J1::itemName, j1 ) );
+ recordItems.push_back( RifOpmDeckTools::item( B::J2::itemName, j1 ) ); // Same as j1 for single cell
+ recordItems.push_back( RifOpmDeckTools::item( B::K1::itemName, k1 ) );
+ recordItems.push_back( RifOpmDeckTools::item( B::K2::itemName, k1 ) ); // Same as k1 for single cell
+ recordItems.push_back( RifOpmDeckTools::item( B::DIRECTION::itemName, faceStr ) );
+
+ bcconKw.addRecord( Opm::DeckRecord{ std::move( recordItems ) } );
+ }
+
+ m_fileDeck->insert( insertPos.value(), bcconKw );
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------------
+///
+//--------------------------------------------------------------------------------------------------
+bool RifOpmFlowDeckFile::addBcpropKeyword( std::string section,
+ const std::vector& boundaryConditions,
+ const std::vector& boundaryConditionProperties )
+{
+ if ( m_fileDeck.get() == nullptr ) return false;
+
+ if ( boundaryConditions.empty() ) return true; // Nothing to add
+
+ // Find insertion point within the section
+ auto insertPos = internal::findSectionInsertionPoint( m_fileDeck, section );
+ if ( !insertPos.has_value() )
+ {
+ return false; // Section not found
+ }
+
+ // Create the BCPROP keyword using OPM's BCPROP parser keyword
+ using B = Opm::ParserKeywords::BCPROP;
+
+ Opm::DeckKeyword bcpropKw( ( Opm::ParserKeywords::BCPROP() ) );
+
+ // Add one entry per boundary condition
+ for ( const auto& bc : boundaryConditions )
+ {
+ if ( bc.boundaryCondition <= 0 ) continue; // Skip entries without a valid boundary condition
+
+ // Find the corresponding property record
+ // The properties vector should be indexed by boundaryCondition - 1
+ size_t propIndex = static_cast( bc.boundaryCondition - 1 );
+ if ( propIndex < boundaryConditionProperties.size() )
+ {
+ const auto& propRecord = boundaryConditionProperties[propIndex];
+
+ // Create a new record with the boundary condition INDEX
+ std::vector recordItems;
+
+ // Add INDEX field
+ recordItems.push_back( RifOpmDeckTools::item( B::INDEX::itemName, bc.boundaryCondition ) );
+
+ // Copy all items from the property record (which doesn't include INDEX)
+ for ( size_t i = 0; i < propRecord.size(); ++i )
+ {
+ recordItems.push_back( propRecord.getItem( i ) );
+ }
+
+ bcpropKw.addRecord( Opm::DeckRecord{ std::move( recordItems ) } );
+ }
+ }
+
+ m_fileDeck->insert( insertPos.value(), bcpropKw );
+ return true;
+}
+
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
diff --git a/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.h b/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.h
index 29630a5f498..262edd44451 100644
--- a/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.h
+++ b/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.h
@@ -29,9 +29,16 @@ namespace Opm
{
class FileDeck;
class DeckKeyword;
+class DeckItem;
+class DeckRecord;
class ParseContext;
} // namespace Opm
+namespace RigEclipseResultTools
+{
+struct BorderCellFace;
+}
+
//==================================================================================================
///
///
@@ -81,6 +88,11 @@ class RifOpmFlowDeckFile
std::optional alpha,
std::optional beta );
+ bool addBcconKeyword( std::string section, const std::vector& borderCellFaces );
+ bool addBcpropKeyword( std::string section,
+ const std::vector& boundaryConditions,
+ const std::vector& boundaryConditionProperties );
+
private:
void splitDatesIfNecessary();
diff --git a/ApplicationLibCode/GeoMech/GeoMechDataModel/CMakeLists.txt b/ApplicationLibCode/GeoMech/GeoMechDataModel/CMakeLists.txt
index aac48d14e59..b265192075f 100644
--- a/ApplicationLibCode/GeoMech/GeoMechDataModel/CMakeLists.txt
+++ b/ApplicationLibCode/GeoMech/GeoMechDataModel/CMakeLists.txt
@@ -113,7 +113,13 @@ add_library(
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(
- RigGeoMechDataModel PUBLIC LibCore cafPdmCvf cafTensor cafUserInterface
- CommonCode ResultStatisticsCache
+ RigGeoMechDataModel
+ PUBLIC LibCore
+ cafPdmCvf
+ cafTensor
+ cafUserInterface
+ CommonCode
+ ResultStatisticsCache
+ ApplicationLibCode
)
target_link_libraries(RigGeoMechDataModel PRIVATE ResInsightCommonSettings)
diff --git a/ApplicationLibCode/ProjectDataModel/Completions/RimFishbones.cpp b/ApplicationLibCode/ProjectDataModel/Completions/RimFishbones.cpp
index 240a0611dde..b85fa32b9ec 100644
--- a/ApplicationLibCode/ProjectDataModel/Completions/RimFishbones.cpp
+++ b/ApplicationLibCode/ProjectDataModel/Completions/RimFishbones.cpp
@@ -576,6 +576,8 @@ void RimFishbones::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& u
m_lateralOpenHoleRoghnessFactor.uiCapability()->setUiName( "Open Hole Roughness Factor [m]" );
m_lateralTubingRoghnessFactor.uiCapability()->setUiName( "Tubing Roughness Factor [m]" );
+ m_lateralDiameter.uiCapability()->setUiName( "Lateral Diameter [mm]" );
+
m_icdOrificeDiameter.uiCapability()->setUiName( "ICD Orifice Diameter [mm]" );
}
else if ( wellPath->unitSystem() == RiaDefines::EclipseUnitSystem::UNITS_FIELD )
@@ -586,6 +588,8 @@ void RimFishbones::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& u
m_lateralOpenHoleRoghnessFactor.uiCapability()->setUiName( "Open Hole Roughness Factor [ft]" );
m_lateralTubingRoghnessFactor.uiCapability()->setUiName( "Tubing Roughness Factor [ft]" );
+ m_lateralDiameter.uiCapability()->setUiName( "Lateral Diameter [in]" );
+
m_icdOrificeDiameter.uiCapability()->setUiName( "ICD Orifice Diameter [in]" );
}
}
diff --git a/ApplicationLibCode/ProjectDataModel/Completions/RimFishbonesCollection.cpp b/ApplicationLibCode/ProjectDataModel/Completions/RimFishbonesCollection.cpp
index b57c96f2826..7247d9e0e90 100644
--- a/ApplicationLibCode/ProjectDataModel/Completions/RimFishbonesCollection.cpp
+++ b/ApplicationLibCode/ProjectDataModel/Completions/RimFishbonesCollection.cpp
@@ -160,8 +160,9 @@ RimFishbones* RimFishbonesCollection::appendFishbonesSubsAtLocations( const std:
auto* obj = new RimFishbones;
obj->setValveLocations( subLocations );
- RimFishbonesDefines::RicFishbonesSystemParameters customParameters = RimFishbonesDefines::drillingStandardParameters();
+ appendFishbonesSubs( obj );
+ RimFishbonesDefines::RicFishbonesSystemParameters customParameters = RimFishbonesDefines::drillingStandardParameters();
if ( drillingType == RimFishbonesDefines::DrillingType::EXTENDED )
{
customParameters = RimFishbonesDefines::drillingExtendedParameters();
@@ -171,14 +172,13 @@ RimFishbones* RimFishbonesCollection::appendFishbonesSubsAtLocations( const std:
customParameters = RimFishbonesDefines::acidJettingParameters();
}
+ // NB: Setting the system parameters must be done after appendFishbonesSubs, as the latter sets some default values
obj->setSystemParameters( customParameters.lateralsPerSub,
customParameters.lateralLength,
customParameters.holeDiameter,
customParameters.buildAngle,
customParameters.icdsPerSub );
- appendFishbonesSubs( obj );
-
return obj;
}
diff --git a/ApplicationLibCode/ProjectDataModel/ContourMap/RimStatisticsContourMap.cpp b/ApplicationLibCode/ProjectDataModel/ContourMap/RimStatisticsContourMap.cpp
index 1f34cd75504..1b486a44188 100644
--- a/ApplicationLibCode/ProjectDataModel/ContourMap/RimStatisticsContourMap.cpp
+++ b/ApplicationLibCode/ProjectDataModel/ContourMap/RimStatisticsContourMap.cpp
@@ -20,7 +20,7 @@
#include "RiaLogging.h"
#include "RiaPreferencesGrid.h"
-#include "RiaStatisticsTools.h"
+#include "RigStatisticsTools.h"
#include "RicNewStatisticsContourMapViewFeature.h"
@@ -469,16 +469,16 @@ void RimStatisticsContourMap::doStatisticsCalculation( std::map::max() ) minResults[i] = minValue;
+ double minValue = RigStatisticsTools::minimumValue( samples );
+ if ( RigStatisticsTools::isValidNumber( minValue ) && minValue < std::numeric_limits::max() ) minResults[i] = minValue;
- double maxValue = RiaStatisticsTools::maximumValue( samples );
- if ( RiaStatisticsTools::isValidNumber( maxValue ) && maxValue > -std::numeric_limits::max() ) maxResults[i] = maxValue;
+ double maxValue = RigStatisticsTools::maximumValue( samples );
+ if ( RigStatisticsTools::isValidNumber( maxValue ) && maxValue > -std::numeric_limits::max() ) maxResults[i] = maxValue;
}
m_timeResults[timeStep][StatisticsType::P10] = p10Results;
diff --git a/ApplicationLibCode/ProjectDataModel/CorrelationPlots/RimCorrelationMatrixPlot.cpp b/ApplicationLibCode/ProjectDataModel/CorrelationPlots/RimCorrelationMatrixPlot.cpp
index d072df50027..7fd2c6b0895 100644
--- a/ApplicationLibCode/ProjectDataModel/CorrelationPlots/RimCorrelationMatrixPlot.cpp
+++ b/ApplicationLibCode/ProjectDataModel/CorrelationPlots/RimCorrelationMatrixPlot.cpp
@@ -21,7 +21,7 @@
#include "RiaColorTools.h"
#include "RiaPreferences.h"
#include "RiaQDateTimeTools.h"
-#include "RiaStatisticsTools.h"
+#include "RigStatisticsTools.h"
#include "Summary/RiaSummaryCurveDefinition.h"
#include "RifCsvDataTableFormatter.h"
@@ -560,7 +560,7 @@ void RimCorrelationMatrixPlot::createMatrix()
if ( parameterValues.empty() ) continue;
- correlation = RiaStatisticsTools::pearsonCorrelation( parameterValues, caseValuesAtTimestep );
+ correlation = RigStatisticsTools::pearsonCorrelation( parameterValues, caseValuesAtTimestep );
bool validResult = RiaCurveDataTools::isValidValue( correlation, false );
if ( validResult )
diff --git a/ApplicationLibCode/ProjectDataModel/Jobs/RimGenericJob.cpp b/ApplicationLibCode/ProjectDataModel/Jobs/RimGenericJob.cpp
index 58451f68be4..62188afb890 100644
--- a/ApplicationLibCode/ProjectDataModel/Jobs/RimGenericJob.cpp
+++ b/ApplicationLibCode/ProjectDataModel/Jobs/RimGenericJob.cpp
@@ -48,6 +48,7 @@ RimGenericJob::~RimGenericJob()
void RimGenericJob::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
{
menuBuilder << "RicRunJobFeature";
+ menuBuilder << "RicDuplicateJobFeature";
}
//--------------------------------------------------------------------------------------------------
diff --git a/ApplicationLibCode/ProjectDataModel/Jobs/RimJobCollection.cpp b/ApplicationLibCode/ProjectDataModel/Jobs/RimJobCollection.cpp
index 9b01a6c4821..9208d9dd42f 100644
--- a/ApplicationLibCode/ProjectDataModel/Jobs/RimJobCollection.cpp
+++ b/ApplicationLibCode/ProjectDataModel/Jobs/RimJobCollection.cpp
@@ -34,7 +34,7 @@ CAF_PDM_SOURCE_INIT( RimJobCollection, "JobCollection" );
//--------------------------------------------------------------------------------------------------
RimJobCollection::RimJobCollection()
{
- CAF_PDM_InitObject( "Jobs" + RiaDefines::betaFeaturePostfix(), ":/gear.svg" );
+ CAF_PDM_InitObject( "Jobs" + RiaDefines::betaFeaturePostfix(), ":/gear_icon_16x16.png" );
CAF_PDM_InitFieldNoDefault( &m_jobs, "Jobs", "Jobs" );
diff --git a/ApplicationLibCode/ProjectDataModel/Jobs/RimOpmFlowJob.cpp b/ApplicationLibCode/ProjectDataModel/Jobs/RimOpmFlowJob.cpp
index c626959017c..9a56129473d 100644
--- a/ApplicationLibCode/ProjectDataModel/Jobs/RimOpmFlowJob.cpp
+++ b/ApplicationLibCode/ProjectDataModel/Jobs/RimOpmFlowJob.cpp
@@ -482,6 +482,14 @@ void RimOpmFlowJob::setWorkingDirectory( QString workDir )
m_workDir = workDir;
}
+//--------------------------------------------------------------------------------------------------
+///
+//--------------------------------------------------------------------------------------------------
+QString RimOpmFlowJob::mainWorkingDirectory() const
+{
+ return m_workDir().path();
+}
+
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
diff --git a/ApplicationLibCode/ProjectDataModel/Jobs/RimOpmFlowJob.h b/ApplicationLibCode/ProjectDataModel/Jobs/RimOpmFlowJob.h
index 27f05ccb8ac..4905fe85730 100644
--- a/ApplicationLibCode/ProjectDataModel/Jobs/RimOpmFlowJob.h
+++ b/ApplicationLibCode/ProjectDataModel/Jobs/RimOpmFlowJob.h
@@ -62,6 +62,7 @@ class RimOpmFlowJob : public RimGenericJob
void setInputDataFile( QString filename );
QString deckName();
+ QString mainWorkingDirectory() const;
protected:
void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override;
diff --git a/ApplicationLibCode/ProjectDataModel/RimGridCalculation.cpp b/ApplicationLibCode/ProjectDataModel/RimGridCalculation.cpp
index ea91d24e0c9..1570b40b815 100644
--- a/ApplicationLibCode/ProjectDataModel/RimGridCalculation.cpp
+++ b/ApplicationLibCode/ProjectDataModel/RimGridCalculation.cpp
@@ -665,7 +665,7 @@ std::vector RimGridCalculation::getActiveCellValues( const QString&
#pragma omp parallel for
for ( int i = 0; i < static_cast( activeReservoirCells.size() ); i++ )
{
- values[i] = resultAccessor->cellScalarGlobIdx( activeReservoirCells[i] );
+ values[i] = resultAccessor->cellScalarGlobIdx( activeReservoirCells[i].value() );
}
if ( m_releaseMemoryAfterDataIsExtracted )
@@ -698,7 +698,7 @@ void RimGridCalculation::replaceFilteredValuesWithVector( const std::vectorval( reservoirCellIndex ) )
+ if ( !visibility->val( reservoirCellIndex.value() ) )
{
resultValues[i] = inputValues[i];
}
@@ -723,7 +723,7 @@ void RimGridCalculation::replaceFilteredValuesWithDefaultValue( double
for ( int i = 0; i < numActiveCells; i++ )
{
const auto reservoirCellIndex = activeReservoirCellIndices[i];
- if ( !visibility->val( reservoirCellIndex ) )
+ if ( !visibility->val( reservoirCellIndex.value() ) )
{
resultValues[i] = defaultValue;
}
diff --git a/ApplicationLibCode/ProjectDataModel/RimWellTargetMapping.cpp b/ApplicationLibCode/ProjectDataModel/RimWellTargetMapping.cpp
index 69b4d90b403..b8d5364a47a 100644
--- a/ApplicationLibCode/ProjectDataModel/RimWellTargetMapping.cpp
+++ b/ApplicationLibCode/ProjectDataModel/RimWellTargetMapping.cpp
@@ -588,7 +588,7 @@ std::vector RimWellTargetMapping::getVisibilityFilter() const
for ( int i = 0; i < numActiveCells; i++ )
{
const auto reservoirCellIndex = activeReservoirCellIndices[i];
- filter[i] = visibility->val( reservoirCellIndex ) ? 1.0 : 0.0;
+ filter[i] = visibility->val( reservoirCellIndex.value() ) ? 1.0 : 0.0;
}
}
}
diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimFileSummaryCase.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimFileSummaryCase.cpp
index e8b71f907db..a55fe7945f8 100644
--- a/ApplicationLibCode/ProjectDataModel/Summary/RimFileSummaryCase.cpp
+++ b/ApplicationLibCode/ProjectDataModel/Summary/RimFileSummaryCase.cpp
@@ -172,29 +172,41 @@ std::unique_ptr RimFileSummaryCase::findRelatedFilesA
auto startTime = RiaLogging::currentTime();
std::vector warnings;
- std::vector restartFileNames;
- if ( RiaPreferencesSummary::current()->summaryDataReader() == RiaPreferencesSummary::SummaryReaderMode::OPM_COMMON )
+
+ auto findFileCandidates = [&ensembleImportState, &headerFileName, &warnings] -> std::vector
{
- if ( ensembleImportState.useConfigValues() )
+ if ( RiaPreferencesSummary::current()->summaryDataReader() == RiaPreferencesSummary::SummaryReaderMode::OPM_COMMON )
{
- auto realizationNumber = RifOpmSummaryTools::extractRealizationNumber( headerFileName );
- if ( !realizationNumber.has_value() )
+ if ( ensembleImportState.useConfigValues() )
{
- RiaLogging::error( realizationNumber.error() );
- return nullptr;
+ auto realizationNumber = RifOpmSummaryTools::extractRealizationNumber( headerFileName );
+ if ( !realizationNumber.has_value() )
+ {
+ RiaLogging::error( realizationNumber.error() );
+ return {};
+ }
+ return ensembleImportState.restartFilesForRealization( realizationNumber.value() );
+ }
+ else
+ {
+ // If the restart file names are not provided, we search for them
+ return RifEclipseSummaryTools::getRestartFileNamesOpm( headerFileName, warnings );
}
-
- restartFileNames = ensembleImportState.restartFilesForRealization( realizationNumber.value() );
}
else
{
- // If the restart file names are not provided, we search for them
- restartFileNames = RifEclipseSummaryTools::getRestartFileNamesOpm( headerFileName, warnings );
+ return RifEclipseSummaryTools::getRestartFileNames( headerFileName, warnings );
}
- }
- else
+ };
+
+ std::vector restartFileNames;
+ for ( const auto& fileName : findFileCandidates() )
{
- restartFileNames = RifEclipseSummaryTools::getRestartFileNames( headerFileName, warnings );
+ QFileInfo fi( fileName );
+ if ( fi.exists() )
+ {
+ restartFileNames.push_back( fileName );
+ }
}
bool isLoggingEnabled = RiaPreferencesSystem::current()->isLoggingActivatedForKeyword( "OpmSummaryImport" );
diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryEnsemble.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryEnsemble.cpp
index ad6ec4f7b3f..92561b41962 100644
--- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryEnsemble.cpp
+++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryEnsemble.cpp
@@ -22,9 +22,9 @@
#include "RiaFieldHandleTools.h"
#include "RiaFilePathTools.h"
#include "RiaLogging.h"
-#include "RiaStatisticsTools.h"
#include "RiaStdStringTools.h"
#include "RiaTextStringTools.h"
+#include "RigStatisticsTools.h"
#include "Summary/Ensemble/RimSummaryEnsembleParameterCollection.h"
#include "Summary/RiaSummaryAddressAnalyzer.h"
#include "Summary/RiaSummaryTools.h"
@@ -571,7 +571,7 @@ std::vector>
for ( auto parameterValuesPair : parameterValues )
{
double correlation = 0.0;
- double pearson = RiaStatisticsTools::pearsonCorrelation( parameterValuesPair.second, caseValuesAtTimestep );
+ double pearson = RigStatisticsTools::pearsonCorrelation( parameterValuesPair.second, caseValuesAtTimestep );
if ( pearson != std::numeric_limits::infinity() ) correlation = pearson;
correlationResults.push_back( std::make_pair( parameterValuesPair.first, correlation ) );
}
diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.cpp
index e4b03743552..b70f1e3280b 100644
--- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.cpp
+++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.cpp
@@ -21,8 +21,8 @@
#include "RiaLogging.h"
#include "RiaQDateTimeTools.h"
#include "RiaRegressionTextTools.h"
-#include "RiaStatisticsTools.h"
#include "RiaTimeTTools.h"
+#include "RigStatisticsTools.h"
#include "RimEnsembleCurveSet.h"
#include "RimEnsembleCurveSetCollection.h"
@@ -203,7 +203,7 @@ void RimSummaryRegressionAnalysisCurve::onLoadDataAndUpdate( bool updateParentPl
{
for ( size_t i = 0; i < xValues.size(); i++ )
{
- if ( xValues[i] < m_valueRangeX().first || xValues[i] > m_valueRangeX().second || !RiaStatisticsTools::isValidNumber( xValues[i] ) )
+ if ( xValues[i] < m_valueRangeX().first || xValues[i] > m_valueRangeX().second || !RigStatisticsTools::isValidNumber( xValues[i] ) )
{
indicesToRemove.push_back( i );
}
@@ -211,7 +211,7 @@ void RimSummaryRegressionAnalysisCurve::onLoadDataAndUpdate( bool updateParentPl
for ( size_t i = 0; i < yValues.size(); i++ )
{
- if ( yValues[i] < m_valueRangeY().first || yValues[i] > m_valueRangeY().second || !RiaStatisticsTools::isValidNumber( yValues[i] ) )
+ if ( yValues[i] < m_valueRangeY().first || yValues[i] > m_valueRangeY().second || !RigStatisticsTools::isValidNumber( yValues[i] ) )
{
indicesToRemove.push_back( i );
}
diff --git a/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogRftCurve.cpp b/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogRftCurve.cpp
index d8eda4ef483..163ab1a78cc 100644
--- a/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogRftCurve.cpp
+++ b/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogRftCurve.cpp
@@ -27,8 +27,8 @@
#include "RiaResultNames.h"
#include "RiaRftDefines.h"
#include "RiaSimWellBranchTools.h"
-#include "RiaStatisticsTools.h"
#include "RiaTextStringTools.h"
+#include "RigStatisticsTools.h"
#include "Summary/RiaSummaryTools.h"
#include "RifEclipseRftAddress.h"
@@ -670,7 +670,7 @@ void RimWellLogRftCurve::onLoadDataAndUpdate( bool updateParentPlot )
{
for ( const auto& v : values )
{
- if ( RiaStatisticsTools::isValidNumber( v ) ) return true;
+ if ( RigStatisticsTools::isValidNumber( v ) ) return true;
}
return false;
};
diff --git a/ApplicationLibCode/ProjectDataModelCommands/CMakeLists_files.cmake b/ApplicationLibCode/ProjectDataModelCommands/CMakeLists_files.cmake
index 0e1c8389c54..20b230684dc 100644
--- a/ApplicationLibCode/ProjectDataModelCommands/CMakeLists_files.cmake
+++ b/ApplicationLibCode/ProjectDataModelCommands/CMakeLists_files.cmake
@@ -26,6 +26,7 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RimcIntersection.h
${CMAKE_CURRENT_LIST_DIR}/RimcEclipseCase.h
${CMAKE_CURRENT_LIST_DIR}/RimcEclipseStatisticsCase.h
+ ${CMAKE_CURRENT_LIST_DIR}/RimcGridView.h
${CMAKE_CURRENT_LIST_DIR}/RimcIdenticalGridCaseGroup.h
${CMAKE_CURRENT_LIST_DIR}/RimcPressureTable.h
${CMAKE_CURRENT_LIST_DIR}/RimcFishbonesCollection.h
@@ -64,6 +65,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RimcIntersection.cpp
${CMAKE_CURRENT_LIST_DIR}/RimcEclipseCase.cpp
${CMAKE_CURRENT_LIST_DIR}/RimcEclipseStatisticsCase.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/RimcGridView.cpp
${CMAKE_CURRENT_LIST_DIR}/RimcIdenticalGridCaseGroup.cpp
${CMAKE_CURRENT_LIST_DIR}/RimcPressureTable.cpp
${CMAKE_CURRENT_LIST_DIR}/RimcFishbonesCollection.cpp
diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcGridView.cpp b/ApplicationLibCode/ProjectDataModelCommands/RimcGridView.cpp
new file mode 100644
index 00000000000..e42ddad5e66
--- /dev/null
+++ b/ApplicationLibCode/ProjectDataModelCommands/RimcGridView.cpp
@@ -0,0 +1,82 @@
+/////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+// for more details.
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+#include "RimcGridView.h"
+
+#include "RiaApplication.h"
+#include "RiaKeyValueStoreUtil.h"
+
+#include "RimEclipseView.h"
+#include "RimGridView.h"
+
+#include "cafPdmFieldScriptingCapability.h"
+
+#include "cvfArray.h"
+
+CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimEclipseView, RimcGridView_visibleCellsInternal, "visible_cells_internal" );
+
+//--------------------------------------------------------------------------------------------------
+///
+//--------------------------------------------------------------------------------------------------
+RimcGridView_visibleCellsInternal::RimcGridView_visibleCellsInternal( caf::PdmObjectHandle* self )
+ : caf::PdmVoidObjectMethod( self )
+{
+ CAF_PDM_InitObject( "Visible Cells Internal", "", "", "Visible Cells Internal" );
+
+ CAF_PDM_InitScriptableFieldNoDefault( &m_visibilityKey, "VisibilityKey", "" );
+ CAF_PDM_InitScriptableFieldNoDefault( &m_timeStep, "TimeStep", "" );
+}
+
+//--------------------------------------------------------------------------------------------------
+///
+//--------------------------------------------------------------------------------------------------
+std::expected RimcGridView_visibleCellsInternal::execute()
+{
+ auto gridView = self();
+ if ( !gridView )
+ {
+ return std::unexpected( QString( "Grid view is null." ) );
+ }
+
+ if ( m_visibilityKey().isEmpty() )
+ {
+ return std::unexpected( QString( "Visibility key is empty." ) );
+ }
+
+ // Get the cell visibility array
+ cvf::ref visibleCells = gridView->currentTotalCellVisibility();
+
+ if ( visibleCells.isNull() )
+ {
+ return std::unexpected( QString( "Failed to get cell visibility data." ) );
+ }
+
+ // Convert cvf::UByteArray to std::vector where 1.0 = visible, 0.0 = invisible
+ std::vector visibilityValues;
+ visibilityValues.reserve( visibleCells->size() );
+ for ( size_t i = 0; i < visibleCells->size(); ++i )
+ {
+ visibilityValues.push_back( ( *visibleCells )[i] ? 1.0f : 0.0f );
+ }
+
+ // Store the visibility data in the key-value store
+ auto keyValueStore = RiaApplication::instance()->keyValueStore();
+ keyValueStore->set( m_visibilityKey().toStdString(), RiaKeyValueStoreUtil::convertToByteVector( visibilityValues ) );
+
+ return nullptr;
+}
diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcGridView.h b/ApplicationLibCode/ProjectDataModelCommands/RimcGridView.h
new file mode 100644
index 00000000000..82c6d4e2e34
--- /dev/null
+++ b/ApplicationLibCode/ProjectDataModelCommands/RimcGridView.h
@@ -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
+// for more details.
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+#pragma once
+
+#include "cafPdmField.h"
+#include "cafPdmObjectHandle.h"
+#include "cafPdmObjectMethod.h"
+
+#include
+
+//==================================================================================================
+///
+//==================================================================================================
+class RimcGridView_visibleCellsInternal : public caf::PdmVoidObjectMethod
+{
+ CAF_PDM_HEADER_INIT;
+
+public:
+ RimcGridView_visibleCellsInternal( caf::PdmObjectHandle* self );
+
+ std::expected execute() override;
+
+private:
+ caf::PdmField m_visibilityKey;
+ caf::PdmField m_timeStep;
+};
diff --git a/ApplicationLibCode/ReservoirDataModel/CMakeLists_files.cmake b/ApplicationLibCode/ReservoirDataModel/CMakeLists_files.cmake
index 091a9d30b3a..5f0e5f46c63 100644
--- a/ApplicationLibCode/ReservoirDataModel/CMakeLists_files.cmake
+++ b/ApplicationLibCode/ReservoirDataModel/CMakeLists_files.cmake
@@ -4,6 +4,7 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RigActiveCellGrid.h
${CMAKE_CURRENT_LIST_DIR}/RigActiveCellInfo.h
${CMAKE_CURRENT_LIST_DIR}/RigActiveCellLocalGrid.h
+ ${CMAKE_CURRENT_LIST_DIR}/RigTypeSafeIndex.h
${CMAKE_CURRENT_LIST_DIR}/RigAllanDiagramData.h
${CMAKE_CURRENT_LIST_DIR}/RigBasicPlane.h
${CMAKE_CURRENT_LIST_DIR}/RigCaseCellResultCalculator.h
diff --git a/ApplicationLibCode/ReservoirDataModel/ContourMap/RigContourMapProjection.cpp b/ApplicationLibCode/ReservoirDataModel/ContourMap/RigContourMapProjection.cpp
index af6c4a0b685..6f5dde8e00c 100644
--- a/ApplicationLibCode/ReservoirDataModel/ContourMap/RigContourMapProjection.cpp
+++ b/ApplicationLibCode/ReservoirDataModel/ContourMap/RigContourMapProjection.cpp
@@ -18,8 +18,8 @@
#include "RigContourMapProjection.h"
-#include "RiaStatisticsTools.h"
#include "RiaWeightedMeanCalculator.h"
+#include "RigStatisticsTools.h"
#include "RigContourMapCalculator.h"
#include "RigContourMapGrid.h"
@@ -231,7 +231,7 @@ double RigContourMapProjection::calculateValueInMapCell( unsigned int
//--------------------------------------------------------------------------------------------------
double RigContourMapProjection::maxValue( const std::vector& aggregatedResults )
{
- double maxV = RiaStatisticsTools::maximumValue( aggregatedResults );
+ double maxV = RigStatisticsTools::maximumValue( aggregatedResults );
return maxV != -std::numeric_limits::max() ? maxV : -std::numeric_limits::infinity();
}
@@ -240,7 +240,7 @@ double RigContourMapProjection::maxValue( const std::vector& aggregatedR
//--------------------------------------------------------------------------------------------------
double RigContourMapProjection::minValue( const std::vector& aggregatedResults )
{
- double minV = RiaStatisticsTools::minimumValue( aggregatedResults );
+ double minV = RigStatisticsTools::minimumValue( aggregatedResults );
return minV != std::numeric_limits::max() ? minV : std::numeric_limits::infinity();
}
diff --git a/ApplicationLibCode/ReservoirDataModel/ResultAccessors/RigResultAccessorFactory.cpp b/ApplicationLibCode/ReservoirDataModel/ResultAccessors/RigResultAccessorFactory.cpp
index c945ad63953..ea5e04f0eb5 100644
--- a/ApplicationLibCode/ReservoirDataModel/ResultAccessors/RigResultAccessorFactory.cpp
+++ b/ApplicationLibCode/ReservoirDataModel/ResultAccessors/RigResultAccessorFactory.cpp
@@ -371,9 +371,26 @@ cvf::ref RigResultAccessorFactory::createNativeFromResultAddr
bool useGlobalActiveIndex = eclipseCase->results( porosityModel )->isUsingGlobalActiveIndex( resultAddress );
if ( eclipseCase->mainGrid()->gridCount() > 1 )
{
- // Always use active cell indexing for multi-grid cases. If all cells in the main grid is active, and a local grid is created, this
- // option must be set to true
- useGlobalActiveIndex = true;
+ bool isRadialTempGrid = [&eclipseCase]() -> bool
+ {
+ for ( size_t gIdx = 0; gIdx < eclipseCase->gridCount(); gIdx++ )
+ {
+ auto currentGrid = eclipseCase->grid( gIdx );
+ if ( currentGrid && currentGrid->isRadial() && currentGrid->isTempGrid() )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }();
+
+ if ( isRadialTempGrid )
+ {
+ // Always use active cell indexing for temporary LGRs of radial multi-grids. If all cells in the main grid is active, and a
+ // local grid is created, this option must be set to true
+ useGlobalActiveIndex = true;
+ }
}
if ( useGlobalActiveIndex )
{
diff --git a/ApplicationLibCode/ReservoirDataModel/ResultCalculators/RigFaultDistanceResultCalculator.cpp b/ApplicationLibCode/ReservoirDataModel/ResultCalculators/RigFaultDistanceResultCalculator.cpp
index 603e4de4c86..fbceff65910 100644
--- a/ApplicationLibCode/ReservoirDataModel/ResultCalculators/RigFaultDistanceResultCalculator.cpp
+++ b/ApplicationLibCode/ReservoirDataModel/ResultCalculators/RigFaultDistanceResultCalculator.cpp
@@ -87,11 +87,11 @@ void RigFaultDistanceResultCalculator::calculate( const RigEclipseResultAddress&
const auto activeCells = m_resultsData->activeCellInfo()->activeReservoirCellIndices();
for ( auto cellIdx : activeCells )
{
- const RigCell& cell = mainGrid->cell( cellIdx );
+ const RigCell& cell = mainGrid->cell( cellIdx.value() );
if ( cell.isInvalid() ) continue;
for ( auto faceType : faceTypes )
{
- if ( m_resultsData->m_ownerMainGrid->findFaultFromCellIndexAndCellFace( cellIdx, faceType ) )
+ if ( m_resultsData->m_ownerMainGrid->findFaultFromCellIndexAndCellFace( cellIdx.value(), faceType ) )
faultFaceCenters.push_back( cell.faceCenter( faceType ) );
}
}
@@ -122,9 +122,9 @@ void RigFaultDistanceResultCalculator::calculate( const RigEclipseResultAddress&
for ( int activeIndex = 0; activeIndex < static_cast( activeCells.size() ); activeIndex++ )
{
auto cellIdx = activeCells[activeIndex];
- if ( cellIdx == cvf::UNDEFINED_SIZE_T ) continue;
+ if ( cellIdx.value() == cvf::UNDEFINED_SIZE_T ) continue;
- const RigCell& cell = mainGrid->cell( cellIdx );
+ const RigCell& cell = mainGrid->cell( cellIdx.value() );
if ( cell.isInvalid() ) continue;
std::vector candidateFaceIndices;
diff --git a/ApplicationLibCode/ReservoirDataModel/ResultCalculators/RigIndexIjkResultCalculator.cpp b/ApplicationLibCode/ReservoirDataModel/ResultCalculators/RigIndexIjkResultCalculator.cpp
index 55a7d0d8dc8..46bce40eacb 100644
--- a/ApplicationLibCode/ReservoirDataModel/ResultCalculators/RigIndexIjkResultCalculator.cpp
+++ b/ApplicationLibCode/ReservoirDataModel/ResultCalculators/RigIndexIjkResultCalculator.cpp
@@ -106,9 +106,9 @@ void RigIndexIjkResultCalculator::calculate( const RigEclipseResultAddress& resV
for ( int activeIndex = 0; activeIndex < static_cast( activeReservoirCellIndices.size() ); activeIndex++ )
{
auto cellIdx = activeReservoirCellIndices[activeIndex];
- if ( cellIdx == cvf::UNDEFINED_SIZE_T ) continue;
+ if ( cellIdx.value() == cvf::UNDEFINED_SIZE_T ) continue;
- const RigCell& cell = mainGrid->cell( cellIdx );
+ const RigCell& cell = mainGrid->cell( cellIdx.value() );
if ( cell.isInvalid() ) continue;
bool isTemporaryGrid = cell.hostGrid()->isTempGrid();
diff --git a/ApplicationLibCode/ReservoirDataModel/RigActiveCellInfo.cpp b/ApplicationLibCode/ReservoirDataModel/RigActiveCellInfo.cpp
index 92fd87fe975..015324df2da 100644
--- a/ApplicationLibCode/ReservoirDataModel/RigActiveCellInfo.cpp
+++ b/ApplicationLibCode/ReservoirDataModel/RigActiveCellInfo.cpp
@@ -84,7 +84,7 @@ void RigActiveCellInfo::setCellResultIndex( size_t reservoirCellIndex, size_t re
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
-std::vector RigActiveCellInfo::activeReservoirCellIndices() const
+std::vector RigActiveCellInfo::activeReservoirCellIndices() const
{
return m_activeCellIndices;
}
@@ -123,7 +123,7 @@ void RigActiveCellInfo::computeDerivedData()
{
if ( m_cellIndexToResultIndex[i] != cvf::UNDEFINED_SIZE_T )
{
- m_activeCellIndices.push_back( i );
+ m_activeCellIndices.push_back( ReservoirCellIndex( i ) );
}
}
}
diff --git a/ApplicationLibCode/ReservoirDataModel/RigActiveCellInfo.h b/ApplicationLibCode/ReservoirDataModel/RigActiveCellInfo.h
index 2ebf272b989..b9de8f7509b 100644
--- a/ApplicationLibCode/ReservoirDataModel/RigActiveCellInfo.h
+++ b/ApplicationLibCode/ReservoirDataModel/RigActiveCellInfo.h
@@ -20,6 +20,8 @@
#pragma once
+#include "RigTypeSafeIndex.h"
+
#include "cvfBoundingBox.h"
#include "cvfObject.h"
#include "cvfVector3.h"
@@ -35,10 +37,10 @@ class RigActiveCellInfo : public cvf::Object
size_t reservoirCellCount() const;
size_t reservoirActiveCellCount() const;
- bool isActive( size_t reservoirCellIndex ) const;
- size_t cellResultIndex( size_t reservoirCellIndex ) const;
- void setCellResultIndex( size_t reservoirCellIndex, size_t globalResultCellIndex );
- std::vector activeReservoirCellIndices() const;
+ bool isActive( size_t reservoirCellIndex ) const;
+ size_t cellResultIndex( size_t reservoirCellIndex ) const;
+ void setCellResultIndex( size_t reservoirCellIndex, size_t globalResultCellIndex );
+ std::vector activeReservoirCellIndices() const;
void setGridCount( size_t gridCount );
void setGridActiveCellCounts( size_t gridIndex, size_t activeCellCount );
@@ -71,8 +73,8 @@ class RigActiveCellInfo : public cvf::Object
private:
std::vector m_perGridActiveCellInfo;
- std::vector m_cellIndexToResultIndex;
- std::vector m_activeCellIndices;
+ std::vector m_cellIndexToResultIndex;
+ std::vector m_activeCellIndices;
size_t m_reservoirActiveCellCount;
diff --git a/ApplicationLibCode/ReservoirDataModel/RigEclipseResultTools.cpp b/ApplicationLibCode/ReservoirDataModel/RigEclipseResultTools.cpp
index 2656f6f0a37..a4ba3f7b3c2 100644
--- a/ApplicationLibCode/ReservoirDataModel/RigEclipseResultTools.cpp
+++ b/ApplicationLibCode/ReservoirDataModel/RigEclipseResultTools.cpp
@@ -28,6 +28,7 @@
#include "RigEclipseResultAddress.h"
#include "RigMainGrid.h"
+#include "RigTypeSafeIndex.h"
#include "RimEclipseCase.h"
#include "RimEclipseResultCase.h"
#include "RimEclipseView.h"
@@ -67,8 +68,9 @@ void generateBorderResult( RimEclipseCase* eclipseCase, cvf::refeclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->activeReservoirCellIndices();
int numActiveCells = static_cast( activeReservoirCellIdxs.size() );
- std::vector result;
- result.resize( numActiveCells, BorderType::INVISIBLE_CELL );
+ size_t reservoirCellCount =
+ eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->reservoirCellCount();
+ std::vector result( reservoirCellCount, BorderType::INVISIBLE_CELL );
auto grid = eclipseCase->eclipseCaseData()->mainGrid();
@@ -77,9 +79,9 @@ void generateBorderResult( RimEclipseCase* eclipseCase, cvf::refval( cellIdx ) )
+ if ( customVisibility->val( cellIdx.value() ) )
{
- auto neighbors = grid->neighborCells( cellIdx, true /*ignore invalid k layers*/ );
+ auto neighbors = grid->neighborCells( cellIdx.value(), true /*ignore invalid k layers*/ );
int nVisibleNeighbors = 0;
for ( auto nIdx : neighbors )
@@ -89,11 +91,11 @@ void generateBorderResult( RimEclipseCase* eclipseCase, cvf::refeclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->activeReservoirCellIndices();
- int numActiveCells = static_cast( activeReservoirCellIdxs.size() );
-
- std::vector result;
- result.resize( numActiveCells, 0 );
// Check if OPERNUM already exists - if so, keep existing values.
RigEclipseResultAddress operNumAddrNative( RiaDefines::ResultCatType::STATIC_NATIVE,
@@ -147,16 +145,17 @@ void generateOperNumResult( RimEclipseCase* eclipseCase, int borderCellValue )
hasExistingOperNum = true;
}
+ std::vector result;
+
if ( hasExistingOperNum )
{
resultsData->ensureKnownResultLoaded( operNumAddr );
- auto existingValues = resultsData->cellScalarResults( operNumAddr );
- if ( !existingValues.empty() )
+ auto existingValues = resultsData->cellScalarResults( operNumAddr, 0 );
+ result.resize( existingValues.size() );
+
+ for ( size_t i = 0; i < existingValues.size(); i++ )
{
- for ( int i = 0; i < numActiveCells; i++ )
- {
- result[i] = static_cast( existingValues[0][i] );
- }
+ result[i] = static_cast( existingValues[i] );
}
}
@@ -165,15 +164,16 @@ void generateOperNumResult( RimEclipseCase* eclipseCase, int borderCellValue )
if ( resultsData->hasResultEntry( bordNumAddr ) )
{
resultsData->ensureKnownResultLoaded( bordNumAddr );
- auto bordNumValues = resultsData->cellScalarResults( bordNumAddr );
+ auto bordNumValues = resultsData->cellScalarResults( bordNumAddr, 0 );
if ( !bordNumValues.empty() )
{
- for ( int i = 0; i < numActiveCells; i++ )
+ if ( result.empty() ) result.resize( bordNumValues.size(), 0 );
+ for ( auto activeCellIdx : activeReservoirCellIdxs )
{
// If BORDNUM = 1 (BORDER_CELL), assign the border cell value
- if ( static_cast( bordNumValues[0][i] ) == BorderType::BORDER_CELL )
+ if ( static_cast( bordNumValues[activeCellIdx.value()] ) == BorderType::BORDER_CELL )
{
- result[i] = borderCellValue;
+ result[activeCellIdx.value()] = borderCellValue;
}
}
}
@@ -219,18 +219,98 @@ int findMaxOperNumValue( RimEclipseCase* eclipseCase )
if ( !hasOperNum ) return 0;
resultsData->ensureKnownResultLoaded( operNumAddr );
- auto operNumValues = resultsData->cellScalarResults( operNumAddr );
+ auto operNumValues = resultsData->cellScalarResults( operNumAddr, 0 );
if ( operNumValues.empty() ) return 0;
// Find maximum value
int maxValue = 0;
- for ( double value : operNumValues[0] )
+ for ( double value : operNumValues )
{
- int intValue = static_cast( value );
- maxValue = std::max( maxValue, intValue );
+ maxValue = std::max( maxValue, static_cast( value ) );
}
return maxValue;
}
+//--------------------------------------------------------------------------------------------------
+///
+//--------------------------------------------------------------------------------------------------
+std::vector generateBorderCellFaces( RimEclipseCase* eclipseCase )
+{
+ if ( eclipseCase == nullptr ) return {};
+
+ auto resultsData = eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL );
+ if ( !resultsData ) return {};
+
+ auto grid = eclipseCase->eclipseCaseData()->mainGrid();
+ if ( !grid ) return {};
+
+ // Check if BORDNUM result exists
+ RigEclipseResultAddress bordNumAddr( RiaDefines::ResultCatType::GENERATED, RiaDefines::ResultDataType::INTEGER, RiaResultNames::bordnum() );
+ if ( !resultsData->hasResultEntry( bordNumAddr ) ) return {}; // BORDNUM not generated yet
+
+ resultsData->ensureKnownResultLoaded( bordNumAddr );
+ auto bordNumValues = resultsData->cellScalarResults( bordNumAddr, 0 );
+ if ( bordNumValues.empty() ) return {};
+
+ // Check if BCCON result exists to get boundary condition values
+ RigEclipseResultAddress bcconAddr( RiaDefines::ResultCatType::GENERATED, RiaDefines::ResultDataType::INTEGER, "BCCON" );
+ if ( !resultsData->hasResultEntry( bcconAddr ) ) return {};
+
+ resultsData->ensureKnownResultLoaded( bcconAddr );
+ std::vector bcconValues = resultsData->cellScalarResults( bcconAddr, 0 );
+
+ auto activeReservoirCellIdxs =
+ eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->activeReservoirCellIndices();
+
+ std::vector borderCellFaces;
+
+ // Iterate through all active cells
+ for ( auto activeCellIdx : activeReservoirCellIdxs )
+ {
+ // Check if this cell is a border cell
+ int borderValue = static_cast( bordNumValues[activeCellIdx.value()] );
+ if ( borderValue != BorderType::BORDER_CELL ) continue;
+
+ // Get IJK indices for this cell
+ size_t i, j, k;
+ if ( !grid->ijkFromCellIndex( activeCellIdx.value(), &i, &j, &k ) ) continue;
+
+ // Check all 6 faces
+ std::vector faces = cvf::StructGridInterface::validFaceTypes();
+
+ for ( auto faceType : faces )
+ {
+ // Get neighbor cell IJK
+ size_t ni, nj, nk;
+ cvf::StructGridInterface::neighborIJKAtCellFace( i, j, k, faceType, &ni, &nj, &nk );
+
+ // Check if neighbor is within bounds
+ if ( ni >= grid->cellCountI() || nj >= grid->cellCountJ() || nk >= grid->cellCountK() ) continue;
+
+ // Get neighbor reservoir cell index
+ size_t neighborReservoirIdx = grid->cellIndexFromIJK( ni, nj, nk );
+
+ // Find active cell index for neighbor
+ auto it = std::find( activeReservoirCellIdxs.begin(), activeReservoirCellIdxs.end(), ReservoirCellIndex( neighborReservoirIdx ) );
+ if ( it == activeReservoirCellIdxs.end() ) continue; // Neighbor not active
+
+ // Check if neighbor is an interior cell
+ int neighborBorderValue = static_cast( bordNumValues[neighborReservoirIdx] );
+ if ( neighborBorderValue == BorderType::INTERIOR_CELL )
+ {
+ // Get boundary condition value from BCCON grid property
+ int boundaryCondition = static_cast( bcconValues[activeCellIdx.value()] );
+ if ( boundaryCondition > 0 )
+ {
+ // Add this face to the result
+ borderCellFaces.push_back( { cvf::Vec3st( i, j, k ), faceType, boundaryCondition } );
+ }
+ }
+ }
+ }
+
+ return borderCellFaces;
+}
+
} // namespace RigEclipseResultTools
diff --git a/ApplicationLibCode/ReservoirDataModel/RigEclipseResultTools.h b/ApplicationLibCode/ReservoirDataModel/RigEclipseResultTools.h
index c87f621a9fd..ae2b549feea 100644
--- a/ApplicationLibCode/ReservoirDataModel/RigEclipseResultTools.h
+++ b/ApplicationLibCode/ReservoirDataModel/RigEclipseResultTools.h
@@ -22,6 +22,7 @@
#include
#include "cvfArray.h"
+#include "cvfStructGrid.h"
class RimEclipseCase;
class RimEclipseView;
@@ -35,6 +36,13 @@ enum BorderType : int
INTERIOR_CELL = 2
};
+struct BorderCellFace
+{
+ cvf::Vec3st ijk; // Cell indices (0-based)
+ cvf::StructGridInterface::FaceType faceType;
+ int boundaryCondition; // BCCON grid value
+};
+
void createResultVector( RimEclipseCase& eclipseCase, const QString& resultName, const std::vector& intValues );
void generateBorderResult( RimEclipseCase* eclipseCase, cvf::ref customVisibility, const QString& resultName = "BORDER" );
@@ -43,4 +51,6 @@ void generateOperNumResult( RimEclipseCase* eclipseCase, int borderCellValue = -
int findMaxOperNumValue( RimEclipseCase* eclipseCase );
+std::vector generateBorderCellFaces( RimEclipseCase* eclipseCase );
+
} // namespace RigEclipseResultTools
diff --git a/ApplicationLibCode/ReservoirDataModel/RigTypeSafeIndex.h b/ApplicationLibCode/ReservoirDataModel/RigTypeSafeIndex.h
new file mode 100644
index 00000000000..5ca92026dd2
--- /dev/null
+++ b/ApplicationLibCode/ReservoirDataModel/RigTypeSafeIndex.h
@@ -0,0 +1,76 @@
+/////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+// for more details.
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+#pragma once
+
+#include
+#include
+
+template
+class TypeSafeIndex
+{
+public:
+ // Default constructor
+ constexpr TypeSafeIndex() noexcept
+ : m_value( 0 )
+ {
+ }
+
+ // Explicit constructor from size_t (prevents implicit conversion)
+ explicit constexpr TypeSafeIndex( size_t value ) noexcept
+ : m_value( value )
+ {
+ }
+
+ // Explicit conversion to size_t
+ explicit constexpr operator size_t() const noexcept { return m_value; }
+
+ // Getter for when you need the value
+ constexpr size_t value() const noexcept { return m_value; }
+
+ // Comparison operators
+ constexpr bool operator==( const TypeSafeIndex& other ) const noexcept = default;
+ constexpr auto operator<=>( const TypeSafeIndex& other ) const noexcept = default;
+
+ // Arithmetic operators (if needed)
+ constexpr TypeSafeIndex& operator++() noexcept
+ {
+ ++m_value;
+ return *this;
+ }
+
+private:
+ size_t m_value;
+};
+
+// Define specific types using tag structs
+struct ReservoirCellIndexTag
+{
+};
+
+using ReservoirCellIndex = TypeSafeIndex;
+
+// Hash support for using in std::unordered_map, etc.
+namespace std
+{
+template
+struct hash>
+{
+ size_t operator()( const TypeSafeIndex& idx ) const noexcept { return hash{}( idx.value() ); }
+};
+} // namespace std
diff --git a/ApplicationLibCode/ReservoirDataModel/Well/RigWellTargetMapping.cpp b/ApplicationLibCode/ReservoirDataModel/Well/RigWellTargetMapping.cpp
index 9faec09ce9c..72ec48b5d30 100644
--- a/ApplicationLibCode/ReservoirDataModel/Well/RigWellTargetMapping.cpp
+++ b/ApplicationLibCode/ReservoirDataModel/Well/RigWellTargetMapping.cpp
@@ -1027,16 +1027,16 @@ void RigWellTargetMapping::computeStatisticsAndCreateVectors( RimEclipseCase&
double p10, p50, p90, mean;
RigStatisticsMath::calculateStatisticsCurves( samples, &p10, &p50, &p90, &mean, RigStatisticsMath::PercentileStyle::SWITCHED );
- if ( RiaStatisticsTools::isValidNumber( p10 ) ) p10Results[i] = p10;
- if ( RiaStatisticsTools::isValidNumber( p50 ) ) p50Results[i] = p50;
- if ( RiaStatisticsTools::isValidNumber( p90 ) ) p90Results[i] = p90;
- if ( RiaStatisticsTools::isValidNumber( mean ) ) meanResults[i] = mean;
+ if ( RigStatisticsTools::isValidNumber( p10 ) ) p10Results[i] = p10;
+ if ( RigStatisticsTools::isValidNumber( p50 ) ) p50Results[i] = p50;
+ if ( RigStatisticsTools::isValidNumber( p90 ) ) p90Results[i] = p90;
+ if ( RigStatisticsTools::isValidNumber( mean ) ) meanResults[i] = mean;
- double minValue = RiaStatisticsTools::minimumValue( samples );
- if ( RiaStatisticsTools::isValidNumber( minValue ) && minValue < std::numeric_limits::max() ) minResults[i] = minValue;
+ double minValue = RigStatisticsTools::minimumValue( samples );
+ if ( RigStatisticsTools::isValidNumber( minValue ) && minValue < std::numeric_limits::max() ) minResults[i] = minValue;
- double maxValue = RiaStatisticsTools::maximumValue( samples );
- if ( RiaStatisticsTools::isValidNumber( maxValue ) && maxValue > -std::numeric_limits::max() ) maxResults[i] = maxValue;
+ double maxValue = RigStatisticsTools::maximumValue( samples );
+ if ( RigStatisticsTools::isValidNumber( maxValue ) && maxValue > -std::numeric_limits::max() ) maxResults[i] = maxValue;
}
createResultVectorIfDefined( targetCase, resultName + "_P10", p10Results );
diff --git a/ApplicationLibCode/ResultStatisticsCache/CMakeLists.txt b/ApplicationLibCode/ResultStatisticsCache/CMakeLists.txt
index 121aae877b4..1d362a26a6c 100644
--- a/ApplicationLibCode/ResultStatisticsCache/CMakeLists.txt
+++ b/ApplicationLibCode/ResultStatisticsCache/CMakeLists.txt
@@ -2,11 +2,16 @@ project(ResultStatisticsCache)
add_library(
${PROJECT_NAME}
- RigStatisticsCalculator.h RigStatisticsCalculator.cpp
- RigStatisticsDataCache.h RigStatisticsDataCache.cpp RigStatisticsMath.h
+ RigStatisticsCalculator.h
+ RigStatisticsCalculator.cpp
+ RigStatisticsDataCache.h
+ RigStatisticsDataCache.cpp
+ RigStatisticsMath.h
RigStatisticsMath.cpp
+ RigStatisticsTools.h
+ RigStatisticsTools.cpp
)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
-target_link_libraries(${PROJECT_NAME} LibCore ApplicationLibCode)
+target_link_libraries(${PROJECT_NAME} LibCore)
diff --git a/ApplicationLibCode/ResultStatisticsCache/RigStatisticsMath.cpp b/ApplicationLibCode/ResultStatisticsCache/RigStatisticsMath.cpp
index a61b4a4b9c4..090ffab6f70 100644
--- a/ApplicationLibCode/ResultStatisticsCache/RigStatisticsMath.cpp
+++ b/ApplicationLibCode/ResultStatisticsCache/RigStatisticsMath.cpp
@@ -50,7 +50,7 @@ void RigStatisticsMath::calculateBasicStatistics( const std::vector& val
for ( size_t i = 0; i < values.size(); i++ )
{
double val = values[i];
- if ( RiaStatisticsTools::isInvalidNumber( val ) ) continue;
+ if ( RigStatisticsTools::isInvalidNumber( val ) ) continue;
validValueCount++;
@@ -110,7 +110,7 @@ void RigStatisticsMath::calculateStatisticsCurves( const std::vector& va
sortedValues.erase( std::remove_if( sortedValues.begin(),
sortedValues.end(),
- []( double x ) { return !RiaStatisticsTools::isValidNumber( x ); } ),
+ []( double x ) { return !RigStatisticsTools::isValidNumber( x ); } ),
sortedValues.end() );
std::sort( sortedValues.begin(), sortedValues.end() );
@@ -174,7 +174,7 @@ std::vector RigStatisticsMath::calculateNearestRankPercentiles( const st
for ( size_t i = 0; i < inputValues.size(); ++i )
{
- if ( RiaStatisticsTools::isValidNumber( inputValues[i] ) )
+ if ( RigStatisticsTools::isValidNumber( inputValues[i] ) )
{
sortedValues.push_back( inputValues[i] );
}
@@ -218,7 +218,7 @@ std::vector RigStatisticsMath::calculateInterpolatedPercentiles( const s
for ( size_t i = 0; i < inputValues.size(); ++i )
{
- if ( RiaStatisticsTools::isValidNumber( inputValues[i] ) )
+ if ( RigStatisticsTools::isValidNumber( inputValues[i] ) )
{
sortedValues.push_back( inputValues[i] );
}
@@ -290,7 +290,7 @@ RigHistogramCalculator::RigHistogramCalculator( double min, double max, size_t n
//--------------------------------------------------------------------------------------------------
void RigHistogramCalculator::addValue( double value )
{
- if ( RiaStatisticsTools::isInvalidNumber( value ) ) return;
+ if ( RigStatisticsTools::isInvalidNumber( value ) ) return;
size_t index = 0;
diff --git a/ApplicationLibCode/ResultStatisticsCache/RigStatisticsMath.h b/ApplicationLibCode/ResultStatisticsCache/RigStatisticsMath.h
index f8e9355bbed..47714a5bdf3 100644
--- a/ApplicationLibCode/ResultStatisticsCache/RigStatisticsMath.h
+++ b/ApplicationLibCode/ResultStatisticsCache/RigStatisticsMath.h
@@ -17,7 +17,7 @@
/////////////////////////////////////////////////////////////////////////////////
#pragma once
-#include "RiaStatisticsTools.h"
+#include "RigStatisticsTools.h"
#include
#include
@@ -107,7 +107,7 @@ class MinMaxAccumulator
void addValue( double value )
{
- if ( RiaStatisticsTools::isValidNumber( value ) )
+ if ( RigStatisticsTools::isValidNumber( value ) )
{
if ( value < min )
{
@@ -186,7 +186,7 @@ class PosNegAccumulator
void addValue( double value )
{
- if ( RiaStatisticsTools::isValidNumber( value ) )
+ if ( RigStatisticsTools::isValidNumber( value ) )
{
if ( value < pos && value > 0 )
{
@@ -231,7 +231,7 @@ class SumCountAccumulator
void addValue( double value )
{
- if ( RiaStatisticsTools::isValidNumber( value ) )
+ if ( RigStatisticsTools::isValidNumber( value ) )
{
valueSum += value;
++sampleCount;
@@ -265,7 +265,7 @@ class UniqueValueAccumulator
void addValue( double value )
{
- if ( RiaStatisticsTools::isValidNumber( value ) )
+ if ( RigStatisticsTools::isValidNumber( value ) )
{
uniqueValues.insert( static_cast( value ) );
}
diff --git a/ApplicationLibCode/Application/Tools/RiaStatisticsTools.cpp b/ApplicationLibCode/ResultStatisticsCache/RigStatisticsTools.cpp
similarity index 96%
rename from ApplicationLibCode/Application/Tools/RiaStatisticsTools.cpp
rename to ApplicationLibCode/ResultStatisticsCache/RigStatisticsTools.cpp
index 5605065f65b..07b4899741c 100644
--- a/ApplicationLibCode/Application/Tools/RiaStatisticsTools.cpp
+++ b/ApplicationLibCode/ResultStatisticsCache/RigStatisticsTools.cpp
@@ -18,14 +18,14 @@
//
/////////////////////////////////////////////////////////////////////////////////
-#include "RiaStatisticsTools.h"
+#include "RigStatisticsTools.h"
#include "RigStatisticsMath.h"
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
-double RiaStatisticsTools::pearsonCorrelation( const std::vector& xValues, const std::vector& yValues )
+double RigStatisticsTools::pearsonCorrelation( const std::vector& xValues, const std::vector& yValues )
{
const double eps = 1.0e-8;
double rangeX = 0.0, rangeY = 0.0;
diff --git a/ApplicationLibCode/Application/Tools/RiaStatisticsTools.h b/ApplicationLibCode/ResultStatisticsCache/RigStatisticsTools.h
similarity index 93%
rename from ApplicationLibCode/Application/Tools/RiaStatisticsTools.h
rename to ApplicationLibCode/ResultStatisticsCache/RigStatisticsTools.h
index 65d83623d68..20897e416b6 100644
--- a/ApplicationLibCode/Application/Tools/RiaStatisticsTools.h
+++ b/ApplicationLibCode/ResultStatisticsCache/RigStatisticsTools.h
@@ -29,7 +29,7 @@
//
//
//==================================================================================================
-class RiaStatisticsTools
+class RigStatisticsTools
{
public:
template
@@ -50,7 +50,7 @@ class RiaStatisticsTools
NumberType minValue = std::numeric_limits::max();
for ( NumberType value : values )
{
- if ( RiaStatisticsTools::isValidNumber( value ) )
+ if ( RigStatisticsTools::isValidNumber( value ) )
{
minValue = std::min( minValue, value );
}
@@ -65,7 +65,7 @@ class RiaStatisticsTools
NumberType maxValue = -std::numeric_limits::max();
for ( NumberType value : values )
{
- if ( RiaStatisticsTools::isValidNumber( value ) )
+ if ( RigStatisticsTools::isValidNumber( value ) )
{
maxValue = std::max( maxValue, value );
}
diff --git a/ApplicationLibCode/UnitTests/CMakeLists.txt b/ApplicationLibCode/UnitTests/CMakeLists.txt
index 5f48e491b16..a2f778ce7a2 100644
--- a/ApplicationLibCode/UnitTests/CMakeLists.txt
+++ b/ApplicationLibCode/UnitTests/CMakeLists.txt
@@ -15,6 +15,7 @@ set(SOURCE_UNITTEST_FILES
${CMAKE_CURRENT_LIST_DIR}/RifReaderEclipseSummary-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RigActiveCellInfo-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RigEclipseCaseDataTools-Test.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/RigEclipseResultTools-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RigReservoir-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RigResdataGridConverter-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RigGridExportAdapter-Test.cpp
@@ -74,7 +75,7 @@ set(SOURCE_UNITTEST_FILES
${CMAKE_CURRENT_LIST_DIR}/RifColorLegendData-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RifRoffReader-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RifElasticPropertiesReader-Test.cpp
- ${CMAKE_CURRENT_LIST_DIR}/RiaStatisticsTools-Test.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/RigStatisticsTools-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RifStimPlanXmlReader-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RifThermalFractureReader-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RigWellPathGeometryExporter-Test.cpp
diff --git a/ApplicationLibCode/UnitTests/RifOpmFlowDeckFile-Test.cpp b/ApplicationLibCode/UnitTests/RifOpmFlowDeckFile-Test.cpp
index c8ec84c9d13..6115330eada 100644
--- a/ApplicationLibCode/UnitTests/RifOpmFlowDeckFile-Test.cpp
+++ b/ApplicationLibCode/UnitTests/RifOpmFlowDeckFile-Test.cpp
@@ -1,8 +1,18 @@
#include "gtest/gtest.h"
#include "RiaTestDataDirectory.h"
+
+#include "RifOpmDeckTools.h"
#include "RifOpmFlowDeckFile.h"
+#include "RigEclipseResultTools.h"
+
+#include "cvfStructGrid.h"
+
+#include "opm/input/eclipse/Deck/DeckItem.hpp"
+#include "opm/input/eclipse/Deck/DeckRecord.hpp"
+#include "opm/input/eclipse/Parser/ParserKeywords/B.hpp"
+
#include
#include
#include
@@ -322,3 +332,111 @@ TEST( RifOpmFlowDeckFileTest, AddOperaterSaveAndReload )
// The test validates that our addOperaterKeyword functionality works correctly
// by checking that the OPERATER statement is properly saved to the file.
}
+
+//--------------------------------------------------------------------------------------------------
+/// Test BCPROP keyword generation
+//--------------------------------------------------------------------------------------------------
+TEST( RifOpmFlowDeckFileTest, BcpropKeyword )
+{
+ QTemporaryDir tempDir;
+ ASSERT_TRUE( tempDir.isValid() ) << "Failed to create temporary directory";
+
+ // Load the deck file
+ static const QString testDataFolder = QString( "%1/RifOpmFlowDeckFile/" ).arg( TEST_DATA_DIR );
+ QString fileName = testDataFolder + "SIMPLE_NO_REGDIMS.DATA";
+
+ RifOpmFlowDeckFile deckFile;
+ bool loadSuccess = deckFile.loadDeck( fileName.toStdString() );
+
+ ASSERT_TRUE( loadSuccess ) << "Failed to load deck file";
+
+ // Create boundary conditions with different indices
+ std::vector boundaryConditions;
+ boundaryConditions.push_back( { cvf::Vec3st( 5, 5, 2 ), cvf::StructGridInterface::POS_I, 1 } );
+ boundaryConditions.push_back( { cvf::Vec3st( 5, 6, 2 ), cvf::StructGridInterface::POS_J, 1 } );
+ boundaryConditions.push_back( { cvf::Vec3st( 6, 5, 2 ), cvf::StructGridInterface::NEG_I, 2 } );
+ boundaryConditions.push_back( { cvf::Vec3st( 7, 5, 2 ), cvf::StructGridInterface::POS_K, 2 } );
+
+ // Create boundary condition properties
+ // BC 1: Free flow boundary with specified pressure
+ // BC 2: Fixed pressure boundary with temperature
+ std::vector bcProperties;
+
+ using B = Opm::ParserKeywords::BCPROP;
+
+ // Property for BC 1 (index will be added by addBcpropKeyword)
+ {
+ std::vector items;
+ items.push_back( RifOpmDeckTools::item( B::TYPE::itemName, std::string( "FREE" ) ) );
+ items.push_back( RifOpmDeckTools::item( B::COMPONENT::itemName, std::string( "NONE" ) ) );
+ items.push_back( RifOpmDeckTools::item( B::RATE::itemName, 0.0 ) );
+ items.push_back( RifOpmDeckTools::item( B::PRESSURE::itemName, 200.0 ) ); // 200 bar
+ items.push_back( RifOpmDeckTools::item( B::TEMPERATURE::itemName, 80.0 ) ); // 80 C
+ items.push_back( RifOpmDeckTools::item( B::MECHTYPE::itemName, std::string( "NONE" ) ) );
+ items.push_back( RifOpmDeckTools::item( B::FIXEDX::itemName, 1 ) );
+ items.push_back( RifOpmDeckTools::item( B::FIXEDY::itemName, 1 ) );
+ items.push_back( RifOpmDeckTools::item( B::FIXEDZ::itemName, 1 ) );
+ items.push_back( RifOpmDeckTools::item( B::STRESSXX::itemName, 0.0 ) );
+ items.push_back( RifOpmDeckTools::item( B::STRESSYY::itemName, 0.0 ) );
+ items.push_back( RifOpmDeckTools::item( B::STRESSZZ::itemName, 0.0 ) );
+ items.push_back( RifOpmDeckTools::item( B::DISPX::itemName, 0.0 ) );
+ items.push_back( RifOpmDeckTools::item( B::DISPY::itemName, 0.0 ) );
+ items.push_back( RifOpmDeckTools::item( B::DISPZ::itemName, 0.0 ) );
+ bcProperties.push_back( Opm::DeckRecord{ std::move( items ) } );
+ }
+
+ // Property for BC 2
+ {
+ std::vector items;
+ items.push_back( RifOpmDeckTools::item( B::TYPE::itemName, std::string( "DIRICH" ) ) );
+ items.push_back( RifOpmDeckTools::item( B::COMPONENT::itemName, std::string( "WATER" ) ) );
+ items.push_back( RifOpmDeckTools::item( B::RATE::itemName, 0.0 ) );
+ items.push_back( RifOpmDeckTools::item( B::PRESSURE::itemName, 250.0 ) ); // 250 bar
+ items.push_back( RifOpmDeckTools::item( B::TEMPERATURE::itemName, 90.0 ) ); // 90 C
+ items.push_back( RifOpmDeckTools::item( B::MECHTYPE::itemName, std::string( "NONE" ) ) );
+ items.push_back( RifOpmDeckTools::item( B::FIXEDX::itemName, 1 ) );
+ items.push_back( RifOpmDeckTools::item( B::FIXEDY::itemName, 1 ) );
+ items.push_back( RifOpmDeckTools::item( B::FIXEDZ::itemName, 1 ) );
+ items.push_back( RifOpmDeckTools::item( B::STRESSXX::itemName, 0.0 ) );
+ items.push_back( RifOpmDeckTools::item( B::STRESSYY::itemName, 0.0 ) );
+ items.push_back( RifOpmDeckTools::item( B::STRESSZZ::itemName, 0.0 ) );
+ items.push_back( RifOpmDeckTools::item( B::DISPX::itemName, 0.0 ) );
+ items.push_back( RifOpmDeckTools::item( B::DISPY::itemName, 0.0 ) );
+ items.push_back( RifOpmDeckTools::item( B::DISPZ::itemName, 0.0 ) );
+ bcProperties.push_back( Opm::DeckRecord{ std::move( items ) } );
+ }
+
+ // Add BCPROP keyword
+ bool bcpropAdded = deckFile.addBcpropKeyword( "GRID", boundaryConditions, bcProperties );
+ ASSERT_TRUE( bcpropAdded ) << "Failed to add BCPROP keyword";
+
+ // Save deck and verify format
+ QString outputDeckPath = tempDir.filePath( "output_bcprop.DATA" );
+ bool deckSaved = deckFile.saveDeck( tempDir.path().toStdString(), "output_bcprop.DATA" );
+ ASSERT_TRUE( deckSaved ) << "Failed to save deck file";
+ ASSERT_TRUE( QFile::exists( outputDeckPath ) ) << "Output deck file not created";
+
+ // Read and verify BCPROP content
+ QFile outputFile( outputDeckPath );
+ ASSERT_TRUE( outputFile.open( QIODevice::ReadOnly | QIODevice::Text ) );
+ QString content = QTextStream( &outputFile ).readAll();
+ outputFile.close();
+
+ // Verify BCPROP keyword is present
+ EXPECT_TRUE( content.contains( "BCPROP" ) ) << "BCPROP keyword not found in output";
+
+ // Verify boundary condition types are present
+ EXPECT_TRUE( content.contains( "FREE" ) ) << "FREE boundary condition type not found";
+ EXPECT_TRUE( content.contains( "DIRICH" ) ) << "DIRICH boundary condition type not found";
+
+ // Verify pressure values
+ EXPECT_TRUE( content.contains( "200" ) ) << "Pressure 200 bar not found";
+ EXPECT_TRUE( content.contains( "250" ) ) << "Pressure 250 bar not found";
+
+ // Verify temperature values
+ EXPECT_TRUE( content.contains( "80" ) ) << "Temperature 80 C not found";
+ EXPECT_TRUE( content.contains( "90" ) ) << "Temperature 90 C not found";
+
+ // Verify component
+ EXPECT_TRUE( content.contains( "WATER" ) ) << "Component WATER not found";
+}
diff --git a/ApplicationLibCode/UnitTests/RigEclipseCaseDataTools-Test.cpp b/ApplicationLibCode/UnitTests/RigEclipseCaseDataTools-Test.cpp
index 483c867c696..ed85ffa619e 100644
--- a/ApplicationLibCode/UnitTests/RigEclipseCaseDataTools-Test.cpp
+++ b/ApplicationLibCode/UnitTests/RigEclipseCaseDataTools-Test.cpp
@@ -18,6 +18,7 @@
#include "gtest/gtest.h"
+#include "RiaPorosityModel.h"
#include "RiaResultNames.h"
#include "RiaTestDataDirectory.h"
@@ -592,7 +593,8 @@ TEST( RigEclipseCaseDataToolsTest, GenerateOperNumResultFromBorderResult )
int numActiveCells = static_cast( activeReservoirCellIdxs.size() );
std::vector customOperValues;
- customOperValues.resize( numActiveCells, 5 ); // Set all to 5 initially
+ customOperValues.resize( eclipseCase->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->reservoirCellCount(),
+ 5 ); // Set all to 5 initially
// Add some higher values to test max detection
if ( numActiveCells > 10 )
{
diff --git a/ApplicationLibCode/UnitTests/RigEclipseResultTools-Test.cpp b/ApplicationLibCode/UnitTests/RigEclipseResultTools-Test.cpp
new file mode 100644
index 00000000000..f37a0fa50e6
--- /dev/null
+++ b/ApplicationLibCode/UnitTests/RigEclipseResultTools-Test.cpp
@@ -0,0 +1,203 @@
+/////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+// for more details.
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+#include "gtest/gtest.h"
+
+#include "RiaDefines.h"
+#include "RiaTestDataDirectory.h"
+#include "RifEclipseInputFileTools.h"
+#include "RifOpmFlowDeckFile.h"
+#include "RifReaderEclipseOutput.h"
+#include "RigEclipseResultTools.h"
+
+#include "RigActiveCellInfo.h"
+#include "RigCaseCellResultsData.h"
+#include "RigEclipseCaseData.h"
+#include "RigEclipseResultAddress.h"
+#include "RigMainGrid.h"
+#include "RimEclipseResultCase.h"
+
+#include
+#include
+#include
+#include
+
+#include
+
+//--------------------------------------------------------------------------------------------------
+/// Test BCCON generation from border cells
+///
+/// This test verifies that:
+/// 1. We can generate BORDNUM result identifying border and interior cells
+/// 2. We can extract border cell faces that connect to interior cells
+/// 3. We can write BCCON keyword to an OPM deck file
+///
+/// Test process:
+/// 1. Load reek test model
+/// 2. Create custom visibility for a subset of cells (a box region)
+/// 3. Generate BORDNUM result
+/// 4. Generate border cell faces
+/// 5. Verify border cells and faces are correctly identified
+/// 6. Create OPM deck and add BCCON keyword
+/// 7. Save and verify deck file format
+//--------------------------------------------------------------------------------------------------
+TEST( RigEclipseResultToolsTest, BorderCellBcconGeneration )
+{
+ // Setup test data directory - use BRUGGE model like other tests
+ QDir baseFolder( TEST_MODEL_DIR );
+ bool subFolderExists = baseFolder.cd( "Case_with_10_timesteps/Real0" );
+ ASSERT_TRUE( subFolderExists ) << "Test model directory not found";
+
+ QString inputFilename( "BRUGGE_0000.EGRID" );
+ QString inputFilePath = baseFolder.absoluteFilePath( inputFilename );
+ ASSERT_TRUE( QFile::exists( inputFilePath ) ) << "Test model file not found: " << inputFilePath.toStdString();
+
+ // Step 1: Load original grid using RifReaderEclipseOutput (properly initializes everything)
+ std::unique_ptr testCase( new RimEclipseResultCase );
+ cvf::ref caseData = new RigEclipseCaseData( testCase.get() );
+
+ cvf::ref readerInterfaceEcl = new RifReaderEclipseOutput;
+ bool success = readerInterfaceEcl->open( inputFilePath, caseData.p() );
+ ASSERT_TRUE( success ) << "Failed to load grid";
+
+ testCase->setReservoirData( caseData.p() );
+
+ const RigMainGrid* grid = caseData->mainGrid();
+ ASSERT_NE( grid, nullptr ) << "Grid is null";
+
+ // Step 2: Create custom visibility for a subset of cells (e.g., middle 50% of grid)
+ size_t cellCount = grid->cellCount();
+
+ cvf::ref customVisibility = new cvf::UByteArray( cellCount );
+ customVisibility->setAll( 0 ); // Start with all invisible
+
+ std::vector bcconValues( cellCount, 0 );
+
+ // Make a box in the middle visible
+ size_t startI = grid->cellCountI() / 4;
+ size_t endI = 3 * grid->cellCountI() / 4;
+ size_t startJ = grid->cellCountJ() / 4;
+ size_t endJ = 3 * grid->cellCountJ() / 4;
+ size_t startK = grid->cellCountK() / 4;
+ size_t endK = 3 * grid->cellCountK() / 4;
+
+ for ( size_t i = startI; i < endI; ++i )
+ {
+ for ( size_t j = startJ; j < endJ; ++j )
+ {
+ for ( size_t k = startK; k < endK; ++k )
+ {
+ size_t cellIndex = grid->cellIndexFromIJK( i, j, k );
+ ( *customVisibility )[cellIndex] = 1;
+
+ // Set bccon==1 for half of the cells
+ if ( i > grid->cellCountI() / 2 )
+ {
+ bcconValues[cellIndex] = 1;
+ }
+ }
+ }
+ }
+
+ // Step 3: Generate BORDNUM result
+ RigEclipseResultTools::generateBorderResult( testCase.get(), customVisibility, "BORDNUM" );
+
+ // Verify BORDNUM was created
+ auto resultsData = testCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL );
+ ASSERT_NE( resultsData, nullptr );
+
+ RigEclipseResultAddress bordNumAddr( RiaDefines::ResultCatType::GENERATED, RiaDefines::ResultDataType::INTEGER, "BORDNUM" );
+ ASSERT_TRUE( resultsData->hasResultEntry( bordNumAddr ) ) << "BORDNUM result not created";
+
+ RigEclipseResultTools::createResultVector( *testCase, "BCCON", bcconValues );
+
+ // Step 4: Generate border cell faces
+ auto borderCellFaces = RigEclipseResultTools::generateBorderCellFaces( testCase.get() );
+
+ // Step 5: Verify results
+ EXPECT_GT( borderCellFaces.size(), 0 ) << "No border cell faces generated";
+
+ // Verify that all faces are within the visible region boundaries
+ for ( const auto& face : borderCellFaces )
+ {
+ EXPECT_GE( face.ijk[0], startI );
+ EXPECT_LT( face.ijk[0], endI );
+ EXPECT_GE( face.ijk[1], startJ );
+ EXPECT_LT( face.ijk[1], endJ );
+ EXPECT_GE( face.ijk[2], startK );
+ EXPECT_LT( face.ijk[2], endK );
+
+ // Verify face type is valid
+ EXPECT_GE( static_cast( face.faceType ), static_cast( cvf::StructGridInterface::POS_I ) );
+ EXPECT_LT( static_cast( face.faceType ), static_cast( cvf::StructGridInterface::NO_FACE ) );
+
+ // Verify boundary condition (should be 1 if no BCCON grid property exists)
+ EXPECT_EQ( face.boundaryCondition, 1 );
+ }
+
+ // Step 6: Create OPM deck file and add BCCON keyword
+ QTemporaryDir tempDir;
+ ASSERT_TRUE( tempDir.isValid() ) << "Failed to create temporary directory";
+
+ // Create a minimal deck file with GRID section
+ QString deckFilePath = tempDir.filePath( "test_bccon.DATA" );
+ {
+ QFile deckFile( deckFilePath );
+ ASSERT_TRUE( deckFile.open( QIODevice::WriteOnly | QIODevice::Text ) );
+ QTextStream out( &deckFile );
+ out << "RUNSPEC\n\n";
+ out << "DIMENS\n";
+ out << grid->cellCountI() << " " << grid->cellCountJ() << " " << grid->cellCountK() << " /\n\n";
+ out << "GRID\n\n";
+ out << "-- BCCON will be added here\n\n";
+ out << "PROPS\n\n";
+ out << "SCHEDULE\n\n";
+ deckFile.close();
+ }
+
+ // Load the deck file
+ RifOpmFlowDeckFile deckFile;
+ bool deckLoaded = deckFile.loadDeck( deckFilePath.toStdString() );
+ ASSERT_TRUE( deckLoaded ) << "Failed to load deck file";
+
+ // Add BCCON keyword
+ bool bcconAdded = deckFile.addBcconKeyword( "GRID", borderCellFaces );
+ ASSERT_TRUE( bcconAdded ) << "Failed to add BCCON keyword";
+
+ // Step 7: Save deck and verify format
+ QString outputDeckPath = tempDir.filePath( "output_bccon.DATA" );
+ bool deckSaved = deckFile.saveDeck( tempDir.path().toStdString(), "output_bccon.DATA" );
+ ASSERT_TRUE( deckSaved ) << "Failed to save deck file";
+ ASSERT_TRUE( QFile::exists( outputDeckPath ) ) << "Output deck file not created";
+
+ // Read and verify BCCON content
+ QFile outputFile( outputDeckPath );
+ ASSERT_TRUE( outputFile.open( QIODevice::ReadOnly | QIODevice::Text ) );
+ QString content = QTextStream( &outputFile ).readAll();
+ outputFile.close();
+
+ // Verify BCCON keyword is present
+ EXPECT_TRUE( content.contains( "BCCON" ) ) << "BCCON keyword not found in output";
+
+ // Verify at least one entry exists
+ // BCCON format: counter i1 i2 j1 j2 k1 k2 face
+ // Should have face directions like X, X-, Y, Y-, Z, Z-
+ bool hasFaceDirection = content.contains( "X-" ) || content.contains( "Y-" ) || content.contains( "Z-" ) || content.contains( " X " ) ||
+ content.contains( " Y " ) || content.contains( " Z " );
+ EXPECT_TRUE( hasFaceDirection ) << "BCCON entries don't contain face directions";
+}
diff --git a/ApplicationLibCode/UnitTests/RiaStatisticsTools-Test.cpp b/ApplicationLibCode/UnitTests/RigStatisticsTools-Test.cpp
similarity index 90%
rename from ApplicationLibCode/UnitTests/RiaStatisticsTools-Test.cpp
rename to ApplicationLibCode/UnitTests/RigStatisticsTools-Test.cpp
index 6dd5002ee7a..36577b539de 100644
--- a/ApplicationLibCode/UnitTests/RiaStatisticsTools-Test.cpp
+++ b/ApplicationLibCode/UnitTests/RigStatisticsTools-Test.cpp
@@ -18,7 +18,7 @@
#include "gtest/gtest.h"
-#include "RiaStatisticsTools.h"
+#include "RigStatisticsTools.h"
#include
//--------------------------------------------------------------------------------------------------
@@ -38,7 +38,7 @@ TEST( RiaStatisticsTools, NoCorrelation )
{
b.push_back( (double)std::rand() );
}
- double correlation = RiaStatisticsTools::pearsonCorrelation( a, b );
+ double correlation = RigStatisticsTools::pearsonCorrelation( a, b );
EXPECT_LE( correlation, 0.25 );
}
@@ -59,7 +59,7 @@ TEST( RiaStatisticsTools, FullCorrelation )
{
b.push_back( i * 2.0 + 1.0 );
}
- double correlation = RiaStatisticsTools::pearsonCorrelation( a, b );
+ double correlation = RigStatisticsTools::pearsonCorrelation( a, b );
EXPECT_NEAR( correlation, 1.0, 1.0e-2 );
}
@@ -80,7 +80,7 @@ TEST( RiaStatisticsTools, NegativeCorrelation )
{
b.push_back( i * -2.0 + 1.0 );
}
- double correlation = RiaStatisticsTools::pearsonCorrelation( a, b );
+ double correlation = RigStatisticsTools::pearsonCorrelation( a, b );
EXPECT_NEAR( correlation, -1.0, 1.0e-2 );
}
@@ -95,7 +95,7 @@ TEST( RiaStatisticsTools, MinValue )
{
a.push_back( static_cast( i ) );
}
- double minValue = RiaStatisticsTools::minimumValue( a );
+ double minValue = RigStatisticsTools::minimumValue( a );
EXPECT_EQ( minValue, 10.0 );
}
@@ -110,6 +110,6 @@ TEST( RiaStatisticsTools, MaxValue )
{
a.push_back( static_cast( i ) );
}
- double maxValue = RiaStatisticsTools::maximumValue( a );
+ double maxValue = RigStatisticsTools::maximumValue( a );
EXPECT_EQ( maxValue, 999.0 );
}
diff --git a/ApplicationLibCode/UserInterface/RiuViewer.cpp b/ApplicationLibCode/UserInterface/RiuViewer.cpp
index 22f94b57729..1ba6c9489a9 100644
--- a/ApplicationLibCode/UserInterface/RiuViewer.cpp
+++ b/ApplicationLibCode/UserInterface/RiuViewer.cpp
@@ -605,7 +605,7 @@ void RiuViewer::showZScaleLabel( bool enable )
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
-void RiuViewer::setZScale( int scale )
+void RiuViewer::setZScale( double scale )
{
bool isScaleChanged = m_zScale != scale;
m_zScale = scale;
diff --git a/ApplicationLibCode/UserInterface/RiuViewer.h b/ApplicationLibCode/UserInterface/RiuViewer.h
index 3989bd9ebf3..440f5ceabb4 100644
--- a/ApplicationLibCode/UserInterface/RiuViewer.h
+++ b/ApplicationLibCode/UserInterface/RiuViewer.h
@@ -87,7 +87,7 @@ class RiuViewer : public caf::Viewer, public RiuInterfaceToViewWindow
void hideZScaleCheckbox( bool hide );
void showZScaleLabel( bool enable );
- void setZScale( int scale );
+ void setZScale( double scale );
void showHistogram( bool enable );
void setHistogram( double min, double max, const std::vector& histogram );
diff --git a/Fwk/AppFwk/cafTests/cafTestApplication/TamComboBox.cpp b/Fwk/AppFwk/cafTests/cafTestApplication/TamComboBox.cpp
index 8a5c064c58c..1a4f7e1f69e 100644
--- a/Fwk/AppFwk/cafTests/cafTestApplication/TamComboBox.cpp
+++ b/Fwk/AppFwk/cafTests/cafTestApplication/TamComboBox.cpp
@@ -68,6 +68,9 @@ void TamComboBox::defineEditorAttribute( const caf::PdmFieldHandle* field,
auto attr = dynamic_cast( attribute );
if ( attr )
{
- attr->enableEditableContent = true;
+ attr->enableEditableContent = true;
+ attr->enableAutoComplete = false;
+ attr->adjustWidthToContents = true;
+ attr->notifyWhenTextIsEdited = false;
}
}
diff --git a/Fwk/AppFwk/cafUserInterface/cafPdmUiComboBoxEditor.cpp b/Fwk/AppFwk/cafUserInterface/cafPdmUiComboBoxEditor.cpp
index 11d504fdfcd..ddf49a464ba 100644
--- a/Fwk/AppFwk/cafUserInterface/cafPdmUiComboBoxEditor.cpp
+++ b/Fwk/AppFwk/cafUserInterface/cafPdmUiComboBoxEditor.cpp
@@ -311,57 +311,6 @@ QMargins PdmUiComboBoxEditor::calculateLabelContentMargins() const
return contentMargins;
}
-//--------------------------------------------------------------------------------------------------
-// Special class used to prevent a combo box to steal focus when scrolling
-// the QScrollArea using the mouse wheel
-//
-// Based on
-// http://stackoverflow.com/questions/5821802/qspinbox-inside-a-qscrollarea-how-to-prevent-spin-box-from-stealing-focus-when
-//--------------------------------------------------------------------------------------------------
-class CustomQComboBox : public QComboBox
-{
-public:
- explicit CustomQComboBox( QWidget* parent = nullptr )
- : QComboBox( parent )
- {
- }
-
- //--------------------------------------------------------------------------------------------------
- ///
- //--------------------------------------------------------------------------------------------------
- void wheelEvent( QWheelEvent* e ) override
- {
- if ( hasFocus() )
- {
- QComboBox::wheelEvent( e );
- }
- else
- {
- // Ignore the event to make sure event is handled by another widget
- e->ignore();
- }
- }
-
-protected:
- //--------------------------------------------------------------------------------------------------
- ///
- //--------------------------------------------------------------------------------------------------
- void focusInEvent( QFocusEvent* e ) override
- {
- setFocusPolicy( Qt::WheelFocus );
- QComboBox::focusInEvent( e );
- }
-
- //--------------------------------------------------------------------------------------------------
- ///
- //--------------------------------------------------------------------------------------------------
- void focusOutEvent( QFocusEvent* e ) override
- {
- setFocusPolicy( Qt::StrongFocus );
- QComboBox::focusOutEvent( e );
- }
-};
-
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -386,6 +335,7 @@ QWidget* PdmUiComboBoxEditor::createEditorWidget( QWidget* parent )
m_layout->addWidget( m_comboBox );
connect( m_comboBox, SIGNAL( activated( int ) ), this, SLOT( slotIndexActivated( int ) ) );
+ connect( m_comboBox, SIGNAL( signalFocusOutEvent() ), this, SLOT( slotSetCurrentTextToField() ) );
m_autoValueToolButton = new QToolButton( m_placeholder );
m_autoValueToolButton->setCheckable( true );
@@ -445,6 +395,20 @@ void PdmUiComboBoxEditor::slotEditTextChanged( const QString& text )
this->setValueToField( text );
}
+//--------------------------------------------------------------------------------------------------
+///
+//--------------------------------------------------------------------------------------------------
+void PdmUiComboBoxEditor::slotSetCurrentTextToField()
+{
+ if ( m_attributes.enableEditableContent )
+ {
+ // Use the text directly, as the item text could be entered directly by the user
+
+ auto text = m_comboBox->currentText();
+ this->setValueToField( text );
+ }
+}
+
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -483,4 +447,48 @@ void PdmUiComboBoxEditor::slotApplyAutoValue()
configureAndUpdateUi( "" );
}
+//--------------------------------------------------------------------------------------------------
+///
+//--------------------------------------------------------------------------------------------------
+CustomQComboBox::CustomQComboBox( QWidget* parent /*= nullptr */ )
+ : QComboBox( parent )
+{
+}
+
+//--------------------------------------------------------------------------------------------------
+///
+//--------------------------------------------------------------------------------------------------
+void CustomQComboBox::wheelEvent( QWheelEvent* e )
+{
+ if ( hasFocus() )
+ {
+ QComboBox::wheelEvent( e );
+ }
+ else
+ {
+ // Ignore the event to make sure event is handled by another widget
+ e->ignore();
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+///
+//--------------------------------------------------------------------------------------------------
+void CustomQComboBox::focusInEvent( QFocusEvent* e )
+{
+ setFocusPolicy( Qt::WheelFocus );
+ QComboBox::focusInEvent( e );
+}
+
+//--------------------------------------------------------------------------------------------------
+///
+//--------------------------------------------------------------------------------------------------
+void CustomQComboBox::focusOutEvent( QFocusEvent* e )
+{
+ setFocusPolicy( Qt::StrongFocus );
+ QComboBox::focusOutEvent( e );
+
+ emit signalFocusOutEvent();
+}
+
} // end namespace caf
diff --git a/Fwk/AppFwk/cafUserInterface/cafPdmUiComboBoxEditor.h b/Fwk/AppFwk/cafUserInterface/cafPdmUiComboBoxEditor.h
index adac7873cee..9642f00bff1 100644
--- a/Fwk/AppFwk/cafUserInterface/cafPdmUiComboBoxEditor.h
+++ b/Fwk/AppFwk/cafUserInterface/cafPdmUiComboBoxEditor.h
@@ -108,6 +108,7 @@ protected slots:
void slotIndexActivated( int index );
void slotEditTextChanged( const QString& );
+ void slotSetCurrentTextToField();
void slotNextButtonPressed();
void slotPreviousButtonPressed();
void slotApplyAutoValue();
@@ -127,4 +128,27 @@ protected slots:
int m_interactiveEditCursorPosition;
};
+//--------------------------------------------------------------------------------------------------
+// Special class used to prevent a combo box to steal focus when scrolling
+// the QScrollArea using the mouse wheel
+//
+// Based on
+// http://stackoverflow.com/questions/5821802/qspinbox-inside-a-qscrollarea-how-to-prevent-spin-box-from-stealing-focus-when
+//--------------------------------------------------------------------------------------------------
+class CustomQComboBox : public QComboBox
+{
+ Q_OBJECT
+public:
+ explicit CustomQComboBox( QWidget* parent = nullptr );
+
+ void wheelEvent( QWheelEvent* e ) override;
+
+protected:
+ void focusInEvent( QFocusEvent* e ) override;
+ void focusOutEvent( QFocusEvent* e ) override;
+
+signals:
+ void signalFocusOutEvent();
+};
+
} // end namespace caf
diff --git a/GrpcInterface/CMakeLists.txt b/GrpcInterface/CMakeLists.txt
index 7bcb2c5713a..c3c1eb034e6 100644
--- a/GrpcInterface/CMakeLists.txt
+++ b/GrpcInterface/CMakeLists.txt
@@ -217,7 +217,7 @@ list(
list(APPEND GRPC_PYTHON_SOURCES ${GRPC_PYTHON_GENERATED_SOURCES})
add_library(
- ${PROJECT_NAME} OBJECT
+ ${PROJECT_NAME} STATIC
${SOURCE_GROUP_HEADER_FILES} ${SOURCE_GROUP_SOURCE_FILES}
${GRPC_HEADER_FILES} ${GRPC_CPP_SOURCES})
diff --git a/GrpcInterface/Python/rips/tests/test_view.py b/GrpcInterface/Python/rips/tests/test_view.py
new file mode 100644
index 00000000000..fe11e6441de
--- /dev/null
+++ b/GrpcInterface/Python/rips/tests/test_view.py
@@ -0,0 +1,64 @@
+import sys
+import os
+
+sys.path.insert(1, os.path.join(sys.path[0], "../../"))
+
+import dataroot
+
+
+def test_visible_cells(rips_instance, initialize_test):
+ """Test the visible_cells() method on a view"""
+ case_root_path = dataroot.PATH + "/TEST10K_FLT_LGR_NNC"
+ case_path = case_root_path + "/TEST10K_FLT_LGR_NNC.EGRID"
+ case = rips_instance.project.load_case(path=case_path)
+
+ # Get the first view or create one
+ views = case.views()
+ if views:
+ view = views[0]
+ else:
+ view = case.create_view()
+
+ # Test visible_cells at time step 0
+ visibility = view.visible_cells(time_step=0)
+
+ # Verify we got a list
+ assert isinstance(visibility, list)
+
+ # Verify we have data
+ assert len(visibility) > 0
+
+ # Verify all values are either 0 or 1
+ for v in visibility:
+ assert v in [0, 1]
+
+ # Count visible and invisible cells
+ visible_count = sum(visibility)
+ invisible_count = len(visibility) - visible_count
+
+ # Total cells should match grid size
+ assert len(visibility) == visible_count + invisible_count
+
+ # The visibility array should have the correct size (can be all visible, all invisible, or mixed)
+ # This depends on the view settings
+ assert visible_count + invisible_count > 0
+
+
+def test_visible_cells_different_timesteps(rips_instance, initialize_test):
+ """Test visible_cells() at different time steps"""
+ case_root_path = dataroot.PATH + "/TEST10K_FLT_LGR_NNC"
+ case_path = case_root_path + "/TEST10K_FLT_LGR_NNC.EGRID"
+ case = rips_instance.project.load_case(path=case_path)
+
+ view = case.views()[0] if case.views() else case.create_view()
+
+ # Test at time step 0
+ visibility_ts0 = view.visible_cells(time_step=0)
+ assert len(visibility_ts0) > 0
+
+ # Test at time step 1 (if it exists)
+ visibility_ts1 = view.visible_cells(time_step=1)
+ assert len(visibility_ts1) > 0
+
+ # Both should have the same number of cells
+ assert len(visibility_ts0) == len(visibility_ts1)
diff --git a/GrpcInterface/Python/rips/view.py b/GrpcInterface/Python/rips/view.py
index 3b9294007e3..f95dcbbf6b3 100644
--- a/GrpcInterface/Python/rips/view.py
+++ b/GrpcInterface/Python/rips/view.py
@@ -4,6 +4,9 @@
import Commands_pb2 as Cmd
+import uuid
+import rips.project
+
import rips.case # Circular import of Case, which already imports View. Use full name.
from .pdmobject import add_method
from .resinsight_classes import (
@@ -211,6 +214,37 @@ def case(self):
return None
+@add_method(View)
+def visible_cells(self, time_step=0):
+ """Get the visibility status of all cells in the view
+
+ Arguments:
+ time_step (int): The time step to get visibility for. Defaults to 0.
+
+ Returns:
+ List[int]: A list where 1 represents a visible cell and 0 represents an invisible cell
+ """
+
+ # Generate temporary key for key-value store
+ visibility_key = f"{uuid.uuid4()}_visibility"
+
+ # Get the project
+ project = self.ancestor(rips.project.Project)
+ try:
+ # Call internal method to store visibility data
+ self.visible_cells_internal(visibility_key=visibility_key, time_step=time_step)
+
+ # Retrieve visibility data from key-value store
+ visibility_values = project.key_values(visibility_key)
+
+ # Convert floats to integers (1 for visible, 0 for invisible)
+ return [int(v) for v in visibility_values]
+
+ finally:
+ # Clean up temporary key from key-value store
+ project.remove_key_values(visibility_key)
+
+
@add_method(ViewWindow)
def export_snapshot(self, prefix="", export_folder=""):
"""Export snapshot for the current view
diff --git a/ResInsightVersion.cmake b/ResInsightVersion.cmake
index 787d82d6680..80d3bf4f74a 100644
--- a/ResInsightVersion.cmake
+++ b/ResInsightVersion.cmake
@@ -1,7 +1,7 @@
set(RESINSIGHT_MAJOR_VERSION 2025)
set(RESINSIGHT_MINOR_VERSION 09)
-set(RESINSIGHT_PATCH_VERSION 2)
+set(RESINSIGHT_PATCH_VERSION 3)
# Opional text with no restrictions
set(RESINSIGHT_VERSION_TEXT "-dev")
@@ -11,7 +11,7 @@ set(RESINSIGHT_VERSION_TEXT "-dev")
# Must be unique and increasing within one combination of major/minor/patch version
# The uniqueness of this text is independent of RESINSIGHT_VERSION_TEXT
# Format of text must be ".xx"
-set(RESINSIGHT_DEV_VERSION ".04")
+set(RESINSIGHT_DEV_VERSION ".01")
set(STRPRODUCTVER ${RESINSIGHT_MAJOR_VERSION}.${RESINSIGHT_MINOR_VERSION}.${RESINSIGHT_PATCH_VERSION}${RESINSIGHT_VERSION_TEXT}${RESINSIGHT_DEV_VERSION})