Refactor: Extract RicTransmissibilityCalculator from completion export

Move transmissibility calculation logic (TransmissibilityData,
connection factor computation, non-Darcy flow calculations) from
RicWellPathExportCompletionDataFeatureImpl into a dedicated
RicTransmissibilityCalculator class. Add unit tests for the
extracted calculator.
This commit is contained in:
Magne Sjaastad committed 2026-02-20 09:27:20 +01:00
1 parent 6bfc46f6b8
commit 05623a3fc6
10 files changed
+687 -456

No files matched your search

@@ -19,8 +19,8 @@
#include "RiaEclipseUnitTools.h" #include "RiaEclipseUnitTools.h"
#include "cafAppEnum.h" #include "cafAppEnum.h"
#include "cvfAssert.h" #include "cvfAssert.h"
#include <cmath> #include <cmath>
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@@ -28,7 +28,6 @@
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
double RiaEclipseUnitTools::darcysConstant( RiaDefines::EclipseUnitSystem unitSystem ) double RiaEclipseUnitTools::darcysConstant( RiaDefines::EclipseUnitSystem unitSystem )
{ {
// See "Cartesian transmissibility calculations" in the "Eclipse Technical Description"
// CDARCY Darcys constant // CDARCY Darcys constant
// = 0.00852702 (E300); 0.008527 (ECLIPSE 100) (METRIC) // = 0.00852702 (E300); 0.008527 (ECLIPSE 100) (METRIC)
// = 0.00112712 (E300); 0.001127 (ECLIPSE 100) (FIELD) // = 0.00112712 (E300); 0.001127 (ECLIPSE 100) (FIELD)
@@ -22,6 +22,7 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RicWellPathExportMswTableData.h ${CMAKE_CURRENT_LIST_DIR}/RicWellPathExportMswTableData.h
${CMAKE_CURRENT_LIST_DIR}/RicMswTableDataTools.h ${CMAKE_CURRENT_LIST_DIR}/RicMswTableDataTools.h
${CMAKE_CURRENT_LIST_DIR}/RicScheduleDataGenerator.h ${CMAKE_CURRENT_LIST_DIR}/RicScheduleDataGenerator.h
${CMAKE_CURRENT_LIST_DIR}/RicTransmissibilityCalculator.h
) )
set(SOURCE_GROUP_SOURCE_FILES set(SOURCE_GROUP_SOURCE_FILES
@@ -48,6 +49,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RicWellPathExportMswTableData.cpp ${CMAKE_CURRENT_LIST_DIR}/RicWellPathExportMswTableData.cpp
${CMAKE_CURRENT_LIST_DIR}/RicMswTableDataTools.cpp ${CMAKE_CURRENT_LIST_DIR}/RicMswTableDataTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RicScheduleDataGenerator.cpp ${CMAKE_CURRENT_LIST_DIR}/RicScheduleDataGenerator.cpp
${CMAKE_CURRENT_LIST_DIR}/RicTransmissibilityCalculator.cpp
) )
list(APPEND COMMAND_CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES}) list(APPEND COMMAND_CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})
@@ -23,6 +23,7 @@
#include "RicMswCompletions.h" #include "RicMswCompletions.h"
#include "RicMswExportInfo.h" #include "RicMswExportInfo.h"
#include "RicMswSegment.h" #include "RicMswSegment.h"
#include "RicTransmissibilityCalculator.h"
#include "RicWellPathExportCompletionDataFeatureImpl.h" #include "RicWellPathExportCompletionDataFeatureImpl.h"
#include "RicWellPathExportMswTableData.h" #include "RicWellPathExportMswTableData.h"
@@ -113,9 +114,9 @@ std::vector<RigCompletionData> RicFishbonesTransmissibilityCalculationFeatureImp
} }
else else
{ {
mainBoreDirection = RicWellPathExportCompletionDataFeatureImpl::calculateCellMainDirection( settings.caseToApply, mainBoreDirection = RicTransmissibilityCalculator::calculateCellMainDirection( settings.caseToApply,
globalCellIndex, globalCellIndex,
wellBorePart.lengthsInCell ); wellBorePart.lengthsInCell );
} }
} }
@@ -137,13 +138,13 @@ std::vector<RigCompletionData> RicFishbonesTransmissibilityCalculationFeatureImp
{ {
// No change in transmissibility for main bore // No change in transmissibility for main bore
auto transmissibilityAndPermeability = auto transmissibilityAndPermeability =
RicWellPathExportCompletionDataFeatureImpl::calculateTransmissibilityData( settings.caseToApply, RicTransmissibilityCalculator::calculateTransmissibilityData( settings.caseToApply,
wellPath, wellPath,
wellBorePart.lengthsInCell, wellBorePart.lengthsInCell,
wellBorePart.skinFactor, wellBorePart.skinFactor,
wellBorePart.wellRadius, wellBorePart.wellRadius,
globalCellIndex, globalCellIndex,
settings.useLateralNTG ); settings.useLateralNTG );
transmissibility = transmissibilityAndPermeability.connectionFactor(); transmissibility = transmissibilityAndPermeability.connectionFactor();
kh = transmissibilityAndPermeability.kh(); kh = transmissibilityAndPermeability.kh();
@@ -151,24 +152,22 @@ std::vector<RigCompletionData> RicFishbonesTransmissibilityCalculationFeatureImp
else else
{ {
// Adjust transmissibility for fishbone laterals // Adjust transmissibility for fishbone laterals
auto transmissibilityAndPermeability = auto transmissibilityAndPermeability = RicTransmissibilityCalculator::calculateTransmissibilityData( settings.caseToApply,
RicWellPathExportCompletionDataFeatureImpl::calculateTransmissibilityData( settings.caseToApply, wellPath,
wellPath, wellBorePart.lengthsInCell,
wellBorePart.lengthsInCell, wellBorePart.skinFactor,
wellBorePart.skinFactor, wellBorePart.wellRadius,
wellBorePart.wellRadius, globalCellIndex,
globalCellIndex, settings.useLateralNTG,
settings.useLateralNTG, numberOfLaterals,
numberOfLaterals, mainBoreDirection );
mainBoreDirection );
transmissibility = transmissibilityAndPermeability.connectionFactor(); transmissibility = transmissibilityAndPermeability.connectionFactor();
kh = transmissibilityAndPermeability.kh(); kh = transmissibilityAndPermeability.kh();
} }
auto direction = RicWellPathExportCompletionDataFeatureImpl::calculateCellMainDirection( settings.caseToApply, auto direction =
globalCellIndex, RicTransmissibilityCalculator::calculateCellMainDirection( settings.caseToApply, globalCellIndex, wellBorePart.lengthsInCell );
wellBorePart.lengthsInCell );
completion.setTransAndWPImultBackgroundDataFromFishbone( transmissibility, completion.setTransAndWPImultBackgroundDataFromFishbone( transmissibility,
wellBorePart.skinFactor, wellBorePart.skinFactor,
@@ -0,0 +1,316 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RicTransmissibilityCalculator.h"
#include <optional>
#include "RiaEclipseUnitTools.h"
#include "RiaFractureDefines.h"
#include "RigCaseCellResultsData.h"
#include "RigEclipseCaseData.h"
#include "RigEclipseResultAddress.h"
#include "RigMainGrid.h"
#include "RigPerforationTransmissibilityEquations.h"
#include "RigResultAccessorFactory.h"
#include "RigTransmissibilityEquations.h"
#include "RimEclipseCase.h"
#include "RimNonDarcyPerforationParameters.h"
#include "RimWellPath.h"
namespace internal
{
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaDefines::PorosityModelType porosityModel( RimEclipseCase* eclipseCase )
{
if ( eclipseCase && eclipseCase->eclipseCaseData() && eclipseCase->eclipseCaseData()->mainGrid() &&
eclipseCase->eclipseCaseData()->mainGrid()->isDualPorosity() )
{
return RiaDefines::PorosityModelType::FRACTURE_MODEL;
}
return RiaDefines::PorosityModelType::MATRIX_MODEL;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::ref<RigResultAccessor> loadResultAccessor( RimEclipseCase* eclipseCase, const QString& resultName )
{
if ( !eclipseCase || !eclipseCase->eclipseCaseData() ) return nullptr;
auto porModel = porosityModel( eclipseCase );
auto* results = eclipseCase->results( porModel );
if ( !results ) return nullptr;
results->ensureKnownResultLoaded( RigEclipseResultAddress( resultName ) );
return RigResultAccessorFactory::createFromResultAddress( eclipseCase->eclipseCaseData(), 0, porModel, 0, RigEclipseResultAddress( resultName ) );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
struct CellProperties
{
double dx, dy, dz;
double permx, permy, permz;
};
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::optional<CellProperties> loadCellProperties( RimEclipseCase* eclipseCase, size_t globalCellIndex )
{
auto dxAcc = loadResultAccessor( eclipseCase, "DX" );
auto dyAcc = loadResultAccessor( eclipseCase, "DY" );
auto dzAcc = loadResultAccessor( eclipseCase, "DZ" );
auto permxAcc = loadResultAccessor( eclipseCase, "PERMX" );
auto permyAcc = loadResultAccessor( eclipseCase, "PERMY" );
auto permzAcc = loadResultAccessor( eclipseCase, "PERMZ" );
if ( dxAcc.isNull() || dyAcc.isNull() || dzAcc.isNull() || permxAcc.isNull() || permyAcc.isNull() || permzAcc.isNull() )
{
return std::nullopt;
}
CellProperties props;
props.dx = dxAcc->cellScalarGlobIdx( globalCellIndex );
props.dy = dyAcc->cellScalarGlobIdx( globalCellIndex );
props.dz = dzAcc->cellScalarGlobIdx( globalCellIndex );
props.permx = permxAcc->cellScalarGlobIdx( globalCellIndex );
props.permy = permyAcc->cellScalarGlobIdx( globalCellIndex );
props.permz = permzAcc->cellScalarGlobIdx( globalCellIndex );
return props;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double loadNtg( RimEclipseCase* eclipseCase, size_t globalCellIndex )
{
auto ntgAcc = loadResultAccessor( eclipseCase, "NTG" );
if ( ntgAcc.notNull() ) return ntgAcc->cellScalarGlobIdx( globalCellIndex );
return 1.0;
}
} // namespace internal
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigCompletionData::CellDirection RicTransmissibilityCalculator::calculateCellMainDirection( RimEclipseCase* eclipseCase,
size_t globalCellIndex,
const cvf::Vec3d& lengthsInCell )
{
if ( !eclipseCase || !eclipseCase->eclipseCaseData() ) return RigCompletionData::CellDirection::DIR_K;
auto props = internal::loadCellProperties( eclipseCase, globalCellIndex );
if ( !props ) return RigCompletionData::CellDirection::DIR_K;
double xLengthFraction = fabs( lengthsInCell.x() / props->dx );
double yLengthFraction = fabs( lengthsInCell.y() / props->dy );
double zLengthFraction = fabs( lengthsInCell.z() / props->dz );
if ( xLengthFraction > yLengthFraction && xLengthFraction > zLengthFraction )
{
return RigCompletionData::CellDirection::DIR_I;
}
else if ( yLengthFraction > xLengthFraction && yLengthFraction > zLengthFraction )
{
return RigCompletionData::CellDirection::DIR_J;
}
else
{
return RigCompletionData::CellDirection::DIR_K;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TransmissibilityData RicTransmissibilityCalculator::calculateTransmissibilityData( RimEclipseCase* eclipseCase,
const RimWellPath* wellPath,
const cvf::Vec3d& internalCellLengths,
double skinFactor,
double wellRadius,
size_t globalCellIndex,
bool useLateralNTG,
size_t volumeScaleConstant,
RigCompletionData::CellDirection directionForVolumeScaling )
{
if ( !eclipseCase || !eclipseCase->eclipseCaseData() || !wellPath ) return TransmissibilityData();
auto props = internal::loadCellProperties( eclipseCase, globalCellIndex );
if ( !props )
{
return TransmissibilityData();
}
double ntg = internal::loadNtg( eclipseCase, globalCellIndex );
double latNtg = useLateralNTG ? ntg : 1.0;
const double totalKh = RigTransmissibilityEquations::totalKh( props->permx, props->permy, props->permz, internalCellLengths, latNtg, ntg );
const double effectiveK =
RigTransmissibilityEquations::effectiveK( props->permx, props->permy, props->permz, internalCellLengths, latNtg, ntg );
const double effectiveH = RigTransmissibilityEquations::effectiveH( internalCellLengths, latNtg, ntg );
double darcy = RiaEclipseUnitTools::darcysConstant( wellPath->unitSystem() );
double dx = props->dx;
double dy = props->dy;
double dz = props->dz;
if ( volumeScaleConstant != 1 )
{
if ( directionForVolumeScaling == RigCompletionData::CellDirection::DIR_I ) dx = dx / volumeScaleConstant;
if ( directionForVolumeScaling == RigCompletionData::CellDirection::DIR_J ) dy = dy / volumeScaleConstant;
if ( directionForVolumeScaling == RigCompletionData::CellDirection::DIR_K ) dz = dz / volumeScaleConstant;
}
const double transx = RigTransmissibilityEquations::wellBoreTransmissibilityComponent( internalCellLengths.x() * latNtg,
props->permy,
props->permz,
dy,
dz,
wellRadius,
skinFactor,
darcy );
const double transy = RigTransmissibilityEquations::wellBoreTransmissibilityComponent( internalCellLengths.y() * latNtg,
props->permx,
props->permz,
dx,
dz,
wellRadius,
skinFactor,
darcy );
const double transz = RigTransmissibilityEquations::wellBoreTransmissibilityComponent( internalCellLengths.z() * ntg,
props->permy,
props->permx,
dy,
dx,
wellRadius,
skinFactor,
darcy );
const double totalConnectionFactor = RigTransmissibilityEquations::totalConnectionFactor( transx, transy, transz );
TransmissibilityData trData;
trData.setData( effectiveH, effectiveK, totalConnectionFactor, totalKh );
return trData;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RicTransmissibilityCalculator::calculateDFactor( RimEclipseCase* eclipseCase,
double effectiveH,
size_t globalCellIndex,
const RimNonDarcyPerforationParameters* nonDarcyParameters,
const double effectivePermeability )
{
using EQ = RigPerforationTransmissibilityEquations;
if ( !eclipseCase || !eclipseCase->eclipseCaseData() )
{
return std::numeric_limits<double>::infinity();
}
double porosity = 0.0;
{
auto poroAcc = internal::loadResultAccessor( eclipseCase, "PORO" );
if ( poroAcc.notNull() )
{
porosity = poroAcc->cellScalar( globalCellIndex );
}
}
const double betaFactor = EQ::betaFactor( nonDarcyParameters->inertialCoefficientBeta0(),
effectivePermeability,
nonDarcyParameters->permeabilityScalingFactor(),
porosity,
nonDarcyParameters->porosityScalingFactor() );
const double alpha = RiaDefines::nonDarcyFlowAlpha( eclipseCase->eclipseCaseData()->unitsType() );
return EQ::dFactor( alpha,
betaFactor,
effectivePermeability,
effectiveH,
nonDarcyParameters->wellRadius(),
nonDarcyParameters->relativeGasDensity(),
nonDarcyParameters->gasViscosity() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RicTransmissibilityCalculator::calculateTransmissibilityAsEclipseDoes( RimEclipseCase* eclipseCase,
double skinFactor,
double wellRadius,
size_t globalCellIndex,
RigCompletionData::CellDirection direction )
{
if ( !eclipseCase || !eclipseCase->eclipseCaseData() ) return cvf::UNDEFINED_DOUBLE;
auto props = internal::loadCellProperties( eclipseCase, globalCellIndex );
if ( !props ) return cvf::UNDEFINED_DOUBLE;
double ntg = internal::loadNtg( eclipseCase, globalCellIndex );
double darcy = RiaEclipseUnitTools::darcysConstant( eclipseCase->eclipseCaseData()->unitsType() );
double trans = cvf::UNDEFINED_DOUBLE;
if ( direction == RigCompletionData::CellDirection::DIR_I )
{
trans = RigTransmissibilityEquations::wellBoreTransmissibilityComponent( props->dx,
props->permy,
props->permz,
props->dy,
props->dz,
wellRadius,
skinFactor,
darcy );
}
else if ( direction == RigCompletionData::CellDirection::DIR_J )
{
trans = RigTransmissibilityEquations::wellBoreTransmissibilityComponent( props->dy,
props->permx,
props->permz,
props->dx,
props->dz,
wellRadius,
skinFactor,
darcy );
}
else if ( direction == RigCompletionData::CellDirection::DIR_K )
{
trans = RigTransmissibilityEquations::wellBoreTransmissibilityComponent( props->dz * ntg,
props->permy,
props->permx,
props->dy,
props->dx,
wellRadius,
skinFactor,
darcy );
}
return trans;
}
@@ -0,0 +1,101 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RigCompletionData.h"
#include "cvfVector3.h"
class RimEclipseCase;
class RimNonDarcyPerforationParameters;
class RimWellPath;
//==================================================================================================
///
//==================================================================================================
class TransmissibilityData
{
public:
TransmissibilityData()
: m_isValid( false )
, m_effectiveH( 0.0 )
, m_effectiveK( 0.0 )
, m_connectionFactor( 0.0 )
, m_kh( 0.0 )
{
}
bool isValid() const { return m_isValid; }
void setData( double effectiveH, double effectiveK, double connectionFactor, double kh )
{
m_isValid = true;
m_effectiveH = effectiveH;
m_effectiveK = effectiveK;
m_connectionFactor = connectionFactor;
m_kh = kh;
}
double effectiveH() const { return m_effectiveH; }
double effectiveK() const { return m_effectiveK; }
double connectionFactor() const { return m_connectionFactor; }
double kh() const { return m_kh; }
private:
bool m_isValid;
double m_effectiveH;
double m_effectiveK;
double m_connectionFactor;
double m_kh;
};
//==================================================================================================
///
//==================================================================================================
class RicTransmissibilityCalculator
{
public:
static RigCompletionData::CellDirection
calculateCellMainDirection( RimEclipseCase* eclipseCase, size_t globalCellIndex, const cvf::Vec3d& lengthsInCell );
static TransmissibilityData
calculateTransmissibilityData( RimEclipseCase* eclipseCase,
const RimWellPath* wellPath,
const cvf::Vec3d& internalCellLengths,
double skinFactor,
double wellRadius,
size_t globalCellIndex,
bool useLateralNTG,
size_t volumeScaleConstant = 1,
RigCompletionData::CellDirection directionForVolumeScaling = RigCompletionData::CellDirection::DIR_I );
static double calculateDFactor( RimEclipseCase* eclipseCase,
double effectiveH,
size_t globalCellIndex,
const RimNonDarcyPerforationParameters* nonDarcyParameters,
const double effectivePermeability );
static double calculateTransmissibilityAsEclipseDoes( RimEclipseCase* eclipseCase,
double skinFactor,
double wellRadius,
size_t globalCellIndex,
RigCompletionData::CellDirection direction );
};
@@ -18,9 +18,6 @@
#include "RicWellPathExportCompletionDataFeatureImpl.h" #include "RicWellPathExportCompletionDataFeatureImpl.h"
#include "RiaEclipseUnitTools.h"
#include "RiaFilePathTools.h"
#include "RiaFractureDefines.h"
#include "RiaLogging.h" #include "RiaLogging.h"
#include "RiaPreferencesSystem.h" #include "RiaPreferencesSystem.h"
#include "RiaWeightedMeanCalculator.h" #include "RiaWeightedMeanCalculator.h"
@@ -28,9 +25,9 @@
#include "ExportCommands/RicExportLgrFeature.h" #include "ExportCommands/RicExportLgrFeature.h"
#include "RicExportCompletionDataSettingsUi.h" #include "RicExportCompletionDataSettingsUi.h"
#include "RicExportFeatureImpl.h"
#include "RicExportFractureCompletionsImpl.h" #include "RicExportFractureCompletionsImpl.h"
#include "RicFishbonesTransmissibilityCalculationFeatureImp.h" #include "RicFishbonesTransmissibilityCalculationFeatureImp.h"
#include "RicTransmissibilityCalculator.h"
#include "RicWellPathExportCompletionsFileTools.h" #include "RicWellPathExportCompletionsFileTools.h"
#include "RicWellPathExportMswCompletionsImpl.h" #include "RicWellPathExportMswCompletionsImpl.h"
#include "RicWellPathFractureReportItem.h" #include "RicWellPathFractureReportItem.h"
@@ -40,46 +37,26 @@
#include "RifThermalToStimPlanFractureXmlOutput.h" #include "RifThermalToStimPlanFractureXmlOutput.h"
#include "RigActiveCellInfo.h" #include "RigActiveCellInfo.h"
#include "RigCaseCellResultsData.h"
#include "RigEclipseCaseData.h" #include "RigEclipseCaseData.h"
#include "RigEclipseResultAddress.h"
#include "RigMainGrid.h" #include "RigMainGrid.h"
#include "RigPerforationTransmissibilityEquations.h"
#include "RigResultAccessorFactory.h"
#include "RigTransmissibilityEquations.h"
#include "RigVirtualPerforationTransmissibilities.h"
#include "Well/RigWellLogExtractionTools.h"
#include "Well/RigWellLogExtractor.h" #include "Well/RigWellLogExtractor.h"
#include "Well/RigWellPath.h" #include "Well/RigWellPath.h"
#include "Well/RigWellPathIntersectionTools.h" #include "Well/RigWellPathIntersectionTools.h"
#include "RimEclipseCase.h" #include "RimEclipseCase.h"
#include "RimFileWellPath.h"
#include "RimFishbones.h"
#include "RimFishbonesCollection.h" #include "RimFishbonesCollection.h"
#include "RimFractureTemplate.h"
#include "RimNonDarcyPerforationParameters.h" #include "RimNonDarcyPerforationParameters.h"
#include "RimPerforationCollection.h" #include "RimPerforationCollection.h"
#include "RimPerforationInterval.h" #include "RimPerforationInterval.h"
#include "RimProject.h"
#include "RimSimWellInView.h"
#include "RimThermalFractureTemplate.h" #include "RimThermalFractureTemplate.h"
#include "RimWellPath.h" #include "RimWellPath.h"
#include "RimWellPathCollection.h"
#include "RimWellPathCompletions.h" #include "RimWellPathCompletions.h"
#include "RimWellPathFracture.h" #include "RimWellPathFracture.h"
#include "RimWellPathFractureCollection.h" #include "RimWellPathFractureCollection.h"
#include "RimWellPathValve.h"
#include "Riu3DMainWindowTools.h" #include "Riu3DMainWindowTools.h"
#include "RiuMainWindow.h"
#include "cafPdmUiPropertyViewDialog.h"
#include "cafProgressInfo.h" #include "cafProgressInfo.h"
#include "cafSelectionManager.h"
#include "cafUtils.h"
#include "cvfPlane.h"
#include <QDir> #include <QDir>
@@ -551,11 +528,11 @@ RigCompletionData RicWellPathExportCompletionDataFeatureImpl::combineEclipseCell
{ {
// calculate trans for main bore - but as Eclipse will do it! // calculate trans for main bore - but as Eclipse will do it!
double transmissibilityEclipseCalculation = double transmissibilityEclipseCalculation =
RicWellPathExportCompletionDataFeatureImpl::calculateTransmissibilityAsEclipseDoes( settings.caseToApply(), RicTransmissibilityCalculator::calculateTransmissibilityAsEclipseDoes( settings.caseToApply(),
combinedSkinFactor, combinedSkinFactor,
combinedDiameter / 2, combinedDiameter / 2,
cellIndexIJK.globalCellIndex(), cellIndexIJK.globalCellIndex(),
cellDirection ); cellDirection );
double defaultKh = RigCompletionData::defaultValue(); double defaultKh = RigCompletionData::defaultValue();
double wpimult = combinedTrans / transmissibilityEclipseCalculation; double wpimult = combinedTrans / transmissibilityEclipseCalculation;
@@ -1201,20 +1178,23 @@ std::vector<RigCompletionData>
cell.startMD ); cell.startMD );
RigCompletionData::CellDirection direction = RigCompletionData::CellDirection direction =
calculateCellMainDirection( settings.caseToApply, cell.globCellIndex, cell.intersectionLengthsInCellCS ); RicTransmissibilityCalculator::calculateCellMainDirection( settings.caseToApply,
cell.globCellIndex,
cell.intersectionLengthsInCellCS );
double transmissibility = 0.0; double transmissibility = 0.0;
double kh = RigCompletionData::defaultValue(); double kh = RigCompletionData::defaultValue();
double dFactor = RigCompletionData::defaultValue(); double dFactor = RigCompletionData::defaultValue();
{ {
auto transmissibilityData = calculateTransmissibilityData( settings.caseToApply, auto transmissibilityData =
wellPath, RicTransmissibilityCalculator::calculateTransmissibilityData( settings.caseToApply,
cell.intersectionLengthsInCellCS, wellPath,
interval->skinFactor(), cell.intersectionLengthsInCellCS,
interval->diameter( unitSystem ) / 2, interval->skinFactor(),
cell.globCellIndex, interval->diameter( unitSystem ) / 2,
settings.useLateralNTG ); cell.globCellIndex,
settings.useLateralNTG );
transmissibility = transmissibilityData.connectionFactor(); transmissibility = transmissibilityData.connectionFactor();
kh = transmissibilityData.kh(); kh = transmissibilityData.kh();
@@ -1226,11 +1206,12 @@ std::vector<RigCompletionData>
const double effectivePermeability = const double effectivePermeability =
nonDarcyParameters->gridPermeabilityScalingFactor() * transmissibilityData.effectiveK(); nonDarcyParameters->gridPermeabilityScalingFactor() * transmissibilityData.effectiveK();
dFactor = calculateDFactor( settings.caseToApply, dFactor =
effectiveH, RicTransmissibilityCalculator::calculateDFactor( settings.caseToApply,
cell.globCellIndex, effectiveH,
wellPath->perforationIntervalCollection()->nonDarcyParameters(), cell.globCellIndex,
effectivePermeability ); wellPath->perforationIntervalCollection()->nonDarcyParameters(),
effectivePermeability );
} }
} }
@@ -1297,330 +1278,6 @@ void RicWellPathExportCompletionDataFeatureImpl::appendCompletionData( std::map<
} }
} }
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigCompletionData::CellDirection RicWellPathExportCompletionDataFeatureImpl::calculateCellMainDirection( RimEclipseCase* eclipseCase,
size_t globalCellIndex,
const cvf::Vec3d& lengthsInCell )
{
RigEclipseCaseData* eclipseCaseData = eclipseCase->eclipseCaseData();
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DX" ) );
cvf::ref<RigResultAccessor> dxAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "DX" ) );
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DY" ) );
cvf::ref<RigResultAccessor> dyAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "DY" ) );
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DZ" ) );
cvf::ref<RigResultAccessor> dzAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "DZ" ) );
double xLengthFraction = fabs( lengthsInCell.x() / dxAccessObject->cellScalarGlobIdx( globalCellIndex ) );
double yLengthFraction = fabs( lengthsInCell.y() / dyAccessObject->cellScalarGlobIdx( globalCellIndex ) );
double zLengthFraction = fabs( lengthsInCell.z() / dzAccessObject->cellScalarGlobIdx( globalCellIndex ) );
if ( xLengthFraction > yLengthFraction && xLengthFraction > zLengthFraction )
{
return RigCompletionData::CellDirection::DIR_I;
}
else if ( yLengthFraction > xLengthFraction && yLengthFraction > zLengthFraction )
{
return RigCompletionData::CellDirection::DIR_J;
}
else
{
return RigCompletionData::CellDirection::DIR_K;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TransmissibilityData
RicWellPathExportCompletionDataFeatureImpl::calculateTransmissibilityData( RimEclipseCase* eclipseCase,
const RimWellPath* wellPath,
const cvf::Vec3d& internalCellLengths,
double skinFactor,
double wellRadius,
size_t globalCellIndex,
bool useLateralNTG,
size_t volumeScaleConstant,
RigCompletionData::CellDirection directionForVolumeScaling )
{
RigEclipseCaseData* eclipseCaseData = eclipseCase->eclipseCaseData();
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DX" ) );
cvf::ref<RigResultAccessor> dxAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "DX" ) );
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DY" ) );
cvf::ref<RigResultAccessor> dyAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "DY" ) );
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DZ" ) );
cvf::ref<RigResultAccessor> dzAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "DZ" ) );
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMX" ) );
cvf::ref<RigResultAccessor> permxAccessObject =
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "PERMX" ) );
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMY" ) );
cvf::ref<RigResultAccessor> permyAccessObject =
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "PERMY" ) );
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMZ" ) );
cvf::ref<RigResultAccessor> permzAccessObject =
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "PERMZ" ) );
if ( dxAccessObject.isNull() || dyAccessObject.isNull() || dzAccessObject.isNull() || permxAccessObject.isNull() ||
permyAccessObject.isNull() || permzAccessObject.isNull() )
{
return TransmissibilityData();
}
double ntg = 1.0;
{
// Trigger loading from file
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "NTG" ) );
cvf::ref<RigResultAccessor> ntgAccessObject =
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "NTG" ) );
if ( ntgAccessObject.notNull() )
{
ntg = ntgAccessObject->cellScalarGlobIdx( globalCellIndex );
}
}
double latNtg = useLateralNTG ? ntg : 1.0;
double dx = dxAccessObject->cellScalarGlobIdx( globalCellIndex );
double dy = dyAccessObject->cellScalarGlobIdx( globalCellIndex );
double dz = dzAccessObject->cellScalarGlobIdx( globalCellIndex );
double permx = permxAccessObject->cellScalarGlobIdx( globalCellIndex );
double permy = permyAccessObject->cellScalarGlobIdx( globalCellIndex );
double permz = permzAccessObject->cellScalarGlobIdx( globalCellIndex );
const double totalKh = RigTransmissibilityEquations::totalKh( permx, permy, permz, internalCellLengths, latNtg, ntg );
const double effectiveK = RigTransmissibilityEquations::effectiveK( permx, permy, permz, internalCellLengths, latNtg, ntg );
const double effectiveH = RigTransmissibilityEquations::effectiveH( internalCellLengths, latNtg, ntg );
double darcy = RiaEclipseUnitTools::darcysConstant( wellPath->unitSystem() );
if ( volumeScaleConstant != 1 )
{
if ( directionForVolumeScaling == RigCompletionData::CellDirection::DIR_I ) dx = dx / volumeScaleConstant;
if ( directionForVolumeScaling == RigCompletionData::CellDirection::DIR_J ) dy = dy / volumeScaleConstant;
if ( directionForVolumeScaling == RigCompletionData::CellDirection::DIR_K ) dz = dz / volumeScaleConstant;
}
const double transx = RigTransmissibilityEquations::wellBoreTransmissibilityComponent( internalCellLengths.x() * latNtg,
permy,
permz,
dy,
dz,
wellRadius,
skinFactor,
darcy );
const double transy = RigTransmissibilityEquations::wellBoreTransmissibilityComponent( internalCellLengths.y() * latNtg,
permx,
permz,
dx,
dz,
wellRadius,
skinFactor,
darcy );
const double transz = RigTransmissibilityEquations::wellBoreTransmissibilityComponent( internalCellLengths.z() * ntg,
permy,
permx,
dy,
dx,
wellRadius,
skinFactor,
darcy );
const double totalConnectionFactor = RigTransmissibilityEquations::totalConnectionFactor( transx, transy, transz );
TransmissibilityData trData;
trData.setData( effectiveH, effectiveK, totalConnectionFactor, totalKh );
return trData;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RicWellPathExportCompletionDataFeatureImpl::calculateDFactor( RimEclipseCase* eclipseCase,
double effectiveH,
size_t globalCellIndex,
const RimNonDarcyPerforationParameters* nonDarcyParameters,
const double effectivePermeability )
{
using EQ = RigPerforationTransmissibilityEquations;
if ( !eclipseCase || !eclipseCase->eclipseCaseData() )
{
return std::numeric_limits<double>::infinity();
}
RigEclipseCaseData* eclipseCaseData = eclipseCase->eclipseCaseData();
double porosity = 0.0;
{
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PORO" ) );
cvf::ref<RigResultAccessor> poroAccessObject =
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "PORO" ) );
if ( poroAccessObject.notNull() )
{
porosity = poroAccessObject->cellScalar( globalCellIndex );
}
}
const double betaFactor = EQ::betaFactor( nonDarcyParameters->inertialCoefficientBeta0(),
effectivePermeability,
nonDarcyParameters->permeabilityScalingFactor(),
porosity,
nonDarcyParameters->porosityScalingFactor() );
const double alpha = RiaDefines::nonDarcyFlowAlpha( eclipseCaseData->unitsType() );
return EQ::dFactor( alpha,
betaFactor,
effectivePermeability,
effectiveH,
nonDarcyParameters->wellRadius(),
nonDarcyParameters->relativeGasDensity(),
nonDarcyParameters->gasViscosity() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RicWellPathExportCompletionDataFeatureImpl::calculateTransmissibilityAsEclipseDoes( RimEclipseCase* eclipseCase,
double skinFactor,
double wellRadius,
size_t globalCellIndex,
RigCompletionData::CellDirection direction )
{
RigEclipseCaseData* eclipseCaseData = eclipseCase->eclipseCaseData();
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DX" ) );
cvf::ref<RigResultAccessor> dxAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "DX" ) );
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DY" ) );
cvf::ref<RigResultAccessor> dyAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "DY" ) );
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DZ" ) );
cvf::ref<RigResultAccessor> dzAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "DZ" ) );
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMX" ) );
cvf::ref<RigResultAccessor> permxAccessObject =
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "PERMX" ) );
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMY" ) );
cvf::ref<RigResultAccessor> permyAccessObject =
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "PERMY" ) );
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMZ" ) );
cvf::ref<RigResultAccessor> permzAccessObject =
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "PERMZ" ) );
double ntg = 1.0;
if ( eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "NTG" ) ) )
{
cvf::ref<RigResultAccessor> ntgAccessObject =
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( "NTG" ) );
ntg = ntgAccessObject->cellScalarGlobIdx( globalCellIndex );
}
double dx = dxAccessObject->cellScalarGlobIdx( globalCellIndex );
double dy = dyAccessObject->cellScalarGlobIdx( globalCellIndex );
double dz = dzAccessObject->cellScalarGlobIdx( globalCellIndex );
double permx = permxAccessObject->cellScalarGlobIdx( globalCellIndex );
double permy = permyAccessObject->cellScalarGlobIdx( globalCellIndex );
double permz = permzAccessObject->cellScalarGlobIdx( globalCellIndex );
RiaDefines::EclipseUnitSystem units = eclipseCaseData->unitsType();
double darcy = RiaEclipseUnitTools::darcysConstant( units );
double trans = cvf::UNDEFINED_DOUBLE;
if ( direction == RigCompletionData::CellDirection::DIR_I )
{
trans = RigTransmissibilityEquations::wellBoreTransmissibilityComponent( dx, permy, permz, dy, dz, wellRadius, skinFactor, darcy );
}
else if ( direction == RigCompletionData::CellDirection::DIR_J )
{
trans = RigTransmissibilityEquations::wellBoreTransmissibilityComponent( dy, permx, permz, dx, dz, wellRadius, skinFactor, darcy );
}
else if ( direction == RigCompletionData::CellDirection::DIR_K )
{
trans =
RigTransmissibilityEquations::wellBoreTransmissibilityComponent( dz * ntg, permy, permx, dy, dx, wellRadius, skinFactor, darcy );
}
return trans;
}
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// ///
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@@ -54,70 +54,12 @@ class SubSegmentIntersectionInfo;
//================================================================================================== //==================================================================================================
using QFilePtr = std::shared_ptr<QFile>; using QFilePtr = std::shared_ptr<QFile>;
class TransmissibilityData
{
public:
TransmissibilityData()
: m_isValid( false )
, m_effectiveH( 0.0 )
, m_effectiveK( 0.0 )
, m_connectionFactor( 0.0 )
, m_kh( 0.0 )
{
}
bool isValid() const { return m_isValid; }
void setData( double effectiveH, double effectiveK, double connectionFactor, double kh )
{
m_isValid = true;
m_effectiveH = effectiveH;
m_effectiveK = effectiveK;
m_connectionFactor = connectionFactor;
m_kh = kh;
}
double effectiveH() const { return m_effectiveH; }
double effectiveK() const { return m_effectiveK; }
double connectionFactor() const { return m_connectionFactor; }
double kh() const { return m_kh; }
private:
bool m_isValid;
double m_effectiveH;
double m_effectiveK;
double m_connectionFactor;
double m_kh;
};
//================================================================================================== //==================================================================================================
/// ///
//================================================================================================== //==================================================================================================
class RicWellPathExportCompletionDataFeatureImpl class RicWellPathExportCompletionDataFeatureImpl
{ {
public: public:
static RigCompletionData::CellDirection
calculateCellMainDirection( RimEclipseCase* eclipseCase, size_t globalCellIndex, const cvf::Vec3d& lengthsInCell );
static TransmissibilityData
calculateTransmissibilityData( RimEclipseCase* eclipseCase,
const RimWellPath* wellPath,
const cvf::Vec3d& internalCellLengths,
double skinFactor,
double wellRadius,
size_t globalCellIndex,
bool useLateralNTG,
size_t volumeScaleConstant = 1,
RigCompletionData::CellDirection directionForVolumeScaling = RigCompletionData::CellDirection::DIR_I );
static double calculateDFactor( RimEclipseCase* eclipseCase,
double effectiveH,
size_t globalCellIndex,
const RimNonDarcyPerforationParameters* nonDarcyParameters,
const double effectivePermeability );
static void exportCompletions( const std::vector<RimWellPath*>& wellPaths, const RicExportCompletionDataSettingsUi& exportSettings ); static void exportCompletions( const std::vector<RimWellPath*>& wellPaths, const RicExportCompletionDataSettingsUi& exportSettings );
static std::vector<RigCompletionData> computeStaticCompletionsForWellPath( RimWellPath* wellPath, RimEclipseCase* eclipseCase ); static std::vector<RigCompletionData> computeStaticCompletionsForWellPath( RimWellPath* wellPath, RimEclipseCase* eclipseCase );
@@ -140,12 +82,6 @@ private:
const RicExportCompletionDataSettingsUi& settings, const RicExportCompletionDataSettingsUi& settings,
const std::optional<QDateTime>& exportDate = std::nullopt ); const std::optional<QDateTime>& exportDate = std::nullopt );
static double calculateTransmissibilityAsEclipseDoes( RimEclipseCase* eclipseCase,
double skinFactor,
double wellRadius,
size_t globalCellIndex,
RigCompletionData::CellDirection direction );
static RigCompletionData combineEclipseCellCompletions( const std::vector<RigCompletionData>& completions, static RigCompletionData combineEclipseCellCompletions( const std::vector<RigCompletionData>& completions,
const RicExportCompletionDataSettingsUi& settings ); const RicExportCompletionDataSettingsUi& settings );
@@ -26,7 +26,6 @@
//================================================================================================== //==================================================================================================
namespace RifEclipseSummaryAddressDefines namespace RifEclipseSummaryAddressDefines
{ {
// Based on list in ecl_smspec.c and list of types taken from Eclipse Reference Manual ecl_rm_2011.1.pdf
enum class SummaryCategory enum class SummaryCategory
{ {
SUMMARY_INVALID, SUMMARY_INVALID,
@@ -124,6 +124,7 @@ set(SOURCE_UNITTEST_FILES
${CMAKE_CURRENT_LIST_DIR}/RifSurfio-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RifSurfio-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RifRmsWellPathReader-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RifRmsWellPathReader-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RiaAngleUtils-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RiaAngleUtils-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RicTransmissibilityCalculator-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/cafVecIjk-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/cafVecIjk-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RiaSummaryAddressCollectionTools-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RiaSummaryAddressCollectionTools-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RimMockSummaryCase.h ${CMAKE_CURRENT_LIST_DIR}/RimMockSummaryCase.h
@@ -0,0 +1,221 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "CompletionExportCommands/RicTransmissibilityCalculator.h"
#include "RifReaderMockModel.h"
#include "RigActiveCellInfo.h"
#include "RigCaseCellResultsData.h"
#include "RigEclipseCaseData.h"
#include "RigEclipseResultAddress.h"
#include "RigEclipseResultInfo.h"
#include "RigMainGrid.h"
#include "RimEclipseCase.h"
#include "RimEclipseResultCase.h"
#include "RimWellPath.h"
//--------------------------------------------------------------------------------------------------
/// Helper: Register a STATIC_NATIVE result with a uniform value for all cells
//--------------------------------------------------------------------------------------------------
static void addStaticResult( RigCaseCellResultsData* cellResults, const QString& name, size_t cellCount, double value )
{
RigEclipseResultAddress address( RiaDefines::ResultCatType::STATIC_NATIVE, name );
cellResults->createResultEntry( address, false );
auto timeStepInfo = RigEclipseTimeStepInfo( QDateTime(), 0, 0.0 );
cellResults->setTimeStepInfos( address, { timeStepInfo } );
auto* timesteps = cellResults->modifiableCellScalarResultTimesteps( address );
timesteps->resize( 1 );
( *timesteps )[0].assign( cellCount, value );
}
//--------------------------------------------------------------------------------------------------
/// Helper: Create a RimEclipseCase with mock grid and populated result data
//--------------------------------------------------------------------------------------------------
static RimEclipseCase*
createMockEclipseCase( double dxVal, double dyVal, double dzVal, double permxVal, double permyVal, double permzVal, double ntgVal )
{
auto* eclipseCase = new RimEclipseResultCase;
cvf::ref<RigEclipseCaseData> caseData = new RigEclipseCaseData( eclipseCase );
{
cvf::ref<RifReaderMockModel> mockReader = new RifReaderMockModel;
mockReader->setWorldCoordinates( cvf::Vec3d( 0, 0, 0 ), cvf::Vec3d( 100, 100, 100 ) );
mockReader->setCellCounts( cvf::Vec3st( 2, 2, 2 ) );
mockReader->enableWellData( false );
mockReader->open( "", caseData.p() );
caseData->mainGrid()->computeCachedData();
}
eclipseCase->setReservoirData( caseData.p() );
size_t cellCount = caseData->mainGrid()->totalCellCount();
RigCaseCellResultsData* cellResults = caseData->results( RiaDefines::PorosityModelType::MATRIX_MODEL );
addStaticResult( cellResults, "DX", cellCount, dxVal );
addStaticResult( cellResults, "DY", cellCount, dyVal );
addStaticResult( cellResults, "DZ", cellCount, dzVal );
addStaticResult( cellResults, "PERMX", cellCount, permxVal );
addStaticResult( cellResults, "PERMY", cellCount, permyVal );
addStaticResult( cellResults, "PERMZ", cellCount, permzVal );
addStaticResult( cellResults, "NTG", cellCount, ntgVal );
return eclipseCase;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RicTransmissibilityCalculator, CalculateCellMainDirection_DirectionI )
{
RimEclipseCase* eclipseCase = createMockEclipseCase( 10.0, 20.0, 30.0, 100.0, 100.0, 10.0, 1.0 );
// lengthsInCell = (8, 3, 2) → fractions: x=0.8, y=0.15, z=0.067 → DIR_I
cvf::Vec3d lengthsInCell( 8.0, 3.0, 2.0 );
auto direction = RicTransmissibilityCalculator::calculateCellMainDirection( eclipseCase, 0, lengthsInCell );
EXPECT_EQ( RigCompletionData::CellDirection::DIR_I, direction );
delete eclipseCase;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RicTransmissibilityCalculator, CalculateCellMainDirection_DirectionK )
{
RimEclipseCase* eclipseCase = createMockEclipseCase( 10.0, 20.0, 30.0, 100.0, 100.0, 10.0, 1.0 );
// lengthsInCell = (1, 1, 25) → fractions: x=0.1, y=0.05, z=0.833 → DIR_K
cvf::Vec3d lengthsInCell( 1.0, 1.0, 25.0 );
auto direction = RicTransmissibilityCalculator::calculateCellMainDirection( eclipseCase, 0, lengthsInCell );
EXPECT_EQ( RigCompletionData::CellDirection::DIR_K, direction );
delete eclipseCase;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RicTransmissibilityCalculator, CalculateTransmissibilityData_Valid )
{
RimEclipseCase* eclipseCase = createMockEclipseCase( 100.0, 100.0, 10.0, 100.0, 100.0, 10.0, 1.0 );
RimWellPath wellPath;
wellPath.setUnitSystem( RiaDefines::EclipseUnitSystem::UNITS_METRIC );
cvf::Vec3d internalCellLengths( 50.0, 50.0, 5.0 );
double skinFactor = 0.0;
double wellRadius = 0.1;
auto result =
RicTransmissibilityCalculator::calculateTransmissibilityData( eclipseCase, &wellPath, internalCellLengths, skinFactor, wellRadius, 0, false );
EXPECT_TRUE( result.isValid() );
EXPECT_GT( result.connectionFactor(), 0.0 );
EXPECT_GT( result.kh(), 0.0 );
EXPECT_GT( result.effectiveH(), 0.0 );
EXPECT_GT( result.effectiveK(), 0.0 );
delete eclipseCase;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RicTransmissibilityCalculator, CalculateTransmissibilityAsEclipseDoes_DirectionK )
{
RimEclipseCase* eclipseCase = createMockEclipseCase( 100.0, 100.0, 10.0, 100.0, 100.0, 10.0, 1.0 );
double skinFactor = 0.0;
double wellRadius = 0.1;
double trans = RicTransmissibilityCalculator::calculateTransmissibilityAsEclipseDoes( eclipseCase,
skinFactor,
wellRadius,
0,
RigCompletionData::CellDirection::DIR_K );
EXPECT_GT( trans, 0.0 );
delete eclipseCase;
}
//--------------------------------------------------------------------------------------------------
/// Helper: Set up fracture active cell info and enable dual porosity
//--------------------------------------------------------------------------------------------------
static void enableDualPorosity( RigEclipseCaseData* caseData )
{
size_t cellCount = caseData->mainGrid()->totalCellCount();
RigActiveCellInfo* fractureActiveCellInfo = caseData->activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL );
fractureActiveCellInfo->setReservoirCellCount( cellCount );
for ( size_t i = 0; i < cellCount; i++ )
{
fractureActiveCellInfo->setCellResultIndex( i, i );
}
fractureActiveCellInfo->setGridCount( 1 );
fractureActiveCellInfo->setGridActiveCellCounts( 0, cellCount );
fractureActiveCellInfo->computeDerivedData();
caseData->mainGrid()->setDualPorosity( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RicTransmissibilityCalculator, CalculateCellMainDirection_DualPorosity_UsesFractureProperties )
{
// Matrix: DX=10, DY=20, DZ=30. Fracture: DX=100, DY=20, DZ=30.
// lengthsInCell = (8, 3, 2)
// Matrix fracs: x=8/10=0.8, y=3/20=0.15, z=2/30=0.067 → DIR_I
// Fracture fracs: x=8/100=0.08, y=3/20=0.15, z=2/30=0.067 → DIR_J
RimEclipseCase* eclipseCase = createMockEclipseCase( 10.0, 20.0, 30.0, 100.0, 100.0, 10.0, 1.0 );
RigEclipseCaseData* caseData = eclipseCase->eclipseCaseData();
size_t cellCount = caseData->mainGrid()->totalCellCount();
enableDualPorosity( caseData );
// Populate fracture results with different DX
RigCaseCellResultsData* fractureResults = caseData->results( RiaDefines::PorosityModelType::FRACTURE_MODEL );
addStaticResult( fractureResults, "DX", cellCount, 100.0 );
addStaticResult( fractureResults, "DY", cellCount, 20.0 );
addStaticResult( fractureResults, "DZ", cellCount, 30.0 );
addStaticResult( fractureResults, "PERMX", cellCount, 100.0 );
addStaticResult( fractureResults, "PERMY", cellCount, 100.0 );
addStaticResult( fractureResults, "PERMZ", cellCount, 10.0 );
cvf::Vec3d lengthsInCell( 8.0, 3.0, 2.0 );
auto direction = RicTransmissibilityCalculator::calculateCellMainDirection( eclipseCase, 0, lengthsInCell );
// With matrix properties this would be DIR_I, but with fracture properties it should be DIR_J
EXPECT_EQ( RigCompletionData::CellDirection::DIR_J, direction );
delete eclipseCase;
}