diff --git a/ApplicationLibCode/Application/Tools/RiaEclipseUnitTools.cpp b/ApplicationLibCode/Application/Tools/RiaEclipseUnitTools.cpp index 45f13f274c..c7c10636d5 100644 --- a/ApplicationLibCode/Application/Tools/RiaEclipseUnitTools.cpp +++ b/ApplicationLibCode/Application/Tools/RiaEclipseUnitTools.cpp @@ -19,8 +19,8 @@ #include "RiaEclipseUnitTools.h" #include "cafAppEnum.h" - #include "cvfAssert.h" + #include //-------------------------------------------------------------------------------------------------- @@ -28,7 +28,6 @@ //-------------------------------------------------------------------------------------------------- double RiaEclipseUnitTools::darcysConstant( RiaDefines::EclipseUnitSystem unitSystem ) { - // See "Cartesian transmissibility calculations" in the "Eclipse Technical Description" // CDARCY Darcys constant // = 0.00852702 (E300); 0.008527 (ECLIPSE 100) (METRIC) // = 0.00112712 (E300); 0.001127 (ECLIPSE 100) (FIELD) diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/CMakeLists_files.cmake b/ApplicationLibCode/Commands/CompletionExportCommands/CMakeLists_files.cmake index 4ca51a0952..f8193ed570 100644 --- a/ApplicationLibCode/Commands/CompletionExportCommands/CMakeLists_files.cmake +++ b/ApplicationLibCode/Commands/CompletionExportCommands/CMakeLists_files.cmake @@ -22,6 +22,7 @@ set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RicWellPathExportMswTableData.h ${CMAKE_CURRENT_LIST_DIR}/RicMswTableDataTools.h ${CMAKE_CURRENT_LIST_DIR}/RicScheduleDataGenerator.h + ${CMAKE_CURRENT_LIST_DIR}/RicTransmissibilityCalculator.h ) set(SOURCE_GROUP_SOURCE_FILES @@ -48,6 +49,7 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RicWellPathExportMswTableData.cpp ${CMAKE_CURRENT_LIST_DIR}/RicMswTableDataTools.cpp ${CMAKE_CURRENT_LIST_DIR}/RicScheduleDataGenerator.cpp + ${CMAKE_CURRENT_LIST_DIR}/RicTransmissibilityCalculator.cpp ) list(APPEND COMMAND_CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES}) diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/RicFishbonesTransmissibilityCalculationFeatureImp.cpp b/ApplicationLibCode/Commands/CompletionExportCommands/RicFishbonesTransmissibilityCalculationFeatureImp.cpp index c63a2345c0..33cc018e36 100644 --- a/ApplicationLibCode/Commands/CompletionExportCommands/RicFishbonesTransmissibilityCalculationFeatureImp.cpp +++ b/ApplicationLibCode/Commands/CompletionExportCommands/RicFishbonesTransmissibilityCalculationFeatureImp.cpp @@ -23,6 +23,7 @@ #include "RicMswCompletions.h" #include "RicMswExportInfo.h" #include "RicMswSegment.h" +#include "RicTransmissibilityCalculator.h" #include "RicWellPathExportCompletionDataFeatureImpl.h" #include "RicWellPathExportMswTableData.h" @@ -113,9 +114,9 @@ std::vector RicFishbonesTransmissibilityCalculationFeatureImp } else { - mainBoreDirection = RicWellPathExportCompletionDataFeatureImpl::calculateCellMainDirection( settings.caseToApply, - globalCellIndex, - wellBorePart.lengthsInCell ); + mainBoreDirection = RicTransmissibilityCalculator::calculateCellMainDirection( settings.caseToApply, + globalCellIndex, + wellBorePart.lengthsInCell ); } } @@ -137,13 +138,13 @@ std::vector RicFishbonesTransmissibilityCalculationFeatureImp { // No change in transmissibility for main bore auto transmissibilityAndPermeability = - RicWellPathExportCompletionDataFeatureImpl::calculateTransmissibilityData( settings.caseToApply, - wellPath, - wellBorePart.lengthsInCell, - wellBorePart.skinFactor, - wellBorePart.wellRadius, - globalCellIndex, - settings.useLateralNTG ); + RicTransmissibilityCalculator::calculateTransmissibilityData( settings.caseToApply, + wellPath, + wellBorePart.lengthsInCell, + wellBorePart.skinFactor, + wellBorePart.wellRadius, + globalCellIndex, + settings.useLateralNTG ); transmissibility = transmissibilityAndPermeability.connectionFactor(); kh = transmissibilityAndPermeability.kh(); @@ -151,24 +152,22 @@ std::vector RicFishbonesTransmissibilityCalculationFeatureImp else { // Adjust transmissibility for fishbone laterals - auto transmissibilityAndPermeability = - RicWellPathExportCompletionDataFeatureImpl::calculateTransmissibilityData( settings.caseToApply, - wellPath, - wellBorePart.lengthsInCell, - wellBorePart.skinFactor, - wellBorePart.wellRadius, - globalCellIndex, - settings.useLateralNTG, - numberOfLaterals, - mainBoreDirection ); + auto transmissibilityAndPermeability = RicTransmissibilityCalculator::calculateTransmissibilityData( settings.caseToApply, + wellPath, + wellBorePart.lengthsInCell, + wellBorePart.skinFactor, + wellBorePart.wellRadius, + globalCellIndex, + settings.useLateralNTG, + numberOfLaterals, + mainBoreDirection ); transmissibility = transmissibilityAndPermeability.connectionFactor(); kh = transmissibilityAndPermeability.kh(); } - auto direction = RicWellPathExportCompletionDataFeatureImpl::calculateCellMainDirection( settings.caseToApply, - globalCellIndex, - wellBorePart.lengthsInCell ); + auto direction = + RicTransmissibilityCalculator::calculateCellMainDirection( settings.caseToApply, globalCellIndex, wellBorePart.lengthsInCell ); completion.setTransAndWPImultBackgroundDataFromFishbone( transmissibility, wellBorePart.skinFactor, diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/RicTransmissibilityCalculator.cpp b/ApplicationLibCode/Commands/CompletionExportCommands/RicTransmissibilityCalculator.cpp new file mode 100644 index 0000000000..157acf725b --- /dev/null +++ b/ApplicationLibCode/Commands/CompletionExportCommands/RicTransmissibilityCalculator.cpp @@ -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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RicTransmissibilityCalculator.h" + +#include + +#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 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 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::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; +} diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/RicTransmissibilityCalculator.h b/ApplicationLibCode/Commands/CompletionExportCommands/RicTransmissibilityCalculator.h new file mode 100644 index 0000000000..1740786837 --- /dev/null +++ b/ApplicationLibCode/Commands/CompletionExportCommands/RicTransmissibilityCalculator.h @@ -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 +// 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 ); +}; diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.cpp b/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.cpp index ed9e50a653..7796c503df 100644 --- a/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.cpp +++ b/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.cpp @@ -18,9 +18,6 @@ #include "RicWellPathExportCompletionDataFeatureImpl.h" -#include "RiaEclipseUnitTools.h" -#include "RiaFilePathTools.h" -#include "RiaFractureDefines.h" #include "RiaLogging.h" #include "RiaPreferencesSystem.h" #include "RiaWeightedMeanCalculator.h" @@ -28,9 +25,9 @@ #include "ExportCommands/RicExportLgrFeature.h" #include "RicExportCompletionDataSettingsUi.h" -#include "RicExportFeatureImpl.h" #include "RicExportFractureCompletionsImpl.h" #include "RicFishbonesTransmissibilityCalculationFeatureImp.h" +#include "RicTransmissibilityCalculator.h" #include "RicWellPathExportCompletionsFileTools.h" #include "RicWellPathExportMswCompletionsImpl.h" #include "RicWellPathFractureReportItem.h" @@ -40,46 +37,26 @@ #include "RifThermalToStimPlanFractureXmlOutput.h" #include "RigActiveCellInfo.h" -#include "RigCaseCellResultsData.h" #include "RigEclipseCaseData.h" -#include "RigEclipseResultAddress.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/RigWellPath.h" #include "Well/RigWellPathIntersectionTools.h" #include "RimEclipseCase.h" -#include "RimFileWellPath.h" -#include "RimFishbones.h" #include "RimFishbonesCollection.h" -#include "RimFractureTemplate.h" #include "RimNonDarcyPerforationParameters.h" #include "RimPerforationCollection.h" #include "RimPerforationInterval.h" -#include "RimProject.h" -#include "RimSimWellInView.h" #include "RimThermalFractureTemplate.h" #include "RimWellPath.h" -#include "RimWellPathCollection.h" #include "RimWellPathCompletions.h" #include "RimWellPathFracture.h" #include "RimWellPathFractureCollection.h" -#include "RimWellPathValve.h" #include "Riu3DMainWindowTools.h" -#include "RiuMainWindow.h" -#include "cafPdmUiPropertyViewDialog.h" #include "cafProgressInfo.h" -#include "cafSelectionManager.h" -#include "cafUtils.h" - -#include "cvfPlane.h" #include @@ -551,11 +528,11 @@ RigCompletionData RicWellPathExportCompletionDataFeatureImpl::combineEclipseCell { // calculate trans for main bore - but as Eclipse will do it! double transmissibilityEclipseCalculation = - RicWellPathExportCompletionDataFeatureImpl::calculateTransmissibilityAsEclipseDoes( settings.caseToApply(), - combinedSkinFactor, - combinedDiameter / 2, - cellIndexIJK.globalCellIndex(), - cellDirection ); + RicTransmissibilityCalculator::calculateTransmissibilityAsEclipseDoes( settings.caseToApply(), + combinedSkinFactor, + combinedDiameter / 2, + cellIndexIJK.globalCellIndex(), + cellDirection ); double defaultKh = RigCompletionData::defaultValue(); double wpimult = combinedTrans / transmissibilityEclipseCalculation; @@ -1201,20 +1178,23 @@ std::vector cell.startMD ); RigCompletionData::CellDirection direction = - calculateCellMainDirection( settings.caseToApply, cell.globCellIndex, cell.intersectionLengthsInCellCS ); + RicTransmissibilityCalculator::calculateCellMainDirection( settings.caseToApply, + cell.globCellIndex, + cell.intersectionLengthsInCellCS ); double transmissibility = 0.0; double kh = RigCompletionData::defaultValue(); double dFactor = RigCompletionData::defaultValue(); { - auto transmissibilityData = calculateTransmissibilityData( settings.caseToApply, - wellPath, - cell.intersectionLengthsInCellCS, - interval->skinFactor(), - interval->diameter( unitSystem ) / 2, - cell.globCellIndex, - settings.useLateralNTG ); + auto transmissibilityData = + RicTransmissibilityCalculator::calculateTransmissibilityData( settings.caseToApply, + wellPath, + cell.intersectionLengthsInCellCS, + interval->skinFactor(), + interval->diameter( unitSystem ) / 2, + cell.globCellIndex, + settings.useLateralNTG ); transmissibility = transmissibilityData.connectionFactor(); kh = transmissibilityData.kh(); @@ -1226,11 +1206,12 @@ std::vector const double effectivePermeability = nonDarcyParameters->gridPermeabilityScalingFactor() * transmissibilityData.effectiveK(); - dFactor = calculateDFactor( settings.caseToApply, - effectiveH, - cell.globCellIndex, - wellPath->perforationIntervalCollection()->nonDarcyParameters(), - effectivePermeability ); + dFactor = + RicTransmissibilityCalculator::calculateDFactor( settings.caseToApply, + effectiveH, + cell.globCellIndex, + 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 dxAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData, - 0, - RiaDefines::PorosityModelType::MATRIX_MODEL, - 0, - RigEclipseResultAddress( "DX" ) ); - eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DY" ) ); - cvf::ref dyAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData, - 0, - RiaDefines::PorosityModelType::MATRIX_MODEL, - 0, - RigEclipseResultAddress( "DY" ) ); - eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DZ" ) ); - cvf::ref 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 dxAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData, - 0, - RiaDefines::PorosityModelType::MATRIX_MODEL, - 0, - RigEclipseResultAddress( "DX" ) ); - eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DY" ) ); - cvf::ref dyAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData, - 0, - RiaDefines::PorosityModelType::MATRIX_MODEL, - 0, - RigEclipseResultAddress( "DY" ) ); - eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DZ" ) ); - cvf::ref dzAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData, - 0, - RiaDefines::PorosityModelType::MATRIX_MODEL, - 0, - RigEclipseResultAddress( "DZ" ) ); - - eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMX" ) ); - cvf::ref permxAccessObject = - RigResultAccessorFactory::createFromResultAddress( eclipseCaseData, - 0, - RiaDefines::PorosityModelType::MATRIX_MODEL, - 0, - RigEclipseResultAddress( "PERMX" ) ); - eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMY" ) ); - cvf::ref permyAccessObject = - RigResultAccessorFactory::createFromResultAddress( eclipseCaseData, - 0, - RiaDefines::PorosityModelType::MATRIX_MODEL, - 0, - RigEclipseResultAddress( "PERMY" ) ); - eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMZ" ) ); - cvf::ref 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 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::infinity(); - } - - RigEclipseCaseData* eclipseCaseData = eclipseCase->eclipseCaseData(); - - double porosity = 0.0; - { - eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PORO" ) ); - cvf::ref 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 dxAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData, - 0, - RiaDefines::PorosityModelType::MATRIX_MODEL, - 0, - RigEclipseResultAddress( "DX" ) ); - eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DY" ) ); - cvf::ref dyAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData, - 0, - RiaDefines::PorosityModelType::MATRIX_MODEL, - 0, - RigEclipseResultAddress( "DY" ) ); - eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DZ" ) ); - cvf::ref dzAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData, - 0, - RiaDefines::PorosityModelType::MATRIX_MODEL, - 0, - RigEclipseResultAddress( "DZ" ) ); - - eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMX" ) ); - cvf::ref permxAccessObject = - RigResultAccessorFactory::createFromResultAddress( eclipseCaseData, - 0, - RiaDefines::PorosityModelType::MATRIX_MODEL, - 0, - RigEclipseResultAddress( "PERMX" ) ); - eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMY" ) ); - cvf::ref permyAccessObject = - RigResultAccessorFactory::createFromResultAddress( eclipseCaseData, - 0, - RiaDefines::PorosityModelType::MATRIX_MODEL, - 0, - RigEclipseResultAddress( "PERMY" ) ); - eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMZ" ) ); - cvf::ref 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 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; -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.h b/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.h index d501010f8a..7722ca4dae 100644 --- a/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.h +++ b/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.h @@ -54,70 +54,12 @@ class SubSegmentIntersectionInfo; //================================================================================================== using QFilePtr = std::shared_ptr; -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 { 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& wellPaths, const RicExportCompletionDataSettingsUi& exportSettings ); static std::vector computeStaticCompletionsForWellPath( RimWellPath* wellPath, RimEclipseCase* eclipseCase ); @@ -140,12 +82,6 @@ class RicWellPathExportCompletionDataFeatureImpl const RicExportCompletionDataSettingsUi& settings, const std::optional& exportDate = std::nullopt ); - static double calculateTransmissibilityAsEclipseDoes( RimEclipseCase* eclipseCase, - double skinFactor, - double wellRadius, - size_t globalCellIndex, - RigCompletionData::CellDirection direction ); - static RigCompletionData combineEclipseCellCompletions( const std::vector& completions, const RicExportCompletionDataSettingsUi& settings ); diff --git a/ApplicationLibCode/FileInterface/RifEclipseSummaryAddressDefines.h b/ApplicationLibCode/FileInterface/RifEclipseSummaryAddressDefines.h index 03543c3dcc..4e6c6815ab 100644 --- a/ApplicationLibCode/FileInterface/RifEclipseSummaryAddressDefines.h +++ b/ApplicationLibCode/FileInterface/RifEclipseSummaryAddressDefines.h @@ -26,7 +26,6 @@ //================================================================================================== 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 { SUMMARY_INVALID, diff --git a/ApplicationLibCode/UnitTests/CMakeLists.txt b/ApplicationLibCode/UnitTests/CMakeLists.txt index 6f0df61093..85253b8c87 100644 --- a/ApplicationLibCode/UnitTests/CMakeLists.txt +++ b/ApplicationLibCode/UnitTests/CMakeLists.txt @@ -124,6 +124,7 @@ set(SOURCE_UNITTEST_FILES ${CMAKE_CURRENT_LIST_DIR}/RifSurfio-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RifRmsWellPathReader-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RiaAngleUtils-Test.cpp + ${CMAKE_CURRENT_LIST_DIR}/RicTransmissibilityCalculator-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/cafVecIjk-Test.cpp ) diff --git a/ApplicationLibCode/UnitTests/RicTransmissibilityCalculator-Test.cpp b/ApplicationLibCode/UnitTests/RicTransmissibilityCalculator-Test.cpp new file mode 100644 index 0000000000..1fee997a6b --- /dev/null +++ b/ApplicationLibCode/UnitTests/RicTransmissibilityCalculator-Test.cpp @@ -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 +// 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 caseData = new RigEclipseCaseData( eclipseCase ); + + { + cvf::ref 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; +}