mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-24 22:27:55 -05:00
Enable flow diagnostics for grids imported with opm-common
* Update flowdiag-app submodule with grid source abstraction * Add opm-common grid source for flow diagnostics * Enable flow diagnostics for grids imported with opm-common * Test flow graph from opm-common matches resdata * Test flow diagnostics results are equal for all grid readers * Document EGrid constructor behavior in flow diagnostics grid source
This commit is contained in:
1 parent
5259663a20
commit
e217ffb5c6
8 files changed
+497
-4
No files matched your search
@@ -86,6 +86,7 @@ set(SOURCE_GROUP_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifStimPlanCsvSummaryReader.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifReaderOpmCommon.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifReaderOpmCommonActive.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifOpmCommonFlowDiagGridSource.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifInpExportTools.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifEdfmTools.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifFaultReactivationModelExporter.cpp
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RifOpmCommonFlowDiagGridSource.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "opm/io/eclipse/EGrid.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <tuple>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RifOpmCommonFlowDiagGridSource::RifOpmCommonFlowDiagGridSource( const std::string& gridFileName )
|
||||
{
|
||||
// The EGrid constructor only indexes the file and reads small topology arrays (GRIDHEAD, ACTNUM, NNC, LGR info). COORD and ZCORN are
|
||||
// loaded lazily on the first geometry access, which is never done here.
|
||||
m_grids.push_back( std::make_unique<Opm::EclIO::EGrid>( gridFileName ) );
|
||||
|
||||
m_lgrNames = m_grids.front()->list_of_lgrs();
|
||||
m_lgrParents = m_grids.front()->list_of_lgr_parents();
|
||||
|
||||
for ( const auto& lgrName : m_lgrNames )
|
||||
{
|
||||
m_grids.push_back( std::make_unique<Opm::EclIO::EGrid>( gridFileName, lgrName ) );
|
||||
}
|
||||
|
||||
if ( m_grids.front()->hasKey( "NNA1" ) )
|
||||
{
|
||||
RiaLogging::warning( "Flow diagnostics: NNCs between local grids are not supported for opm-common grid import, and are ignored." );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RifOpmCommonFlowDiagGridSource::~RifOpmCommonFlowDiagGridSource() = default;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
int RifOpmCommonFlowDiagGridSource::numGrids() const
|
||||
{
|
||||
return static_cast<int>( m_grids.size() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::string RifOpmCommonFlowDiagGridSource::gridName( int gridID ) const
|
||||
{
|
||||
if ( gridID == 0 ) return "";
|
||||
|
||||
return m_lgrNames[gridID - 1];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::array<int, 3> RifOpmCommonFlowDiagGridSource::dimensions( int gridID ) const
|
||||
{
|
||||
return m_grids[gridID]->dimension();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<int> RifOpmCommonFlowDiagGridSource::activeToGlobal( int gridID ) const
|
||||
{
|
||||
// EGrid marks matrix-inactive cells with -1, also for dual porosity, matching resdata's matrix active cells
|
||||
const auto& activeIndexes = m_grids[gridID]->active_indexes();
|
||||
|
||||
std::vector<int> globalIndexes;
|
||||
for ( int globalIndex = 0; globalIndex < static_cast<int>( activeIndexes.size() ); globalIndex++ )
|
||||
{
|
||||
if ( activeIndexes[globalIndex] >= 0 ) globalIndexes.push_back( globalIndex );
|
||||
}
|
||||
|
||||
return globalIndexes;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<bool> RifOpmCommonFlowDiagGridSource::activeIsSubdivided( int gridID ) const
|
||||
{
|
||||
const auto dims = dimensions( gridID );
|
||||
|
||||
std::vector<bool> isGlobalCellSubdivided( static_cast<size_t>( dims[0] ) * dims[1] * dims[2], false );
|
||||
for ( int lgrIndex = 0; lgrIndex < static_cast<int>( m_lgrNames.size() ); lgrIndex++ )
|
||||
{
|
||||
if ( parentGridId( lgrIndex ) != gridID ) continue;
|
||||
|
||||
for ( int hostCell : m_grids[lgrIndex + 1]->hostCellsGlobalIndex() )
|
||||
{
|
||||
if ( hostCell >= 0 && hostCell < static_cast<int>( isGlobalCellSubdivided.size() ) ) isGlobalCellSubdivided[hostCell] = true;
|
||||
}
|
||||
}
|
||||
|
||||
const auto globalIndexes = activeToGlobal( gridID );
|
||||
std::vector<bool> isSubdivided( globalIndexes.size() );
|
||||
for ( size_t activeIndex = 0; activeIndex < globalIndexes.size(); activeIndex++ )
|
||||
{
|
||||
isSubdivided[activeIndex] = isGlobalCellSubdivided[globalIndexes[activeIndex]];
|
||||
}
|
||||
|
||||
return isSubdivided;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<Opm::ECLGraphNNC> RifOpmCommonFlowDiagGridSource::nncs() const
|
||||
{
|
||||
std::vector<Opm::ECLGraphNNC> nncs;
|
||||
|
||||
for ( int gridID = 0; gridID < numGrids(); gridID++ )
|
||||
{
|
||||
// nnc_connections() lists NNC1/NNC2 (TRANNNC) followed by NNCG/NNCL (TRANGL). The input index is the position
|
||||
// within each of these arrays, as used by resdata to map NNC flux vectors in restart files.
|
||||
int nncIndex = 0;
|
||||
int globalNncIndex = 0;
|
||||
|
||||
for ( const auto& c : m_grids[gridID]->nnc_connections( gridID ) )
|
||||
{
|
||||
const bool isGlobalLocal = c.grid1_Id != gridID;
|
||||
const int inputIndex = isGlobalLocal ? globalNncIndex++ : nncIndex++;
|
||||
|
||||
nncs.push_back( { c.grid1_Id, c.grid2_Id, c.grid1_CellIdx - 1, c.grid2_CellIdx - 1, c.transValue, inputIndex } );
|
||||
}
|
||||
}
|
||||
|
||||
// Same ordering as resdata's ecl_nnc_export()
|
||||
std::sort( nncs.begin(),
|
||||
nncs.end(),
|
||||
[]( const Opm::ECLGraphNNC& a, const Opm::ECLGraphNNC& b )
|
||||
{ return std::tie( a.grid1, a.grid2, a.glob1, a.glob2 ) < std::tie( b.grid1, b.grid2, b.glob1, b.glob2 ); } );
|
||||
|
||||
return nncs;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
int RifOpmCommonFlowDiagGridSource::parentGridId( int lgrIndex ) const
|
||||
{
|
||||
if ( lgrIndex >= static_cast<int>( m_lgrParents.size() ) ) return 0;
|
||||
|
||||
auto it = std::find( m_lgrNames.begin(), m_lgrNames.end(), m_lgrParents[lgrIndex] );
|
||||
if ( it == m_lgrNames.end() ) return 0;
|
||||
|
||||
return static_cast<int>( std::distance( m_lgrNames.begin(), it ) ) + 1;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "opm/utility/ECLGraphGridSource.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm::EclIO
|
||||
{
|
||||
class EGrid;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Grid topology for flow diagnostics read directly from EGRID using opm-common. Reads only grid
|
||||
// headers, ACTNUM, LGR host cells and NNC arrays, never cell geometry, so it works the same for
|
||||
// full and active-cells-only grid import.
|
||||
//==================================================================================================
|
||||
class RifOpmCommonFlowDiagGridSource : public Opm::ECLGraphGridSource
|
||||
{
|
||||
public:
|
||||
explicit RifOpmCommonFlowDiagGridSource( const std::string& gridFileName );
|
||||
~RifOpmCommonFlowDiagGridSource() override;
|
||||
|
||||
int numGrids() const override;
|
||||
std::string gridName( int gridID ) const override;
|
||||
std::array<int, 3> dimensions( int gridID ) const override;
|
||||
std::vector<int> activeToGlobal( int gridID ) const override;
|
||||
std::vector<bool> activeIsSubdivided( int gridID ) const override;
|
||||
std::vector<Opm::ECLGraphNNC> nncs() const override;
|
||||
|
||||
private:
|
||||
int parentGridId( int lgrIndex ) const;
|
||||
|
||||
private:
|
||||
// Main grid followed by LGRs, in EGRID order
|
||||
std::vector<std::unique_ptr<Opm::EclIO::EGrid>> m_grids;
|
||||
std::vector<std::string> m_lgrNames;
|
||||
std::vector<std::string> m_lgrParents;
|
||||
};
|
||||
@@ -23,8 +23,10 @@
|
||||
#include "RiuMessageDialog.h"
|
||||
|
||||
#include "RifEclipseOutputFileTools.h"
|
||||
#include "RifOpmCommonFlowDiagGridSource.h"
|
||||
#include "RifReaderEclipseOutput.h"
|
||||
#include "RifReaderInterface.h"
|
||||
#include "RifReaderOpmCommon.h"
|
||||
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigCaseCellResultsData.h"
|
||||
@@ -47,14 +49,25 @@
|
||||
class RigOpmFlowDiagStaticData
|
||||
{
|
||||
public:
|
||||
RigOpmFlowDiagStaticData( const ecl_grid_type* mainGrid, const std::wstring& initFilename, RiaDefines::EclipseUnitSystem caseUnitSystem )
|
||||
RigOpmFlowDiagStaticData( const ecl_grid_type* mainGrid,
|
||||
const Opm::ECLGraphGridSource* gridSource,
|
||||
const std::wstring& initFilename,
|
||||
RiaDefines::EclipseUnitSystem caseUnitSystem )
|
||||
: m_initData( initFilename )
|
||||
{
|
||||
try
|
||||
{
|
||||
if ( mainGrid )
|
||||
{
|
||||
m_eclGraph = std::make_unique<Opm::ECLGraph>( Opm::ECLGraph::load( mainGrid, m_initData ) );
|
||||
m_eclGraph = std::make_unique<Opm::ECLGraph>( Opm::ECLGraph::load( mainGrid, m_initData ) );
|
||||
}
|
||||
else if ( gridSource )
|
||||
{
|
||||
m_eclGraph = std::make_unique<Opm::ECLGraph>( Opm::ECLGraph::load( *gridSource, m_initData ) );
|
||||
}
|
||||
|
||||
if ( m_eclGraph )
|
||||
{
|
||||
m_poreVolume = m_eclGraph->poreVolume();
|
||||
}
|
||||
|
||||
@@ -447,6 +460,8 @@ bool RigFlowDiagSolverInterface::ensureStaticDataObjectInstanceCreated()
|
||||
auto fileReader = eclipseCaseData->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->readerInterface();
|
||||
ecl_grid_type* mainGrid = nullptr;
|
||||
|
||||
std::unique_ptr<RifOpmCommonFlowDiagGridSource> opmGridSource;
|
||||
|
||||
if ( auto eclOutput = dynamic_cast<const RifReaderEclipseOutput*>( fileReader ) )
|
||||
{
|
||||
mainGrid = eclOutput->loadAllGrids();
|
||||
@@ -455,11 +470,23 @@ bool RigFlowDiagSolverInterface::ensureStaticDataObjectInstanceCreated()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ( dynamic_cast<const RifReaderOpmCommon*>( fileReader ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
opmGridSource = std::make_unique<RifOpmCommonFlowDiagGridSource>( m_eclipseCase->gridFileName().toStdString() );
|
||||
}
|
||||
catch ( const std::exception& e )
|
||||
{
|
||||
RiaLogging::warning( std::string( "Flow diagnostics: Could not read grid topology. " ) + e.what() );
|
||||
}
|
||||
}
|
||||
|
||||
RiaDefines::EclipseUnitSystem caseUnitSystem = eclipseCaseData->unitsType();
|
||||
try
|
||||
{
|
||||
m_opmFlowDiagStaticData = std::make_unique<RigOpmFlowDiagStaticData>( mainGrid, initFileName, caseUnitSystem );
|
||||
m_opmFlowDiagStaticData =
|
||||
std::make_unique<RigOpmFlowDiagStaticData>( mainGrid, opmGridSource.get(), initFileName, caseUnitSystem );
|
||||
}
|
||||
catch ( const std::exception& e )
|
||||
{
|
||||
@@ -470,6 +497,22 @@ bool RigFlowDiagSolverInterface::ensureStaticDataObjectInstanceCreated()
|
||||
{
|
||||
ecl_grid_free( mainGrid );
|
||||
}
|
||||
|
||||
// Flow diagnostics results are indexed by the graph's cell index, assumed equal to the active cell result index
|
||||
auto* eclGraphOwner = m_opmFlowDiagStaticData ? &m_opmFlowDiagStaticData->m_eclGraph : nullptr;
|
||||
if ( eclGraphOwner && *eclGraphOwner )
|
||||
{
|
||||
auto& eclGraph = *eclGraphOwner;
|
||||
const size_t activeCellCount =
|
||||
eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->reservoirActiveCellCount();
|
||||
if ( eclGraph->numCells() != activeCellCount )
|
||||
{
|
||||
RiaLogging::warning( "Flow diagnostics disabled: Cell count in flow graph (" + std::to_string( eclGraph->numCells() ) +
|
||||
") differs from active cell count (" + std::to_string( activeCellCount ) + ")." );
|
||||
eclGraph.reset();
|
||||
m_opmFlowDiagStaticData->m_poreVolume.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ set(SOURCE_UNITTEST_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifOpmFlowDeckFile-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifReaderEclipseOutput-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifReaderOpmCommon-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifOpmCommonFlowDiagGridSource-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifReaderEclipseSummary-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigActiveCellInfo-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCaseCellResultsData-Test.cpp
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "RiaTestDataDirectory.h"
|
||||
|
||||
#include "RifOpmCommonFlowDiagGridSource.h"
|
||||
|
||||
#include "opm/utility/ECLGraph.hpp"
|
||||
#include "opm/utility/ECLResultData.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace
|
||||
{
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Build the flow graph with both resdata and opm-common, and require identical topology
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void compareGraphs( const std::filesystem::path& gridFile )
|
||||
{
|
||||
auto initFile = gridFile;
|
||||
initFile.replace_extension( ".INIT" );
|
||||
|
||||
if ( !std::filesystem::exists( gridFile ) || !std::filesystem::exists( initFile ) )
|
||||
{
|
||||
GTEST_SKIP() << "Test file not found: " << gridFile;
|
||||
}
|
||||
|
||||
const Opm::ECLInitFileData init( initFile.string() );
|
||||
|
||||
const auto resdataGraph = Opm::ECLGraph::load( gridFile.string(), init );
|
||||
|
||||
const RifOpmCommonFlowDiagGridSource opmSource( gridFile.string() );
|
||||
const auto opmGraph = Opm::ECLGraph::load( opmSource, init );
|
||||
|
||||
EXPECT_EQ( resdataGraph.numGrids(), opmGraph.numGrids() );
|
||||
EXPECT_EQ( resdataGraph.activeGrids(), opmGraph.activeGrids() );
|
||||
EXPECT_EQ( resdataGraph.numCells(), opmGraph.numCells() );
|
||||
EXPECT_EQ( resdataGraph.numConnections(), opmGraph.numConnections() );
|
||||
EXPECT_EQ( resdataGraph.neighbours(), opmGraph.neighbours() );
|
||||
EXPECT_EQ( resdataGraph.poreVolume(), opmGraph.poreVolume() );
|
||||
EXPECT_EQ( resdataGraph.transmissibility(), opmGraph.transmissibility() );
|
||||
EXPECT_EQ( resdataGraph.localCellID(), opmGraph.localCellID() );
|
||||
|
||||
auto restartFile = gridFile;
|
||||
restartFile.replace_extension( ".UNRST" );
|
||||
if ( !std::filesystem::exists( restartFile ) ) return;
|
||||
|
||||
const Opm::ECLRestartData restart( restartFile.string() );
|
||||
if ( !restart.selectReportStep( 1 ) ) return;
|
||||
|
||||
for ( const auto phase : resdataGraph.activePhases() )
|
||||
{
|
||||
EXPECT_EQ( resdataGraph.flux( restart, phase ), opmGraph.flux( restart, phase ) );
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RifOpmCommonFlowDiagGridSource, SameGraphAsResdataWithLgrAndNnc )
|
||||
{
|
||||
const auto gridFile = std::filesystem::path( TEST_MODEL_DIR ) / "TEST10K_FLT_LGR_NNC/TEST10K_FLT_LGR_NNC.EGRID";
|
||||
if ( !std::filesystem::exists( gridFile ) ) GTEST_SKIP() << "Test file not found: " << gridFile;
|
||||
|
||||
// Make sure the model exercises LGR and NNC handling
|
||||
const RifOpmCommonFlowDiagGridSource opmSource( gridFile.string() );
|
||||
EXPECT_GT( opmSource.numGrids(), 1 );
|
||||
EXPECT_FALSE( opmSource.nncs().empty() );
|
||||
|
||||
compareGraphs( gridFile );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RifOpmCommonFlowDiagGridSource, SameGraphAsResdata )
|
||||
{
|
||||
compareGraphs( std::filesystem::path( TEST_MODEL_DIR ) / "flow_diagnostics_test/SIMPLE_SUMMARY2.EGRID" );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RifOpmCommonFlowDiagGridSource, SameGraphAsResdataWithNestedLgr )
|
||||
{
|
||||
compareGraphs( std::filesystem::path( TEST_MODEL_DIR ) / "NestedHybridGrid/DROGON_NESTED.EGRID" );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RifOpmCommonFlowDiagGridSource, SameGraphAsResdataOpmFlowOutput )
|
||||
{
|
||||
compareGraphs( std::filesystem::path( TEST_MODEL_DIR ) / "msw-export/opm-simulation-reference/BASE_MODEL_1.EGRID" );
|
||||
compareGraphs( std::filesystem::path( TEST_MODEL_DIR ) / "synthetic_ensemble_with_dynamic_filter/Real0/SYNTH.EGRID" );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Norne from a resinsight-tutorials checkout next to the ResInsight checkout. Skipped if not present.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RifOpmCommonFlowDiagGridSource, SameGraphAsResdataNorne )
|
||||
{
|
||||
compareGraphs( std::filesystem::path( TEST_MODEL_DIR ) / "../../resinsight-tutorials/model-data/norne/NORNE_ATW2013_RFTPLT_V2.EGRID" );
|
||||
}
|
||||
@@ -10,10 +10,15 @@
|
||||
#include "RigFlowDiagSolverInterface.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RimEclipseResultCase.h"
|
||||
#include "RimFlowDiagSolution.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QString>
|
||||
|
||||
#include "RiaPreferencesGrid.h"
|
||||
|
||||
#include "cafPdmField.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
@@ -154,3 +159,96 @@ TEST( FlowDiagnosticsTest, calculateRelPermCurves_Oil_in_Oil_Gas )
|
||||
<< "Relative permeability mismatch at index " << i << " - Expected: " << expectedKr[i] << ", Actual: " << krogCurve->yVals[i];
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void setOnlyLoadActiveCells( bool onlyActive )
|
||||
{
|
||||
auto field = dynamic_cast<caf::PdmField<bool>*>( RiaPreferencesGrid::current()->findField( "onlyLoadActiveCells" ) );
|
||||
ASSERT_NE( field, nullptr );
|
||||
field->setValue( onlyActive );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Run flow diagnostics for the last time step, with all wells as tracers
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::map<RigFlowDiagResultAddress, std::vector<double>>
|
||||
calculateFlowDiagnostics( const QString& egridFilePath, RiaDefines::GridModelReader reader, bool onlyActiveCells )
|
||||
{
|
||||
RiaPreferencesGrid::current()->setGridModelReaderOverride( reader );
|
||||
setOnlyLoadActiveCells( onlyActiveCells );
|
||||
|
||||
auto eclipseCase = std::make_unique<RimEclipseResultCase>();
|
||||
eclipseCase->setGridFileName( egridFilePath );
|
||||
const bool isOpen = eclipseCase->openEclipseGridFile();
|
||||
|
||||
RiaPreferencesGrid::current()->setGridModelReaderOverride( RiaDefines::GridModelReader::NOT_SET );
|
||||
setOnlyLoadActiveCells( false );
|
||||
|
||||
if ( !isOpen ) return {};
|
||||
|
||||
auto flowDiagSolution = eclipseCase->defaultFlowDiagSolution();
|
||||
if ( !flowDiagSolution || eclipseCase->timeStepDates().empty() ) return {};
|
||||
|
||||
const size_t timeStepIndex = eclipseCase->timeStepDates().size() - 1;
|
||||
|
||||
RigFlowDiagSolverInterface solver( eclipseCase.get() );
|
||||
|
||||
auto result = solver.calculate( timeStepIndex,
|
||||
RigFlowDiagResultAddress::PHASE_ALL,
|
||||
flowDiagSolution->allInjectorTracerActiveCellIndices( timeStepIndex ),
|
||||
flowDiagSolution->allProducerTracerActiveCellIndices( timeStepIndex ) );
|
||||
|
||||
return result.nativeResults();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Flow diagnostics must give the same results for grids imported with resdata and opm-common,
|
||||
/// including when only active cell geometry is imported
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void compareResultsForAllGridReaders( const QString& egridFilePath )
|
||||
{
|
||||
if ( !QFileInfo::exists( egridFilePath ) )
|
||||
{
|
||||
GTEST_SKIP() << "Test file not found: " << egridFilePath.toStdString();
|
||||
}
|
||||
|
||||
const auto resdataResults = calculateFlowDiagnostics( egridFilePath, RiaDefines::GridModelReader::RESDATA, false );
|
||||
ASSERT_FALSE( resdataResults.empty() );
|
||||
|
||||
for ( bool onlyActiveCells : { false, true } )
|
||||
{
|
||||
SCOPED_TRACE( onlyActiveCells ? "opm-common, active cells only" : "opm-common" );
|
||||
|
||||
const auto opmResults = calculateFlowDiagnostics( egridFilePath, RiaDefines::GridModelReader::OPM_COMMON, onlyActiveCells );
|
||||
ASSERT_EQ( resdataResults.size(), opmResults.size() );
|
||||
|
||||
for ( const auto& [address, values] : resdataResults )
|
||||
{
|
||||
auto it = opmResults.find( address );
|
||||
ASSERT_NE( it, opmResults.end() ) << address.variableName;
|
||||
EXPECT_EQ( values, it->second ) << address.variableName;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( FlowDiagnosticsTest, SameResultsForAllGridReaders )
|
||||
{
|
||||
compareResultsForAllGridReaders( QString( TEST_MODEL_DIR ) + "/synthetic_ensemble_with_dynamic_filter/Real0/SYNTH.EGRID" );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Norne from a resinsight-tutorials checkout next to the ResInsight checkout. Skipped if not present.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( FlowDiagnosticsTest, SameResultsForAllGridReadersNorne )
|
||||
{
|
||||
compareResultsForAllGridReaders( QString( TEST_MODEL_DIR ) +
|
||||
"/../../resinsight-tutorials/model-data/norne/NORNE_ATW2013_RFTPLT_V2.EGRID" );
|
||||
}
|
||||
Reference in new issue
Block a user