diff --git a/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelFeature.cpp b/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelFeature.cpp index 39122635a0..fbf55beb9f 100644 --- a/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelFeature.cpp +++ b/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelFeature.cpp @@ -26,8 +26,10 @@ #include "RicExportFeatureImpl.h" #include "RifEclipseInputFileTools.h" +#include "RifOpmFlowDeckFile.h" #include "RifReaderEclipseOutput.h" +#include "ProjectDataModel/Jobs/RimKeywordBcprop.h" #include "Rim3dView.h" #include "RimDialogData.h" #include "RimEclipseCase.h" @@ -53,6 +55,9 @@ #include "cafProgressInfo.h" #include "cafSelectionManager.h" +#include "opm/input/eclipse/Deck/DeckKeyword.hpp" +#include "opm/input/eclipse/Deck/DeckRecord.hpp" + #include #include #include @@ -255,6 +260,72 @@ void RicExportEclipseSectorModelFeature::executeCommand( RimEclipseView* RifEclipseInputFileTools::saveFaults( stream, view->eclipseCase()->mainGrid(), min, max, refinement ); } } + + // Export simulation input if enabled + if ( exportSettings.m_exportSimulationInput() ) + { + // Load the deck file + QFileInfo fi( view->eclipseCase()->gridFileName() ); + QString dataFileName = fi.absolutePath() + "/" + fi.completeBaseName() + ".DATA"; + + RifOpmFlowDeckFile deckFile; + if ( !deckFile.loadDeck( dataFileName.toStdString() ) ) + { + RiaLogging::error( QString( "Unable to load deck file '%1'" ).arg( dataFileName ) ); + return; + } + + // Only change values when exporting to modified box: original values should just work (tm) for full grid box + if ( exportSettings.exportGridBox() != RicExportEclipseSectorModelUi::GridBoxSelection::FULL_GRID_BOX ) + { + // Generate border cell faces + auto borderCellFaces = RigEclipseResultTools::generateBorderCellFaces( view->eclipseCase() ); + + if ( !borderCellFaces.empty() ) + { + // Add BCCON keyword + if ( !deckFile.addBcconKeyword( "GRID", borderCellFaces ) ) + { + RiaLogging::error( "Failed to add BCCON keyword to deck file" ); + } + + // Build BCPROP records from the UI configuration + std::vector bcpropRecords; + for ( const auto& bcprop : exportSettings.m_bcpropKeywords ) + { + if ( bcprop != nullptr ) + { + Opm::DeckKeyword kw = bcprop->keyword(); + const auto& record = kw.getRecord( 0 ); + bcpropRecords.push_back( record ); + } + } + + // Add BCPROP keyword + if ( !deckFile.addBcpropKeyword( "GRID", borderCellFaces, bcpropRecords ) ) + { + RiaLogging::error( "Failed to add BCPROP keyword to deck file" ); + } + } + else + { + RiaLogging::warning( "No border cells found - skipping BCCON/BCPROP keyword generation" ); + } + } + + // Save the modified deck file to the export directory + QFileInfo exportGridInfo( exportSettings.exportGridFilename() ); + QString outputFolder = exportGridInfo.absolutePath(); + QString outputFile = exportGridInfo.completeBaseName() + ".DATA"; + if ( !deckFile.saveDeck( outputFolder.toStdString(), outputFile.toStdString() ) ) + { + RiaLogging::error( QString( "Failed to save modified deck file to '%1/%2'" ).arg( outputFolder ).arg( outputFile ) ); + } + else + { + RiaLogging::info( QString( "Saved modified deck file to '%1/%2'" ).arg( outputFolder ).arg( outputFile ) ); + } + } } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelUi.cpp b/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelUi.cpp index 5732fb0cd7..bc74b64830 100644 --- a/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelUi.cpp +++ b/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelUi.cpp @@ -29,6 +29,8 @@ #include "RigEclipseResultAddress.h" #include "RigMainGrid.h" +#include "ProjectDataModel/Jobs/RimKeywordBcprop.h" +#include "RimEclipseCase.h" #include "RimEclipseResultDefinition.h" #include "RimEclipseView.h" #include "RimSimWellInView.h" @@ -37,6 +39,7 @@ #include "cafPdmUiFilePathEditor.h" #include "cafPdmUiGroup.h" #include "cafPdmUiLineEditor.h" +#include "cafPdmUiTableViewEditor.h" #include "cafPdmUiTreeSelectionEditor.h" #include @@ -134,12 +137,23 @@ RicExportEclipseSectorModelUi::RicExportEclipseSectorModelUi() CAF_PDM_InitFieldNoDefault( &m_exportFolder, "ExportFolder", "Export Folder" ); m_exportFolder = defaultFolder(); + CAF_PDM_InitFieldNoDefault( &m_bcpropKeywords, "BcpropKeywords", "BCPROP Keywords" ); + + CAF_PDM_InitField( &m_exportSimulationInput, "ExportSimulationInput", false, "Export Simulation Input" ); + m_exportGridFilename = defaultGridFileName(); m_exportParametersFilename = defaultResultsFileName(); m_exportFaultsFilename = defaultFaultsFileName(); + // Add 10 default BCPROP keywords + for ( int i = 0; i < 10; ++i ) + { + m_bcpropKeywords.push_back( new RimKeywordBcprop() ); + } + m_tabNames << "Grid Data"; m_tabNames << "Parameters"; + m_tabNames << "Simulation Input"; } //-------------------------------------------------------------------------------------------------- @@ -170,6 +184,18 @@ void RicExportEclipseSectorModelUi::setCaseData( RigEclipseCaseData* caseData /* m_visibleMin = visibleMin; m_visibleMax = visibleMax; + // Check if a .DATA file exists next to the grid file + m_exportSimulationInput = false; + if ( eclipseView && eclipseView->eclipseCase() ) + { + QFileInfo fi( eclipseView->eclipseCase()->gridFileName() ); + QString dataFileName = fi.absolutePath() + "/" + fi.completeBaseName() + ".DATA"; + if ( QFile::exists( dataFileName ) ) + { + m_exportSimulationInput = true; + } + } + if ( minI == std::numeric_limits::max() ) minI = m_visibleMin.x() + 1; if ( minJ == std::numeric_limits::max() ) minJ = m_visibleMin.y() + 1; if ( minK == std::numeric_limits::max() ) minK = m_visibleMin.z() + 1; @@ -271,6 +297,15 @@ void RicExportEclipseSectorModelUi::defineEditorAttribute( const caf::PdmFieldHa myAttr->heightHint = 280; } } + else if ( field == &m_bcpropKeywords ) + { + auto* tvAttr = dynamic_cast( attribute ); + if ( tvAttr ) + { + tvAttr->resizePolicy = caf::PdmUiTableViewEditorAttribute::RESIZE_TO_FIT_CONTENT; + tvAttr->alwaysEnforceResizePolicy = true; + } + } else if ( field == &m_visibleWellsPadding ) { if ( lineEditorAttr ) @@ -398,6 +433,11 @@ void RicExportEclipseSectorModelUi::defineUiOrdering( QString uiConfigName, caf: resultsGroup->add( &selectedKeywords ); } } + else if ( uiConfigName == m_tabNames[2] ) + { + m_bcpropKeywords.uiCapability()->setUiEditorTypeName( caf::PdmUiTableViewEditor::uiEditorTypeName() ); + uiOrdering.add( &m_bcpropKeywords ); + } uiOrdering.skipRemainingFields( true ); } diff --git a/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelUi.h b/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelUi.h index 8e8bef316a..411c739354 100644 --- a/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelUi.h +++ b/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelUi.h @@ -34,6 +34,7 @@ class RigEclipseCaseData; class RigSimWellData; class RimEclipseView; +class RimKeywordBcprop; //================================================================================================== /// @@ -99,6 +100,10 @@ public: caf::PdmField exportGridBox; + caf::PdmChildArrayField m_bcpropKeywords; + + caf::PdmField m_exportSimulationInput; + caf::PdmField m_visibleWellsPadding; caf::PdmField refinementCountI; diff --git a/ApplicationLibCode/ProjectDataModel/Jobs/CMakeLists_files.cmake b/ApplicationLibCode/ProjectDataModel/Jobs/CMakeLists_files.cmake index 69b8114747..c75482a68a 100644 --- a/ApplicationLibCode/ProjectDataModel/Jobs/CMakeLists_files.cmake +++ b/ApplicationLibCode/ProjectDataModel/Jobs/CMakeLists_files.cmake @@ -6,6 +6,7 @@ set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RimKeywordWconprod.h ${CMAKE_CURRENT_LIST_DIR}/RimKeywordWconinje.h ${CMAKE_CURRENT_LIST_DIR}/RimJobMonitor.h + ${CMAKE_CURRENT_LIST_DIR}/RimKeywordBcprop.h ) set(SOURCE_GROUP_SOURCE_FILES @@ -16,6 +17,7 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RimKeywordWconprod.cpp ${CMAKE_CURRENT_LIST_DIR}/RimKeywordWconinje.cpp ${CMAKE_CURRENT_LIST_DIR}/RimJobMonitor.cpp + ${CMAKE_CURRENT_LIST_DIR}/RimKeywordBcprop.cpp ) list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES}) diff --git a/ApplicationLibCode/ProjectDataModel/Jobs/RimKeywordBcprop.cpp b/ApplicationLibCode/ProjectDataModel/Jobs/RimKeywordBcprop.cpp new file mode 100644 index 0000000000..e8feeecd61 --- /dev/null +++ b/ApplicationLibCode/ProjectDataModel/Jobs/RimKeywordBcprop.cpp @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// 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 "RimKeywordBcprop.h" + +#include "RifOpmDeckTools.h" + +#include "cafPdmFieldCapability.h" + +#include "opm/input/eclipse/Deck/DeckItem.hpp" +#include "opm/input/eclipse/Deck/DeckKeyword.hpp" +#include "opm/input/eclipse/Deck/DeckRecord.hpp" +#include "opm/input/eclipse/Parser/ParserKeyword.hpp" +#include "opm/input/eclipse/Parser/ParserKeywords/B.hpp" + +CAF_PDM_SOURCE_INIT( RimKeywordBcprop, "KeywordBcprop" ); + +namespace caf +{ +template <> +void caf::AppEnum::setUp() +{ + addItem( RimKeywordBcprop::Type::DIRICHLET, "DIRICHLET", "DIRICHLET" ); + addItem( RimKeywordBcprop::Type::FREE, "FREE", "FREE" ); + addItem( RimKeywordBcprop::Type::RATE, "RATE", "RATE" ); + addItem( RimKeywordBcprop::Type::THERMAL, "THERMAL", "THERMAL" ); + addItem( RimKeywordBcprop::Type::NONE, "NONE", "NONE" ); + setDefault( RimKeywordBcprop::Type::NONE ); +} + +template <> +void caf::AppEnum::setUp() +{ + addItem( RimKeywordBcprop::Component::GAS, "GAS", "GAS" ); + addItem( RimKeywordBcprop::Component::OIL, "OIL", "OIL" ); + addItem( RimKeywordBcprop::Component::WATER, "WATER", "WATER" ); + addItem( RimKeywordBcprop::Component::SOLVET, "SOLVET", "SOLVET" ); + addItem( RimKeywordBcprop::Component::POLYMER, "POLYMER", "POLYMER" ); + addItem( RimKeywordBcprop::Component::MICR, "MICR", "MICR" ); + addItem( RimKeywordBcprop::Component::OXYG, "OXYG", "OXYG" ); + addItem( RimKeywordBcprop::Component::UREA, "UREA", "UREA" ); + addItem( RimKeywordBcprop::Component::NONE, "NONE", "NONE" ); + setDefault( RimKeywordBcprop::Component::NONE ); +} +} // namespace caf + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimKeywordBcprop::RimKeywordBcprop() +{ + CAF_PDM_InitObject( "BCPROP Keyword" ); + CAF_PDM_InitField( &m_index, "index", 0, "Index" ); + CAF_PDM_InitField( &m_type, "type", caf::AppEnum( Type::NONE ), "Type" ); + CAF_PDM_InitField( &m_component, "component", caf::AppEnum( Component::NONE ), "Component" ); + CAF_PDM_InitField( &m_rate, "rate", 0.0, "Rate" ); + CAF_PDM_InitFieldNoDefault( &m_press, "press", "Pressure" ); + CAF_PDM_InitFieldNoDefault( &m_temp, "temp", "Temperature" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimKeywordBcprop::~RimKeywordBcprop() +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimKeywordBcprop::uiOrdering( caf::PdmUiGroup* uiGroup ) +{ + uiGroup->add( &m_index ); + uiGroup->add( &m_type ); + uiGroup->add( &m_component ); + uiGroup->add( &m_rate ); + uiGroup->add( &m_press ); + uiGroup->add( &m_temp ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +Opm::DeckKeyword RimKeywordBcprop::keyword() +{ + using B = Opm::ParserKeywords::BCPROP; + + std::vector items; + + items.push_back( RifOpmDeckTools::item( B::INDEX::itemName, m_index() ) ); + items.push_back( RifOpmDeckTools::item( B::TYPE::itemName, m_type().text().toStdString() ) ); + items.push_back( RifOpmDeckTools::item( B::COMPONENT::itemName, m_component().text().toStdString() ) ); + items.push_back( RifOpmDeckTools::item( B::RATE::itemName, m_rate() ) ); + items.push_back( m_press().has_value() ? RifOpmDeckTools::item( B::PRESSURE::itemName, m_press().value() ) + : RifOpmDeckTools::defaultItem( B::PRESSURE::itemName ) ); + items.push_back( m_temp().has_value() ? RifOpmDeckTools::item( B::TEMPERATURE::itemName, m_temp().value() ) + : RifOpmDeckTools::defaultItem( B::TEMPERATURE::itemName ) ); + + Opm::DeckKeyword kw( ( Opm::ParserKeywords::BCPROP() ) ); + kw.addRecord( Opm::DeckRecord{ std::move( items ) } ); + + return kw; +} diff --git a/ApplicationLibCode/ProjectDataModel/Jobs/RimKeywordBcprop.h b/ApplicationLibCode/ProjectDataModel/Jobs/RimKeywordBcprop.h new file mode 100644 index 0000000000..47f6e8664b --- /dev/null +++ b/ApplicationLibCode/ProjectDataModel/Jobs/RimKeywordBcprop.h @@ -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. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "cafAppEnum.h" +#include "cafPdmField.h" +#include "cafPdmObject.h" + +#include + +#include +#include + +namespace Opm +{ +class DeckKeyword; +class DeckRecord; +class DeckItem; +} // namespace Opm + +//================================================================================================== +/// +/// +//================================================================================================== +class RimKeywordBcprop : public caf::PdmObject +{ + CAF_PDM_HEADER_INIT; + +public: + enum class Type + { + DIRICHLET, + FREE, + RATE, + THERMAL, + NONE + }; + + enum class Component + { + GAS, + OIL, + WATER, + SOLVET, + POLYMER, + MICR, + OXYG, + UREA, + NONE + }; + + RimKeywordBcprop(); + ~RimKeywordBcprop() override; + + void uiOrdering( caf::PdmUiGroup* uiGroup ); + + Opm::DeckKeyword keyword(); + +private: + caf::PdmField m_index; + caf::PdmField> m_type; + caf::PdmField> m_component; + caf::PdmField m_rate; + caf::PdmField> m_press; + caf::PdmField> m_temp; +};