2016-12-16 14:17:56 +01:00
/////////////////////////////////////////////////////////////////////////////////
//
// 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 <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RigFlowDiagSolverInterface.h"
2016-12-20 11:41:05 +01:00
#include "RifEclipseOutputFileTools.h"
2017-01-09 19:14:07 +01:00
#include "RifReaderInterface.h"
#include "RigActiveCellInfo.h"
#include "RigCaseCellResultsData.h"
2017-01-10 09:51:39 +01:00
#include "RigEclipseCaseData.h"
2017-03-13 11:03:34 +01:00
2017-01-09 19:14:07 +01:00
#include "RigFlowDiagInterfaceTools.h"
2017-03-13 11:03:34 +01:00
#include "opm/flowdiagnostics/DerivedQuantities.hpp"
2017-01-09 19:14:07 +01:00
2016-12-20 11:41:05 +01:00
#include "RimEclipseCase.h"
2017-01-09 19:14:07 +01:00
#include "RimEclipseResultCase.h"
#include "RimFlowDiagSolution.h"
2016-12-20 11:41:05 +01:00
#include <QMessageBox>
2017-01-12 16:36:48 +01:00
#include "cafProgressInfo.h"
2016-12-16 14:17:56 +01:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigFlowDiagTimeStepResult :: RigFlowDiagTimeStepResult ( size_t activeCellCount )
2017-03-24 15:10:39 +01:00
: m_activeCellCount ( activeCellCount ), m_lorenzCoefficient ( HUGE_VAL )
2016-12-20 11:41:05 +01:00
{
}
2016-12-16 14:17:56 +01:00
2016-12-20 11:41:05 +01:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-01-06 15:12:13 +01:00
void RigFlowDiagTimeStepResult :: setTracerTOF ( const std :: string & tracerName , const std :: map < int , double >& cellValues )
2016-12-20 11:41:05 +01:00
{
std :: set < std :: string > tracers ;
tracers . insert ( tracerName );
2017-01-06 15:12:13 +01:00
RigFlowDiagResultAddress resAddr ( RIG_FLD_TOF_RESNAME , tracers );
2016-12-20 11:41:05 +01:00
2017-01-06 15:12:13 +01:00
this -> addResult ( resAddr , cellValues );
2016-12-20 11:41:05 +01:00
2017-01-06 15:12:13 +01:00
std :: vector < double >& activeCellValues = m_nativeResults [ resAddr ];
for ( double & val : activeCellValues )
{
val = val * 1.15741e-5 ; // days pr second. Converting to days
}
2016-12-20 11:41:05 +01:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-01-06 15:12:13 +01:00
void RigFlowDiagTimeStepResult :: setTracerFraction ( const std :: string & tracerName , const std :: map < int , double >& cellValues )
2016-12-20 11:41:05 +01:00
{
std :: set < std :: string > tracers ;
tracers . insert ( tracerName );
this -> addResult ( RigFlowDiagResultAddress ( RIG_FLD_CELL_FRACTION_RESNAME , tracers ), cellValues );
}
2016-12-16 14:17:56 +01:00
2017-03-13 11:03:34 +01:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigFlowDiagTimeStepResult :: setInjProdWellPairFlux ( const std :: string & injectorTracerName ,
const std :: string & producerTracerName ,
const std :: pair < double , double >& injProdFluxes )
{
m_injProdWellPairFluxes [ std :: make_pair ( injectorTracerName , producerTracerName )] = injProdFluxes ;
}
2016-12-16 14:17:56 +01:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2016-12-20 11:41:05 +01:00
void RigFlowDiagTimeStepResult :: addResult ( const RigFlowDiagResultAddress & resAddr , const std :: map < int , double >& cellValues )
2016-12-16 14:17:56 +01:00
{
2016-12-20 11:41:05 +01:00
std :: vector < double >& activeCellValues = m_nativeResults [ resAddr ];
2017-03-22 14:04:13 +01:00
CVF_ASSERT ( activeCellValues . empty ());
2016-12-20 11:41:05 +01:00
activeCellValues . resize ( m_activeCellCount , HUGE_VAL );
2016-12-16 14:17:56 +01:00
2016-12-20 11:41:05 +01:00
for ( const auto & pairIt : cellValues )
{
activeCellValues [ pairIt . first ] = pairIt . second ;
}
}
2017-01-06 14:41:00 +01:00
class RigOpmFldStaticData : public cvf :: Object
{
public :
2017-03-24 15:10:39 +01:00
RigOpmFldStaticData ( const std :: string & grid , const std :: string & init ) : m_eclGraph ( Opm :: ECLGraph :: load ( grid , init )), m_hasUnifiedRestartFile ( false )
{
m_poreVolume = m_eclGraph . poreVolume ();
}
2017-01-06 14:41:00 +01:00
2017-03-24 15:10:39 +01:00
Opm :: ECLGraph m_eclGraph ;
std :: vector < double > m_poreVolume ;
std :: unique_ptr < Opm :: FlowDiagnostics :: Toolbox > m_fldToolbox ;
bool m_hasUnifiedRestartFile ;
QStringList m_restartFileNames ;
2017-01-06 14:41:00 +01:00
};
2016-12-20 11:41:05 +01:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigFlowDiagSolverInterface :: RigFlowDiagSolverInterface ( RimEclipseResultCase * eclipseCase )
: m_eclipseCase ( eclipseCase )
{
2016-12-16 14:17:56 +01:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigFlowDiagSolverInterface ::~ RigFlowDiagSolverInterface ()
{
}
2017-01-06 14:41:00 +01:00
2016-12-16 14:17:56 +01:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2016-12-20 11:41:05 +01:00
RigFlowDiagTimeStepResult RigFlowDiagSolverInterface :: calculate ( size_t timeStepIndex ,
std :: map < std :: string , std :: vector < int > > injectorTracers ,
std :: map < std :: string , std :: vector < int > > producerTracers )
2016-12-16 14:17:56 +01:00
{
2016-12-20 11:41:05 +01:00
using namespace Opm :: FlowDiagnostics ;
2017-03-15 09:10:16 +01:00
RigFlowDiagTimeStepResult result ( m_eclipseCase -> eclipseCaseData () -> activeCellInfo ( RifReaderInterface :: MATRIX_RESULTS ) -> reservoirActiveCellCount ());
2016-12-20 11:41:05 +01:00
2017-03-13 11:03:34 +01:00
caf :: ProgressInfo progressInfo ( 8 , "Calculating Flow Diagnostics" );
2017-01-12 16:36:48 +01:00
2017-01-06 14:41:00 +01:00
if ( m_opmFldData . isNull () )
{
2017-01-12 16:36:48 +01:00
progressInfo . setProgressDescription ( "Grid access" );
2017-01-06 14:41:00 +01:00
// Get set of files
QString gridFileName = m_eclipseCase -> gridFileName ();
2016-12-20 11:41:05 +01:00
2017-01-06 14:41:00 +01:00
QStringList m_filesWithSameBaseName ;
2016-12-20 11:41:05 +01:00
2017-01-06 14:41:00 +01:00
if ( ! RifEclipseOutputFileTools :: findSiblingFilesWithSameBaseName ( gridFileName , & m_filesWithSameBaseName ) ) return result ;
2016-12-20 11:41:05 +01:00
2017-01-06 14:41:00 +01:00
QString initFileName = RifEclipseOutputFileTools :: firstFileNameOfType ( m_filesWithSameBaseName , ECL_INIT_FILE );
2016-12-20 11:41:05 +01:00
2017-01-06 14:41:00 +01:00
m_opmFldData = new RigOpmFldStaticData ( gridFileName . toStdString (),
initFileName . toStdString ());
2016-12-20 11:41:05 +01:00
2017-01-12 16:36:48 +01:00
progressInfo . incrementProgress ();
progressInfo . setProgressDescription ( "Calculating Connectivities" );
2017-01-06 14:41:00 +01:00
const Opm :: FlowDiagnostics :: ConnectivityGraph connGraph =
2017-03-24 15:10:39 +01:00
Opm :: FlowDiagnostics :: ConnectivityGraph { static_cast < int > ( m_opmFldData -> m_eclGraph . numCells ()),
m_opmFldData -> m_eclGraph . neighbours () };
2016-12-20 11:41:05 +01:00
2017-01-12 16:36:48 +01:00
progressInfo . incrementProgress ();
progressInfo . setProgressDescription ( "Initialize Solver" );
2017-01-06 14:41:00 +01:00
// Create the Toolbox.
2016-12-20 11:41:05 +01:00
2017-03-24 15:10:39 +01:00
m_opmFldData -> m_fldToolbox . reset ( new Opm :: FlowDiagnostics :: Toolbox { connGraph });
m_opmFldData -> m_fldToolbox -> assignPoreVolume ( m_opmFldData -> m_poreVolume );
2016-12-20 15:33:39 +01:00
2017-01-06 14:41:00 +01:00
// Look for unified restart file
2016-12-20 15:33:39 +01:00
2017-01-06 14:41:00 +01:00
QString restartFileName = RifEclipseOutputFileTools :: firstFileNameOfType ( m_filesWithSameBaseName , ECL_UNIFIED_RESTART_FILE );
if ( ! restartFileName . isEmpty () )
{
2017-03-24 15:10:39 +01:00
m_opmFldData -> m_eclGraph . assignFluxDataSource ( restartFileName . toStdString ());
2017-01-06 14:41:00 +01:00
m_opmFldData -> m_hasUnifiedRestartFile = true ;
}
else
2016-12-20 11:41:05 +01:00
{
2017-01-06 14:41:00 +01:00
2017-03-24 15:10:39 +01:00
m_opmFldData -> m_restartFileNames = RifEclipseOutputFileTools :: filterFileNamesOfType ( m_filesWithSameBaseName , ECL_RESTART_FILE );
2017-01-06 14:41:00 +01:00
2017-03-24 15:10:39 +01:00
size_t restartFileCount = static_cast < size_t > ( m_opmFldData -> m_restartFileNames . size ());
2017-03-15 09:10:16 +01:00
size_t maxTimeStepCount = m_eclipseCase -> eclipseCaseData () -> results ( RifReaderInterface :: MATRIX_RESULTS ) -> maxTimeStepCount ();
2017-01-06 14:41:00 +01:00
if ( restartFileCount <= timeStepIndex && restartFileCount != maxTimeStepCount )
{
QMessageBox :: critical ( nullptr , "ResInsight" , "Flow Diagnostics: Could not find all the restart files. Results will not be loaded." );
return result ;
}
2017-03-24 15:10:39 +01:00
m_opmFldData -> m_restartFileNames . sort (); // To make sure they are sorted in increasing *.X000N order. Hack. Should probably be actual time stored on file.
2017-01-06 14:41:00 +01:00
m_opmFldData -> m_hasUnifiedRestartFile = false ;
2016-12-20 11:41:05 +01:00
}
2017-01-06 14:41:00 +01:00
}
2017-01-12 16:36:48 +01:00
progressInfo . setProgress ( 3 );
progressInfo . setProgressDescription ( "Assigning Flux Field" );
2016-12-20 11:41:05 +01:00
2017-01-06 14:41:00 +01:00
if ( ! m_opmFldData -> m_hasUnifiedRestartFile )
{
2017-03-24 15:10:39 +01:00
QString restartFileName = m_opmFldData -> m_restartFileNames [ static_cast < int > ( timeStepIndex )];
m_opmFldData -> m_eclGraph . assignFluxDataSource ( restartFileName . toStdString ());
2016-12-20 11:41:05 +01:00
}
2017-01-12 12:04:54 +01:00
size_t resultIndexWithMaxTimeSteps = cvf :: UNDEFINED_SIZE_T ;
2017-03-15 09:10:16 +01:00
m_eclipseCase -> eclipseCaseData () -> results ( RifReaderInterface :: MATRIX_RESULTS ) -> maxTimeStepCount ( & resultIndexWithMaxTimeSteps );
2016-12-20 11:41:05 +01:00
2017-03-15 09:10:16 +01:00
int reportStepNumber = m_eclipseCase -> eclipseCaseData () -> results ( RifReaderInterface :: MATRIX_RESULTS ) -> reportStepNumber ( resultIndexWithMaxTimeSteps , timeStepIndex );
2017-01-12 12:04:54 +01:00
2017-03-24 15:10:39 +01:00
if ( ! m_opmFldData -> m_eclGraph . selectReportStep ( reportStepNumber ) )
2016-12-20 11:41:05 +01:00
{
QMessageBox :: critical ( nullptr , "ResInsight" , "Flow Diagnostics: Could not find the requested timestep in the result file. Results will not be loaded." );
return result ;
}
2017-01-06 14:41:00 +01:00
// Set up flow Toolbox with timestep data
2017-03-13 11:03:34 +01:00
Opm :: FlowDiagnostics :: CellSetValues sumWellFluxPrCell ;
2016-12-20 11:41:05 +01:00
{
2017-03-24 15:10:39 +01:00
Opm :: FlowDiagnostics :: ConnectionValues connectionsVals = RigFlowDiagInterfaceTools :: extractFluxField ( m_opmFldData -> m_eclGraph , false );
2016-12-20 11:41:05 +01:00
2017-03-24 15:10:39 +01:00
m_opmFldData -> m_fldToolbox -> assignConnectionFlux ( connectionsVals );
2016-12-20 11:41:05 +01:00
2017-01-12 16:36:48 +01:00
progressInfo . incrementProgress ();
2017-02-28 16:03:43 +01:00
Opm :: ECLWellSolution wsol = Opm :: ECLWellSolution { - 1.0 , false };
2017-01-06 14:41:00 +01:00
const std :: vector < Opm :: ECLWellSolution :: WellData > well_fluxes =
2017-03-24 15:10:39 +01:00
wsol . solution ( m_opmFldData -> m_eclGraph . rawResultData (), m_opmFldData -> m_eclGraph . numGrids ());
2017-01-06 14:41:00 +01:00
2017-03-24 15:10:39 +01:00
sumWellFluxPrCell = RigFlowDiagInterfaceTools :: extractWellFlows ( m_opmFldData -> m_eclGraph , well_fluxes );
2017-03-09 16:05:48 +01:00
2017-03-24 15:10:39 +01:00
m_opmFldData -> m_fldToolbox -> assignInflowFlux ( sumWellFluxPrCell );
2017-03-09 16:05:48 +01:00
2017-03-13 11:03:34 +01:00
// Filter connection cells with inconsistent well in flow direction (Hack, we should do something better)
2017-03-09 16:05:48 +01:00
for ( auto & tracerCellIdxsPair : injectorTracers )
{
std :: vector < int > filteredCellIndices ;
for ( int activeCellIdx : tracerCellIdxsPair . second )
{
auto activeCellIdxFluxPair = sumWellFluxPrCell . find ( activeCellIdx );
if ( activeCellIdxFluxPair -> second > 0 )
{
filteredCellIndices . push_back ( activeCellIdx );
}
}
if ( tracerCellIdxsPair . second . size () != filteredCellIndices . size ()) tracerCellIdxsPair . second = filteredCellIndices ;
}
for ( auto & tracerCellIdxsPair : producerTracers )
{
std :: vector < int > filteredCellIndices ;
for ( int activeCellIdx : tracerCellIdxsPair . second )
{
auto activeCellIdxFluxPair = sumWellFluxPrCell . find ( activeCellIdx );
if ( activeCellIdxFluxPair -> second < 0 )
{
filteredCellIndices . push_back ( activeCellIdx );
}
}
if ( tracerCellIdxsPair . second . size () != filteredCellIndices . size ()) tracerCellIdxsPair . second = filteredCellIndices ;
}
2017-01-06 14:41:00 +01:00
}
2017-01-12 16:36:48 +01:00
progressInfo . incrementProgress ();
progressInfo . setProgressDescription ( "Injector Solution" );
2017-01-06 14:41:00 +01:00
{
2017-03-13 11:03:34 +01:00
// Injection Solution
std :: vector < CellSet > injectorCellSets ;
2017-01-06 14:41:00 +01:00
for ( const auto & tIt : injectorTracers )
{
2017-03-13 11:03:34 +01:00
injectorCellSets . push_back ( CellSet ( CellSetID ( tIt . first ), tIt . second ));
2016-12-20 11:41:05 +01:00
}
2017-03-13 11:03:34 +01:00
std :: unique_ptr < Toolbox :: Forward > injectorSolution ;
2017-03-06 16:33:29 +01:00
try
{
2017-03-24 15:10:39 +01:00
injectorSolution . reset ( new Toolbox :: Forward ( m_opmFldData -> m_fldToolbox -> computeInjectionDiagnostics ( injectorCellSets )));
2017-03-06 16:33:29 +01:00
}
2017-03-09 16:05:48 +01:00
catch ( const std :: exception & e )
2016-12-20 11:41:05 +01:00
{
2017-03-10 11:46:50 +01:00
QMessageBox :: critical ( nullptr , "ResInsight" , "Flow Diagnostics: " + QString ( e . what ()));
return result ;
2016-12-20 11:41:05 +01:00
}
2017-03-13 11:03:34 +01:00
for ( const CellSetID & tracerId : injectorSolution -> fd . startPoints () )
{
CellSetValues tofVals = injectorSolution -> fd . timeOfFlight ( tracerId );
result . setTracerTOF ( tracerId . to_string (), tofVals );
CellSetValues fracVals = injectorSolution -> fd . concentration ( tracerId );
result . setTracerFraction ( tracerId . to_string (), fracVals );
}
2017-01-12 16:36:48 +01:00
2017-03-13 11:03:34 +01:00
progressInfo . incrementProgress ();
progressInfo . setProgressDescription ( "Producer Solution" );
// Producer Solution
std :: vector < CellSet > prodjCellSets ;
2017-01-06 14:41:00 +01:00
for ( const auto & tIt : producerTracers )
{
2017-03-13 11:03:34 +01:00
prodjCellSets . push_back ( CellSet ( CellSetID ( tIt . first ), tIt . second ));
2017-01-06 14:41:00 +01:00
}
2017-03-13 11:03:34 +01:00
std :: unique_ptr < Toolbox :: Reverse > producerSolution ;
try
2017-03-06 16:33:29 +01:00
{
2017-03-24 15:10:39 +01:00
producerSolution . reset ( new Toolbox :: Reverse ( m_opmFldData -> m_fldToolbox -> computeProductionDiagnostics ( prodjCellSets )));
2017-03-06 16:33:29 +01:00
}
2017-03-13 11:03:34 +01:00
catch ( const std :: exception & e )
2017-01-06 14:41:00 +01:00
{
2017-03-10 11:46:50 +01:00
QMessageBox :: critical ( nullptr , "ResInsight" , "Flow Diagnostics: " + QString ( e . what ()));
return result ;
2017-01-06 14:41:00 +01:00
}
2017-03-13 11:03:34 +01:00
for ( const CellSetID & tracerId : producerSolution -> fd . startPoints () )
{
CellSetValues tofVals = producerSolution -> fd . timeOfFlight ( tracerId );
result . setTracerTOF ( tracerId . to_string (), tofVals );
CellSetValues fracVals = producerSolution -> fd . concentration ( tracerId );
result . setTracerFraction ( tracerId . to_string (), fracVals );
}
progressInfo . incrementProgress ();
progressInfo . setProgressDescription ( "Well pair fluxes" );
int producerTracerCount = static_cast < int > ( prodjCellSets . size ());
#pragma omp parallel for
for ( int pIdx = 0 ; pIdx < producerTracerCount ; ++ pIdx )
{
const auto & prodCellSet = prodjCellSets [ pIdx ];
for ( const auto & injCellSet : injectorCellSets )
{
std :: pair < double , double > fluxPair = injectorProducerPairFlux ( * ( injectorSolution . get ()),
* ( producerSolution . get ()),
injCellSet ,
prodCellSet ,
sumWellFluxPrCell );
#pragma omp critical
{
result . setInjProdWellPairFlux ( injCellSet . id (). to_string (),
prodCellSet . id (). to_string (),
fluxPair );
}
}
}
2017-03-24 15:10:39 +01:00
try
{
Graph flowCapStorCapCurve = flowCapacityStorageCapacityCurve ( * ( injectorSolution . get ()),
* ( producerSolution . get ()),
m_opmFldData -> m_poreVolume );
result . setFlowCapStorageCapCurve ( flowCapStorCapCurve );
result . setSweepEfficiencyCurve ( sweepEfficiency ( flowCapStorCapCurve ));
result . setLorenzCoefficient ( lorenzCoefficient ( flowCapStorCapCurve ));
}
catch ( const std :: exception & e )
{
QMessageBox :: critical ( nullptr , "ResInsight" , "Flow Diagnostics: " + QString ( e . what ()));
}
2017-01-06 14:41:00 +01:00
}
2016-12-17 10:46:57 +01:00
2016-12-20 11:41:05 +01:00
return result ; // Relying on implicit move constructor
2016-12-16 14:17:56 +01:00
}