mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-26 15:10:22 -05:00
#15021: Add unit tests for completion export and non-Darcy D-factor
This commit is contained in:
1 parent
1e380d0a03
commit
a6c2b1b911
7 files changed
+970
-2
No files matched your search
@@ -77,6 +77,9 @@ set(SOURCE_UNITTEST_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifElementPropertyTableReader-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimRelocatePath-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigTransmissibilityCondenser-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigPerforationTransmissibilityEquations-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimFractureTemplateNonDarcy-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimStimPlanFractureTemplate-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiaEclipseUnitTools-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiaTextFileCompare-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifCaseRealizationParametersReader-Test.cpp
|
||||
@@ -220,6 +223,7 @@ set(SOURCE_UNITTEST_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigAllGridCellsResultAccessor-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicScheduleDataGenerator-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicWellPathExportCompletionDataFeatureImpl-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicCompletionDataExportTools-Test.cpp
|
||||
)
|
||||
|
||||
if(RESINSIGHT_ENABLE_GRPC)
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "CompletionExportCommands/RicCompletionDataExportTools.h"
|
||||
#include "CompletionExportCommands/RicExportCompletionDataSettingsUi.h"
|
||||
|
||||
#include "RifTextDataTableFormatter.h"
|
||||
|
||||
#include "RigCompletionData.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QString>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace
|
||||
{
|
||||
RigCompletionData
|
||||
makePerforation( double transmissibility, double diameter, double skin, double dFactor, double startMD = 100.0, double endMD = 110.0 )
|
||||
{
|
||||
RigCompletionData completion( "W", RigCompletionDataGridCell(), startMD );
|
||||
completion.setTransAndWPImultBackgroundDataFromPerforation( transmissibility,
|
||||
skin,
|
||||
diameter,
|
||||
dFactor,
|
||||
RigCompletionData::defaultValue(),
|
||||
RigCompletionData::CellDirection::DIR_K );
|
||||
completion.setDepthRange( startMD, endMD );
|
||||
return completion;
|
||||
}
|
||||
|
||||
// Owns the PDM object locally, so it is not alive after the test environment is torn down
|
||||
struct ExplicitTransSettings
|
||||
{
|
||||
ExplicitTransSettings() { m_settings.compdatExport = RicExportCompletionDataSettingsUi::CompdatExport::TRANSMISSIBILITIES; }
|
||||
|
||||
operator const RicExportCompletionDataSettingsUi&() const { return m_settings; }
|
||||
|
||||
RicExportCompletionDataSettingsUi m_settings;
|
||||
};
|
||||
|
||||
ExplicitTransSettings explicitTransSettings()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
QString exportTable( void ( *exporter )( RifTextDataTableFormatter&, const QString&, const std::vector<RigCompletionData>& ),
|
||||
const std::vector<RigCompletionData>& completions )
|
||||
{
|
||||
QString text;
|
||||
QTextStream stream( &text );
|
||||
RifTextDataTableFormatter formatter( stream );
|
||||
exporter( formatter, QString(), completions );
|
||||
return text;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RicCompletionDataExportTools, CombineSumsTransmissibilityAndWeightsValues )
|
||||
{
|
||||
const auto combined = RicCompletionDataExportTools::combineEclipseCellCompletions( { makePerforation( 1.0, 0.2, 1.0, 2.0 ),
|
||||
makePerforation( 3.0, 0.4, 5.0, 4.0 ) },
|
||||
explicitTransSettings() );
|
||||
|
||||
EXPECT_DOUBLE_EQ( 4.0, combined.transmissibility() );
|
||||
EXPECT_DOUBLE_EQ( ( 0.2 * 1.0 + 0.4 * 3.0 ) / 4.0, combined.diameter() );
|
||||
EXPECT_DOUBLE_EQ( ( 1.0 * 1.0 + 5.0 * 3.0 ) / 4.0, combined.skinFactor() );
|
||||
EXPECT_EQ( RigCompletionData::CompletionType::PERFORATION, combined.completionType() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// The combined D-factor is the transmissibility weighted mean divided by the number of completions
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RicCompletionDataExportTools, CombineDFactorIsWeightedMeanDividedByCount )
|
||||
{
|
||||
const auto combined = RicCompletionDataExportTools::combineEclipseCellCompletions( { makePerforation( 1.0, 0.2, 0.0, 2.0 ),
|
||||
makePerforation( 3.0, 0.2, 0.0, 4.0 ) },
|
||||
explicitTransSettings() );
|
||||
|
||||
EXPECT_DOUBLE_EQ( ( 2.0 * 1.0 + 4.0 * 3.0 ) / 4.0 / 2.0, combined.dFactor() );
|
||||
|
||||
const auto single =
|
||||
RicCompletionDataExportTools::combineEclipseCellCompletions( { makePerforation( 2.0, 0.2, 0.0, 3.0 ) }, explicitTransSettings() );
|
||||
EXPECT_DOUBLE_EQ( 3.0, single.dFactor() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RicCompletionDataExportTools, CombineMergesMeasuredDepthRange )
|
||||
{
|
||||
const auto combined = RicCompletionDataExportTools::combineEclipseCellCompletions( { makePerforation( 1.0, 0.2, 0.0, 1.0, 100.0, 110.0 ),
|
||||
makePerforation( 1.0, 0.2, 0.0, 1.0, 105.0, 130.0 ) },
|
||||
explicitTransSettings() );
|
||||
|
||||
ASSERT_TRUE( combined.startMD().has_value() );
|
||||
ASSERT_TRUE( combined.endMD().has_value() );
|
||||
EXPECT_DOUBLE_EQ( 100.0, combined.startMD().value() );
|
||||
EXPECT_DOUBLE_EQ( 130.0, combined.endMD().value() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RicCompletionDataExportTools, CombineIgnoresNegativeTransmissibility )
|
||||
{
|
||||
const auto combined = RicCompletionDataExportTools::combineEclipseCellCompletions( { makePerforation( 2.0, 0.2, 0.0, 1.0 ),
|
||||
makePerforation( -1.0, 0.2, 0.0, 1.0 ) },
|
||||
explicitTransSettings() );
|
||||
|
||||
EXPECT_DOUBLE_EQ( 2.0, combined.transmissibility() );
|
||||
// Skipped completions must not count in the divisor of the combined D-factor
|
||||
EXPECT_DOUBLE_EQ( 1.0, combined.dFactor() );
|
||||
ASSERT_FALSE( combined.metadata().empty() );
|
||||
EXPECT_EQ( "ERROR", combined.metadata().front().name );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RicCompletionDataExportTools, CombineDifferentWellsGivesUnexportedCompletion )
|
||||
{
|
||||
RigCompletionData other = makePerforation( 1.0, 0.2, 0.0, 1.0 );
|
||||
RigCompletionData otherWell( "OTHER", RigCompletionDataGridCell(), 100.0 );
|
||||
otherWell.setTransAndWPImultBackgroundDataFromPerforation( 1.0, 0.0, 0.2, 1.0, 1.0, RigCompletionData::CellDirection::DIR_K );
|
||||
|
||||
const auto combined = RicCompletionDataExportTools::combineEclipseCellCompletions( { other, otherWell }, explicitTransSettings() );
|
||||
|
||||
EXPECT_TRUE( RigCompletionData::isDefaultValue( combined.transmissibility() ) );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RicCompletionDataExportTools, CombineIgnoresInfiniteTransmissibility )
|
||||
{
|
||||
const auto combined = RicCompletionDataExportTools::combineEclipseCellCompletions( { makePerforation( HUGE_VAL, 0.2, 0.0, 1.0 ) },
|
||||
explicitTransSettings() );
|
||||
|
||||
EXPECT_DOUBLE_EQ( 0.0, combined.transmissibility() );
|
||||
ASSERT_FALSE( combined.metadata().empty() );
|
||||
EXPECT_EQ( "ERROR", combined.metadata().front().name );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RicCompletionDataExportTools, MainGridCompletionsKeepsCellsWithoutLgr )
|
||||
{
|
||||
std::vector<RigCompletionData> completions = { makePerforation( 1.0, 0.2, 0.0, 1.0 ), makePerforation( 2.0, 0.2, 0.0, 1.0 ) };
|
||||
|
||||
EXPECT_EQ( 2u, RicCompletionDataExportTools::mainGridCompletions( completions ).size() );
|
||||
EXPECT_TRUE( RicCompletionDataExportTools::subGridsCompletions( completions ).empty() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RicCompletionDataExportTools, AppendCompletionDataGroupsByGlobalCellIndex )
|
||||
{
|
||||
std::map<size_t, std::vector<RigCompletionData>> grouped;
|
||||
|
||||
RicCompletionDataExportTools::appendCompletionData( &grouped,
|
||||
{ makePerforation( 1.0, 0.2, 0.0, 1.0 ), makePerforation( 2.0, 0.2, 0.0, 1.0 ) } );
|
||||
RicCompletionDataExportTools::appendCompletionData( &grouped, { makePerforation( 3.0, 0.2, 0.0, 1.0 ) } );
|
||||
|
||||
ASSERT_EQ( 1u, grouped.size() );
|
||||
EXPECT_EQ( 3u, grouped.begin()->second.size() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RicCompletionDataExportTools, CompdatUsesDefaultMarkerWithoutDFactor )
|
||||
{
|
||||
const QString text = exportTable( RicCompletionDataExportTools::exportCompdatTableUsingFormatter,
|
||||
{ makePerforation( 1.5, 0.2, 0.0, RigCompletionData::defaultValue() ) } );
|
||||
|
||||
EXPECT_TRUE( text.contains( "COMPDAT" ) ) << text.toStdString();
|
||||
EXPECT_TRUE( text.contains( "'Z'" ) ) << text.toStdString();
|
||||
// The D-factor column is the last one before the direction and is defaulted
|
||||
EXPECT_TRUE( text.contains( QRegularExpression( "1\\*\\s+'Z'" ) ) ) << text.toStdString();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RicCompletionDataExportTools, CompdatWritesDFactorWhenSet )
|
||||
{
|
||||
auto completion = makePerforation( 1.5, 0.2, 0.0, 2.5 );
|
||||
completion.setPerConnectionDfactor();
|
||||
|
||||
const QString text = exportTable( RicCompletionDataExportTools::exportCompdatTableUsingFormatter, { completion } );
|
||||
|
||||
EXPECT_FALSE( text.contains( QRegularExpression( "1\\*\\s+'Z'" ) ) ) << text.toStdString();
|
||||
EXPECT_TRUE( text.contains( "-2.5" ) ) << text.toStdString();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RicCompletionDataExportTools, CompdatSkipsCompletionsWithoutTransmissibility )
|
||||
{
|
||||
const QString text =
|
||||
exportTable( RicCompletionDataExportTools::exportCompdatTableUsingFormatter, { makePerforation( 0.0, 0.2, 0.0, 1.0 ) } );
|
||||
|
||||
EXPECT_FALSE( text.contains( "'Z'" ) ) << text.toStdString();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RicCompletionDataExportTools, ComplumpOnlyExportsCompletionsWithNumber )
|
||||
{
|
||||
auto withNumber = makePerforation( 1.5, 0.2, 0.0, 1.0 );
|
||||
withNumber.setCompletionNumber( 7 );
|
||||
auto withoutNumber = makePerforation( 1.5, 0.2, 0.0, 1.0 );
|
||||
|
||||
const QString text = exportTable( RicCompletionDataExportTools::exportComplumpTableUsingFormatter, { withoutNumber } );
|
||||
EXPECT_FALSE( text.contains( "7" ) ) << text.toStdString();
|
||||
|
||||
const QString textWithNumber = exportTable( RicCompletionDataExportTools::exportComplumpTableUsingFormatter, { withNumber } );
|
||||
EXPECT_TRUE( textWithNumber.contains( "COMPLUMP" ) );
|
||||
EXPECT_TRUE( textWithNumber.contains( QRegularExpression( "\\s7\\s" ) ) ) << textWithNumber.toStdString();
|
||||
}
|
||||
@@ -20,13 +20,143 @@
|
||||
|
||||
#include "Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.h"
|
||||
|
||||
#include "CompletionExportCommands/RicCompletionDataExportTools.h"
|
||||
#include "CompletionExportCommands/RicTransmissibilityCalculator.h"
|
||||
#include "RiaEclipseUnitTools.h"
|
||||
|
||||
#include "RiaFractureDefines.h"
|
||||
#include "RifReaderMockModel.h"
|
||||
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigCaseCellResultsData.h"
|
||||
#include "RigCompletionData.h"
|
||||
#include "RigEclipseCaseData.h"
|
||||
#include "RigEclipseResultAddress.h"
|
||||
#include "RigEclipseResultInfo.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigPerforationTransmissibilityEquations.h"
|
||||
|
||||
#include "Well/RigWellPath.h"
|
||||
|
||||
#include "RimEclipseResultCase.h"
|
||||
#include "RimNonDarcyPerforationParameters.h"
|
||||
#include "RimOilField.h"
|
||||
#include "RimPerforationCollection.h"
|
||||
#include "RimPerforationInterval.h"
|
||||
#include "RimProject.h"
|
||||
#include "RimWellPath.h"
|
||||
#include "RimWellPathCollection.h"
|
||||
|
||||
#include "cafPdmField.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <optional>
|
||||
|
||||
namespace
|
||||
{
|
||||
void addStaticResult( RigCaseCellResultsData* cellResults, const QString& name, size_t cellCount, double value )
|
||||
{
|
||||
RigEclipseResultAddress address( RiaDefines::ResultCatType::STATIC_NATIVE, name );
|
||||
cellResults->createResultEntry( address, false );
|
||||
cellResults->setTimeStepInfos( address, { RigEclipseTimeStepInfo( QDateTime(), 0, 0.0 ) } );
|
||||
|
||||
auto* timesteps = cellResults->modifiableCellScalarResultTimesteps( address );
|
||||
timesteps->resize( 1 );
|
||||
( *timesteps )[0].assign( cellCount, value );
|
||||
}
|
||||
|
||||
// 2x2x2 grid with 50 unit cells, z = 0 at the top. PERMX = PERMY = 100 mD, PERMZ = 10 mD, NTG = 1, PORO = 0.2
|
||||
RimEclipseResultCase* createMockEclipseCase( RiaDefines::EclipseUnitSystem unitSystem )
|
||||
{
|
||||
auto* eclipseCase = new RimEclipseResultCase;
|
||||
|
||||
cvf::ref<RigEclipseCaseData> caseData = new RigEclipseCaseData( eclipseCase );
|
||||
caseData->setUnitsType( unitSystem );
|
||||
cvf::ref<RifReaderMockModel> mockReader = new RifReaderMockModel;
|
||||
mockReader->setWorldCoordinates( cvf::Vec3d( 0, 0, -100 ), cvf::Vec3d( 100, 100, 0 ) );
|
||||
mockReader->setCellCounts( cvf::Vec3st( 2, 2, 2 ) );
|
||||
mockReader->enableWellData( false );
|
||||
mockReader->open( "", caseData.p() );
|
||||
caseData->mainGrid()->computeCachedData();
|
||||
|
||||
eclipseCase->setReservoirData( caseData.p() );
|
||||
|
||||
const size_t cellCount = caseData->mainGrid()->totalCellCount();
|
||||
RigCaseCellResultsData* cellResults = caseData->results( RiaDefines::PorosityModelType::MATRIX_MODEL );
|
||||
addStaticResult( cellResults, "DX", cellCount, 50.0 );
|
||||
addStaticResult( cellResults, "DY", cellCount, 50.0 );
|
||||
addStaticResult( cellResults, "DZ", cellCount, 50.0 );
|
||||
addStaticResult( cellResults, "PERMX", cellCount, 100.0 );
|
||||
addStaticResult( cellResults, "PERMY", cellCount, 100.0 );
|
||||
addStaticResult( cellResults, "PERMZ", cellCount, 10.0 );
|
||||
addStaticResult( cellResults, "NTG", cellCount, 1.0 );
|
||||
addStaticResult( cellResults, "PORO", cellCount, 0.2 );
|
||||
|
||||
return eclipseCase;
|
||||
}
|
||||
|
||||
// Vertical well through the two upper-left cells, perforated over the full grid height
|
||||
class PerforatedWellFixture
|
||||
{
|
||||
public:
|
||||
explicit PerforatedWellFixture( RiaDefines::EclipseUnitSystem unitSystem )
|
||||
: unitSystem( unitSystem )
|
||||
, wellRadius( unitSystem == RiaDefines::EclipseUnitSystem::UNITS_FIELD ? 0.328 : 0.1 )
|
||||
, eclipseCase( createMockEclipseCase( unitSystem ) )
|
||||
, project( new RimProject )
|
||||
, wellPath( new RimWellPath )
|
||||
{
|
||||
wellPath->setName( "TestWell" );
|
||||
wellPath->setUnitSystem( unitSystem );
|
||||
|
||||
cvf::ref<RigWellPath> geometry = new RigWellPath( { cvf::Vec3d( 25, 25, 10 ), cvf::Vec3d( 25, 25, -110 ) }, { 0.0, 120.0 } );
|
||||
wellPath->setWellPathGeometry( geometry.p() );
|
||||
project->oilFields[0]->wellPathCollection->addWellPath( wellPath );
|
||||
|
||||
auto* interval = new RimPerforationInterval;
|
||||
interval->setStartAndEndMD( 10.0, 110.0 );
|
||||
interval->setDiameter( 2.0 * wellRadius );
|
||||
interval->setSkinFactor( 0.0 );
|
||||
wellPath->perforationIntervalCollection()->appendPerforation( interval );
|
||||
}
|
||||
|
||||
~PerforatedWellFixture()
|
||||
{
|
||||
delete eclipseCase;
|
||||
project->close();
|
||||
delete project;
|
||||
}
|
||||
|
||||
void setNonDarcyType( RimNonDarcyPerforationParameters::NonDarcyFlowEnum type )
|
||||
{
|
||||
auto* params = const_cast<RimNonDarcyPerforationParameters*>( wellPath->perforationIntervalCollection()->nonDarcyParameters() );
|
||||
auto* field = dynamic_cast<caf::PdmField<caf::AppEnum<RimNonDarcyPerforationParameters::NonDarcyFlowEnum>>*>(
|
||||
params->findField( "NonDarcyFlowType" ) );
|
||||
ASSERT_NE( nullptr, field );
|
||||
field->setValue( type );
|
||||
}
|
||||
|
||||
void setUserDefinedDFactor( double dFactor )
|
||||
{
|
||||
auto* params = const_cast<RimNonDarcyPerforationParameters*>( wellPath->perforationIntervalCollection()->nonDarcyParameters() );
|
||||
auto* field = dynamic_cast<caf::PdmField<double>*>( params->findField( "UserDefinedDFactor" ) );
|
||||
ASSERT_NE( nullptr, field );
|
||||
field->setValue( dFactor );
|
||||
}
|
||||
|
||||
std::vector<RigCompletionData> completions()
|
||||
{
|
||||
return RicWellPathExportCompletionDataFeatureImpl::completionDataForWellPath( wellPath, eclipseCase );
|
||||
}
|
||||
|
||||
RiaDefines::EclipseUnitSystem unitSystem;
|
||||
double wellRadius;
|
||||
RimEclipseResultCase* eclipseCase;
|
||||
RimProject* project;
|
||||
RimWellPath* wellPath;
|
||||
};
|
||||
|
||||
RigCompletionData makeCompletion( double startMD, double endMD, std::optional<int> completionNumber )
|
||||
{
|
||||
RigCompletionData completion( "W", RigCompletionDataGridCell( 0, nullptr ), startMD );
|
||||
@@ -49,7 +179,7 @@ std::optional<int> numberForAllOrders( std::vector<RigCompletionData> completion
|
||||
for ( size_t i : order )
|
||||
permuted.push_back( completions[i] );
|
||||
|
||||
auto number = RicWellPathExportCompletionDataFeatureImpl::completionNumberForCell( permuted );
|
||||
auto number = RicCompletionDataExportTools::completionNumberForCell( permuted );
|
||||
if ( !firstSet )
|
||||
{
|
||||
first = number;
|
||||
@@ -104,5 +234,134 @@ TEST( RicWellPathExportCompletionDataFeatureImpl, CompletionNumberIgnoresContrib
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RicWellPathExportCompletionDataFeatureImpl, CompletionNumberEmptyCompletionsGivesNoNumber )
|
||||
{
|
||||
EXPECT_EQ( std::nullopt, RicWellPathExportCompletionDataFeatureImpl::completionNumberForCell( {} ) );
|
||||
EXPECT_EQ( std::nullopt, RicCompletionDataExportTools::completionNumberForCell( {} ) );
|
||||
}
|
||||
|
||||
class CompdatNonDarcyTest : public ::testing::TestWithParam<RiaDefines::EclipseUnitSystem>
|
||||
{
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Without non-Darcy flow, COMPDAT connections get a transmissibility but the D-factor is defaulted
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST_P( CompdatNonDarcyTest, WithoutNonDarcyHasDefaultDFactor )
|
||||
{
|
||||
PerforatedWellFixture fixture( GetParam() );
|
||||
fixture.setNonDarcyType( RimNonDarcyPerforationParameters::NON_DARCY_NONE );
|
||||
|
||||
auto completions = fixture.completions();
|
||||
ASSERT_EQ( 2u, completions.size() );
|
||||
|
||||
for ( const auto& completion : completions )
|
||||
{
|
||||
EXPECT_GT( completion.transmissibility(), 0.0 );
|
||||
EXPECT_TRUE( RigCompletionData::isDefaultValue( completion.dFactor() ) );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// User-defined well D-factor is distributed as D_conn = D_well * sum(T) / T_conn and exported negative
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST_P( CompdatNonDarcyTest, WithUserDefinedDFactor )
|
||||
{
|
||||
PerforatedWellFixture fixture( GetParam() );
|
||||
fixture.setNonDarcyType( RimNonDarcyPerforationParameters::NON_DARCY_USER_DEFINED );
|
||||
fixture.setUserDefinedDFactor( 0.5 );
|
||||
|
||||
auto completions = fixture.completions();
|
||||
ASSERT_EQ( 2u, completions.size() );
|
||||
|
||||
double sumT = 0.0;
|
||||
for ( const auto& completion : completions )
|
||||
sumT += completion.transmissibility();
|
||||
|
||||
for ( const auto& completion : completions )
|
||||
{
|
||||
EXPECT_LT( completion.dFactor(), 0.0 );
|
||||
EXPECT_NEAR( -0.5 * sumT / completion.transmissibility(), completion.dFactor(), 1.0e-9 );
|
||||
}
|
||||
|
||||
// Identical cells share the well D-factor equally
|
||||
EXPECT_NEAR( completions[0].dFactor(), completions[1].dFactor(), 1.0e-9 );
|
||||
EXPECT_NEAR( -1.0, completions[0].dFactor(), 1.0e-9 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Computed D-factor must match the D-factor equation applied to the connection's effective h and K
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST_P( CompdatNonDarcyTest, WithComputedDFactor )
|
||||
{
|
||||
PerforatedWellFixture fixture( GetParam() );
|
||||
fixture.setNonDarcyType( RimNonDarcyPerforationParameters::NON_DARCY_COMPUTED );
|
||||
|
||||
auto completions = fixture.completions();
|
||||
ASSERT_EQ( 2u, completions.size() );
|
||||
|
||||
const auto* params = fixture.wellPath->perforationIntervalCollection()->nonDarcyParameters();
|
||||
const double alpha = RiaDefines::nonDarcyFlowAlpha( GetParam() );
|
||||
|
||||
for ( const auto& completion : completions )
|
||||
{
|
||||
// Vertical well: 50 length units of well inside each cell, along K
|
||||
auto data = RicTransmissibilityCalculator::calculateTransmissibilityData( fixture.eclipseCase,
|
||||
fixture.wellPath,
|
||||
cvf::Vec3d( 0.0, 0.0, 50.0 ),
|
||||
0.0,
|
||||
fixture.wellRadius,
|
||||
completion.completionDataGridCell().globalCellIndex(),
|
||||
false );
|
||||
ASSERT_TRUE( data.isValid() );
|
||||
|
||||
const double effectiveK = params->gridPermeabilityScalingFactor() * data.effectiveK();
|
||||
|
||||
const double beta = RigPerforationTransmissibilityEquations::betaFactor( params->inertialCoefficientBeta0(),
|
||||
effectiveK,
|
||||
params->permeabilityScalingFactor(),
|
||||
0.2,
|
||||
params->porosityScalingFactor() );
|
||||
|
||||
// The radius parameter is entered in meters; the field alpha expects feet
|
||||
const double radiusForDFactor = ( GetParam() == RiaDefines::EclipseUnitSystem::UNITS_FIELD )
|
||||
? params->wellRadius() * RiaEclipseUnitTools::feetPerMeter()
|
||||
: params->wellRadius();
|
||||
|
||||
const double expected = RigPerforationTransmissibilityEquations::dFactor( alpha,
|
||||
beta,
|
||||
effectiveK,
|
||||
data.effectiveH(),
|
||||
radiusForDFactor,
|
||||
params->relativeGasDensity(),
|
||||
params->gasViscosity() );
|
||||
|
||||
ASSERT_TRUE( std::isfinite( expected ) );
|
||||
EXPECT_GT( expected, 0.0 );
|
||||
EXPECT_FALSE( RigCompletionData::isDefaultValue( completion.dFactor() ) );
|
||||
EXPECT_NEAR( -expected, completion.dFactor(), 1.0e-9 * expected );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// The connection factor itself must not depend on the non-Darcy setting
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST_P( CompdatNonDarcyTest, TransmissibilityIndependentOfNonDarcy )
|
||||
{
|
||||
PerforatedWellFixture fixture( GetParam() );
|
||||
|
||||
fixture.setNonDarcyType( RimNonDarcyPerforationParameters::NON_DARCY_NONE );
|
||||
auto without = fixture.completions();
|
||||
|
||||
fixture.setNonDarcyType( RimNonDarcyPerforationParameters::NON_DARCY_COMPUTED );
|
||||
auto with = fixture.completions();
|
||||
|
||||
ASSERT_EQ( without.size(), with.size() );
|
||||
for ( size_t i = 0; i < with.size(); ++i )
|
||||
{
|
||||
EXPECT_DOUBLE_EQ( without[i].transmissibility(), with[i].transmissibility() );
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P( MetricAndField,
|
||||
CompdatNonDarcyTest,
|
||||
::testing::Values( RiaDefines::EclipseUnitSystem::UNITS_METRIC, RiaDefines::EclipseUnitSystem::UNITS_FIELD ),
|
||||
[]( const ::testing::TestParamInfo<RiaDefines::EclipseUnitSystem>& info )
|
||||
{ return info.param == RiaDefines::EclipseUnitSystem::UNITS_FIELD ? "Field" : "Metric"; } );
|
||||
@@ -0,0 +1,70 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "gtest/gtest.h"
|
||||
|
||||
#include "RigPerforationTransmissibilityEquations.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
TEST( RigPerforationTransmissibilityEquations, BetaFactor )
|
||||
{
|
||||
// beta = inertialCoefficient * Ke^permScaling * phi^porosityScaling
|
||||
double beta = RigPerforationTransmissibilityEquations::betaFactor( 2.0, 100.0, 0.5, 0.25, 2.0 );
|
||||
EXPECT_NEAR( 2.0 * 10.0 * 0.0625, beta, 1.0e-12 );
|
||||
|
||||
// Zero exponents leave only the inertial coefficient
|
||||
EXPECT_DOUBLE_EQ( 3.5, RigPerforationTransmissibilityEquations::betaFactor( 3.5, 123.0, 0.0, 0.3, 0.0 ) );
|
||||
}
|
||||
|
||||
TEST( RigPerforationTransmissibilityEquations, BetaFactorNegativeExponents )
|
||||
{
|
||||
// Typical non-Darcy correlation uses negative exponents: beta = C * Ke^-1.2 * phi^-0.5
|
||||
double beta = RigPerforationTransmissibilityEquations::betaFactor( 1.0e6, 100.0, -1.2, 0.25, -0.5 );
|
||||
EXPECT_NEAR( 1.0e6 * std::pow( 100.0, -1.2 ) * 2.0, beta, 1.0e-6 );
|
||||
|
||||
// Unit permeability and porosity make the exponents irrelevant
|
||||
EXPECT_DOUBLE_EQ( 7.0, RigPerforationTransmissibilityEquations::betaFactor( 7.0, 1.0, -1.2, 1.0, -0.5 ) );
|
||||
|
||||
// Linear in the inertial coefficient
|
||||
const double base = RigPerforationTransmissibilityEquations::betaFactor( 1.0, 50.0, -1.2, 0.2, -0.5 );
|
||||
EXPECT_NEAR( 3.0 * base, RigPerforationTransmissibilityEquations::betaFactor( 3.0, 50.0, -1.2, 0.2, -0.5 ), 1.0e-12 );
|
||||
}
|
||||
|
||||
TEST( RigPerforationTransmissibilityEquations, DFactor )
|
||||
{
|
||||
// D = alpha * beta * Ke / h * 1 / rw * rho / mu
|
||||
double d = RigPerforationTransmissibilityEquations::dFactor( 2.0, 3.0, 40.0, 5.0, 0.1, 0.8, 0.02 );
|
||||
EXPECT_NEAR( 2.0 * 3.0 * 8.0 * 10.0 * 40.0, d, 1.0e-9 );
|
||||
}
|
||||
|
||||
TEST( RigPerforationTransmissibilityEquations, DFactorScaling )
|
||||
{
|
||||
const double base = RigPerforationTransmissibilityEquations::dFactor( 1.0, 1.0, 10.0, 2.0, 0.1, 1.0, 1.0 );
|
||||
|
||||
// Linear in unit constant, beta, permeability, density
|
||||
EXPECT_NEAR( 2.0 * base, RigPerforationTransmissibilityEquations::dFactor( 2.0, 1.0, 10.0, 2.0, 0.1, 1.0, 1.0 ), 1.0e-9 );
|
||||
EXPECT_NEAR( 2.0 * base, RigPerforationTransmissibilityEquations::dFactor( 1.0, 2.0, 10.0, 2.0, 0.1, 1.0, 1.0 ), 1.0e-9 );
|
||||
EXPECT_NEAR( 2.0 * base, RigPerforationTransmissibilityEquations::dFactor( 1.0, 1.0, 20.0, 2.0, 0.1, 1.0, 1.0 ), 1.0e-9 );
|
||||
EXPECT_NEAR( 2.0 * base, RigPerforationTransmissibilityEquations::dFactor( 1.0, 1.0, 10.0, 2.0, 0.1, 2.0, 1.0 ), 1.0e-9 );
|
||||
|
||||
// Inversely proportional to length, well radius and viscosity
|
||||
EXPECT_NEAR( 0.5 * base, RigPerforationTransmissibilityEquations::dFactor( 1.0, 1.0, 10.0, 4.0, 0.1, 1.0, 1.0 ), 1.0e-9 );
|
||||
EXPECT_NEAR( 0.5 * base, RigPerforationTransmissibilityEquations::dFactor( 1.0, 1.0, 10.0, 2.0, 0.2, 1.0, 1.0 ), 1.0e-9 );
|
||||
EXPECT_NEAR( 0.5 * base, RigPerforationTransmissibilityEquations::dFactor( 1.0, 1.0, 10.0, 2.0, 0.1, 1.0, 2.0 ), 1.0e-9 );
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RiaDefines.h"
|
||||
#include "RiaFractureDefines.h"
|
||||
|
||||
#include "RimEllipseFractureTemplate.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Exposes protected settings of the template, so that the values reported by
|
||||
/// wellFractureIntersectionData() can be verified through the public compute functions
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class TestEllipseFractureTemplate : public RimEllipseFractureTemplate
|
||||
{
|
||||
public:
|
||||
explicit TestEllipseFractureTemplate( RiaDefines::EclipseUnitSystem unit )
|
||||
{
|
||||
setUnitSystem( unit );
|
||||
setDefaultValuesFromUnit();
|
||||
}
|
||||
|
||||
void useFractureData()
|
||||
{
|
||||
m_fractureWidthType = WIDTH_FROM_FRACTURE;
|
||||
m_permeabilityType = CONDUCTIVITY_FROM_FRACTURE;
|
||||
m_betaFactorType = USER_DEFINED_BETA_FACTOR;
|
||||
m_relativePermeability = 1.0;
|
||||
}
|
||||
|
||||
void useUserDefinedWidth( double width )
|
||||
{
|
||||
m_fractureWidthType = USER_DEFINED_WIDTH;
|
||||
m_fractureWidth = width;
|
||||
}
|
||||
|
||||
void useComputedDFactor( double inertialCoefficient )
|
||||
{
|
||||
m_nonDarcyFlowType = NON_DARCY_COMPUTED;
|
||||
m_inertialCoefficient = inertialCoefficient;
|
||||
}
|
||||
|
||||
void useUserDefinedDFactor( double dFactor )
|
||||
{
|
||||
m_nonDarcyFlowType = NON_DARCY_USER_DEFINED;
|
||||
m_userDefinedDFactor = dFactor;
|
||||
}
|
||||
|
||||
void setRelativePermeability( double value ) { m_relativePermeability = value; }
|
||||
void setOrientation( FracOrientationEnum orientation ) { m_orientationType = orientation; }
|
||||
double wellDiameterValue() const { return m_wellDiameter; }
|
||||
double gasDensity() const { return m_relativeGasDensity; }
|
||||
double gasViscosityValue() const { return m_gasViscosity; }
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Ellipse templates report the user entered width and permeability as intersection data
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimFractureTemplateNonDarcy, EllipseIntersectionDataMetric )
|
||||
{
|
||||
TestEllipseFractureTemplate tmpl( RiaDefines::EclipseUnitSystem::UNITS_METRIC );
|
||||
tmpl.useFractureData();
|
||||
|
||||
// Metric defaults: width 0.01 m, permeability 100000 mD
|
||||
EXPECT_DOUBLE_EQ( 0.01, tmpl.computeFractureWidth( nullptr ) );
|
||||
EXPECT_DOUBLE_EQ( 100000.0, tmpl.computeEffectivePermeability( nullptr ) );
|
||||
EXPECT_NEAR( 1000.0, tmpl.computeKh( nullptr ), 1.0e-9 );
|
||||
}
|
||||
|
||||
TEST( RimFractureTemplateNonDarcy, EllipseIntersectionDataField )
|
||||
{
|
||||
TestEllipseFractureTemplate tmpl( RiaDefines::EclipseUnitSystem::UNITS_FIELD );
|
||||
tmpl.useFractureData();
|
||||
|
||||
// Field defaults: width 0.5 inches (reported in feet), permeability 80000 mD
|
||||
EXPECT_NEAR( 0.5 / 12.0, tmpl.computeFractureWidth( nullptr ), 1.0e-12 );
|
||||
EXPECT_DOUBLE_EQ( 80000.0, tmpl.computeEffectivePermeability( nullptr ) );
|
||||
EXPECT_NEAR( 80000.0 * 0.5 / 12.0, tmpl.computeKh( nullptr ), 1.0e-9 );
|
||||
}
|
||||
|
||||
TEST( RimFractureTemplateNonDarcy, RelativePermeabilityScalesEffectivePermeability )
|
||||
{
|
||||
TestEllipseFractureTemplate tmpl( RiaDefines::EclipseUnitSystem::UNITS_METRIC );
|
||||
tmpl.useFractureData();
|
||||
tmpl.setRelativePermeability( 0.25 );
|
||||
|
||||
EXPECT_DOUBLE_EQ( 25000.0, tmpl.computeEffectivePermeability( nullptr ) );
|
||||
EXPECT_NEAR( 250.0, tmpl.computeKh( nullptr ), 1.0e-9 );
|
||||
}
|
||||
|
||||
TEST( RimFractureTemplateNonDarcy, UserDefinedWidthOverridesIntersectionWidth )
|
||||
{
|
||||
TestEllipseFractureTemplate tmpl( RiaDefines::EclipseUnitSystem::UNITS_METRIC );
|
||||
tmpl.useFractureData();
|
||||
tmpl.useUserDefinedWidth( 0.05 );
|
||||
|
||||
EXPECT_DOUBLE_EQ( 0.05, tmpl.computeFractureWidth( nullptr ) );
|
||||
EXPECT_NEAR( 100000.0 * 0.05, tmpl.computeKh( nullptr ), 1.0e-9 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// D = alpha * beta * Ke / h * 1 / rw * gamma / mu, using the intersection data of the template
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimFractureTemplateNonDarcy, ComputedDFactorMatchesEquation )
|
||||
{
|
||||
TestEllipseFractureTemplate tmpl( RiaDefines::EclipseUnitSystem::UNITS_METRIC );
|
||||
tmpl.useFractureData();
|
||||
tmpl.useComputedDFactor( 0.5 );
|
||||
|
||||
const double alpha = RiaDefines::nonDarcyFlowAlpha( RiaDefines::EclipseUnitSystem::UNITS_METRIC );
|
||||
const double radius = tmpl.wellDiameterValue() / 2.0;
|
||||
const double ke = 100000.0;
|
||||
const double h = 0.01;
|
||||
|
||||
const double expected = alpha * 0.5 * ke * tmpl.gasDensity() / ( h * radius * tmpl.gasViscosityValue() );
|
||||
|
||||
EXPECT_NEAR( expected, tmpl.computeDFactor( nullptr ), expected * 1.0e-12 );
|
||||
EXPECT_NEAR( radius, tmpl.computeWellRadiusForDFactorCalculation( nullptr ), 1.0e-12 );
|
||||
}
|
||||
|
||||
TEST( RimFractureTemplateNonDarcy, ComputedDFactorUsesUnitDependentAlpha )
|
||||
{
|
||||
TestEllipseFractureTemplate metric( RiaDefines::EclipseUnitSystem::UNITS_METRIC );
|
||||
metric.useFractureData();
|
||||
metric.useComputedDFactor( 1.0 );
|
||||
metric.useUserDefinedWidth( 0.01 );
|
||||
|
||||
TestEllipseFractureTemplate field( RiaDefines::EclipseUnitSystem::UNITS_FIELD );
|
||||
field.useFractureData();
|
||||
field.useComputedDFactor( 1.0 );
|
||||
field.useUserDefinedWidth( 0.01 );
|
||||
|
||||
// Make everything but alpha, permeability and well radius equal between the two templates
|
||||
const double ratio = ( field.computeDFactor( nullptr ) / metric.computeDFactor( nullptr ) );
|
||||
|
||||
// The field well diameter is given in inches, but the radius used in the D-factor must be in feet
|
||||
EXPECT_NEAR( field.wellDiameterValue() / 2.0 / 12.0, field.computeWellRadiusForDFactorCalculation( nullptr ), 1.0e-12 );
|
||||
|
||||
const double alphaRatio = RiaDefines::nonDarcyFlowAlpha( RiaDefines::EclipseUnitSystem::UNITS_FIELD ) /
|
||||
RiaDefines::nonDarcyFlowAlpha( RiaDefines::EclipseUnitSystem::UNITS_METRIC );
|
||||
const double permRatio = field.computeEffectivePermeability( nullptr ) / metric.computeEffectivePermeability( nullptr );
|
||||
const double metricRadius = metric.wellDiameterValue() / 2.0; // [m]
|
||||
const double fieldRadius = field.wellDiameterValue() / 2.0 / 12.0; // inches -> [ft]
|
||||
const double radiusRatio = metricRadius / fieldRadius;
|
||||
|
||||
EXPECT_NEAR( alphaRatio * permRatio * radiusRatio, ratio, ratio * 1.0e-12 );
|
||||
}
|
||||
|
||||
TEST( RimFractureTemplateNonDarcy, DFactorScaleFactorAndAlongWellPathCorrection )
|
||||
{
|
||||
TestEllipseFractureTemplate tmpl( RiaDefines::EclipseUnitSystem::UNITS_METRIC );
|
||||
tmpl.useFractureData();
|
||||
tmpl.useComputedDFactor( 0.5 );
|
||||
|
||||
const double base = tmpl.computeDFactor( nullptr );
|
||||
|
||||
tmpl.setScaleFactors( 1.0, 1.0, 3.0, 1.0 );
|
||||
EXPECT_NEAR( 3.0 * base, tmpl.computeDFactor( nullptr ), base * 1.0e-12 );
|
||||
|
||||
// Linear inflow correction factor of 1.2 for fractures along the well path
|
||||
tmpl.setScaleFactors( 1.0, 1.0, 1.0, 1.0 );
|
||||
tmpl.setOrientation( RimFractureTemplate::ALONG_WELL_PATH );
|
||||
EXPECT_NEAR( 1.2 * base, tmpl.computeDFactor( nullptr ), base * 1.0e-12 );
|
||||
}
|
||||
|
||||
TEST( RimFractureTemplateNonDarcy, UserDefinedDFactorIsScaledButNotComputed )
|
||||
{
|
||||
TestEllipseFractureTemplate tmpl( RiaDefines::EclipseUnitSystem::UNITS_METRIC );
|
||||
tmpl.useFractureData();
|
||||
tmpl.useUserDefinedDFactor( 0.125 );
|
||||
|
||||
EXPECT_DOUBLE_EQ( 0.125, tmpl.computeDFactor( nullptr ) );
|
||||
|
||||
tmpl.setScaleFactors( 1.0, 1.0, 2.0, 1.0 );
|
||||
EXPECT_DOUBLE_EQ( 0.25, tmpl.computeDFactor( nullptr ) );
|
||||
}
|
||||
|
||||
TEST( RimFractureTemplateNonDarcy, ZeroWidthGivesInfiniteDFactor )
|
||||
{
|
||||
TestEllipseFractureTemplate tmpl( RiaDefines::EclipseUnitSystem::UNITS_METRIC );
|
||||
tmpl.useFractureData();
|
||||
tmpl.useComputedDFactor( 0.5 );
|
||||
tmpl.useUserDefinedWidth( 0.0 );
|
||||
|
||||
EXPECT_EQ( std::numeric_limits<double>::infinity(), tmpl.computeDFactor( nullptr ) );
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "Well/RigWellPath.h"
|
||||
|
||||
#include "RimStimPlanFractureTemplate.h"
|
||||
#include "RimWellPath.h"
|
||||
#include "RimWellPathFracture.h"
|
||||
#include "RimWellPathFractureCollection.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
static const QString STIMPLAN_TEMPLATE_TEST_DATA_DIRECTORY = QString( "%1/RimStimPlanFractureTemplate/" ).arg( TEST_DATA_DIR );
|
||||
static const QString STIMPLAN_XML_READER_TEST_DATA_DIRECTORY = QString( "%1/RifStimPlanXmlReader/" ).arg( TEST_DATA_DIR );
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Gives access to the protected settings needed to load a template from file
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class TestStimPlanFractureTemplate : public RimStimPlanFractureTemplate
|
||||
{
|
||||
public:
|
||||
explicit TestStimPlanFractureTemplate( const QString& fileName )
|
||||
{
|
||||
setFileName( fileName );
|
||||
loadDataAndUpdate();
|
||||
m_fractureWidthType = WIDTH_FROM_FRACTURE;
|
||||
m_permeabilityType = CONDUCTIVITY_FROM_FRACTURE;
|
||||
}
|
||||
|
||||
void useBetaFromFile() { m_betaFactorType = BETA_FACTOR_FROM_FRACTURE; }
|
||||
void setPerforationLengthValue( double length ) { m_perforationLength = length; }
|
||||
void setOrientation( FracOrientationEnum orientation ) { m_orientationType = orientation; }
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Width, conductivity and beta at the well position are taken from the fracture grid, and the
|
||||
/// beta factor is converted from file units to Forchheimer units
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimStimPlanFractureTemplateTest, IntersectionDataAtWellCenterFromFile )
|
||||
{
|
||||
TestStimPlanFractureTemplate tmpl( STIMPLAN_TEMPLATE_TEST_DATA_DIRECTORY + "uniform_width_beta.xml" );
|
||||
tmpl.useBetaFromFile();
|
||||
|
||||
RimWellPathFracture fracture;
|
||||
fracture.setFractureTemplate( &tmpl );
|
||||
|
||||
ASSERT_TRUE( fracture.fractureGrid() != nullptr );
|
||||
|
||||
// Width 0.01 m and conductivity 300 md-m gives permeability 30000 mD
|
||||
EXPECT_NEAR( 0.01, tmpl.computeFractureWidth( &fracture ), 1.0e-12 );
|
||||
EXPECT_NEAR( 30000.0, tmpl.computeEffectivePermeability( &fracture ), 1.0e-6 );
|
||||
EXPECT_NEAR( 300.0, tmpl.computeKh( &fracture ), 1.0e-9 );
|
||||
|
||||
// 2.0265e8 [/m] divided by 1.01325e8 gives 2.0 Forchheimer units
|
||||
EXPECT_NEAR( 2.0, tmpl.getOrComputeBetaFactor( &fracture ), 1.0e-9 );
|
||||
}
|
||||
|
||||
TEST( RimStimPlanFractureTemplateTest, BetaFactorIsUserDefinedByDefault )
|
||||
{
|
||||
TestStimPlanFractureTemplate tmpl( STIMPLAN_TEMPLATE_TEST_DATA_DIRECTORY + "uniform_width_beta.xml" );
|
||||
|
||||
RimWellPathFracture fracture;
|
||||
fracture.setFractureTemplate( &tmpl );
|
||||
|
||||
EXPECT_NEAR( 0.006083236, tmpl.getOrComputeBetaFactor( &fracture ), 1.0e-12 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Without a fracture instance there is no intersection data: width is zero and beta is undefined
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimStimPlanFractureTemplateTest, NoFractureInstanceGivesNoIntersectionData )
|
||||
{
|
||||
TestStimPlanFractureTemplate tmpl( STIMPLAN_TEMPLATE_TEST_DATA_DIRECTORY + "uniform_width_beta.xml" );
|
||||
tmpl.useBetaFromFile();
|
||||
|
||||
EXPECT_DOUBLE_EQ( 0.0, tmpl.computeFractureWidth( nullptr ) );
|
||||
EXPECT_TRUE( std::isinf( tmpl.getOrComputeBetaFactor( nullptr ) ) );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// A file without width values has no width data, and the conductivity at the well is used directly
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimStimPlanFractureTemplateTest, FileWithoutWidthUsesConductivityDirectly )
|
||||
{
|
||||
TestStimPlanFractureTemplate tmpl( STIMPLAN_XML_READER_TEST_DATA_DIRECTORY + "small_fracture.xml" );
|
||||
|
||||
RimWellPathFracture fracture;
|
||||
fracture.setFractureTemplate( &tmpl );
|
||||
|
||||
ASSERT_TRUE( fracture.fractureGrid() != nullptr );
|
||||
|
||||
// The conductivity at the well center is 301.7 md-m in the file
|
||||
EXPECT_NEAR( 301.7, tmpl.computeKh( &fracture ), 1.0e-9 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// For fractures along the well path, the values are averaged along the part of the well path
|
||||
/// that intersects the fracture
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimStimPlanFractureTemplateTest, IntersectionDataAlongWellPath )
|
||||
{
|
||||
// Horizontal well along the X axis at depth 2015 m, with measured depth equal to the X coordinate + 500
|
||||
std::vector<cvf::Vec3d> wellPathPoints;
|
||||
std::vector<double> measuredDepths;
|
||||
for ( int i = 0; i <= 20; i++ )
|
||||
{
|
||||
const double x = -500.0 + i * 50.0;
|
||||
wellPathPoints.push_back( cvf::Vec3d( x, 0.0, -2015.0 ) );
|
||||
measuredDepths.push_back( x + 500.0 );
|
||||
}
|
||||
|
||||
RimWellPath wellPath;
|
||||
wellPath.setWellPathGeometry( new RigWellPath( wellPathPoints, measuredDepths ) );
|
||||
|
||||
TestStimPlanFractureTemplate tmpl( STIMPLAN_TEMPLATE_TEST_DATA_DIRECTORY + "uniform_width_beta.xml" );
|
||||
tmpl.useBetaFromFile();
|
||||
tmpl.setOrientation( RimFractureTemplate::ALONG_WELL_PATH );
|
||||
tmpl.setPerforationLengthValue( 80.0 );
|
||||
|
||||
auto fracture = new RimWellPathFracture;
|
||||
wellPath.fractureCollection()->addFracture( fracture );
|
||||
fracture->setFractureUnit( RiaDefines::EclipseUnitSystem::UNITS_METRIC );
|
||||
fracture->setFractureTemplate( &tmpl );
|
||||
fracture->setMeasuredDepth( 500.0 );
|
||||
|
||||
ASSERT_TRUE( fracture->fractureGrid() != nullptr );
|
||||
|
||||
EXPECT_NEAR( 0.01, tmpl.computeFractureWidth( fracture ), 1.0e-9 );
|
||||
EXPECT_NEAR( 30000.0, tmpl.computeEffectivePermeability( fracture ), 1.0e-3 );
|
||||
EXPECT_NEAR( 300.0, tmpl.computeKh( fracture ), 1.0e-6 );
|
||||
EXPECT_NEAR( 2.0, tmpl.getOrComputeBetaFactor( fracture ), 1.0e-9 );
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
<contours>
|
||||
<grid xCount="4" yCount="5" uom="m" cs="tvd" frac="1">
|
||||
<xs><![CDATA[0
|
||||
50 100 150
|
||||
]]></xs>
|
||||
<ys><![CDATA[2035 2025 2015 2005 1995 ]]></ys>
|
||||
</grid>
|
||||
<properties>
|
||||
<property name="CONDUCTIVITY" uom="md-m">
|
||||
<time value="117.238">
|
||||
<depth>1995.00</depth>
|
||||
<![CDATA[300 300 300 300]]>
|
||||
<depth>2005.00</depth>
|
||||
<![CDATA[300 300 300 300]]>
|
||||
<depth>2015.00</depth>
|
||||
<![CDATA[300 300 300 300]]>
|
||||
<depth>2025.00</depth>
|
||||
<![CDATA[300 300 300 300]]>
|
||||
<depth>2035.00</depth>
|
||||
<![CDATA[300 300 300 300]]>
|
||||
</time>
|
||||
</property>
|
||||
<property name="Effective Width" uom="m">
|
||||
<time value="117.238">
|
||||
<depth>1995.00</depth>
|
||||
<![CDATA[0.01 0.01 0.01 0.01]]>
|
||||
<depth>2005.00</depth>
|
||||
<![CDATA[0.01 0.01 0.01 0.01]]>
|
||||
<depth>2015.00</depth>
|
||||
<![CDATA[0.01 0.01 0.01 0.01]]>
|
||||
<depth>2025.00</depth>
|
||||
<![CDATA[0.01 0.01 0.01 0.01]]>
|
||||
<depth>2035.00</depth>
|
||||
<![CDATA[0.01 0.01 0.01 0.01]]>
|
||||
</time>
|
||||
</property>
|
||||
<property name="Beta" uom="/m">
|
||||
<time value="117.238">
|
||||
<depth>1995.00</depth>
|
||||
<![CDATA[2.0265e8 2.0265e8 2.0265e8 2.0265e8]]>
|
||||
<depth>2005.00</depth>
|
||||
<![CDATA[2.0265e8 2.0265e8 2.0265e8 2.0265e8]]>
|
||||
<depth>2015.00</depth>
|
||||
<![CDATA[2.0265e8 2.0265e8 2.0265e8 2.0265e8]]>
|
||||
<depth>2025.00</depth>
|
||||
<![CDATA[2.0265e8 2.0265e8 2.0265e8 2.0265e8]]>
|
||||
<depth>2035.00</depth>
|
||||
<![CDATA[2.0265e8 2.0265e8 2.0265e8 2.0265e8]]>
|
||||
</time>
|
||||
</property>
|
||||
</properties>
|
||||
</contours>
|
||||
Reference in new issue
Block a user