diff --git a/ApplicationExeCode/Resources/GridAndSummaryEnsemble.svg b/ApplicationExeCode/Resources/GridAndSummaryEnsemble.svg new file mode 100644 index 0000000000..a515af80ea --- /dev/null +++ b/ApplicationExeCode/Resources/GridAndSummaryEnsemble.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ApplicationExeCode/Resources/ResInsight.qrc b/ApplicationExeCode/Resources/ResInsight.qrc index aee7bb79d6..a639764a1c 100644 --- a/ApplicationExeCode/Resources/ResInsight.qrc +++ b/ApplicationExeCode/Resources/ResInsight.qrc @@ -142,6 +142,7 @@ SummaryCases16x16.png SummaryCurve16x16.png SummaryCurveFilter16x16.png + GridAndSummaryEnsemble.svg SummaryEnsemble.svg SummaryGroup16x16.png SummaryPlotLight16x16.png diff --git a/ApplicationLibCode/Commands/CMakeLists_files.cmake b/ApplicationLibCode/Commands/CMakeLists_files.cmake index dad7149947..3c8f3d4c58 100644 --- a/ApplicationLibCode/Commands/CMakeLists_files.cmake +++ b/ApplicationLibCode/Commands/CMakeLists_files.cmake @@ -101,6 +101,8 @@ set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RicCreateContourMapPolygonTools.h ${CMAKE_CURRENT_LIST_DIR}/RicPolygonFromImageDialog.h ${CMAKE_CURRENT_LIST_DIR}/RicCreateSummaryEnsembleFeature.h + ${CMAKE_CURRENT_LIST_DIR}/RicImportGridAndSummaryEnsembleDialog.h + ${CMAKE_CURRENT_LIST_DIR}/RicImportGridAndSummaryEnsembleFeature.h ${CMAKE_CURRENT_LIST_DIR}/RicHistogramPlotTools.h ${CMAKE_CURRENT_LIST_DIR}/RicCreateGridStatisticsPlotFeature.h ) @@ -208,6 +210,8 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RicCreateContourMapPolygonTools.cpp ${CMAKE_CURRENT_LIST_DIR}/RicPolygonFromImageDialog.cpp ${CMAKE_CURRENT_LIST_DIR}/RicCreateSummaryEnsembleFeature.cpp + ${CMAKE_CURRENT_LIST_DIR}/RicImportGridAndSummaryEnsembleDialog.cpp + ${CMAKE_CURRENT_LIST_DIR}/RicImportGridAndSummaryEnsembleFeature.cpp ${CMAKE_CURRENT_LIST_DIR}/RicHistogramPlotTools.cpp ${CMAKE_CURRENT_LIST_DIR}/RicCreateGridStatisticsPlotFeature.cpp ) diff --git a/ApplicationLibCode/Commands/RicImportEnsembleFeature.h b/ApplicationLibCode/Commands/RicImportEnsembleFeature.h index c723df58a2..e969930fd2 100644 --- a/ApplicationLibCode/Commands/RicImportEnsembleFeature.h +++ b/ApplicationLibCode/Commands/RicImportEnsembleFeature.h @@ -26,6 +26,7 @@ #include class RimSummaryCase; +class RimSummaryEnsemble; //================================================================================================== /// @@ -41,6 +42,12 @@ class RicImportEnsembleFeature : public caf::CmdFeature RiaDefines::EnsembleGroupingMode groupingMode, bool isEnsemble = false ); + static RimSummaryEnsemble* importSingleEnsembleFileSet( const QStringList& fileNames, + bool useEnsembleNameDialog, + RiaDefines::EnsembleGroupingMode groupingMode, + RiaDefines::FileType fileType, + const QString& defaultEnsembleName = QString() ); + private: void onActionTriggered( bool isChecked ) override; void setupActionLook( QAction* actionToSetup ) override; @@ -51,10 +58,4 @@ class RicImportEnsembleFeature : public caf::CmdFeature RiaDefines::EnsembleGroupingMode groupingMode, RiaDefines::FileType fileType, const QString& defaultEnsembleName = QString() ); - - static RimSummaryEnsemble* importSingleEnsembleFileSet( const QStringList& fileNames, - bool useEnsembleNameDialog, - RiaDefines::EnsembleGroupingMode groupingMode, - RiaDefines::FileType fileType, - const QString& defaultEnsembleName = QString() ); }; diff --git a/ApplicationLibCode/Commands/RicImportGridAndSummaryEnsembleDialog.cpp b/ApplicationLibCode/Commands/RicImportGridAndSummaryEnsembleDialog.cpp new file mode 100644 index 0000000000..6b695ed5d2 --- /dev/null +++ b/ApplicationLibCode/Commands/RicImportGridAndSummaryEnsembleDialog.cpp @@ -0,0 +1,758 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2026- 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 "RicImportGridAndSummaryEnsembleDialog.h" + +#include "RicRecursiveFileSearchDialog.h" + +#include "RiaApplication.h" +#include "RiaEnsembleNameTools.h" +#include "RiaFilePathTools.h" +#include "RiaFileSearchTools.h" +#include "RiaPreferences.h" +#include "RiaStdStringTools.h" +#include "RiaStringListSerializer.h" + +#include "RiuFileDialogTools.h" +#include "RiuTools.h" + +#include "cafAppEnum.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RicImportGridAndSummaryEnsembleDialogResult + RicImportGridAndSummaryEnsembleDialog::runDialog( QWidget* parent, bool defaultGridChecked, bool defaultSummaryChecked ) +{ + const QString pathRegistryKey = "RicImportGridAndSummaryEnsembleDialog_path"; + + auto* app = RiaApplication::instance(); + + RicImportGridAndSummaryEnsembleDialog dialog( parent ); + { + QSignalBlocker blocker1( dialog.m_pathFilterField ); + + dialog.setWindowTitle( "Import Grid and Summary Ensemble" ); + + // Default path (index 0 fallback) + QString defaultDir = app->lastUsedDialogDirectory( "GRID_SUMMARY_ENSEMBLE" ); + RiaFilePathTools::appendSeparatorIfNo( defaultDir ); + defaultDir += "*"; + dialog.m_pathFilterField->addItem( QDir::toNativeSeparators( defaultDir ) ); + + // Registry history starts at index 1 + RicRecursiveFileSearchDialog::populateComboBoxHistoryFromRegistry( dialog.m_pathFilterField, pathRegistryKey ); + + // Use most recently used path from registry (index 1) when preference is enabled + if ( RiaPreferences::current()->useRecentlyUsedFolderAsDefault() && dialog.m_pathFilterField->count() > 1 ) + { + dialog.m_pathFilterField->setCurrentIndex( 1 ); + } + + dialog.m_pathFilterField->setEditable( true ); + + for ( const auto& s : caf::AppEnum::uiTexts() ) + { + dialog.m_ensembleGroupingMode->addItem( s ); + } + + dialog.m_createGridEnsembleCheckBox->setChecked( defaultGridChecked ); + dialog.m_createSummaryEnsembleCheckBox->setChecked( defaultSummaryChecked ); + + dialog.updateEffectiveFilter(); + dialog.clearFileList(); + dialog.setOkButtonEnabled( false ); + + dialog.resize( 800, 150 ); + } + + if ( dialog.exec() != QDialog::Accepted ) + { + return {}; + } + + // Save registry history + const int maxItemsInRegistry = 10; + { + RiaStringListSerializer s( pathRegistryKey ); + s.addString( dialog.m_pathFilterField->currentText(), maxItemsInRegistry ); + } + + // Save last used directory + QString rootDir = dialog.rootDirWithSeparator(); + if ( !rootDir.isEmpty() ) + { + QString dirToSave = rootDir; + if ( dirToSave.endsWith( RiaFilePathTools::separator() ) ) dirToSave.chop( 1 ); + app->setLastUsedDialogDirectory( "GRID_SUMMARY_ENSEMBLE", dirToSave ); + } + + // Collect checked realization items + RicImportGridAndSummaryEnsembleDialogResult result; + result.ok = true; + result.rootDir = dialog.rootDirWithSeparator(); + result.pathFilter = dialog.pathFilterWithoutRoot(); + result.groupingMode = dialog.ensembleGroupingMode(); + result.createGridEnsemble = dialog.m_createGridEnsembleCheckBox->isChecked(); + result.createSummaryEnsemble = dialog.m_createSummaryEnsembleCheckBox->isChecked(); + + auto* rootItem = dialog.m_filePathModel.invisibleRootItem(); + for ( int i = 0; i < rootItem->rowCount(); ++i ) + { + auto ensembleItem = rootItem->child( i ); + if ( !ensembleItem ) continue; + + for ( int j = 0; j < ensembleItem->rowCount(); ++j ) + { + auto childItem = ensembleItem->child( j ); + if ( !childItem || !childItem->isCheckable() ) continue; + if ( childItem->checkState() != Qt::Checked ) continue; + + QString basePath = childItem->data( Qt::UserRole ).toString(); + if ( basePath.isEmpty() ) continue; + + auto it = dialog.m_foundRealizations.find( basePath ); + if ( it == dialog.m_foundRealizations.end() ) continue; + + if ( !it->gridFile.isEmpty() ) result.gridFiles.append( it->gridFile ); + if ( !it->summaryFile.isEmpty() ) result.summaryFiles.append( it->summaryFile ); + } + } + + return result; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RicImportGridAndSummaryEnsembleDialog::RicImportGridAndSummaryEnsembleDialog( QWidget* parent ) + : QDialog( parent, RiuTools::defaultDialogFlags() ) + , m_blockItemUpdates( false ) +{ + // Create widgets + m_pathFilterField = new QComboBox(); + m_browseButton = new QPushButton( "..." ); + m_effectiveFilterLabel = new QLabel(); + m_effectiveFilterLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); + m_searchButton = new QPushButton( "Search" ); + m_searchButton->setFixedWidth( 75 ); + + m_useRealizationStarCheckBox = new QCheckBox( "Use 'realization-*' in filter" ); + m_ensembleGroupingMode = new QComboBox(); + + m_createGridEnsembleCheckBox = new QCheckBox( "Create Grid Ensemble" ); + m_createSummaryEnsembleCheckBox = new QCheckBox( "Create Summary Ensemble" ); + m_createGridEnsembleCheckBox->setChecked( true ); + m_createSummaryEnsembleCheckBox->setChecked( true ); + + m_outputGroup = new QGroupBox( "Files Found" ); + m_treeFilterLineEdit = new QLineEdit(); + m_treeFilterLineEdit->setPlaceholderText( "Select Realizations: 1, 5-7, !4, 9-18:3" ); + m_treeFilterButton = new QPushButton( "Apply" ); + m_fileTreeView = new QTreeView(); + + m_buttons = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel ); + + // Tree view setup + m_fileTreeView->setModel( &m_filePathModel ); + m_fileTreeView->setHeaderHidden( true ); + m_fileTreeView->setSelectionMode( QAbstractItemView::ExtendedSelection ); + m_fileTreeView->setContextMenuPolicy( Qt::CustomContextMenu ); + m_fileTreeView->setVisible( false ); + m_fileTreeView->setMinimumHeight( 350 ); + + m_browseButton->setFixedWidth( 25 ); + + // Initially hide filter widgets + m_treeFilterLineEdit->setVisible( false ); + m_treeFilterButton->setVisible( false ); + + // Connect signals + connect( m_pathFilterField, SIGNAL( currentTextChanged( const QString& ) ), this, SLOT( slotPathFilterChanged( const QString& ) ) ); + connect( m_pathFilterField, SIGNAL( editTextChanged( const QString& ) ), this, SLOT( slotPathFilterChanged( const QString& ) ) ); + + connect( m_browseButton, SIGNAL( clicked() ), this, SLOT( slotBrowseClicked() ) ); + connect( m_useRealizationStarCheckBox, SIGNAL( clicked() ), this, SLOT( slotUseRealizationStarClicked() ) ); + connect( m_searchButton, SIGNAL( clicked() ), this, SLOT( slotSearchClicked() ) ); + connect( m_treeFilterButton, SIGNAL( clicked() ), this, SLOT( slotFilterTreeViewClicked() ) ); + connect( m_treeFilterLineEdit, &QLineEdit::returnPressed, m_treeFilterButton, &QPushButton::click ); + connect( m_treeFilterLineEdit, &QLineEdit::textEdited, m_treeFilterButton, &QPushButton::click ); + + connect( m_buttons, SIGNAL( accepted() ), this, SLOT( slotOkClicked() ) ); + connect( m_buttons, SIGNAL( rejected() ), this, SLOT( slotCancelClicked() ) ); + + m_buttons->button( QDialogButtonBox::Ok )->setDefault( true ); + + // itemChanged: propagate check state to children and upward + QObject::connect( &m_filePathModel, + &QStandardItemModel::itemChanged, + [this]( QStandardItem* item ) + { + if ( m_blockItemUpdates ) return; + + if ( item->isCheckable() ) + { + RicRecursiveFileSearchDialog::setCheckedStateChildItems( item, item->checkState() ); + } + + if ( item->checkState() == Qt::Checked ) + { + auto parentItem = item->parent(); + if ( parentItem ) + { + m_blockItemUpdates = true; + parentItem->setCheckState( Qt::Checked ); + m_blockItemUpdates = false; + } + } + } ); + + // Build layout + QVBoxLayout* dialogLayout = new QVBoxLayout(); + + // Search group + QGroupBox* searchGroup = new QGroupBox( "Search" ); + QGridLayout* searchGridLayout = new QGridLayout(); + int row = 0; + + searchGridLayout->addWidget( new QLabel( "Path pattern" ), row, 0 ); + searchGridLayout->addWidget( m_pathFilterField, row, 1, 1, 2 ); + searchGridLayout->addWidget( m_browseButton, row, 3 ); + + row++; + { + QHBoxLayout* hLayout = new QHBoxLayout(); + hLayout->addWidget( m_useRealizationStarCheckBox ); + hLayout->addWidget( new QLabel( "Ensemble Grouping" ) ); + hLayout->addWidget( m_ensembleGroupingMode ); + hLayout->addStretch( 1 ); + searchGridLayout->addLayout( hLayout, row, 1 ); + } + + row++; + searchGridLayout->addWidget( new QLabel( "Effective filter" ), row, 0 ); + searchGridLayout->addWidget( m_effectiveFilterLabel, row, 1, 1, 2 ); + searchGridLayout->addWidget( m_searchButton, row, 3 ); + + searchGroup->setLayout( searchGridLayout ); + + // Import group + QGroupBox* importGroup = new QGroupBox( "Import" ); + QHBoxLayout* importLayout = new QHBoxLayout(); + importLayout->addWidget( m_createGridEnsembleCheckBox ); + importLayout->addWidget( m_createSummaryEnsembleCheckBox ); + importLayout->addStretch( 1 ); + importGroup->setLayout( importLayout ); + + // Files Found group + QGridLayout* outputGridLayout = new QGridLayout(); + outputGridLayout->addWidget( m_treeFilterLineEdit, 0, 0 ); + outputGridLayout->addWidget( m_treeFilterButton, 0, 1 ); + outputGridLayout->addWidget( m_fileTreeView, 1, 0, 1, 2 ); + m_outputGroup->setLayout( outputGridLayout ); + + dialogLayout->addWidget( searchGroup ); + dialogLayout->addWidget( importGroup ); + dialogLayout->addWidget( m_outputGroup ); + dialogLayout->addWidget( m_buttons ); + + setLayout( dialogLayout ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RicImportGridAndSummaryEnsembleDialog::cleanPathFilter() const +{ + QString pathFilterText = m_pathFilterField->currentText().trimmed(); + pathFilterText = RiaFilePathTools::toInternalSeparator( pathFilterText ); + pathFilterText = RiaFilePathTools::removeDuplicatePathSeparators( pathFilterText ); + pathFilterText.replace( QString( "**" ), QString( "*" ) ); + + if ( m_useRealizationStarCheckBox->isChecked() ) + { + const QString pattern = "realization-\\d+"; + QRegularExpression regexp( pattern, QRegularExpression::CaseInsensitiveOption ); + pathFilterText.replace( regexp, "realization-*" ); + } + + return pathFilterText; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RicImportGridAndSummaryEnsembleDialog::rootDirWithSeparator() const +{ + QString rootDir = RiaFilePathTools::rootSearchPathFromSearchFilter( cleanPathFilter() ); + return RiaFilePathTools::appendSeparatorIfNo( rootDir ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RicImportGridAndSummaryEnsembleDialog::pathFilterWithoutRoot() const +{ + QString pathFilter = cleanPathFilter(); + QString rootDir = RiaFilePathTools::rootSearchPathFromSearchFilter( pathFilter ); + + pathFilter.remove( 0, rootDir.size() ); + if ( pathFilter.startsWith( RiaFilePathTools::separator() ) ) pathFilter.remove( 0, 1 ); + return pathFilter; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicImportGridAndSummaryEnsembleDialog::updateEffectiveFilter() +{ + QString pathFilter = pathFilterWithoutRoot(); + if ( pathFilter == "*" || pathFilter.endsWith( QString( RiaFilePathTools::separator() ) + "*" ) ) + { + pathFilter.chop( 1 ); + pathFilter = pathFilter + "..."; + } + + QString effFilter = QString( "%1%2/*.EGRID|SMSPEC|ESMRY" ).arg( rootDirWithSeparator() ).arg( pathFilter ); + effFilter = RiaFilePathTools::removeDuplicatePathSeparators( effFilter ); + + m_effectiveFilterLabel->setText( QDir::toNativeSeparators( effFilter ) ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicImportGridAndSummaryEnsembleDialog::setOkButtonEnabled( bool enabled ) +{ + m_buttons->button( QDialogButtonBox::Ok )->setEnabled( enabled ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QStringList RicImportGridAndSummaryEnsembleDialog::findMatchingFiles( const QStringList& extensions ) +{ + if ( cleanPathFilter().isEmpty() ) return {}; + + QString rootDir = rootDirWithSeparator(); + QString pathFilter = pathFilterWithoutRoot(); + if ( rootDir.size() > 1 && rootDir.endsWith( RiaFilePathTools::separator() ) ) rootDir.chop( 1 ); + + QStringList matchingFolders; + RiaFileSearchTools::findMatchingFoldersRecursively( rootDir, pathFilter, matchingFolders ); + + QStringList nameFilters; + for ( const auto& ext : extensions ) + { + nameFilters.append( "*." + ext ); + } + + return RiaFileSearchTools::findFilesInFolders( matchingFolders, nameFilters ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicImportGridAndSummaryEnsembleDialog::updateFileListWidget() +{ + m_filePathModel.clear(); + m_foundRealizations.clear(); + + QStringList gridFiles = findMatchingFiles( { "EGRID" } ); + QStringList summaryFiles = findMatchingFiles( { "SMSPEC", "ESMRY" } ); + + // Sort so ESMRY comes before SMSPEC (alphabetically E < S) + std::sort( summaryFiles.begin(), summaryFiles.end() ); + + QString rootDir = rootDirWithSeparator(); + int rootLen = rootDir.size(); + + // Build m_foundRealizations map using relative base path (no extension) as key + for ( const auto& filePath : gridFiles ) + { + QString rel = filePath.mid( rootLen ); + QFileInfo fi( rel ); + QString basePath = QDir::cleanPath( fi.path() + "/" + fi.completeBaseName() ); + if ( !m_foundRealizations.contains( basePath ) ) + { + m_foundRealizations[basePath].gridFile = filePath; + } + else if ( m_foundRealizations[basePath].gridFile.isEmpty() ) + { + m_foundRealizations[basePath].gridFile = filePath; + } + } + + for ( const auto& filePath : summaryFiles ) + { + QString rel = filePath.mid( rootLen ); + QFileInfo fi( rel ); + QString basePath = QDir::cleanPath( fi.path() + "/" + fi.completeBaseName() ); + if ( !m_foundRealizations.contains( basePath ) ) + { + m_foundRealizations[basePath].summaryFile = filePath; + } + else if ( m_foundRealizations[basePath].summaryFile.isEmpty() ) + { + // Only store first match per base (ESMRY wins since it sorts before SMSPEC alphabetically) + m_foundRealizations[basePath].summaryFile = filePath; + } + } + + if ( m_foundRealizations.isEmpty() ) return; + + auto mode = ensembleGroupingMode(); + + if ( mode == RiaDefines::EnsembleGroupingMode::NONE ) + { + // Single "Files" ensemble item, one child per base path + auto rootItem = m_filePathModel.invisibleRootItem(); + auto ensembleItem = new QStandardItem( "Files" ); + ensembleItem->setCheckable( true ); + ensembleItem->setCheckState( Qt::Checked ); + rootItem->appendRow( ensembleItem ); + + for ( auto it = m_foundRealizations.begin(); it != m_foundRealizations.end(); ++it ) + { + auto childItem = new QStandardItem( QDir::toNativeSeparators( it.key() ) ); + childItem->setCheckable( true ); + childItem->setCheckState( Qt::Checked ); + childItem->setData( it.key(), Qt::UserRole ); + ensembleItem->appendRow( childItem ); + } + } + else + { + // Build list of representative files for grouping (grid if available, else summary) + QStringList representativeFiles; + QMap repFileToBasePath; + for ( auto it = m_foundRealizations.begin(); it != m_foundRealizations.end(); ++it ) + { + QString rep = it.value().gridFile.isEmpty() ? it.value().summaryFile : it.value().gridFile; + if ( !rep.isEmpty() ) + { + representativeFiles.append( rep ); + repFileToBasePath[rep] = it.key(); + } + } + + // Sort numerically + QCollator collator; + collator.setNumericMode( true ); + std::sort( representativeFiles.begin(), representativeFiles.end(), collator ); + + auto grouping = RiaEnsembleNameTools::groupFilesByEnsembleName( representativeFiles, mode ); + + auto rootItem = m_filePathModel.invisibleRootItem(); + for ( const auto& [groupName, groupFiles] : grouping ) + { + auto ensembleItem = new QStandardItem( QDir::toNativeSeparators( groupName ) ); + ensembleItem->setCheckable( true ); + ensembleItem->setCheckState( Qt::Checked ); + rootItem->appendRow( ensembleItem ); + + for ( const auto& repFile : groupFiles ) + { + QString basePath = repFileToBasePath.value( repFile ); + if ( basePath.isEmpty() ) continue; + + auto childItem = new QStandardItem( QDir::toNativeSeparators( basePath ) ); + childItem->setCheckable( true ); + childItem->setCheckState( Qt::Checked ); + childItem->setData( basePath, Qt::UserRole ); + ensembleItem->appendRow( childItem ); + } + } + } + + // Expand first item + if ( m_filePathModel.rowCount() > 0 ) + { + QModelIndex index = m_filePathModel.index( 0, 0 ); + m_fileTreeView->expand( index ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicImportGridAndSummaryEnsembleDialog::clearFileList() +{ + m_foundRealizations.clear(); + m_filePathModel.clear(); + m_outputGroup->setTitle( "Files Found" ); + setOkButtonEnabled( false ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RiaDefines::EnsembleGroupingMode RicImportGridAndSummaryEnsembleDialog::ensembleGroupingMode() const +{ + if ( m_ensembleGroupingMode->currentIndex() == 0 ) return RiaDefines::EnsembleGroupingMode::FMU_FOLDER_STRUCTURE; + if ( m_ensembleGroupingMode->currentIndex() == 1 ) return RiaDefines::EnsembleGroupingMode::EVEREST_FOLDER_STRUCTURE; + if ( m_ensembleGroupingMode->currentIndex() == 2 ) return RiaDefines::EnsembleGroupingMode::NONE; + if ( m_ensembleGroupingMode->currentIndex() == 3 ) return RiaDefines::EnsembleGroupingMode::RESINSIGHT_OPMFLOW_STRUCTURE; + return RiaDefines::EnsembleGroupingMode::FMU_FOLDER_STRUCTURE; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicImportGridAndSummaryEnsembleDialog::slotPathFilterChanged( const QString& /*text*/ ) +{ + updateEffectiveFilter(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicImportGridAndSummaryEnsembleDialog::slotBrowseClicked() +{ + QString folder = RiuFileDialogTools::getExistingDirectory( this, "Select folder", rootDirWithSeparator() ); + if ( folder.isEmpty() ) return; + RiaFilePathTools::appendSeparatorIfNo( folder ); + folder += "*"; + m_pathFilterField->addItem( QDir::toNativeSeparators( folder ) ); + m_pathFilterField->setCurrentText( QDir::toNativeSeparators( folder ) ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicImportGridAndSummaryEnsembleDialog::slotUseRealizationStarClicked() +{ + updateEffectiveFilter(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- +/// Strip trailing realization number and separator from a filename stem, e.g. +/// "ECLIPSE_001" -> "ECLIPSE", "MODEL-3" -> "MODEL", "BASE" -> "BASE" +//-------------------------------------------------------------------------------------------------- +static QString stripTrailingRealizationNumber( const QString& name ) +{ + static const QRegularExpression re( "[_\\-]?\\d+$" ); + QString stripped = name; + stripped.remove( re ); + return stripped; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QStringList RicImportGridAndSummaryEnsembleDialog::checkForMultipleFilenamesAndUncheckOutliers() +{ + QStringList warnings; + + auto* rootItem = m_filePathModel.invisibleRootItem(); + for ( int i = 0; i < rootItem->rowCount(); ++i ) + { + auto ensembleItem = rootItem->child( i ); + if ( !ensembleItem ) continue; + + // Count occurrences of each canonical filename (trailing realization number stripped) + QMap canonicalCounts; + for ( int j = 0; j < ensembleItem->rowCount(); ++j ) + { + auto childItem = ensembleItem->child( j ); + if ( !childItem ) continue; + + QString basePath = childItem->data( Qt::UserRole ).toString(); + if ( basePath.isEmpty() ) continue; + + QString canonical = stripTrailingRealizationNumber( QFileInfo( basePath ).fileName() ); + canonicalCounts[canonical]++; + } + + if ( canonicalCounts.size() <= 1 ) continue; + + // Find the most common canonical filename + QString mostCommon; + int maxCount = 0; + for ( auto it = canonicalCounts.constBegin(); it != canonicalCounts.constEnd(); ++it ) + { + if ( it.value() > maxCount ) + { + maxCount = it.value(); + mostCommon = it.key(); + } + } + + // Untick outliers + QStringList outlierNames; + m_blockItemUpdates = true; + for ( int j = 0; j < ensembleItem->rowCount(); ++j ) + { + auto childItem = ensembleItem->child( j ); + if ( !childItem ) continue; + + QString basePath = childItem->data( Qt::UserRole ).toString(); + QString canonical = stripTrailingRealizationNumber( QFileInfo( basePath ).fileName() ); + if ( canonical != mostCommon ) + { + childItem->setCheckState( Qt::Unchecked ); + if ( !outlierNames.contains( canonical ) ) outlierNames.append( canonical ); + } + } + m_blockItemUpdates = false; + + QStringList allNames = canonicalCounts.keys(); + warnings.append( QString( "Ensemble '%1' contains multiple filenames: '%2'.\n" + "Only '%3' (%4 realizations) is selected. Deselected: '%5'." ) + .arg( ensembleItem->text() ) + .arg( allNames.join( ", " ) ) + .arg( mostCommon ) + .arg( maxCount ) + .arg( outlierNames.join( ", " ) ) ); + } + + return warnings; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicImportGridAndSummaryEnsembleDialog::slotSearchClicked() +{ + clearFileList(); + + m_treeFilterLineEdit->setVisible( true ); + m_treeFilterButton->setVisible( true ); + + if ( !m_fileTreeView->isVisible() ) + { + m_fileTreeView->setVisible( true ); + if ( height() < 550 ) resize( width(), 550 ); + } + + updateFileListWidget(); + + int realizationCount = m_foundRealizations.size(); + if ( realizationCount > 0 ) + { + m_outputGroup->setTitle( QString( "Files Found (%1)" ).arg( realizationCount ) ); + setOkButtonEnabled( true ); + m_buttons->button( QDialogButtonBox::Ok )->setFocus(); + } + else + { + m_outputGroup->setTitle( "Files Found" ); + + // Add "No files found" status item + auto rootItem = m_filePathModel.invisibleRootItem(); + auto statusItem = new QStandardItem( "No files found" ); + rootItem->appendRow( statusItem ); + } + + QStringList warnings = checkForMultipleFilenamesAndUncheckOutliers(); + if ( !warnings.isEmpty() ) + { + QMessageBox msgBox( QMessageBox::Warning, "Multiple Filenames Found", warnings.join( "\n\n" ), QMessageBox::Ok, this ); + // Force the dialog wide enough so LF-separated lines are not word-wrapped + auto* layout = qobject_cast( msgBox.layout() ); + if ( layout ) + { + auto* spacer = new QSpacerItem( 600, 0, QSizePolicy::Minimum, QSizePolicy::Expanding ); + layout->addItem( spacer, layout->rowCount(), 0, 1, layout->columnCount() ); + } + msgBox.exec(); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicImportGridAndSummaryEnsembleDialog::slotFilterTreeViewClicked() +{ + QString filterText = m_treeFilterLineEdit->text(); + auto values = RiaStdStringTools::valuesFromRangeSelection( filterText.toStdString() ); + + auto items = RicRecursiveFileSearchDialog::firstLevelItems( m_filePathModel.invisibleRootItem() ); + for ( auto item : items ) + { + if ( item->checkState() == Qt::Unchecked ) continue; + + if ( filterText.isEmpty() ) + { + RicRecursiveFileSearchDialog::setCheckedStateChildItems( item, Qt::Checked ); + } + else + { + RicRecursiveFileSearchDialog::setCheckedStateChildItems( item, Qt::Unchecked ); + + for ( auto val : values ) + { + QString searchString = "realization-" + QString::number( val ); + + QList matchingItems; + RicRecursiveFileSearchDialog::findItemsMatching( item, searchString, matchingItems ); + for ( auto matchedItem : matchingItems ) + { + matchedItem->setCheckState( Qt::Checked ); + } + } + } + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicImportGridAndSummaryEnsembleDialog::slotOkClicked() +{ + accept(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicImportGridAndSummaryEnsembleDialog::slotCancelClicked() +{ + reject(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicImportGridAndSummaryEnsembleDialog::showEvent( QShowEvent* event ) +{ + m_searchButton->setFocus(); + QDialog::showEvent( event ); +} diff --git a/ApplicationLibCode/Commands/RicImportGridAndSummaryEnsembleDialog.h b/ApplicationLibCode/Commands/RicImportGridAndSummaryEnsembleDialog.h new file mode 100644 index 0000000000..19653c2125 --- /dev/null +++ b/ApplicationLibCode/Commands/RicImportGridAndSummaryEnsembleDialog.h @@ -0,0 +1,116 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2026- 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 "Summary/RiaSummaryDefines.h" + +#include +#include +#include +#include +#include + +class QCheckBox; +class QComboBox; +class QDialogButtonBox; +class QGroupBox; +class QLabel; +class QLineEdit; +class QPushButton; +class QTreeView; + +struct RicImportGridAndSummaryEnsembleDialogResult +{ + bool ok; + QStringList gridFiles; + QStringList summaryFiles; + QString rootDir; + QString pathFilter; + RiaDefines::EnsembleGroupingMode groupingMode; + bool createGridEnsemble; + bool createSummaryEnsemble; +}; + +//================================================================================================== +/// +//================================================================================================== +class RicImportGridAndSummaryEnsembleDialog : public QDialog +{ + Q_OBJECT + +public: + static RicImportGridAndSummaryEnsembleDialogResult runDialog( QWidget* parent, bool defaultGridChecked, bool defaultSummaryChecked ); + +private: + explicit RicImportGridAndSummaryEnsembleDialog( QWidget* parent ); + + QString cleanPathFilter() const; + QString rootDirWithSeparator() const; + QString pathFilterWithoutRoot() const; + void updateEffectiveFilter(); + void setOkButtonEnabled( bool enabled ); + + QStringList findMatchingFiles( const QStringList& extensions ); + + void updateFileListWidget(); + void clearFileList(); + QStringList checkForMultipleFilenamesAndUncheckOutliers(); + + RiaDefines::EnsembleGroupingMode ensembleGroupingMode() const; + +private slots: + void slotPathFilterChanged( const QString& text ); + void slotBrowseClicked(); + void slotUseRealizationStarClicked(); + void slotSearchClicked(); + void slotFilterTreeViewClicked(); + void slotOkClicked(); + void slotCancelClicked(); + void showEvent( QShowEvent* event ) override; + +private: + struct RealizationFiles + { + QString gridFile; + QString summaryFile; + }; + + QComboBox* m_pathFilterField; + QPushButton* m_browseButton; + QCheckBox* m_useRealizationStarCheckBox; + QComboBox* m_ensembleGroupingMode; + QLabel* m_effectiveFilterLabel; + QPushButton* m_searchButton; + + QCheckBox* m_createGridEnsembleCheckBox; + QCheckBox* m_createSummaryEnsembleCheckBox; + + QGroupBox* m_outputGroup; + QLineEdit* m_treeFilterLineEdit; + QPushButton* m_treeFilterButton; + QTreeView* m_fileTreeView; + + QDialogButtonBox* m_buttons; + + QStandardItemModel m_filePathModel; + + QMap m_foundRealizations; + + bool m_blockItemUpdates; +}; diff --git a/ApplicationLibCode/Commands/RicImportGridAndSummaryEnsembleFeature.cpp b/ApplicationLibCode/Commands/RicImportGridAndSummaryEnsembleFeature.cpp new file mode 100644 index 0000000000..411af90a5f --- /dev/null +++ b/ApplicationLibCode/Commands/RicImportGridAndSummaryEnsembleFeature.cpp @@ -0,0 +1,126 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2026- 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 "RicImportGridAndSummaryEnsembleFeature.h" + +#include "RiaGuiApplication.h" +#include "Summary/RiaSummaryPlotTools.h" + +#include "RicImportGridAndSummaryEnsembleDialog.h" +#include "RicNewViewFeature.h" + +#include "EnsembleFileSet/RimEnsembleFileSetTools.h" +#include "Rim3dView.h" +#include "RimReservoirGridEnsemble.h" +#include "RimSummaryEnsemble.h" +#include "RimViewNameConfig.h" + +#include "RiuPlotMainWindow.h" +#include "RiuPlotMainWindowTools.h" + +#include +#include +#include +#include + +CAF_CMD_SOURCE_INIT( RicImportGridAndSummaryEnsembleFeature, "RicImportGridAndSummaryEnsembleFeature" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicImportGridAndSummaryEnsembleFeature::onActionTriggered( bool isChecked ) +{ + QPointer activeWindowBeforeImport = RiaGuiApplication::widgetToUseAsParent(); + + bool startedFromPlotWindow = ( RiaGuiApplication::activeMainWindow() == RiaGuiApplication::instance()->mainPlotWindow() ); + + auto result = RicImportGridAndSummaryEnsembleDialog::runDialog( activeWindowBeforeImport, !startedFromPlotWindow, startedFromPlotWindow ); + + if ( !result.ok ) return; + if ( !result.createGridEnsemble && !result.createSummaryEnsemble ) return; + + // Build the union of base paths (extension-stripped) from both lists so every realization + // appears exactly once. findPathPattern requires each realization number to be unique across + // the input; duplicating paths with different extensions breaks the pattern detection. + auto stripExtension = []( const QString& path ) -> QString + { + int dot = path.lastIndexOf( '.' ); + return dot != -1 ? path.left( dot ) : path; + }; + + QStringList strippedPaths; + + for ( const auto& f : result.gridFiles ) + strippedPaths << stripExtension( f ); + for ( const auto& f : result.summaryFiles ) + strippedPaths << stripExtension( f ); + + strippedPaths.removeDuplicates(); + + if ( strippedPaths.isEmpty() ) return; + + auto fileSets = RimEnsembleFileSetTools::createEnsembleFileSets( strippedPaths, result.groupingMode ); + if ( fileSets.empty() ) return; + + bool gridEnsemblesCreated = false; + if ( result.createGridEnsemble && !result.gridFiles.isEmpty() ) + { + auto gridEnsembles = RimEnsembleFileSetTools::createGridEnsemblesFromFileSets( fileSets ); + if ( !gridEnsembles.empty() ) + { + gridEnsemblesCreated = true; + auto firstEnsemble = gridEnsembles.front(); + auto cases = firstEnsemble->cases(); + if ( !cases.empty() ) + { + auto view = RicNewViewFeature::addReservoirView( cases.front(), nullptr, firstEnsemble->viewCollection() ); + if ( view ) view->nameConfig()->setAddCaseName( true ); + } + } + } + + if ( result.createSummaryEnsemble && !result.summaryFiles.isEmpty() ) + { + auto summaryEnsembles = RimEnsembleFileSetTools::createSummaryEnsemblesFromFileSets( fileSets ); + for ( auto ensemble : summaryEnsembles ) + { + RiaSummaryPlotTools::createAndAppendDefaultSummaryMultiPlot( {}, { ensemble } ); + } + if ( !summaryEnsembles.empty() ) RiuPlotMainWindowTools::showPlotMainWindow(); + } + + if ( gridEnsemblesCreated && activeWindowBeforeImport ) + { + QTimer::singleShot( 500, + activeWindowBeforeImport, + [activeWindowBeforeImport]() + { + activeWindowBeforeImport->raise(); + activeWindowBeforeImport->activateWindow(); + } ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicImportGridAndSummaryEnsembleFeature::setupActionLook( QAction* actionToSetup ) +{ + actionToSetup->setIcon( QIcon( ":/GridAndSummaryEnsemble.svg" ) ); + actionToSetup->setText( "Import Grid and Summary Ensemble" ); +} diff --git a/ApplicationLibCode/Commands/RicImportGridAndSummaryEnsembleFeature.h b/ApplicationLibCode/Commands/RicImportGridAndSummaryEnsembleFeature.h new file mode 100644 index 0000000000..e832de180f --- /dev/null +++ b/ApplicationLibCode/Commands/RicImportGridAndSummaryEnsembleFeature.h @@ -0,0 +1,33 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2026- 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 RicImportGridAndSummaryEnsembleFeature : public caf::CmdFeature +{ + CAF_CMD_HEADER_INIT; + +protected: + void onActionTriggered( bool isChecked ) override; + void setupActionLook( QAction* actionToSetup ) override; +}; diff --git a/ApplicationLibCode/Commands/RicRecursiveFileSearchDialog.cpp b/ApplicationLibCode/Commands/RicRecursiveFileSearchDialog.cpp index f44a7682dd..3bf7a4a0fb 100644 --- a/ApplicationLibCode/Commands/RicRecursiveFileSearchDialog.cpp +++ b/ApplicationLibCode/Commands/RicRecursiveFileSearchDialog.cpp @@ -59,69 +59,6 @@ namespace { -// Get all first level items, usually the ensemble items -auto firstLevelItems = []( QStandardItem* rootItem ) -> QList -{ - QList firstLevelItems; - - for ( int i = 0; i < rootItem->rowCount(); ++i ) - { - QStandardItem* item = rootItem->child( i ); - if ( item ) - { - firstLevelItems.append( item ); - } - } - - return firstLevelItems; -}; - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void setCheckedStateChildItems( QStandardItem* parentItem, Qt::CheckState checkState ) -{ - if ( !parentItem ) return; - - for ( int i = 0; i < parentItem->rowCount(); ++i ) - { - auto childItem = parentItem->child( i ); - if ( childItem && childItem->isCheckable() ) - { - childItem->setCheckState( checkState ); - } - - setCheckedStateChildItems( childItem, checkState ); - } -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void findItemsMatching( QStandardItem* parentItem, const QString& substring, QList& matchingItems ) -{ - if ( !parentItem ) return; - - for ( int i = 0; i < parentItem->rowCount(); ++i ) - { - auto searchString = substring + "/"; - - auto childItem = parentItem->child( i ); - if ( childItem ) - { - auto textToMatch = childItem->text(); - textToMatch.replace( '\\', '/' ); - - if ( childItem && textToMatch.contains( searchString, Qt::CaseInsensitive ) ) - { - matchingItems.append( childItem ); - } - } - - findItemsMatching( childItem, substring, matchingItems ); - } -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -799,6 +736,60 @@ QString RicRecursiveFileSearchDialog::replaceWithRealizationStar( const QString& return textWithStar; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QList RicRecursiveFileSearchDialog::firstLevelItems( QStandardItem* rootItem ) +{ + QList items; + for ( int i = 0; i < rootItem->rowCount(); ++i ) + { + QStandardItem* item = rootItem->child( i ); + if ( item ) items.append( item ); + } + return items; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicRecursiveFileSearchDialog::setCheckedStateChildItems( QStandardItem* parentItem, Qt::CheckState checkState ) +{ + if ( !parentItem ) return; + for ( int i = 0; i < parentItem->rowCount(); ++i ) + { + auto childItem = parentItem->child( i ); + if ( childItem && childItem->isCheckable() ) + { + childItem->setCheckState( checkState ); + } + setCheckedStateChildItems( childItem, checkState ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicRecursiveFileSearchDialog::findItemsMatching( QStandardItem* parentItem, const QString& substring, QList& matchingItems ) +{ + if ( !parentItem ) return; + for ( int i = 0; i < parentItem->rowCount(); ++i ) + { + auto searchString = substring + "/"; + auto childItem = parentItem->child( i ); + if ( childItem ) + { + auto textToMatch = childItem->text(); + textToMatch.replace( '\\', '/' ); + if ( textToMatch.contains( searchString, Qt::CaseInsensitive ) ) + { + matchingItems.append( childItem ); + } + } + findItemsMatching( childItem, substring, matchingItems ); + } +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/Commands/RicRecursiveFileSearchDialog.h b/ApplicationLibCode/Commands/RicRecursiveFileSearchDialog.h index bae7706a8c..cd2d738060 100644 --- a/ApplicationLibCode/Commands/RicRecursiveFileSearchDialog.h +++ b/ApplicationLibCode/Commands/RicRecursiveFileSearchDialog.h @@ -83,6 +83,11 @@ class RicRecursiveFileSearchDialog : public QDialog static RiaDefines::FileType mapSummaryFileType( RicRecursiveFileSearchDialog::FileType fileType ); + static QList firstLevelItems( QStandardItem* rootItem ); + static void setCheckedStateChildItems( QStandardItem* parentItem, Qt::CheckState checkState ); + static void findItemsMatching( QStandardItem* parentItem, const QString& substring, QList& matchingItems ); + static void populateComboBoxHistoryFromRegistry( QComboBox* comboBox, const QString& registryKey ); + private: RicRecursiveFileSearchDialog( QWidget* parent, const std::vector& fileTypes ); ~RicRecursiveFileSearchDialog() override; @@ -113,8 +118,6 @@ class RicRecursiveFileSearchDialog : public QDialog QStringList createFileNameFilterList(); static QString replaceWithRealizationStar( const QString& text ); - static void populateComboBoxHistoryFromRegistry( QComboBox* comboBox, const QString& registryKey ); - static QStringList fileTypeToExtensionStrings( const std::vector& fileTypes ); private slots: diff --git a/ApplicationLibCode/ProjectDataModel/EnsembleFileSet/RimEnsembleFileSetTools.cpp b/ApplicationLibCode/ProjectDataModel/EnsembleFileSet/RimEnsembleFileSetTools.cpp index 4da25c239f..23fa670f0a 100644 --- a/ApplicationLibCode/ProjectDataModel/EnsembleFileSet/RimEnsembleFileSetTools.cpp +++ b/ApplicationLibCode/ProjectDataModel/EnsembleFileSet/RimEnsembleFileSetTools.cpp @@ -103,6 +103,7 @@ std::vector createGridEnsemblesFromFileSets( const st project->assignIdToCaseGroup( ensemble ); eclipseCaseColl->reservoirGridEnsembles.push_back( ensemble ); + ensemble->createGridCasesFromEnsembleFileSet(); ensemble->loadDataAndUpdate(); ensembles.push_back( ensemble ); } diff --git a/ApplicationLibCode/ProjectDataModel/RimReservoirGridEnsemble.cpp b/ApplicationLibCode/ProjectDataModel/RimReservoirGridEnsemble.cpp index f6a79ce179..b9e07db85c 100644 --- a/ApplicationLibCode/ProjectDataModel/RimReservoirGridEnsemble.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimReservoirGridEnsemble.cpp @@ -482,6 +482,14 @@ RimEclipseView* RimReservoirGridEnsemble::addViewForCase( RimEclipseCase* eclips return m_viewCollection->addView( eclipseCase ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimEclipseViewCollection* RimReservoirGridEnsemble::viewCollection() const +{ + return m_viewCollection; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/RimReservoirGridEnsemble.h b/ApplicationLibCode/ProjectDataModel/RimReservoirGridEnsemble.h index d39c118fd6..e697955b64 100644 --- a/ApplicationLibCode/ProjectDataModel/RimReservoirGridEnsemble.h +++ b/ApplicationLibCode/ProjectDataModel/RimReservoirGridEnsemble.h @@ -106,6 +106,7 @@ class RimReservoirGridEnsemble : public RimNamedObject, public RimReservoirGridE RimEclipseView* addViewForCase( RimEclipseCase* eclipseCase ); std::vector allViews() const; std::set casesInViews() const override; + RimEclipseViewCollection* viewCollection() const; // Well target mapping void addWellTargetMapping( RimWellTargetMapping* wellTargetMapping ); diff --git a/ApplicationLibCode/UserInterface/RiuMainWindow.cpp b/ApplicationLibCode/UserInterface/RiuMainWindow.cpp index 6473b7d0a6..81e0d89e53 100644 --- a/ApplicationLibCode/UserInterface/RiuMainWindow.cpp +++ b/ApplicationLibCode/UserInterface/RiuMainWindow.cpp @@ -596,6 +596,7 @@ void RiuMainWindow::createToolBars() toolbar->addAction( cmdFeatureMgr->action( "RicImportEclipseCaseFeature" ) ); toolbar->addAction( cmdFeatureMgr->action( "RicOpenProjectFeature" ) ); toolbar->addAction( cmdFeatureMgr->action( "RicSaveProjectFeature" ) ); + toolbar->addAction( cmdFeatureMgr->action( "RicImportGridAndSummaryEnsembleFeature" ) ); } if ( RiaPreferences::current()->useUndoRedo() ) diff --git a/ApplicationLibCode/UserInterface/RiuMenuBarBuildTools.cpp b/ApplicationLibCode/UserInterface/RiuMenuBarBuildTools.cpp index 6c4891e435..c43784150c 100644 --- a/ApplicationLibCode/UserInterface/RiuMenuBarBuildTools.cpp +++ b/ApplicationLibCode/UserInterface/RiuMenuBarBuildTools.cpp @@ -123,6 +123,9 @@ void RiuMenuBarBuildTools::addImportMenuWithActions( QObject* parent, QMenu* men QMenu* importRoffMenu = importMenu->addMenu( QIcon( ":/Case48x48.png" ), "Roff Grid Models" ); importRoffMenu->addAction( cmdFeatureMgr->action( "RicImportRoffCaseFeature" ) ); + importMenu->addSeparator(); + importMenu->addAction( cmdFeatureMgr->action( "RicImportGridAndSummaryEnsembleFeature" ) ); + importMenu->addSeparator(); QMenu* importSummaryMenu = importMenu->addMenu( QIcon( ":/SummaryCase.svg" ), "Summary Cases" ); importSummaryMenu->addAction( cmdFeatureMgr->action( "RicImportSummaryCaseFeature" ) ); diff --git a/ApplicationLibCode/UserInterface/RiuPlotMainWindow.cpp b/ApplicationLibCode/UserInterface/RiuPlotMainWindow.cpp index f5b26a49fe..03e8a7a679 100644 --- a/ApplicationLibCode/UserInterface/RiuPlotMainWindow.cpp +++ b/ApplicationLibCode/UserInterface/RiuPlotMainWindow.cpp @@ -394,6 +394,7 @@ QStringList RiuPlotMainWindow::toolbarCommandIds( const QString& toolbarName ) commandIds << "RicImportEclipseCaseFeature"; commandIds << "RicImportSummaryCaseFeature"; commandIds << "RicImportEnsembleFeature"; + commandIds << "RicImportGridAndSummaryEnsembleFeature"; commandIds << "RicOpenProjectFeature"; commandIds << "RicSaveProjectFeature"; }