mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-27 23:50:19 -05:00
WELSPECS and COMPDAT export improvements (#13068)
* Generate keywords directly. * Make sure ensemble settings in duplicated job is reset. * Use same sort as file export
This commit is contained in:
11 files changed
+316
-207
No files matched your search
@@ -3,6 +3,7 @@ set(SOURCE_GROUP_HEADER_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimOpmFlowJob.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimJobCollection.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimDeckPositionDlg.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimKeywordFactory.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimKeywordWconprod.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimKeywordWconinje.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimJobMonitor.h
|
||||
@@ -14,6 +15,7 @@ set(SOURCE_GROUP_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimOpmFlowJob.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimJobCollection.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimDeckPositionDlg.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimKeywordFactory.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimKeywordWconprod.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimKeywordWconinje.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimJobMonitor.cpp
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2025 Equinor ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RimKeywordFactory.h"
|
||||
|
||||
#include "Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.h"
|
||||
|
||||
#include "RifOpmDeckTools.h"
|
||||
|
||||
#include "RimEclipseCase.h"
|
||||
#include "RimWellPath.h"
|
||||
#include "RimWellPathCompletionSettings.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/C.hpp"
|
||||
#include "opm/input/eclipse/Parser/ParserKeywords/W.hpp"
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
///
|
||||
//==================================================================================================
|
||||
namespace RimKeywordFactory
|
||||
{
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
Opm::DeckKeyword welspecsKeyword( const std::string wellGrpName, RimEclipseCase* eCase, RimWellPath* wellPath )
|
||||
{
|
||||
if ( eCase == nullptr || wellPath == nullptr || wellPath->completionSettings() == nullptr )
|
||||
{
|
||||
return Opm::DeckKeyword();
|
||||
}
|
||||
|
||||
auto ijPos = RicWellPathExportCompletionDataFeatureImpl::wellPathUpperGridIntersectionIJ( eCase, wellPath );
|
||||
auto compSettings = wellPath->completionSettings();
|
||||
|
||||
auto wellName = compSettings->wellNameForExport().toStdString();
|
||||
|
||||
using W = Opm::ParserKeywords::WELSPECS;
|
||||
|
||||
std::vector<Opm::DeckItem> items;
|
||||
|
||||
items.push_back( RifOpmDeckTools::item( W::WELL::itemName, wellName ) );
|
||||
items.push_back( RifOpmDeckTools::item( W::GROUP::itemName, wellGrpName ) );
|
||||
items.push_back( RifOpmDeckTools::item( W::HEAD_I::itemName, ijPos.second.x() + 1 ) );
|
||||
items.push_back( RifOpmDeckTools::item( W::HEAD_J::itemName, ijPos.second.y() + 1 ) );
|
||||
|
||||
auto refDepth = compSettings->referenceDepth();
|
||||
items.push_back( refDepth.has_value() ? RifOpmDeckTools::item( W::REF_DEPTH::itemName, refDepth.value() )
|
||||
: RifOpmDeckTools::defaultItem( W::REF_DEPTH::itemName ) );
|
||||
|
||||
items.push_back( RifOpmDeckTools::item( W::PHASE::itemName, compSettings->wellTypeNameForExport().toStdString() ) );
|
||||
|
||||
auto dRadius = compSettings->drainageRadius();
|
||||
items.push_back( dRadius.has_value() ? RifOpmDeckTools::item( W::D_RADIUS::itemName, dRadius.value() )
|
||||
: RifOpmDeckTools::defaultItem( W::D_RADIUS::itemName ) );
|
||||
|
||||
items.push_back( RifOpmDeckTools::item( W::INFLOW_EQ::itemName, compSettings->gasInflowEquationForExport().toStdString() ) );
|
||||
items.push_back( RifOpmDeckTools::item( W::AUTO_SHUTIN::itemName, compSettings->automaticWellShutInForExport().toStdString() ) );
|
||||
items.push_back( RifOpmDeckTools::item( W::CROSSFLOW::itemName, compSettings->allowWellCrossFlowForExport().toStdString() ) );
|
||||
items.push_back( RifOpmDeckTools::item( W::P_TABLE::itemName, compSettings->wellBoreFluidPVT() ) );
|
||||
items.push_back( RifOpmDeckTools::item( W::DENSITY_CALC::itemName, compSettings->hydrostaticDensityForExport().toStdString() ) );
|
||||
items.push_back( RifOpmDeckTools::item( W::FIP_REGION::itemName, compSettings->fluidInPlaceRegion() ) );
|
||||
|
||||
Opm::DeckKeyword kw( ( Opm::ParserKeywords::WELSPECS() ) );
|
||||
kw.addRecord( Opm::DeckRecord{ std::move( items ) } );
|
||||
|
||||
return kw;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
Opm::DeckKeyword compdatKeyword( RimEclipseCase* eCase, RimWellPath* wellPath )
|
||||
{
|
||||
if ( eCase == nullptr || wellPath == nullptr || wellPath->completionSettings() == nullptr )
|
||||
{
|
||||
return Opm::DeckKeyword();
|
||||
}
|
||||
|
||||
auto compdata = RicWellPathExportCompletionDataFeatureImpl::completionDataForWellPath( wellPath, eCase );
|
||||
|
||||
using C = Opm::ParserKeywords::COMPDAT;
|
||||
|
||||
Opm::DeckKeyword kw( ( Opm::ParserKeywords::COMPDAT() ) );
|
||||
|
||||
auto wellName = wellPath->completionSettings()->wellNameForExport().toStdString();
|
||||
|
||||
for ( auto& cd : compdata )
|
||||
{
|
||||
std::vector<Opm::DeckItem> items;
|
||||
|
||||
items.push_back( RifOpmDeckTools::item( C::WELL::itemName, wellName ) );
|
||||
items.push_back( RifOpmDeckTools::item( C::I::itemName, cd.completionDataGridCell().localCellIndexI() + 1 ) );
|
||||
items.push_back( RifOpmDeckTools::item( C::J::itemName, cd.completionDataGridCell().localCellIndexJ() + 1 ) );
|
||||
items.push_back( RifOpmDeckTools::item( C::K1::itemName, cd.completionDataGridCell().localCellIndexK() + 1 ) );
|
||||
items.push_back( RifOpmDeckTools::item( C::K2::itemName, cd.completionDataGridCell().localCellIndexK() + 1 ) );
|
||||
items.push_back( RifOpmDeckTools::item( C::STATE::itemName, "OPEN" ) );
|
||||
|
||||
auto satTable = cd.saturation();
|
||||
items.push_back( satTable != cd.defaultValue() ? RifOpmDeckTools::item( C::SAT_TABLE::itemName, satTable )
|
||||
: RifOpmDeckTools::defaultItem( C::SAT_TABLE::itemName ) );
|
||||
|
||||
auto transmissibility = cd.transmissibility();
|
||||
items.push_back( transmissibility != cd.defaultValue()
|
||||
? RifOpmDeckTools::item( C::CONNECTION_TRANSMISSIBILITY_FACTOR::itemName, transmissibility )
|
||||
: RifOpmDeckTools::defaultItem( C::CONNECTION_TRANSMISSIBILITY_FACTOR::itemName ) );
|
||||
|
||||
auto diameter = cd.diameter();
|
||||
items.push_back( diameter != cd.defaultValue() ? RifOpmDeckTools::item( C::DIAMETER::itemName, diameter )
|
||||
: RifOpmDeckTools::defaultItem( C::DIAMETER::itemName ) );
|
||||
auto kh = cd.kh();
|
||||
items.push_back( kh != cd.defaultValue() ? RifOpmDeckTools::item( C::Kh::itemName, kh )
|
||||
: RifOpmDeckTools::defaultItem( C::Kh::itemName ) );
|
||||
auto skinFactor = cd.skinFactor();
|
||||
items.push_back( skinFactor != cd.defaultValue() ? RifOpmDeckTools::item( C::SKIN::itemName, skinFactor )
|
||||
: RifOpmDeckTools::defaultItem( C::SKIN::itemName ) );
|
||||
auto dFactor = cd.dFactor();
|
||||
items.push_back( dFactor != cd.defaultValue() ? RifOpmDeckTools::item( C::D_FACTOR::itemName, dFactor )
|
||||
: RifOpmDeckTools::defaultItem( C::D_FACTOR::itemName ) );
|
||||
|
||||
items.push_back( RifOpmDeckTools::item( C::DIR::itemName, cd.directionStringXYZ().toStdString() ) );
|
||||
|
||||
kw.addRecord( Opm::DeckRecord{ std::move( items ) } );
|
||||
}
|
||||
|
||||
return kw;
|
||||
}
|
||||
|
||||
} // namespace RimKeywordFactory
|
||||
@@ -0,0 +1,41 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2025 Equinor ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
class RimEclipseCase;
|
||||
class RimWellPath;
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
class DeckKeyword;
|
||||
} // namespace Opm
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
///
|
||||
//==================================================================================================
|
||||
namespace RimKeywordFactory
|
||||
{
|
||||
|
||||
Opm::DeckKeyword welspecsKeyword( const std::string wellGrpName, RimEclipseCase* eCase, RimWellPath* wellPath );
|
||||
Opm::DeckKeyword compdatKeyword( RimEclipseCase* eCase, RimWellPath* wellPath );
|
||||
|
||||
} // namespace RimKeywordFactory
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "RimEclipseCaseCollection.h"
|
||||
#include "RimEclipseCaseEnsemble.h"
|
||||
#include "RimFishbones.h"
|
||||
#include "RimKeywordFactory.h"
|
||||
#include "RimKeywordWconinje.h"
|
||||
#include "RimKeywordWconprod.h"
|
||||
#include "RimOilField.h"
|
||||
@@ -800,39 +801,16 @@ bool RimOpmFlowJob::onPrepare()
|
||||
}
|
||||
else
|
||||
{
|
||||
// export new well settings from resinsight
|
||||
exportBasicWellSettings();
|
||||
if ( !QFile::exists( wellTempFile() ) )
|
||||
mergePosition = mergeBasicWellSettings();
|
||||
if ( mergePosition < 0 )
|
||||
{
|
||||
RiaLogging::error( "Could not find exported well data from ResInsight: " + wellTempFile() );
|
||||
RiaLogging::error( "Unable to merge new well data into DATA file. Please check file format." );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_wellOpenType == WellOpenType::OPEN_AT_DATE )
|
||||
{
|
||||
if ( !m_deckFile->mergeWellDeckAtTimeStep( m_openTimeStep(), wellTempFile().toStdString() ) )
|
||||
{
|
||||
RiaLogging::error( "Unable to merge new well data into DATA file at selected time step due to parse errors." );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mergePosition = m_deckFile->mergeWellDeckAtPosition( mergePosition, wellTempFile().toStdString() );
|
||||
if ( mergePosition < m_openWellDeckPosition() )
|
||||
{
|
||||
RiaLogging::error( "Unable to merge new well data into DATA file due to parse errors." );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
QFile::remove( wellTempFile() );
|
||||
}
|
||||
|
||||
Opm::DeckKeyword openKeyword = m_wconinjeKeyword->keyword( wellNameInDeck );
|
||||
if ( m_wellOpenKeyword() == "WCONPROD" )
|
||||
{
|
||||
openKeyword = m_wconprodKeyword->keyword( wellNameInDeck );
|
||||
}
|
||||
Opm::DeckKeyword openKeyword = ( m_wellOpenKeyword() == "WCONPROD" ) ? m_wconprodKeyword->keyword( wellNameInDeck )
|
||||
: m_wconinjeKeyword->keyword( wellNameInDeck );
|
||||
|
||||
// open new well at selected timestep
|
||||
if ( m_wellOpenType == WellOpenType::OPEN_AT_DATE )
|
||||
@@ -1025,43 +1003,38 @@ RimEclipseCase* RimOpmFlowJob::findExistingCase( QString filename )
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimOpmFlowJob::exportBasicWellSettings()
|
||||
int RimOpmFlowJob::mergeBasicWellSettings()
|
||||
{
|
||||
RicExportCompletionDataSettingsUi exportSettings;
|
||||
const int failure = -1;
|
||||
|
||||
exportSettings.fileSplit = RicExportCompletionDataSettingsUi::ExportSplit::UNIFIED_FILE;
|
||||
exportSettings.caseToApply = m_eclipseCase();
|
||||
exportSettings.setCustomFileName( wellTempFile() );
|
||||
exportSettings.includeMsw = false;
|
||||
exportSettings.setExportDataSourceAsComment( false );
|
||||
int mergePosition = m_openWellDeckPosition();
|
||||
|
||||
exportSettings.folder = workingDirectory();
|
||||
auto compdatKw = RimKeywordFactory::compdatKeyword( m_eclipseCase(), m_wellPath() );
|
||||
auto welspecsKw = RimKeywordFactory::welspecsKeyword( m_wellGroupName().toStdString(), m_eclipseCase(), m_wellPath() );
|
||||
|
||||
std::vector<RimWellPathFracture*> wellPathFractures;
|
||||
std::vector<RimFishbones*> wellPathFishbones;
|
||||
std::vector<RimPerforationInterval*> wellPathPerforations;
|
||||
|
||||
auto topLevelWell = m_wellPath->topLevelWellPath();
|
||||
|
||||
std::set<RimWellPath*> uniquePaths;
|
||||
for ( auto w : topLevelWell->allWellPathLaterals() )
|
||||
if ( m_wellOpenType == WellOpenType::OPEN_AT_DATE )
|
||||
{
|
||||
uniquePaths.insert( w );
|
||||
// reverse order for correct insertion order
|
||||
if ( !m_deckFile->mergeKeywordAtTimeStep( m_openTimeStep(), compdatKw ) ) return failure;
|
||||
if ( !m_deckFile->mergeKeywordAtTimeStep( m_openTimeStep(), welspecsKw ) ) return failure;
|
||||
return mergePosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
mergePosition = m_deckFile->mergeKeywordAtPosition( mergePosition, welspecsKw );
|
||||
if ( mergePosition < 0 ) return failure;
|
||||
mergePosition = m_deckFile->mergeKeywordAtPosition( mergePosition, compdatKw );
|
||||
if ( mergePosition < 0 ) return failure;
|
||||
}
|
||||
|
||||
for ( auto w : uniquePaths )
|
||||
// increase wells and connections in welldims to make sure they are big enough
|
||||
auto additionalConnections = (int)compdatKw.size();
|
||||
auto welldims = m_deckFile->welldims();
|
||||
if ( !m_deckFile->setWelldims( (int)welldims[0] + 1, (int)( welldims[1] + additionalConnections ), (int)welldims[2] + 1, (int)welldims[3] + 1 ) )
|
||||
{
|
||||
auto fractures = w->descendantsIncludingThisOfType<RimWellPathFracture>();
|
||||
wellPathFractures.insert( wellPathFractures.end(), fractures.begin(), fractures.end() );
|
||||
|
||||
auto fishbones = w->descendantsIncludingThisOfType<RimFishbones>();
|
||||
wellPathFishbones.insert( wellPathFishbones.end(), fishbones.begin(), fishbones.end() );
|
||||
|
||||
auto perforations = w->descendantsIncludingThisOfType<RimPerforationInterval>();
|
||||
wellPathPerforations.insert( wellPathPerforations.end(), perforations.begin(), perforations.end() );
|
||||
return failure;
|
||||
}
|
||||
|
||||
RicWellPathExportCompletionDataFeatureImpl::exportCompletions( { topLevelWell }, exportSettings );
|
||||
return mergePosition;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -1086,30 +1059,8 @@ std::string RimOpmFlowJob::exportMswWellSettings( int timeStep )
|
||||
|
||||
exportSettings.folder = workingDirectory();
|
||||
|
||||
std::vector<RimWellPathFracture*> wellPathFractures;
|
||||
std::vector<RimFishbones*> wellPathFishbones;
|
||||
std::vector<RimPerforationInterval*> wellPathPerforations;
|
||||
|
||||
auto topLevelWell = m_wellPath->topLevelWellPath();
|
||||
|
||||
std::set<RimWellPath*> uniquePaths;
|
||||
for ( auto w : topLevelWell->allWellPathLaterals() )
|
||||
{
|
||||
uniquePaths.insert( w );
|
||||
}
|
||||
|
||||
for ( auto w : uniquePaths )
|
||||
{
|
||||
auto fractures = w->descendantsIncludingThisOfType<RimWellPathFracture>();
|
||||
wellPathFractures.insert( wellPathFractures.end(), fractures.begin(), fractures.end() );
|
||||
|
||||
auto fishbones = w->descendantsIncludingThisOfType<RimFishbones>();
|
||||
wellPathFishbones.insert( wellPathFishbones.end(), fishbones.begin(), fishbones.end() );
|
||||
|
||||
auto perforations = w->descendantsIncludingThisOfType<RimPerforationInterval>();
|
||||
wellPathPerforations.insert( wellPathPerforations.end(), perforations.begin(), perforations.end() );
|
||||
}
|
||||
|
||||
RicWellPathExportCompletionDataFeatureImpl::exportCompletions( { topLevelWell }, exportSettings );
|
||||
|
||||
QString fileContent = readFileContent( customName );
|
||||
@@ -1153,6 +1104,16 @@ void RimOpmFlowJob::resetEnsembleRunId()
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimOpmFlowJob::initAfterCopy()
|
||||
{
|
||||
m_currentRunId = 0;
|
||||
m_gridEnsemble = nullptr;
|
||||
m_summaryEnsemble = nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
void setWorkingDirectory( QString workDir );
|
||||
void setEclipseCase( RimEclipseCase* eCase );
|
||||
void setInputDataFile( QString filename );
|
||||
void initAfterCopy();
|
||||
|
||||
QString deckName();
|
||||
QString mainWorkingDirectory() const;
|
||||
@@ -101,7 +102,7 @@ private:
|
||||
|
||||
static QString readFileContent( QString filename );
|
||||
|
||||
void exportBasicWellSettings();
|
||||
int mergeBasicWellSettings();
|
||||
std::string exportMswWellSettings( int timeStep );
|
||||
void selectOpenWellPosition();
|
||||
void resetEnsembleRunId();
|
||||
@@ -120,6 +121,7 @@ private:
|
||||
caf::PdmPtrField<RimEclipseCase*> m_eclipseCase;
|
||||
caf::PdmPtrField<RimEclipseCaseEnsemble*> m_gridEnsemble;
|
||||
caf::PdmPtrField<RimSummaryEnsemble*> m_summaryEnsemble;
|
||||
|
||||
caf::PdmField<int> m_openTimeStep;
|
||||
caf::PdmField<bool> m_endTimeStepEnabled;
|
||||
caf::PdmField<int> m_endTimeStep;
|
||||
|
||||
Reference in new issue
Block a user