///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2016- Statoil 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 "RigFlowDiagSolverInterface.h" #include "RiaLogging.h" #include "RifEclipseOutputFileTools.h" #include "RifReaderEclipseOutput.h" #include "RifReaderInterface.h" #include "RigActiveCellInfo.h" #include "RigCaseCellResultsData.h" #include "RigEclipseCaseData.h" #include "RigEclipseResultAddress.h" #include "RigFlowDiagInterfaceTools.h" #include "opm/flowdiagnostics/DerivedQuantities.hpp" #include "opm/utility/ECLPropertyUnitConversion.hpp" #include "opm/utility/ECLPvtCurveCollection.hpp" #include "opm/utility/ECLSaturationFunc.hpp" #include "RimEclipseCase.h" #include "RimEclipseResultCase.h" #include "RimFlowDiagSolution.h" #include "cafProgressInfo.h" #include "cvfTrace.h" //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigFlowDiagTimeStepResult::RigFlowDiagTimeStepResult( size_t activeCellCount ) : m_activeCellCount( activeCellCount ) { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigFlowDiagTimeStepResult::setTracerTOF( const std::string& tracerName, RigFlowDiagResultAddress::PhaseSelection phaseSelection, const std::map& cellValues ) { std::set tracers; tracers.insert( tracerName ); RigFlowDiagResultAddress resAddr( RIG_FLD_TOF_RESNAME, phaseSelection, tracers ); this->addResult( resAddr, cellValues ); std::vector& activeCellValues = m_nativeResults[resAddr]; for ( double& val : activeCellValues ) { val = val * 1.15741e-5; // days pr second. Converting to days } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigFlowDiagTimeStepResult::setTracerFraction( const std::string& tracerName, RigFlowDiagResultAddress::PhaseSelection phaseSelection, const std::map& cellValues ) { std::set tracers; tracers.insert( tracerName ); this->addResult( RigFlowDiagResultAddress( RIG_FLD_CELL_FRACTION_RESNAME, phaseSelection, tracers ), cellValues ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigFlowDiagTimeStepResult::setInjProdWellPairFlux( const std::string& injectorTracerName, const std::string& producerTracerName, const std::pair& injProdFluxes ) { m_injProdWellPairFluxes[std::make_pair( injectorTracerName, producerTracerName )] = injProdFluxes; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigFlowDiagTimeStepResult::addResult( const RigFlowDiagResultAddress& resAddr, const std::map& cellValues ) { std::vector& activeCellValues = m_nativeResults[resAddr]; CVF_ASSERT( activeCellValues.empty() ); activeCellValues.resize( m_activeCellCount, HUGE_VAL ); for ( const auto& pairIt : cellValues ) { activeCellValues[pairIt.first] = pairIt.second; } } class RigOpmFlowDiagStaticData : public cvf::Object { public: RigOpmFlowDiagStaticData( const ecl_grid_type* mainGrid, const std::wstring& init, RiaDefines::EclipseUnitSystem caseUnitSystem ) { Opm::ECLInitFileData initData( init ); try { m_eclGraph.reset( new Opm::ECLGraph( Opm::ECLGraph::load( mainGrid, initData ) ) ); m_hasUnifiedRestartFile = false; m_poreVolume = m_eclGraph->poreVolume(); m_eclSaturationFunc.reset( new Opm::ECLSaturationFunc( *m_eclGraph, initData ) ); } catch ( ... ) { RiaLogging::warning( "Exception during initialization of relative permeability plotting functionality. " "Functionality will not be available." ); } try { m_eclPvtCurveCollection.reset( new Opm::ECLPVT::ECLPvtCurveCollection( *m_eclGraph, initData ) ); } catch ( ... ) { RiaLogging::warning( "Unsupported PVT table format. Could not initialize PVT plotting functionality." ); } // Try and set output unit system to the same system as the eclipse case system std::unique_ptr eclUnitSystem; if ( caseUnitSystem == RiaDefines::EclipseUnitSystem::UNITS_METRIC ) eclUnitSystem = Opm::ECLUnits::metricUnitConventions(); else if ( caseUnitSystem == RiaDefines::EclipseUnitSystem::UNITS_FIELD ) eclUnitSystem = Opm::ECLUnits::fieldUnitConventions(); else if ( caseUnitSystem == RiaDefines::EclipseUnitSystem::UNITS_LAB ) eclUnitSystem = Opm::ECLUnits::labUnitConventions(); if ( eclUnitSystem ) { if ( m_eclSaturationFunc ) { m_eclSaturationFunc->setOutputUnits( eclUnitSystem->clone() ); } if ( m_eclPvtCurveCollection ) { m_eclPvtCurveCollection->setOutputUnits( eclUnitSystem->clone() ); } } } public: std::unique_ptr m_eclGraph; std::vector m_poreVolume; std::unique_ptr m_fldToolbox; bool m_hasUnifiedRestartFile; std::vector m_singleRestartDataTimeSteps; std::unique_ptr m_unifiedRestartData; std::unique_ptr m_eclSaturationFunc; std::unique_ptr m_eclPvtCurveCollection; }; //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigFlowDiagSolverInterface::RigFlowDiagSolverInterface( RimEclipseResultCase* eclipseCase ) : m_eclipseCase( eclipseCase ) , m_pvtCurveErrorCount( 0 ) , m_relpermCurveErrorCount( 0 ) { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigFlowDiagSolverInterface::~RigFlowDiagSolverInterface() { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::string removeCrossFlowEnding( std::string tracerName ) { return RimFlowDiagSolution::removeCrossFlowEnding( QString::fromStdString( tracerName ) ).toStdString(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool hasCrossFlowEnding( std::string tracerName ) { return RimFlowDiagSolution::hasCrossFlowEnding( QString::fromStdString( tracerName ) ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::string addCrossFlowEnding( std::string tracerName ) { return RimFlowDiagSolution::addCrossFlowEnding( QString::fromStdString( tracerName ) ).toStdString(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigFlowDiagTimeStepResult RigFlowDiagSolverInterface::calculate( size_t timeStepIndex, RigFlowDiagResultAddress::PhaseSelection phaseSelection, std::map> injectorTracers, std::map> producerTracers ) { using namespace Opm::FlowDiagnostics; RigFlowDiagTimeStepResult result( m_eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->reservoirActiveCellCount() ); caf::ProgressInfo progressInfo( 8, "Calculating Flow Diagnostics" ); try { progressInfo.setProgressDescription( "Grid access" ); if ( !ensureStaticDataObjectInstanceCreated() ) { return result; } progressInfo.incrementProgress(); progressInfo.setProgressDescription( "Calculating Connectivities" ); CVF_ASSERT( m_opmFlowDiagStaticData.notNull() ); const Opm::FlowDiagnostics::ConnectivityGraph connGraph = Opm::FlowDiagnostics::ConnectivityGraph{ static_cast( m_opmFlowDiagStaticData->m_eclGraph->numCells() ), m_opmFlowDiagStaticData->m_eclGraph->neighbours() }; progressInfo.incrementProgress(); progressInfo.setProgressDescription( "Initialize Solver" ); // Create the Toolbox. m_opmFlowDiagStaticData->m_fldToolbox.reset( new Opm::FlowDiagnostics::Toolbox{ connGraph } ); // Look for unified restart file QStringList m_filesWithSameBaseName; QString gridFileName = m_eclipseCase->gridFileName(); if ( !RifEclipseOutputFileTools::findSiblingFilesWithSameBaseName( gridFileName, &m_filesWithSameBaseName ) ) return result; QString firstRestartFileName = RifEclipseOutputFileTools::firstFileNameOfType( m_filesWithSameBaseName, ECL_UNIFIED_RESTART_FILE ); if ( !firstRestartFileName.isEmpty() ) { m_opmFlowDiagStaticData->m_unifiedRestartData.reset( new Opm::ECLRestartData( Opm::ECLRestartData( firstRestartFileName.toStdString() ) ) ); m_opmFlowDiagStaticData->m_hasUnifiedRestartFile = true; } else { QStringList restartFileNames = RifEclipseOutputFileTools::filterFileNamesOfType( m_filesWithSameBaseName, ECL_RESTART_FILE ); size_t restartFileCount = static_cast( restartFileNames.size() ); size_t maxTimeStepCount = m_eclipseCase->eclipseCaseData()->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->maxTimeStepCount(); if ( restartFileCount <= timeStepIndex && restartFileCount != maxTimeStepCount ) { RiaLogging::errorInMessageBox( nullptr, "ResInsight", "Flow Diagnostics: Could not find all the restart files. Results will " "not be " "loaded." ); return result; } restartFileNames.sort(); // To make sure they are sorted in increasing *.X000N order. Hack. Should probably // be actual time stored on file. m_opmFlowDiagStaticData->m_hasUnifiedRestartFile = false; for ( const auto& restartFileName : restartFileNames ) { m_opmFlowDiagStaticData->m_singleRestartDataTimeSteps.push_back( Opm::ECLRestartData( restartFileName.toStdString() ) ); } } } catch ( const std::exception& e ) { RiaLogging::errorInMessageBox( nullptr, "ResInsight", "Flow Diagnostics Exception: " + QString( e.what() ) ); return result; } progressInfo.setProgress( 3 ); progressInfo.setProgressDescription( "Assigning Flux Field" ); assignPhaseCorrecedPORV( phaseSelection, timeStepIndex ); Opm::ECLRestartData* currentRestartData = nullptr; if ( !m_opmFlowDiagStaticData->m_hasUnifiedRestartFile ) { currentRestartData = &( m_opmFlowDiagStaticData->m_singleRestartDataTimeSteps[timeStepIndex] ); } else { currentRestartData = m_opmFlowDiagStaticData->m_unifiedRestartData.get(); } CVF_ASSERT( currentRestartData ); RigEclipseResultAddress addrToMaxTimeStepCountResult; m_eclipseCase->eclipseCaseData()->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->maxTimeStepCount( &addrToMaxTimeStepCountResult ); int reportStepNumber = m_eclipseCase->eclipseCaseData() ->results( RiaDefines::PorosityModelType::MATRIX_MODEL ) ->reportStepNumber( addrToMaxTimeStepCountResult, timeStepIndex ); if ( !currentRestartData->selectReportStep( reportStepNumber ) ) { RiaLogging::errorInMessageBox( nullptr, "ResInsight", "Flow Diagnostics: Could not find the requested timestep in the result file. " "Results " "will not be loaded." ); return result; } // Set up flow Toolbox with timestep data std::map WellInFluxPrCell; try { if ( m_eclipseCase->eclipseCaseData()->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->hasFlowDiagUsableFluxes() ) { Opm::FlowDiagnostics::ConnectionValues connectionsVals = RigFlowDiagInterfaceTools::extractFluxFieldFromRestartFile( *( m_opmFlowDiagStaticData->m_eclGraph ), *currentRestartData, phaseSelection ); m_opmFlowDiagStaticData->m_fldToolbox->assignConnectionFlux( connectionsVals ); } else { Opm::ECLInitFileData init( getInitFileName() ); Opm::FlowDiagnostics::ConnectionValues connectionVals = RigFlowDiagInterfaceTools::calculateFluxField( ( *m_opmFlowDiagStaticData->m_eclGraph ), init, *currentRestartData, phaseSelection ); m_opmFlowDiagStaticData->m_fldToolbox->assignConnectionFlux( connectionVals ); } progressInfo.incrementProgress(); Opm::ECLWellSolution wsol = Opm::ECLWellSolution{ -1.0, false }; std::vector gridNames = m_opmFlowDiagStaticData->m_eclGraph->activeGrids(); const std::vector well_fluxes = wsol.solution( *currentRestartData, gridNames ); WellInFluxPrCell = RigFlowDiagInterfaceTools::extractWellFlows( *( m_opmFlowDiagStaticData->m_eclGraph ), well_fluxes ); m_opmFlowDiagStaticData->m_fldToolbox->assignInflowFlux( WellInFluxPrCell ); } catch ( const std::exception& e ) { RiaLogging::errorInMessageBox( nullptr, "ResInsight", "Flow Diagnostics Exception: " + QString( e.what() ) ); return result; } progressInfo.incrementProgress(); progressInfo.setProgressDescription( "Injector Solution" ); try { // Injection Solution std::set injectorCrossFlowTracers; std::vector injectorCellSets; std::unique_ptr injectorSolution; { for ( const auto& tIt : injectorTracers ) { std::string tracerName = tIt.first; if ( hasCrossFlowEnding( tracerName ) ) { tracerName = removeCrossFlowEnding( tracerName ); injectorCrossFlowTracers.insert( tracerName ); } injectorCellSets.push_back( CellSet( CellSetID( tracerName ), tIt.second ) ); } injectorSolution.reset( new Toolbox::Forward( m_opmFlowDiagStaticData->m_fldToolbox->computeInjectionDiagnostics( injectorCellSets ) ) ); for ( const CellSetID& tracerId : injectorSolution->fd.startPoints() ) { std::string tracername = tracerId.to_string(); if ( injectorCrossFlowTracers.count( tracername ) ) tracername = addCrossFlowEnding( tracername ); CellSetValues tofVals = injectorSolution->fd.timeOfFlight( tracerId ); result.setTracerTOF( tracername, phaseSelection, tofVals ); CellSetValues fracVals = injectorSolution->fd.concentration( tracerId ); result.setTracerFraction( tracername, phaseSelection, fracVals ); } } progressInfo.incrementProgress(); progressInfo.setProgressDescription( "Producer Solution" ); // Producer Solution std::set producerCrossFlowTracers; std::vector prodjCellSets; std::unique_ptr producerSolution; { for ( const auto& tIt : producerTracers ) { std::string tracerName = tIt.first; if ( hasCrossFlowEnding( tracerName ) ) { tracerName = removeCrossFlowEnding( tracerName ); producerCrossFlowTracers.insert( tracerName ); } prodjCellSets.push_back( CellSet( CellSetID( tracerName ), tIt.second ) ); } producerSolution.reset( new Toolbox::Reverse( m_opmFlowDiagStaticData->m_fldToolbox->computeProductionDiagnostics( prodjCellSets ) ) ); for ( const CellSetID& tracerId : producerSolution->fd.startPoints() ) { std::string tracername = tracerId.to_string(); if ( producerCrossFlowTracers.count( tracername ) ) tracername = addCrossFlowEnding( tracername ); CellSetValues tofVals = producerSolution->fd.timeOfFlight( tracerId ); result.setTracerTOF( tracername, phaseSelection, tofVals ); CellSetValues fracVals = producerSolution->fd.concentration( tracerId ); result.setTracerFraction( tracername, phaseSelection, fracVals ); } } progressInfo.incrementProgress(); progressInfo.setProgressDescription( "Well pair fluxes" ); int producerTracerCount = static_cast( prodjCellSets.size() ); #pragma omp parallel for for ( int pIdx = 0; pIdx < producerTracerCount; ++pIdx ) { const auto& prodCellSet = prodjCellSets[pIdx]; std::string prodTracerName = prodCellSet.id().to_string(); CellSetID prodID( prodTracerName ); std::string uiProducerTracerName = prodTracerName; if ( producerCrossFlowTracers.count( prodTracerName ) ) { uiProducerTracerName = addCrossFlowEnding( prodTracerName ); } for ( const auto& injCellSet : injectorCellSets ) { std::string injTracerName = injCellSet.id().to_string(); CellSetID injID( injTracerName ); std::pair fluxPair = injectorProducerPairFlux( *( injectorSolution.get() ), *( producerSolution.get() ), injID, prodID, WellInFluxPrCell ); std::string uiInjectorTracerName = injTracerName; if ( injectorCrossFlowTracers.count( injTracerName ) ) { uiInjectorTracerName = addCrossFlowEnding( injTracerName ); } #pragma omp critical( critical_section_RigFlowDiagSolverInterface_calculate ) { result.setInjProdWellPairFlux( uiInjectorTracerName, uiProducerTracerName, fluxPair ); } } } } catch ( const std::exception& e ) { RiaLogging::errorInMessageBox( nullptr, "ResInsight", "Flow Diagnostics Exception: " + QString( e.what() ) ); return result; } return result; // Relying on implicit move constructor } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RigFlowDiagSolverInterface::ensureStaticDataObjectInstanceCreated() { if ( m_opmFlowDiagStaticData.isNull() ) { // Get set of files std::wstring initFileName = getInitFileName(); if ( initFileName.empty() ) return false; const RigEclipseCaseData* eclipseCaseData = m_eclipseCase->eclipseCaseData(); if ( eclipseCaseData ) { auto fileReader = eclipseCaseData->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->readerInterface(); auto eclOutput = dynamic_cast( fileReader ); if ( eclOutput ) { ecl_grid_type* mainGrid = eclOutput->loadAllGrids(); if ( !mainGrid ) { return false; } RiaDefines::EclipseUnitSystem caseUnitSystem = eclipseCaseData->unitsType(); m_opmFlowDiagStaticData = new RigOpmFlowDiagStaticData( mainGrid, initFileName, caseUnitSystem ); ecl_grid_free( mainGrid ); } } } return m_opmFlowDiagStaticData.notNull() ? true : false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigFlowDiagSolverInterface::assignPhaseCorrecedPORV( RigFlowDiagResultAddress::PhaseSelection phaseSelection, size_t timeStepIdx ) { RigEclipseCaseData* eclipseCaseData = m_eclipseCase->eclipseCaseData(); const std::vector* phaseSaturation = nullptr; switch ( phaseSelection ) { case RigFlowDiagResultAddress::PHASE_OIL: phaseSaturation = eclipseCaseData->resultValues( RiaDefines::PorosityModelType::MATRIX_MODEL, RiaDefines::ResultCatType::DYNAMIC_NATIVE, RiaResultNames::soil(), timeStepIdx ); break; case RigFlowDiagResultAddress::PHASE_GAS: phaseSaturation = eclipseCaseData->resultValues( RiaDefines::PorosityModelType::MATRIX_MODEL, RiaDefines::ResultCatType::DYNAMIC_NATIVE, RiaResultNames::sgas(), timeStepIdx ); break; case RigFlowDiagResultAddress::PHASE_WAT: phaseSaturation = eclipseCaseData->resultValues( RiaDefines::PorosityModelType::MATRIX_MODEL, RiaDefines::ResultCatType::DYNAMIC_NATIVE, RiaResultNames::swat(), timeStepIdx ); break; default: m_opmFlowDiagStaticData->m_fldToolbox->assignPoreVolume( m_opmFlowDiagStaticData->m_poreVolume ); break; } if ( phaseSaturation ) { std::vector porvAdjusted = m_opmFlowDiagStaticData->m_poreVolume; CAF_ASSERT( porvAdjusted.size() == phaseSaturation->size() ); for ( size_t idx = 0; idx < porvAdjusted.size(); ++idx ) { porvAdjusted[idx] *= phaseSaturation->at( idx ); } m_opmFlowDiagStaticData->m_fldToolbox->assignPoreVolume( porvAdjusted ); } else { m_opmFlowDiagStaticData->m_fldToolbox->assignPoreVolume( m_opmFlowDiagStaticData->m_poreVolume ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigFlowDiagSolverInterface::reportRelPermCurveError( const QString& message ) { if ( m_relpermCurveErrorCount == 0 ) { RiaLogging::warning( "RelPerm curve problems: \n" + message ); } m_relpermCurveErrorCount++; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigFlowDiagSolverInterface::reportPvtCurveError( const QString& message ) { if ( m_pvtCurveErrorCount == 0 ) { RiaLogging::warning( "PVT curve problems: \n" + message ); } m_pvtCurveErrorCount++; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigFlowDiagSolverInterface::FlowCharacteristicsResultFrame RigFlowDiagSolverInterface::calculateFlowCharacteristics( const std::vector* injector_tof, const std::vector* producer_tof, const std::vector& selected_cell_indices, double max_pv_fraction ) { using namespace Opm::FlowDiagnostics; RigFlowDiagSolverInterface::FlowCharacteristicsResultFrame result; if ( injector_tof == nullptr || producer_tof == nullptr ) { return result; } std::vector poreVolume; for ( size_t cellIndex : selected_cell_indices ) { poreVolume.push_back( m_opmFlowDiagStaticData->m_poreVolume[cellIndex] ); } try { Graph flowCapStorCapCurve = flowCapacityStorageCapacityCurve( *injector_tof, *producer_tof, poreVolume, max_pv_fraction ); result.m_storageCapFlowCapCurve = flowCapStorCapCurve; result.m_lorenzCoefficient = lorenzCoefficient( flowCapStorCapCurve ); result.m_dimensionlessTimeSweepEfficiencyCurve = sweepEfficiency( flowCapStorCapCurve ); } catch ( const std::exception& e ) { RiaLogging::errorInMessageBox( nullptr, "ResInsight", "Flow Diagnostics: " + QString( e.what() ) ); } return result; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RigFlowDiagSolverInterface::calculateRelPermCurves( size_t activeCellIndex ) { std::vector retCurveArr; if ( !ensureStaticDataObjectInstanceCreated() ) { return retCurveArr; } CVF_ASSERT( m_opmFlowDiagStaticData.notNull() ); if ( !m_opmFlowDiagStaticData->m_eclSaturationFunc ) { return retCurveArr; } const Opm::ECLSaturationFunc::RawCurve krw{ Opm::ECLSaturationFunc::RawCurve::Function::RelPerm, Opm::ECLSaturationFunc::RawCurve::SubSystem::OilWater, Opm::ECLPhaseIndex::Aqua }; // water rel-perm in oil-water system const Opm::ECLSaturationFunc::RawCurve krg{ Opm::ECLSaturationFunc::RawCurve::Function::RelPerm, Opm::ECLSaturationFunc::RawCurve::SubSystem::OilGas, Opm::ECLPhaseIndex::Vapour }; // gas rel-perm in oil-gas system const Opm::ECLSaturationFunc::RawCurve krow{ Opm::ECLSaturationFunc::RawCurve::Function::RelPerm, Opm::ECLSaturationFunc::RawCurve::SubSystem::OilWater, Opm::ECLPhaseIndex::Liquid }; // oil rel-perm in oil-water system const Opm::ECLSaturationFunc::RawCurve krog{ Opm::ECLSaturationFunc::RawCurve::Function::RelPerm, Opm::ECLSaturationFunc::RawCurve::SubSystem::OilGas, Opm::ECLPhaseIndex::Liquid }; // oil rel-perm in oil-gas system const Opm::ECLSaturationFunc::RawCurve pcgo{ Opm::ECLSaturationFunc::RawCurve::Function::CapPress, Opm::ECLSaturationFunc::RawCurve::SubSystem::OilGas, Opm::ECLPhaseIndex::Vapour }; // gas/oil capillary pressure (Pg-Po) in // G/O system const Opm::ECLSaturationFunc::RawCurve pcow{ Opm::ECLSaturationFunc::RawCurve::Function::CapPress, Opm::ECLSaturationFunc::RawCurve::SubSystem::OilWater, Opm::ECLPhaseIndex::Aqua }; // oil/water capillary pressure (Po-Pw) in // O/W system std::vector> curveIdentNameArr; std::vector satFuncRequests; curveIdentNameArr.push_back( std::make_pair( RelPermCurve::KRW, "KRW" ) ); satFuncRequests.push_back( krw ); curveIdentNameArr.push_back( std::make_pair( RelPermCurve::KRG, "KRG" ) ); satFuncRequests.push_back( krg ); curveIdentNameArr.push_back( std::make_pair( RelPermCurve::KROW, "KROW" ) ); satFuncRequests.push_back( krow ); curveIdentNameArr.push_back( std::make_pair( RelPermCurve::KROG, "KROG" ) ); satFuncRequests.push_back( krog ); curveIdentNameArr.push_back( std::make_pair( RelPermCurve::PCOG, "PCOG" ) ); satFuncRequests.push_back( pcgo ); curveIdentNameArr.push_back( std::make_pair( RelPermCurve::PCOW, "PCOW" ) ); satFuncRequests.push_back( pcow ); try { // Calculate and return curves both with and without endpoint scaling and tag them accordingly // Must use two calls to achieve this const std::array epsModeArr = { { RelPermCurve::EPS_ON, RelPermCurve::EPS_OFF } }; for ( RelPermCurve::EpsMode epsMode : epsModeArr ) { const bool useEps = epsMode == RelPermCurve::EPS_ON ? true : false; Opm::ECLSaturationFunc::SatFuncScaling scaling; if ( !useEps ) { scaling.enable = static_cast( 0 ); } std::vector graphArr = m_opmFlowDiagStaticData->m_eclSaturationFunc->getSatFuncCurve( satFuncRequests, static_cast( activeCellIndex ), scaling ); for ( size_t i = 0; i < graphArr.size(); i++ ) { const RelPermCurve::Ident curveIdent = curveIdentNameArr[i].first; const std::string curveName = curveIdentNameArr[i].second; const Opm::FlowDiagnostics::Graph& srcGraph = graphArr[i]; if ( srcGraph.first.size() > 0 ) { const std::vector& xVals = srcGraph.first; const std::vector& yVals = srcGraph.second; retCurveArr.push_back( { curveIdent, curveName, epsMode, xVals, yVals } ); } } } } catch ( const std::exception& e ) { reportRelPermCurveError( QString( e.what() ) ); return retCurveArr; } return retCurveArr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RigFlowDiagSolverInterface::calculatePvtCurves( PvtCurveType pvtCurveType, int pvtNum ) { std::vector retCurveArr; try { if ( !ensureStaticDataObjectInstanceCreated() ) { return retCurveArr; } CVF_ASSERT( m_opmFlowDiagStaticData.notNull() ); if ( !m_opmFlowDiagStaticData->m_eclPvtCurveCollection ) { return retCurveArr; } // Requesting FVF or Viscosity if ( pvtCurveType == PvtCurveType::PVT_CT_FVF ) { // Bo { std::vector graphArr = m_opmFlowDiagStaticData->m_eclPvtCurveCollection->getPvtCurve( Opm::ECLPVT::RawCurve::FVF, Opm::ECLPhaseIndex::Liquid, pvtNum ); for ( Opm::ECLPVT::PVTGraph srcGraph : graphArr ) { if ( srcGraph.press.size() > 0 ) { retCurveArr.push_back( { PvtCurve::Bo, PvtCurve::OIL, srcGraph.press, srcGraph.value, srcGraph.mixRat } ); } } } // Bg { std::vector graphArr = m_opmFlowDiagStaticData->m_eclPvtCurveCollection->getPvtCurve( Opm::ECLPVT::RawCurve::FVF, Opm::ECLPhaseIndex::Vapour, pvtNum ); for ( Opm::ECLPVT::PVTGraph srcGraph : graphArr ) { if ( srcGraph.press.size() > 0 ) { retCurveArr.push_back( { PvtCurve::Bg, PvtCurve::GAS, srcGraph.press, srcGraph.value, srcGraph.mixRat } ); } } } } else if ( pvtCurveType == PvtCurveType::PVT_CT_VISCOSITY ) { // Visc_o / mu_o { std::vector graphArr = m_opmFlowDiagStaticData->m_eclPvtCurveCollection->getPvtCurve( Opm::ECLPVT::RawCurve::Viscosity, Opm::ECLPhaseIndex::Liquid, pvtNum ); for ( Opm::ECLPVT::PVTGraph srcGraph : graphArr ) { if ( srcGraph.press.size() > 0 ) { retCurveArr.push_back( { PvtCurve::Visc_o, PvtCurve::OIL, srcGraph.press, srcGraph.value, srcGraph.mixRat } ); } } } // Visc_g / mu_g { std::vector graphArr = m_opmFlowDiagStaticData->m_eclPvtCurveCollection->getPvtCurve( Opm::ECLPVT::RawCurve::Viscosity, Opm::ECLPhaseIndex::Vapour, pvtNum ); for ( Opm::ECLPVT::PVTGraph srcGraph : graphArr ) { if ( srcGraph.press.size() > 0 ) { retCurveArr.push_back( { PvtCurve::Visc_g, PvtCurve::GAS, srcGraph.press, srcGraph.value, srcGraph.mixRat } ); } } } } } catch ( const std::exception& e ) { reportPvtCurveError( QString( e.what() ) ); return retCurveArr; } return retCurveArr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RigFlowDiagSolverInterface::calculatePvtDynamicPropertiesFvf( int pvtNum, double pressure, double rs, double rv, double* bo, double* bg ) { if ( bo ) *bo = HUGE_VAL; if ( bg ) *bg = HUGE_VAL; if ( !ensureStaticDataObjectInstanceCreated() ) { return false; } CVF_ASSERT( m_opmFlowDiagStaticData.notNull() ); if ( !m_opmFlowDiagStaticData->m_eclPvtCurveCollection ) { return false; } try { // Bo { std::vector phasePress = { pressure }; std::vector mixRatio = { rs }; std::vector valArr = m_opmFlowDiagStaticData->m_eclPvtCurveCollection->getDynamicPropertyNative( Opm::ECLPVT::RawCurve::FVF, Opm::ECLPhaseIndex::Liquid, pvtNum, phasePress, mixRatio ); if ( valArr.size() > 0 ) { *bo = valArr[0]; } } // Bg { std::vector phasePress = { pressure }; std::vector mixRatio = { rv }; std::vector valArr = m_opmFlowDiagStaticData->m_eclPvtCurveCollection->getDynamicPropertyNative( Opm::ECLPVT::RawCurve::FVF, Opm::ECLPhaseIndex::Vapour, pvtNum, phasePress, mixRatio ); if ( valArr.size() > 0 ) { *bg = valArr[0]; } } } catch ( const std::exception& e ) { reportPvtCurveError( QString( e.what() ) ); return false; } return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RigFlowDiagSolverInterface::calculatePvtDynamicPropertiesViscosity( int pvtNum, double pressure, double rs, double rv, double* mu_o, double* mu_g ) { if ( mu_o ) *mu_o = HUGE_VAL; if ( mu_g ) *mu_g = HUGE_VAL; if ( !ensureStaticDataObjectInstanceCreated() ) { return false; } CVF_ASSERT( m_opmFlowDiagStaticData.notNull() ); if ( !m_opmFlowDiagStaticData->m_eclPvtCurveCollection ) { return false; } try { // mu_o { std::vector phasePress = { pressure }; std::vector mixRatio = { rs }; std::vector valArr = m_opmFlowDiagStaticData->m_eclPvtCurveCollection->getDynamicPropertyNative( Opm::ECLPVT::RawCurve::Viscosity, Opm::ECLPhaseIndex::Liquid, pvtNum, phasePress, mixRatio ); if ( valArr.size() > 0 ) { *mu_o = valArr[0]; } } // mu_o { std::vector phasePress = { pressure }; std::vector mixRatio = { rv }; std::vector valArr = m_opmFlowDiagStaticData->m_eclPvtCurveCollection->getDynamicPropertyNative( Opm::ECLPVT::RawCurve::Viscosity, Opm::ECLPhaseIndex::Vapour, pvtNum, phasePress, mixRatio ); if ( valArr.size() > 0 ) { *mu_g = valArr[0]; } } } catch ( const std::exception& e ) { reportPvtCurveError( QString( e.what() ) ); return false; } return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::wstring RigFlowDiagSolverInterface::getInitFileName() const { QString gridFileName = m_eclipseCase->gridFileName(); QStringList m_filesWithSameBaseName; if ( !RifEclipseOutputFileTools::findSiblingFilesWithSameBaseName( gridFileName, &m_filesWithSameBaseName ) ) return std::wstring(); QString initFileName = RifEclipseOutputFileTools::firstFileNameOfType( m_filesWithSameBaseName, ECL_INIT_FILE ); return initFileName.toStdWString(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigFlowDiagSolverInterface::FlowCharacteristicsResultFrame::FlowCharacteristicsResultFrame() : m_lorenzCoefficient( HUGE_VAL ) { }