mirror of
https://github.com/OPM/ResInsight.git
synced 2026-09-03 20:53:13 -05:00
Rename ApplicationCode to ApplicationLibCode
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
|
||||
set (SOURCE_GROUP_HEADER_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigActiveCellInfo.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCell.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseCaseData.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigGridBase.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigGridManager.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigResultAccessor.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigResultAccessorFactory.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigAllGridCellsResultAccessor.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigActiveCellsResultAccessor.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCellEdgeResultAccessor.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCellGeometryTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCombTransResultAccessor.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCombMultResultAccessor.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigResultModifier.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigResultModifierFactory.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFormationNames.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigAllanDiagramData.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFlowDiagResultAddress.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFlowDiagResults.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFlowDiagResultFrames.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFlowDiagSolverInterface.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFlowDiagInterfaceTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFlowDiagStatCalc.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFlowDiagVisibleCellsStatCalc.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigAccWellFlowCalculator.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellLogExtractor.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseWellLogExtractor.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigLocalGrid.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigMainGrid.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigReservoirBuilderMock.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCaseCellResultsData.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSimWellData.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellPath.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFault.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigNNCData.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/cvfGeometryTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/cvfGeometryTools.inl
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigPipeInCellEvaluator.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigTernaryResultAccessor.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseNativeStatCalc.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseNativeVisibleCellsStatCalc.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseMultiPropertyStatCalc.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellLogCurveData.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellLogExtractionTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigHexIntersectionTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigTimeHistoryResultAccessor.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigLasFileExporter.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSimulationWellCoordsAndMD.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFishbonesGeometry.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigTesselatorTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCellGeometryTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellPathIntersectionTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseResultInfo.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseResultAddress.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigTofAccumulatedPhaseFractionsCalculator.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigTofWellDistributionCalculator.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigTransmissibilityEquations.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigNumberOfFloodedPoreVolumesCalculator.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWeightedMeanCalc.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellPathFormations.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigStimPlanFractureDefinition.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFractureGrid.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFractureCell.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellResultPoint.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellPathGeometryTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellPathGeometryExporter.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSurface.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCaseRealizationParameters.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigGeoMechBoreHoleStressCalculator.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigPolyLinesData.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCaseCellResultCalculator.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigGridCrossPlotCurveGrouping.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseCrossPlotDataExtractor.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEquil.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWbsParameter.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseAllanFaultsStatCalc.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCellFaceGeometryTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigNncConnection.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellDiskData.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigGocadData.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigElasticProperties.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigHistogramData.h
|
||||
)
|
||||
|
||||
|
||||
set (SOURCE_GROUP_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigActiveCellInfo.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCell.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseCaseData.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigGridBase.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigGridManager.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigResultAccessor.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigResultAccessorFactory.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigAllGridCellsResultAccessor.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigActiveCellsResultAccessor.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCellEdgeResultAccessor.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCellGeometryTools.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCombTransResultAccessor.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCombMultResultAccessor.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigResultModifierFactory.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFormationNames.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigAllanDiagramData.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFlowDiagResultAddress.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFlowDiagResults.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFlowDiagResultFrames.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFlowDiagSolverInterface.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFlowDiagStatCalc.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFlowDiagVisibleCellsStatCalc.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigAccWellFlowCalculator.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellLogExtractor.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseWellLogExtractor.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigLocalGrid.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigMainGrid.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigReservoirBuilderMock.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCaseCellResultsData.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSimWellData.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellPath.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFault.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigNNCData.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/cvfGeometryTools.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigTernaryResultAccessor.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseNativeStatCalc.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseNativeVisibleCellsStatCalc.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseMultiPropertyStatCalc.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellLogCurveData.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigHexIntersectionTools.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigTimeHistoryResultAccessor.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigLasFileExporter.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSimulationWellCoordsAndMD.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFishbonesGeometry.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigTesselatorTools.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCellGeometryTools.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellPathIntersectionTools.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseResultInfo.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigTofAccumulatedPhaseFractionsCalculator.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigTofWellDistributionCalculator.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigTransmissibilityEquations.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigNumberOfFloodedPoreVolumesCalculator.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWeightedMeanCalc.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellPathFormations.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigStimPlanFractureDefinition.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFractureGrid.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFractureCell.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellResultPoint.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellPathGeometryTools.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellPathGeometryExporter.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSurface.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCaseRealizationParameters.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigGeoMechBoreHoleStressCalculator.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigPolyLinesData.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCaseCellResultCalculator.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseCrossPlotDataExtractor.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEquil.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWbsParameter.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseAllanFaultsStatCalc.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCellFaceGeometryTools.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigNncConnection.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellDiskData.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigGocadData.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigElasticProperties.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigHistogramData.cpp
|
||||
)
|
||||
|
||||
list(APPEND CODE_HEADER_FILES
|
||||
${SOURCE_GROUP_HEADER_FILES}
|
||||
)
|
||||
|
||||
list(APPEND CODE_SOURCE_FILES
|
||||
${SOURCE_GROUP_SOURCE_FILES}
|
||||
)
|
||||
|
||||
source_group( "ReservoirDataModel" FILES ${SOURCE_GROUP_HEADER_FILES} ${SOURCE_GROUP_SOURCE_FILES} ${CMAKE_CURRENT_LIST_DIR}/CMakeLists_files.cmake )
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
set (SOURCE_GROUP_HEADER_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigGeoMechWellLogExtractor.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCaseToCaseCellMapper.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCaseToCaseCellMapperTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCaseToCaseRangeFilterMapper.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSimulationWellCenterLineCalculator.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellLogFile.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigReservoirGridTools.h
|
||||
)
|
||||
|
||||
set (SOURCE_GROUP_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigGeoMechWellLogExtractor.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCaseToCaseCellMapper.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCaseToCaseCellMapperTools.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCaseToCaseRangeFilterMapper.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSimulationWellCenterLineCalculator.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellLogFile.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigReservoirGridTools.cpp
|
||||
)
|
||||
|
||||
list(APPEND CODE_HEADER_FILES
|
||||
${SOURCE_GROUP_HEADER_FILES}
|
||||
)
|
||||
|
||||
list(APPEND CODE_SOURCE_FILES
|
||||
${SOURCE_GROUP_SOURCE_FILES}
|
||||
)
|
||||
|
||||
source_group( "ReservoirDataModel2" FILES ${SOURCE_GROUP_HEADER_FILES} ${SOURCE_GROUP_SOURCE_FILES} ${CMAKE_CURRENT_LIST_DIR}/CMakeLists_filesNotToUnitTest.cmake )
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
set (SOURCE_GROUP_HEADER_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCompletionData.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCompletionDataGridCell.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseToStimPlanCellTransmissibilityCalculator.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigTransmissibilityCondenser.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFractureTransmissibilityEquations.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellPathStimplanIntersector.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigVirtualPerforationTransmissibilities.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseToStimPlanCalculator.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigPerforationTransmissibilityEquations.h
|
||||
)
|
||||
|
||||
|
||||
set (SOURCE_GROUP_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCompletionData.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigCompletionDataGridCell.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseToStimPlanCellTransmissibilityCalculator.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigTransmissibilityCondenser.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigFractureTransmissibilityEquations.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellPathStimplanIntersector.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigVirtualPerforationTransmissibilities.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEclipseToStimPlanCalculator.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigPerforationTransmissibilityEquations.cpp
|
||||
)
|
||||
|
||||
list(APPEND CODE_HEADER_FILES
|
||||
${SOURCE_GROUP_HEADER_FILES}
|
||||
)
|
||||
|
||||
list(APPEND CODE_SOURCE_FILES
|
||||
${SOURCE_GROUP_SOURCE_FILES}
|
||||
)
|
||||
|
||||
source_group( "ReservoirDataModel\\Completions" FILES ${SOURCE_GROUP_HEADER_FILES} ${SOURCE_GROUP_SOURCE_FILES} ${CMAKE_CURRENT_LIST_DIR}/CMakeLists_files.cmake )
|
||||
@@ -0,0 +1,471 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigCompletionData.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "cvfAssert.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <limits>
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
RigCompletionData::RigCompletionData( const QString& wellName, const RigCompletionDataGridCell& cellIndex, double orderingValue )
|
||||
: m_wellName( wellName )
|
||||
, m_cellIndex( cellIndex )
|
||||
, m_saturation( std::numeric_limits<double>::infinity() )
|
||||
, m_transmissibility( std::numeric_limits<double>::infinity() )
|
||||
, m_diameter( std::numeric_limits<double>::infinity() )
|
||||
, m_kh( std::numeric_limits<double>::infinity() )
|
||||
, m_skinFactor( std::numeric_limits<double>::infinity() )
|
||||
, m_dFactor( std::numeric_limits<double>::infinity() )
|
||||
, m_direction( DIR_UNDEF )
|
||||
, m_connectionState( OPEN )
|
||||
, m_count( 1 )
|
||||
, m_wpimult( std::numeric_limits<double>::infinity() )
|
||||
, m_isMainBore( false )
|
||||
, m_completionType( CT_UNDEFINED )
|
||||
, m_firstOrderingValue( orderingValue )
|
||||
, m_secondOrderingValue( std::numeric_limits<double>::infinity() )
|
||||
{
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
RigCompletionData::~RigCompletionData()
|
||||
{
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
RigCompletionData::RigCompletionData( const RigCompletionData& other )
|
||||
{
|
||||
copy( *this, other );
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
bool RigCompletionData::operator<( const RigCompletionData& other ) const
|
||||
{
|
||||
if ( m_wellName != other.m_wellName )
|
||||
{
|
||||
return ( m_wellName < other.m_wellName );
|
||||
}
|
||||
|
||||
if ( m_completionType != other.m_completionType )
|
||||
{
|
||||
return ( m_completionType < other.m_completionType );
|
||||
}
|
||||
|
||||
if ( m_firstOrderingValue != other.m_firstOrderingValue )
|
||||
{
|
||||
return ( m_firstOrderingValue < other.m_firstOrderingValue );
|
||||
}
|
||||
|
||||
if ( m_secondOrderingValue != other.m_secondOrderingValue )
|
||||
{
|
||||
return ( m_secondOrderingValue < other.m_secondOrderingValue );
|
||||
}
|
||||
|
||||
return m_cellIndex < other.m_cellIndex;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
RigCompletionData& RigCompletionData::operator=( const RigCompletionData& other )
|
||||
{
|
||||
if ( this != &other )
|
||||
{
|
||||
copy( *this, other );
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigCompletionData::isPerforationValve( CompletionType type )
|
||||
{
|
||||
return type == PERFORATION_AICD || type == PERFORATION_ICD || type == PERFORATION_ICV;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigCompletionData::isValve( CompletionType type )
|
||||
{
|
||||
return isPerforationValve( type ) || type == FISHBONES_ICD;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigCompletionData::isWsegValveTypes( CompletionType type )
|
||||
{
|
||||
return type == FISHBONES_ICD || type == PERFORATION_ICD || type == PERFORATION_ICV;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
void RigCompletionData::setFromFracture( double transmissibility, double skinFactor, double diameter )
|
||||
{
|
||||
m_completionType = FRACTURE;
|
||||
m_transmissibility = transmissibility;
|
||||
m_skinFactor = skinFactor;
|
||||
m_diameter = diameter;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCompletionData::setSecondOrderingValue( double orderingValue )
|
||||
{
|
||||
m_secondOrderingValue = orderingValue;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCompletionData::setDiameter( double diameter )
|
||||
{
|
||||
m_diameter = diameter;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCompletionData::setTransmissibility( double transmissibility )
|
||||
{
|
||||
m_transmissibility = transmissibility;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
void RigCompletionData::setTransAndWPImultBackgroundDataFromFishbone( double transmissibility,
|
||||
double skinFactor,
|
||||
double diameter,
|
||||
double kh,
|
||||
CellDirection direction,
|
||||
bool isMainBore )
|
||||
{
|
||||
m_completionType = FISHBONES;
|
||||
m_transmissibility = transmissibility;
|
||||
m_skinFactor = skinFactor;
|
||||
m_diameter = diameter;
|
||||
m_kh = kh;
|
||||
m_direction = direction;
|
||||
m_isMainBore = isMainBore;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
void RigCompletionData::setTransAndWPImultBackgroundDataFromPerforation( double transmissibility,
|
||||
double skinFactor,
|
||||
double diameter,
|
||||
double dFactor,
|
||||
double kh,
|
||||
CellDirection direction )
|
||||
{
|
||||
m_completionType = PERFORATION;
|
||||
m_transmissibility = transmissibility;
|
||||
m_skinFactor = skinFactor;
|
||||
m_diameter = diameter;
|
||||
m_dFactor = dFactor;
|
||||
m_direction = direction;
|
||||
m_kh = kh;
|
||||
m_isMainBore = true;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
void RigCompletionData::setCombinedValuesExplicitTrans( double transmissibility,
|
||||
double kh,
|
||||
double dFactor,
|
||||
double skinFactor,
|
||||
double diameter,
|
||||
CellDirection celldirection,
|
||||
CompletionType completionType )
|
||||
{
|
||||
m_transmissibility = transmissibility;
|
||||
m_kh = kh;
|
||||
m_dFactor = dFactor;
|
||||
m_skinFactor = skinFactor;
|
||||
m_diameter = diameter;
|
||||
m_direction = celldirection;
|
||||
m_completionType = completionType;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
void RigCompletionData::setCombinedValuesImplicitTransWPImult( double wpimult,
|
||||
double kh,
|
||||
double dFactor,
|
||||
double skinFactor,
|
||||
double diameter,
|
||||
CellDirection celldirection,
|
||||
CompletionType completionType )
|
||||
{
|
||||
m_wpimult = wpimult;
|
||||
m_kh = kh;
|
||||
m_dFactor = dFactor;
|
||||
m_direction = celldirection;
|
||||
m_completionType = completionType;
|
||||
m_skinFactor = skinFactor;
|
||||
m_diameter = diameter;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigCompletionData::isNonDarcyFlow() const
|
||||
{
|
||||
if ( !isDefaultValue( m_kh ) ) return true;
|
||||
if ( !isDefaultValue( m_dFactor ) ) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCompletionData::setDFactor( double dFactor )
|
||||
{
|
||||
m_dFactor = dFactor;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCompletionData::setKh( double kh )
|
||||
{
|
||||
m_kh = kh;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
void RigCompletionData::addMetadata( const QString& name, const QString& comment )
|
||||
{
|
||||
m_metadata.push_back( RigCompletionMetaData( name, comment ) );
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
double RigCompletionData::defaultValue()
|
||||
{
|
||||
return std::numeric_limits<double>::infinity();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigCompletionData::isDefaultValue( double num )
|
||||
{
|
||||
return num == defaultValue();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<RigCompletionMetaData>& RigCompletionData::metadata() const
|
||||
{
|
||||
return m_metadata;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const QString& RigCompletionData::wellName() const
|
||||
{
|
||||
return m_wellName;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const RigCompletionDataGridCell& RigCompletionData::completionDataGridCell() const
|
||||
{
|
||||
return m_cellIndex;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
WellConnectionState RigCompletionData::connectionState() const
|
||||
{
|
||||
return m_connectionState;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCompletionData::saturation() const
|
||||
{
|
||||
return m_saturation;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCompletionData::transmissibility() const
|
||||
{
|
||||
return m_transmissibility;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCompletionData::diameter() const
|
||||
{
|
||||
return m_diameter;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCompletionData::kh() const
|
||||
{
|
||||
return m_kh;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCompletionData::skinFactor() const
|
||||
{
|
||||
return m_skinFactor;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCompletionData::dFactor() const
|
||||
{
|
||||
return m_dFactor;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
CellDirection RigCompletionData::direction() const
|
||||
{
|
||||
return m_direction;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigCompletionData::count() const
|
||||
{
|
||||
return m_count;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCompletionData::wpimult() const
|
||||
{
|
||||
return m_wpimult;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCompletionData::CompletionType RigCompletionData::completionType() const
|
||||
{
|
||||
return m_completionType;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigCompletionData::isMainBore() const
|
||||
{
|
||||
return m_isMainBore;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCompletionData::firstOrderingValue() const
|
||||
{
|
||||
return m_firstOrderingValue;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCompletionData::secondOrderingValue() const
|
||||
{
|
||||
return m_secondOrderingValue;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCompletionData::setSourcePdmObject( const caf::PdmObject* object )
|
||||
{
|
||||
m_sourcePdmObject = const_cast<caf::PdmObject*>( object );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const caf::PdmObject* RigCompletionData::sourcePdmObject() const
|
||||
{
|
||||
return m_sourcePdmObject;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
void RigCompletionData::copy( RigCompletionData& target, const RigCompletionData& from )
|
||||
{
|
||||
target.m_metadata = from.m_metadata;
|
||||
target.m_wellName = from.m_wellName;
|
||||
target.m_cellIndex = from.m_cellIndex;
|
||||
target.m_connectionState = from.m_connectionState;
|
||||
target.m_saturation = from.m_saturation;
|
||||
target.m_transmissibility = from.m_transmissibility;
|
||||
target.m_diameter = from.m_diameter;
|
||||
target.m_kh = from.m_kh;
|
||||
target.m_skinFactor = from.m_skinFactor;
|
||||
target.m_dFactor = from.m_dFactor;
|
||||
target.m_direction = from.m_direction;
|
||||
target.m_isMainBore = from.m_isMainBore;
|
||||
target.m_count = from.m_count;
|
||||
target.m_wpimult = from.m_wpimult;
|
||||
target.m_completionType = from.m_completionType;
|
||||
target.m_firstOrderingValue = from.m_firstOrderingValue;
|
||||
target.m_secondOrderingValue = from.m_secondOrderingValue;
|
||||
target.m_sourcePdmObject = from.m_sourcePdmObject;
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RigCompletionDataGridCell.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <cafPdmObject.h>
|
||||
#include <cafPdmPointer.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
enum WellConnectionState
|
||||
{
|
||||
OPEN,
|
||||
SHUT,
|
||||
AUTO,
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
enum CellDirection
|
||||
{
|
||||
DIR_I,
|
||||
DIR_J,
|
||||
DIR_K,
|
||||
DIR_UNDEF,
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
struct RigCompletionMetaData
|
||||
{
|
||||
RigCompletionMetaData( const QString& name, const QString& comment )
|
||||
: name( name )
|
||||
, comment( comment )
|
||||
{
|
||||
}
|
||||
|
||||
QString name;
|
||||
QString comment;
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigCompletionData
|
||||
{
|
||||
public:
|
||||
enum CompletionType
|
||||
{
|
||||
FISHBONES,
|
||||
FRACTURE,
|
||||
PERFORATION,
|
||||
FISHBONES_ICD,
|
||||
PERFORATION_ICD,
|
||||
PERFORATION_AICD,
|
||||
PERFORATION_ICV,
|
||||
CT_UNDEFINED
|
||||
};
|
||||
|
||||
RigCompletionData( const QString& wellName, const RigCompletionDataGridCell& cellIndex, double orderingValue );
|
||||
~RigCompletionData();
|
||||
|
||||
RigCompletionData( const RigCompletionData& other );
|
||||
|
||||
bool operator<( const RigCompletionData& other ) const;
|
||||
RigCompletionData& operator=( const RigCompletionData& other );
|
||||
|
||||
static bool isPerforationValve( CompletionType type );
|
||||
static bool isValve( CompletionType type );
|
||||
static bool isWsegValveTypes( CompletionType type );
|
||||
|
||||
void setFromFracture( double transmissibility, double skinFactor, double diameter );
|
||||
void setSecondOrderingValue( double orderingValue );
|
||||
void setDiameter( double diameter );
|
||||
void setTransmissibility( double transmissibility );
|
||||
|
||||
void setTransAndWPImultBackgroundDataFromFishbone( double transmissibility,
|
||||
double skinFactor,
|
||||
double diameter,
|
||||
double kh,
|
||||
CellDirection direction,
|
||||
bool isMainBore );
|
||||
|
||||
void setTransAndWPImultBackgroundDataFromPerforation( double transmissibility,
|
||||
double skinFactor,
|
||||
double diameter,
|
||||
double dFactor,
|
||||
double kh,
|
||||
CellDirection direction );
|
||||
|
||||
void setCombinedValuesExplicitTrans( double transmissibility,
|
||||
double kh,
|
||||
double dFactor,
|
||||
double skinFactor,
|
||||
double diameter,
|
||||
CellDirection celldirection,
|
||||
CompletionType completionType );
|
||||
|
||||
void setCombinedValuesImplicitTransWPImult( double wpimult,
|
||||
double kh,
|
||||
double dFactor,
|
||||
double skinFactor,
|
||||
double diameter,
|
||||
CellDirection celldirection,
|
||||
CompletionType completionType );
|
||||
|
||||
bool isNonDarcyFlow() const;
|
||||
void setDFactor( double dFactor );
|
||||
void setKh( double kh );
|
||||
|
||||
void addMetadata( const QString& name, const QString& comment );
|
||||
|
||||
static double defaultValue();
|
||||
static bool isDefaultValue( double num );
|
||||
const std::vector<RigCompletionMetaData>& metadata() const;
|
||||
const QString& wellName() const;
|
||||
const RigCompletionDataGridCell& completionDataGridCell() const;
|
||||
WellConnectionState connectionState() const;
|
||||
double saturation() const;
|
||||
double transmissibility() const;
|
||||
double diameter() const; // TODO: should be ft or m
|
||||
double kh() const;
|
||||
double skinFactor() const;
|
||||
double dFactor() const;
|
||||
CellDirection direction() const;
|
||||
size_t count() const;
|
||||
double wpimult() const;
|
||||
CompletionType completionType() const;
|
||||
bool isMainBore() const;
|
||||
|
||||
double firstOrderingValue() const;
|
||||
double secondOrderingValue() const;
|
||||
|
||||
void setSourcePdmObject( const caf::PdmObject* object );
|
||||
const caf::PdmObject* sourcePdmObject() const;
|
||||
|
||||
std::vector<RigCompletionMetaData> m_metadata;
|
||||
|
||||
private:
|
||||
QString m_wellName;
|
||||
RigCompletionDataGridCell m_cellIndex;
|
||||
WellConnectionState m_connectionState;
|
||||
double m_saturation; // TODO: remove, always use default in Eclipse?
|
||||
double m_transmissibility;
|
||||
double m_diameter;
|
||||
double m_kh; // TODO: Remove, always use default in Eclipse?
|
||||
double m_skinFactor;
|
||||
double m_dFactor; // TODO: Remove, always use default in Eclipse?
|
||||
CellDirection m_direction;
|
||||
|
||||
bool m_isMainBore; // to use mainbore for Eclipse calculation
|
||||
|
||||
size_t m_count; // TODO: Remove, usage replaced by WPImult
|
||||
double m_wpimult;
|
||||
|
||||
CompletionType m_completionType;
|
||||
|
||||
double m_firstOrderingValue;
|
||||
double m_secondOrderingValue;
|
||||
|
||||
caf::PdmPointer<caf::PdmObject> m_sourcePdmObject;
|
||||
|
||||
private:
|
||||
static void copy( RigCompletionData& target, const RigCompletionData& from );
|
||||
};
|
||||
@@ -0,0 +1,147 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2018- 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 "RigCompletionDataGridCell.h"
|
||||
|
||||
#include "RigMainGrid.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCompletionDataGridCell::RigCompletionDataGridCell()
|
||||
: m_globalCellIndex( 0 )
|
||||
, m_lgrName( "" )
|
||||
, m_gridIndex( 0 )
|
||||
, m_localCellIndexI( 0 )
|
||||
, m_localCellIndexJ( 0 )
|
||||
, m_localCellIndexK( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCompletionDataGridCell::RigCompletionDataGridCell( size_t globalCellIndex, const RigMainGrid* mainGrid )
|
||||
: m_globalCellIndex( globalCellIndex )
|
||||
{
|
||||
if ( mainGrid )
|
||||
{
|
||||
size_t gridLocalCellIndex;
|
||||
const RigGridBase* grid = mainGrid->gridAndGridLocalIdxFromGlobalCellIdx( globalCellIndex, &gridLocalCellIndex );
|
||||
|
||||
if ( grid )
|
||||
{
|
||||
size_t i = 0;
|
||||
size_t j = 0;
|
||||
size_t k = 0;
|
||||
grid->ijkFromCellIndex( gridLocalCellIndex, &i, &j, &k );
|
||||
|
||||
m_localCellIndexI = i;
|
||||
m_localCellIndexJ = j;
|
||||
m_localCellIndexK = k;
|
||||
|
||||
if ( grid != mainGrid )
|
||||
{
|
||||
m_lgrName = QString::fromStdString( grid->gridName() );
|
||||
}
|
||||
|
||||
m_gridIndex = grid->gridIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigCompletionDataGridCell::operator==( const RigCompletionDataGridCell& other ) const
|
||||
{
|
||||
return m_globalCellIndex == other.m_globalCellIndex;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigCompletionDataGridCell::operator<( const RigCompletionDataGridCell& other ) const
|
||||
{
|
||||
if ( m_gridIndex != other.m_gridIndex ) return m_gridIndex < other.m_gridIndex;
|
||||
|
||||
if ( m_localCellIndexI != other.m_localCellIndexI ) return m_localCellIndexI < other.m_localCellIndexI;
|
||||
if ( m_localCellIndexJ != other.m_localCellIndexJ ) return m_localCellIndexJ < other.m_localCellIndexJ;
|
||||
if ( m_localCellIndexK != other.m_localCellIndexK ) return m_localCellIndexK < other.m_localCellIndexK;
|
||||
|
||||
return m_globalCellIndex < other.m_globalCellIndex;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigCompletionDataGridCell::globalCellIndex() const
|
||||
{
|
||||
return m_globalCellIndex;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigCompletionDataGridCell::localCellIndexI() const
|
||||
{
|
||||
return m_localCellIndexI;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigCompletionDataGridCell::localCellIndexJ() const
|
||||
{
|
||||
return m_localCellIndexJ;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigCompletionDataGridCell::localCellIndexK() const
|
||||
{
|
||||
return m_localCellIndexK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RigCompletionDataGridCell::oneBasedLocalCellIndexString() const
|
||||
{
|
||||
QString text =
|
||||
QString( "[%1, %2, %3]" ).arg( m_localCellIndexI + 1 ).arg( m_localCellIndexJ + 1 ).arg( m_localCellIndexK + 1 );
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RigCompletionDataGridCell::lgrName() const
|
||||
{
|
||||
return m_lgrName;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigCompletionDataGridCell::isMainGridCell() const
|
||||
{
|
||||
return m_lgrName.isEmpty();
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2018- 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 <QString>
|
||||
|
||||
class RigMainGrid;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigCompletionDataGridCell
|
||||
{
|
||||
public:
|
||||
RigCompletionDataGridCell();
|
||||
|
||||
RigCompletionDataGridCell( size_t globalCellIndex, const RigMainGrid* mainGrid );
|
||||
|
||||
bool operator==( const RigCompletionDataGridCell& other ) const;
|
||||
|
||||
bool operator<( const RigCompletionDataGridCell& other ) const;
|
||||
|
||||
size_t globalCellIndex() const;
|
||||
|
||||
size_t localCellIndexI() const;
|
||||
size_t localCellIndexJ() const;
|
||||
size_t localCellIndexK() const;
|
||||
|
||||
QString oneBasedLocalCellIndexString() const;
|
||||
|
||||
QString lgrName() const;
|
||||
|
||||
bool isMainGridCell() const;
|
||||
|
||||
private:
|
||||
size_t m_globalCellIndex;
|
||||
QString m_lgrName;
|
||||
|
||||
size_t m_gridIndex;
|
||||
size_t m_localCellIndexI;
|
||||
size_t m_localCellIndexJ;
|
||||
size_t m_localCellIndexK;
|
||||
};
|
||||
@@ -0,0 +1,269 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2018- 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 "RigEclipseToStimPlanCalculator.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigCaseCellResultsData.h"
|
||||
#include "RigCellGeometryTools.h"
|
||||
#include "RigEclipseCaseData.h"
|
||||
#include "RigFractureCell.h"
|
||||
#include "RigFractureGrid.h"
|
||||
#include "RigFractureTransmissibilityEquations.h"
|
||||
#include "RigHexIntersectionTools.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigResultAccessorFactory.h"
|
||||
#include "RigTransmissibilityCondenser.h"
|
||||
|
||||
#include "RiaWeightedMeanCalculator.h"
|
||||
#include "RimEclipseCase.h"
|
||||
#include "RimEllipseFractureTemplate.h"
|
||||
#include "RimFracture.h"
|
||||
#include "RimFractureContainmentTools.h"
|
||||
#include "RimStimPlanFractureTemplate.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigEclipseToStimPlanCalculator::RigEclipseToStimPlanCalculator( const RimEclipseCase* caseToApply,
|
||||
cvf::Mat4d fractureTransform,
|
||||
double skinFactor,
|
||||
double cDarcy,
|
||||
const RigFractureGrid& fractureGrid,
|
||||
const RimFracture* fracture )
|
||||
: m_case( caseToApply )
|
||||
, m_fractureTransform( fractureTransform )
|
||||
, m_fractureSkinFactor( skinFactor )
|
||||
, m_cDarcy( cDarcy )
|
||||
, m_fractureGrid( fractureGrid )
|
||||
, m_fracture( fracture )
|
||||
{
|
||||
computeValues();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseToStimPlanCalculator::computeValues()
|
||||
{
|
||||
auto reservoirCellIndicesOpenForFlow =
|
||||
RimFractureContainmentTools::reservoirCellIndicesOpenForFlow( m_case, m_fracture );
|
||||
|
||||
for ( size_t i = 0; i < m_fractureGrid.fractureCells().size(); i++ )
|
||||
{
|
||||
const RigFractureCell& fractureCell = m_fractureGrid.fractureCells()[i];
|
||||
if ( !fractureCell.hasNonZeroConductivity() ) continue;
|
||||
|
||||
RigEclipseToStimPlanCellTransmissibilityCalculator eclToFractureTransCalc( m_case,
|
||||
m_fractureTransform,
|
||||
m_fractureSkinFactor,
|
||||
m_cDarcy,
|
||||
fractureCell,
|
||||
reservoirCellIndicesOpenForFlow,
|
||||
m_fracture );
|
||||
|
||||
const std::vector<size_t>& fractureCellContributingEclipseCells =
|
||||
eclToFractureTransCalc.globalIndiciesToContributingEclipseCells();
|
||||
|
||||
if ( !fractureCellContributingEclipseCells.empty() )
|
||||
{
|
||||
m_singleFractureCellCalculators.emplace( i, eclToFractureTransCalc );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using CellIdxSpace = RigTransmissibilityCondenser::CellAddress;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseToStimPlanCalculator::appendDataToTransmissibilityCondenser( bool useFiniteConductivityInFracture,
|
||||
RigTransmissibilityCondenser* condenser ) const
|
||||
{
|
||||
for ( const auto& eclToFractureTransCalc : m_singleFractureCellCalculators )
|
||||
{
|
||||
const std::vector<size_t>& fractureCellContributingEclipseCells =
|
||||
eclToFractureTransCalc.second.globalIndiciesToContributingEclipseCells();
|
||||
|
||||
const std::vector<double>& fractureCellContributingEclipseCellTransmissibilities =
|
||||
eclToFractureTransCalc.second.contributingEclipseCellTransmissibilities();
|
||||
|
||||
size_t stimPlanCellIndex = eclToFractureTransCalc.first;
|
||||
|
||||
for ( size_t i = 0; i < fractureCellContributingEclipseCells.size(); i++ )
|
||||
{
|
||||
if ( useFiniteConductivityInFracture )
|
||||
{
|
||||
condenser->addNeighborTransmissibility( { true,
|
||||
CellIdxSpace::ECLIPSE,
|
||||
fractureCellContributingEclipseCells[i] },
|
||||
{ false, CellIdxSpace::STIMPLAN, stimPlanCellIndex },
|
||||
fractureCellContributingEclipseCellTransmissibilities[i] );
|
||||
}
|
||||
else
|
||||
{
|
||||
condenser->addNeighborTransmissibility( { true,
|
||||
CellIdxSpace::ECLIPSE,
|
||||
fractureCellContributingEclipseCells[i] },
|
||||
{ true, CellIdxSpace::WELL, 1 },
|
||||
fractureCellContributingEclipseCellTransmissibilities[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigEclipseToStimPlanCalculator::totalEclipseAreaOpenForFlow() const
|
||||
{
|
||||
double area = 0.0;
|
||||
|
||||
for ( const auto& singleCellCalc : m_singleFractureCellCalculators )
|
||||
{
|
||||
double cellArea = singleCellCalc.second.areaOpenForFlow();
|
||||
|
||||
area += cellArea;
|
||||
}
|
||||
|
||||
return area;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigEclipseToStimPlanCalculator::areaWeightedMatrixPermeability() const
|
||||
{
|
||||
RiaWeightedMeanCalculator<double> calc;
|
||||
|
||||
{
|
||||
for ( const auto& singleCellCalc : m_singleFractureCellCalculators )
|
||||
{
|
||||
const RigEclipseToStimPlanCellTransmissibilityCalculator& calulator = singleCellCalc.second;
|
||||
|
||||
const std::vector<double>& areas = calulator.contributingEclipseCellIntersectionAreas();
|
||||
const std::vector<double>& permeabilities = calulator.contributingEclipseCellPermeabilities();
|
||||
|
||||
if ( areas.size() == permeabilities.size() )
|
||||
{
|
||||
for ( size_t i = 0; i < areas.size(); i++ )
|
||||
{
|
||||
calc.addValueAndWeight( permeabilities[i], areas[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return calc.weightedMean();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigEclipseToStimPlanCalculator::areaWeightedWidth() const
|
||||
{
|
||||
double width = 0.0;
|
||||
|
||||
auto ellipseFractureTemplate = dynamic_cast<const RimEllipseFractureTemplate*>( m_fracture->fractureTemplate() );
|
||||
if ( ellipseFractureTemplate )
|
||||
{
|
||||
width = ellipseFractureTemplate->width();
|
||||
}
|
||||
|
||||
auto stimPlanFractureTemplate = dynamic_cast<const RimStimPlanFractureTemplate*>( m_fracture->fractureTemplate() );
|
||||
if ( stimPlanFractureTemplate )
|
||||
{
|
||||
auto widthValues = stimPlanFractureTemplate->widthResultValues();
|
||||
if ( !widthValues.empty() )
|
||||
{
|
||||
RiaWeightedMeanCalculator<double> calc;
|
||||
|
||||
for ( const auto& singleCellCalc : m_singleFractureCellCalculators )
|
||||
{
|
||||
double cellArea = singleCellCalc.second.areaOpenForFlow();
|
||||
|
||||
size_t globalStimPlanCellIndex = singleCellCalc.first;
|
||||
double widthValue = widthValues[globalStimPlanCellIndex];
|
||||
|
||||
calc.addValueAndWeight( widthValue, cellArea );
|
||||
}
|
||||
|
||||
width = calc.weightedMean();
|
||||
}
|
||||
else
|
||||
{
|
||||
width = stimPlanFractureTemplate->computeFractureWidth( m_fracture );
|
||||
}
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigEclipseToStimPlanCalculator::areaWeightedConductivity() const
|
||||
{
|
||||
RiaWeightedMeanCalculator<double> calc;
|
||||
|
||||
for ( const auto& singleCellCalc : m_singleFractureCellCalculators )
|
||||
{
|
||||
double cellArea = singleCellCalc.second.areaOpenForFlow();
|
||||
|
||||
calc.addValueAndWeight( singleCellCalc.second.fractureCell().getConductivityValue(), cellArea );
|
||||
}
|
||||
|
||||
return calc.weightedMean();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigEclipseToStimPlanCalculator::longestYSectionOpenForFlow() const
|
||||
{
|
||||
// For each I, find the longest aggregated distance along J with continuous fracture cells with conductivity above
|
||||
// zero connected to Eclipse cells open for flow
|
||||
|
||||
double longestRange = 0.0;
|
||||
|
||||
for ( size_t i = 0; i < m_fractureGrid.iCellCount(); i++ )
|
||||
{
|
||||
double currentAggregatedDistanceY = 0.0;
|
||||
for ( size_t j = 0; j < m_fractureGrid.jCellCount(); j++ )
|
||||
{
|
||||
size_t globalStimPlanCellIndex = m_fractureGrid.getGlobalIndexFromIJ( i, j );
|
||||
|
||||
auto calculatorForCell = m_singleFractureCellCalculators.find( globalStimPlanCellIndex );
|
||||
if ( calculatorForCell != m_singleFractureCellCalculators.end() )
|
||||
{
|
||||
currentAggregatedDistanceY += calculatorForCell->second.fractureCell().cellSizeZ();
|
||||
}
|
||||
else
|
||||
{
|
||||
longestRange = std::max( longestRange, currentAggregatedDistanceY );
|
||||
currentAggregatedDistanceY = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
longestRange = std::max( longestRange, currentAggregatedDistanceY );
|
||||
}
|
||||
|
||||
return longestRange;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2018- 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 "RiaPorosityModel.h"
|
||||
|
||||
#include "RigEclipseToStimPlanCellTransmissibilityCalculator.h"
|
||||
|
||||
#include "cvfMatrix4.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
class QString;
|
||||
|
||||
class RimEclipseCase;
|
||||
class RigFractureGrid;
|
||||
class RigTransmissibilityCondenser;
|
||||
class RimFracture;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigEclipseToStimPlanCalculator
|
||||
{
|
||||
public:
|
||||
explicit RigEclipseToStimPlanCalculator( const RimEclipseCase* caseToApply,
|
||||
cvf::Mat4d fractureTransform,
|
||||
double skinFactor,
|
||||
double cDarcy,
|
||||
const RigFractureGrid& fractureGrid,
|
||||
const RimFracture* fracture );
|
||||
|
||||
void appendDataToTransmissibilityCondenser( bool useFiniteConductivityInFracture,
|
||||
RigTransmissibilityCondenser* condenser ) const;
|
||||
|
||||
// Returns the area intersecting eclipse cells open for flow, from both active and inactive cells
|
||||
// Truncated parts of the fracture are not included
|
||||
double totalEclipseAreaOpenForFlow() const;
|
||||
|
||||
double areaWeightedMatrixPermeability() const;
|
||||
double areaWeightedWidth() const;
|
||||
double areaWeightedConductivity() const;
|
||||
double longestYSectionOpenForFlow() const;
|
||||
|
||||
private:
|
||||
void computeValues();
|
||||
|
||||
private:
|
||||
const RimEclipseCase* m_case;
|
||||
const RimFracture* m_fracture;
|
||||
double m_cDarcy;
|
||||
double m_fractureSkinFactor;
|
||||
cvf::Mat4d m_fractureTransform;
|
||||
const RigFractureGrid& m_fractureGrid;
|
||||
|
||||
std::map<size_t, RigEclipseToStimPlanCellTransmissibilityCalculator> m_singleFractureCellCalculators;
|
||||
};
|
||||
+392
@@ -0,0 +1,392 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigEclipseToStimPlanCellTransmissibilityCalculator.h"
|
||||
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigCaseCellResultsData.h"
|
||||
#include "RigCellGeometryTools.h"
|
||||
#include "RigEclipseCaseData.h"
|
||||
#include "RigFractureCell.h"
|
||||
#include "RigFractureTransmissibilityEquations.h"
|
||||
#include "RigHexIntersectionTools.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigResultAccessorFactory.h"
|
||||
|
||||
#include "RimEclipseCase.h"
|
||||
#include "RimFracture.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "cvfGeometryTools.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigEclipseToStimPlanCellTransmissibilityCalculator::RigEclipseToStimPlanCellTransmissibilityCalculator(
|
||||
const RimEclipseCase* caseToApply,
|
||||
cvf::Mat4d fractureTransform,
|
||||
double skinFactor,
|
||||
double cDarcy,
|
||||
const RigFractureCell& stimPlanCell,
|
||||
const std::set<size_t>& reservoirCellIndicesOpenForFlow,
|
||||
const RimFracture* fracture )
|
||||
: m_case( caseToApply )
|
||||
, m_fractureTransform( fractureTransform )
|
||||
, m_fractureSkinFactor( skinFactor )
|
||||
, m_cDarcy( cDarcy )
|
||||
, m_stimPlanCell( stimPlanCell )
|
||||
, m_fracture( fracture )
|
||||
{
|
||||
calculateStimPlanCellsMatrixTransmissibility( reservoirCellIndicesOpenForFlow );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<size_t>& RigEclipseToStimPlanCellTransmissibilityCalculator::globalIndiciesToContributingEclipseCells() const
|
||||
{
|
||||
return m_globalIndiciesToContributingEclipseCells;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<double>& RigEclipseToStimPlanCellTransmissibilityCalculator::contributingEclipseCellTransmissibilities() const
|
||||
{
|
||||
return m_contributingEclipseCellTransmissibilities;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<double>& RigEclipseToStimPlanCellTransmissibilityCalculator::contributingEclipseCellIntersectionAreas() const
|
||||
{
|
||||
return m_contributingEclipseCellAreas;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<double>& RigEclipseToStimPlanCellTransmissibilityCalculator::contributingEclipseCellPermeabilities() const
|
||||
{
|
||||
return m_contributingEclipseCellPermeabilities;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigEclipseToStimPlanCellTransmissibilityCalculator::areaOpenForFlow() const
|
||||
{
|
||||
double area = 0.0;
|
||||
|
||||
for ( const auto& areaForOneEclipseCell : m_contributingEclipseCellAreas )
|
||||
{
|
||||
area += areaForOneEclipseCell;
|
||||
}
|
||||
|
||||
return area;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const RigFractureCell& RigEclipseToStimPlanCellTransmissibilityCalculator::fractureCell() const
|
||||
{
|
||||
return m_stimPlanCell;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<QString> RigEclipseToStimPlanCellTransmissibilityCalculator::requiredResultNames()
|
||||
{
|
||||
std::vector<QString> resultNames;
|
||||
resultNames.push_back( "PERMX" );
|
||||
resultNames.push_back( "PERMY" );
|
||||
resultNames.push_back( "PERMZ" );
|
||||
|
||||
resultNames.push_back( "DX" );
|
||||
resultNames.push_back( "DY" );
|
||||
resultNames.push_back( "DZ" );
|
||||
|
||||
return resultNames;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<QString> RigEclipseToStimPlanCellTransmissibilityCalculator::optionalResultNames()
|
||||
{
|
||||
std::vector<QString> resultNames;
|
||||
resultNames.push_back( "NTG" );
|
||||
|
||||
return resultNames;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseToStimPlanCellTransmissibilityCalculator::calculateStimPlanCellsMatrixTransmissibility(
|
||||
const std::set<size_t>& reservoirCellIndicesOpenForFlow )
|
||||
{
|
||||
// Not calculating flow into fracture if stimPlan cell cond value is 0 (assumed to be outside the fracture):
|
||||
if ( m_stimPlanCell.getConductivityValue() < 1e-7 ) return;
|
||||
|
||||
const RigEclipseCaseData* eclipseCaseData = m_case->eclipseCaseData();
|
||||
|
||||
RiaDefines::PorosityModelType porosityModel = RiaDefines::PorosityModelType::MATRIX_MODEL;
|
||||
|
||||
cvf::ref<RigResultAccessor> dataAccessObjectDx = createResultAccessor( m_case, "DX" );
|
||||
cvf::ref<RigResultAccessor> dataAccessObjectDy = createResultAccessor( m_case, "DY" );
|
||||
cvf::ref<RigResultAccessor> dataAccessObjectDz = createResultAccessor( m_case, "DZ" );
|
||||
if ( dataAccessObjectDx.isNull() || dataAccessObjectDy.isNull() || dataAccessObjectDz.isNull() )
|
||||
{
|
||||
RiaLogging::error(
|
||||
"Data for DX/DY/DZ is not complete, and these values are required for export of COMPDAT. Make sure "
|
||||
"'Preferences->Compute DEPTH Related Properties' is checked." );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
cvf::ref<RigResultAccessor> dataAccessObjectPermX = createResultAccessor( m_case, "PERMX" );
|
||||
cvf::ref<RigResultAccessor> dataAccessObjectPermY = createResultAccessor( m_case, "PERMY" );
|
||||
cvf::ref<RigResultAccessor> dataAccessObjectPermZ = createResultAccessor( m_case, "PERMZ" );
|
||||
if ( dataAccessObjectPermX.isNull() || dataAccessObjectPermY.isNull() || dataAccessObjectPermZ.isNull() )
|
||||
{
|
||||
RiaLogging::error(
|
||||
"Data for PERMX/PERMY/PERMZ is not complete, and these values are required for export of COMPDAT." );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
cvf::ref<RigResultAccessor> dataAccessObjectNTG = createResultAccessor( m_case, "NTG" );
|
||||
|
||||
const RigActiveCellInfo* activeCellInfo = eclipseCaseData->activeCellInfo( porosityModel );
|
||||
|
||||
std::vector<cvf::Vec3d> stimPlanPolygonTransformed;
|
||||
for ( cvf::Vec3d v : m_stimPlanCell.getPolygon() )
|
||||
{
|
||||
v.transformPoint( m_fractureTransform );
|
||||
stimPlanPolygonTransformed.push_back( v );
|
||||
}
|
||||
|
||||
std::vector<size_t> reservoirCellIndices = getPotentiallyFracturedCellsForPolygon( stimPlanPolygonTransformed );
|
||||
for ( size_t reservoirCellIndex : reservoirCellIndices )
|
||||
{
|
||||
const RigMainGrid* mainGrid = m_case->eclipseCaseData()->mainGrid();
|
||||
if ( !m_fracture->isEclipseCellOpenForFlow( mainGrid, reservoirCellIndicesOpenForFlow, reservoirCellIndex ) )
|
||||
continue;
|
||||
|
||||
std::array<cvf::Vec3d, 8> hexCorners;
|
||||
mainGrid->cellCornerVertices( reservoirCellIndex, hexCorners.data() );
|
||||
|
||||
std::vector<std::vector<cvf::Vec3d>> planeCellPolygons;
|
||||
bool isPlanIntersected =
|
||||
RigHexIntersectionTools::planeHexIntersectionPolygons( hexCorners, m_fractureTransform, planeCellPolygons );
|
||||
if ( !isPlanIntersected || planeCellPolygons.empty() ) continue;
|
||||
|
||||
cvf::Vec3d localX;
|
||||
cvf::Vec3d localY;
|
||||
cvf::Vec3d localZ;
|
||||
RigCellGeometryTools::findCellLocalXYZ( hexCorners, localX, localY, localZ );
|
||||
|
||||
// Transform planCell polygon(s) and averageZdirection to x/y coordinate system (where fracturePolygon already
|
||||
// is located)
|
||||
cvf::Mat4d invertedTransMatrix = m_fractureTransform.getInverted();
|
||||
for ( std::vector<cvf::Vec3d>& planeCellPolygon : planeCellPolygons )
|
||||
{
|
||||
for ( cvf::Vec3d& v : planeCellPolygon )
|
||||
{
|
||||
v.transformPoint( invertedTransMatrix );
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<cvf::Vec3d>> polygonsForStimPlanCellInEclipseCell;
|
||||
cvf::Vec3d areaVector;
|
||||
std::vector<cvf::Vec3d> stimPlanPolygon = m_stimPlanCell.getPolygon();
|
||||
|
||||
for ( const std::vector<cvf::Vec3d>& planeCellPolygon : planeCellPolygons )
|
||||
{
|
||||
std::vector<std::vector<cvf::Vec3d>> clippedPolygons =
|
||||
RigCellGeometryTools::intersectionWithPolygon( planeCellPolygon, stimPlanPolygon );
|
||||
for ( const std::vector<cvf::Vec3d>& clippedPolygon : clippedPolygons )
|
||||
{
|
||||
polygonsForStimPlanCellInEclipseCell.push_back( clippedPolygon );
|
||||
}
|
||||
}
|
||||
|
||||
if ( polygonsForStimPlanCellInEclipseCell.empty() ) continue;
|
||||
|
||||
std::vector<double> areaOfFractureParts;
|
||||
double length;
|
||||
std::vector<double> lengthXareaOfFractureParts;
|
||||
double Ax = 0.0;
|
||||
double Ay = 0.0;
|
||||
double Az = 0.0;
|
||||
|
||||
for ( const std::vector<cvf::Vec3d>& fracturePartPolygon : polygonsForStimPlanCellInEclipseCell )
|
||||
{
|
||||
areaVector = cvf::GeometryTools::polygonAreaNormal3D( fracturePartPolygon );
|
||||
double area = areaVector.length();
|
||||
areaOfFractureParts.push_back( area );
|
||||
|
||||
length = RigCellGeometryTools::polygonLengthInLocalXdirWeightedByArea( fracturePartPolygon );
|
||||
lengthXareaOfFractureParts.push_back( length * area );
|
||||
|
||||
cvf::Plane fracturePlane;
|
||||
fracturePlane.setFromPointAndNormal( static_cast<cvf::Vec3d>( m_fractureTransform.translation() ),
|
||||
static_cast<cvf::Vec3d>( m_fractureTransform.col( 2 ) ) );
|
||||
|
||||
Ax += fabs( area * ( fracturePlane.normal().dot( localY ) ) );
|
||||
Ay += fabs( area * ( fracturePlane.normal().dot( localX ) ) );
|
||||
Az += fabs( area * ( fracturePlane.normal().dot( localZ ) ) );
|
||||
}
|
||||
|
||||
double fractureArea = 0.0;
|
||||
for ( double area : areaOfFractureParts )
|
||||
fractureArea += area;
|
||||
|
||||
double totalAreaXLength = 0.0;
|
||||
for ( double lengtXarea : lengthXareaOfFractureParts )
|
||||
totalAreaXLength += lengtXarea;
|
||||
|
||||
double fractureAreaWeightedlength = totalAreaXLength / fractureArea;
|
||||
|
||||
// Transmissibility for inactive cells is set to zero
|
||||
// Inactive cells must be include in order to compute the fractured area correctly
|
||||
double transmissibility = 0.0;
|
||||
|
||||
bool isActive = true;
|
||||
{
|
||||
// Use main grid cell to evaluate if a cell is active or not.
|
||||
// All cells in temporary grids are active
|
||||
const RigCell& cell = mainGrid->globalCellArray()[reservoirCellIndex];
|
||||
size_t mainGridReservoirIndex = cell.mainGridCellIndex();
|
||||
|
||||
if ( !activeCellInfo->isActive( mainGridReservoirIndex ) )
|
||||
{
|
||||
isActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
double matrixPermeability = 0.0;
|
||||
|
||||
if ( isActive )
|
||||
{
|
||||
double permX = dataAccessObjectPermX->cellScalarGlobIdx( reservoirCellIndex );
|
||||
double permY = dataAccessObjectPermY->cellScalarGlobIdx( reservoirCellIndex );
|
||||
double permZ = dataAccessObjectPermZ->cellScalarGlobIdx( reservoirCellIndex );
|
||||
|
||||
double dx = dataAccessObjectDx->cellScalarGlobIdx( reservoirCellIndex );
|
||||
double dy = dataAccessObjectDy->cellScalarGlobIdx( reservoirCellIndex );
|
||||
double dz = dataAccessObjectDz->cellScalarGlobIdx( reservoirCellIndex );
|
||||
|
||||
double NTG = 1.0;
|
||||
if ( dataAccessObjectNTG.notNull() )
|
||||
{
|
||||
NTG = dataAccessObjectNTG->cellScalarGlobIdx( reservoirCellIndex );
|
||||
}
|
||||
|
||||
double transmissibility_X =
|
||||
RigFractureTransmissibilityEquations::matrixToFractureTrans( permY,
|
||||
NTG,
|
||||
Ay,
|
||||
dx,
|
||||
m_fractureSkinFactor,
|
||||
fractureAreaWeightedlength,
|
||||
m_cDarcy );
|
||||
double transmissibility_Y =
|
||||
RigFractureTransmissibilityEquations::matrixToFractureTrans( permX,
|
||||
NTG,
|
||||
Ax,
|
||||
dy,
|
||||
m_fractureSkinFactor,
|
||||
fractureAreaWeightedlength,
|
||||
m_cDarcy );
|
||||
double transmissibility_Z =
|
||||
RigFractureTransmissibilityEquations::matrixToFractureTrans( permZ,
|
||||
1.0,
|
||||
Az,
|
||||
dz,
|
||||
m_fractureSkinFactor,
|
||||
fractureAreaWeightedlength,
|
||||
m_cDarcy );
|
||||
|
||||
transmissibility = sqrt( transmissibility_X * transmissibility_X + transmissibility_Y * transmissibility_Y +
|
||||
transmissibility_Z * transmissibility_Z );
|
||||
|
||||
matrixPermeability = RigFractureTransmissibilityEquations::matrixPermeability( permX, permY, NTG );
|
||||
}
|
||||
|
||||
m_globalIndiciesToContributingEclipseCells.push_back( reservoirCellIndex );
|
||||
m_contributingEclipseCellTransmissibilities.push_back( transmissibility );
|
||||
m_contributingEclipseCellAreas.push_back( fractureArea );
|
||||
m_contributingEclipseCellPermeabilities.push_back( matrixPermeability );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<size_t> RigEclipseToStimPlanCellTransmissibilityCalculator::getPotentiallyFracturedCellsForPolygon(
|
||||
const std::vector<cvf::Vec3d>& polygon ) const
|
||||
{
|
||||
std::vector<size_t> cellIndices;
|
||||
|
||||
const RigMainGrid* mainGrid = m_case->eclipseCaseData()->mainGrid();
|
||||
if ( !mainGrid ) return cellIndices;
|
||||
|
||||
cvf::BoundingBox polygonBBox;
|
||||
for ( const cvf::Vec3d& nodeCoord : polygon )
|
||||
{
|
||||
polygonBBox.add( nodeCoord );
|
||||
}
|
||||
|
||||
mainGrid->findIntersectingCells( polygonBBox, &cellIndices );
|
||||
|
||||
std::vector<size_t> cellIndicesToLeafCells;
|
||||
for ( const size_t& index : cellIndices )
|
||||
{
|
||||
const RigCell& cell = mainGrid->globalCellArray()[index];
|
||||
if ( !cell.subGrid() )
|
||||
{
|
||||
cellIndicesToLeafCells.push_back( index );
|
||||
}
|
||||
}
|
||||
|
||||
return cellIndicesToLeafCells;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::ref<RigResultAccessor>
|
||||
RigEclipseToStimPlanCellTransmissibilityCalculator::createResultAccessor( const RimEclipseCase* eclipseCase,
|
||||
const QString& uiResultName )
|
||||
{
|
||||
RiaDefines::PorosityModelType porosityModel = RiaDefines::PorosityModelType::MATRIX_MODEL;
|
||||
const RigEclipseCaseData* eclipseCaseData = eclipseCase->eclipseCaseData();
|
||||
|
||||
// Create result accessor object for main grid at time step zero (static result date is always at first time step
|
||||
return RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
||||
0,
|
||||
porosityModel,
|
||||
0,
|
||||
RigEclipseResultAddress( uiResultName ) );
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RiaPorosityModel.h"
|
||||
|
||||
#include "cvfMatrix4.h"
|
||||
#include "cvfObject.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class QString;
|
||||
|
||||
class RimEclipseCase;
|
||||
class RigFractureCell;
|
||||
class RigResultAccessor;
|
||||
class RimFracture;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
/// Calculator used to compute the intersection areas between one RigFractureCell and Eclipse cells
|
||||
/// Both active and inactive Eclipse cells are included. The transmissibility value for inactive cells are set to zero.
|
||||
/// Eclipse reservoir cells open for flow is defined by reservoirCellIndicesOpenForFlow
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigEclipseToStimPlanCellTransmissibilityCalculator
|
||||
{
|
||||
public:
|
||||
explicit RigEclipseToStimPlanCellTransmissibilityCalculator( const RimEclipseCase* caseToApply,
|
||||
cvf::Mat4d fractureTransform,
|
||||
double skinFactor,
|
||||
double cDarcy,
|
||||
const RigFractureCell& stimPlanCell,
|
||||
const std::set<size_t>& reservoirCellIndicesOpenForFlow,
|
||||
const RimFracture* fracture );
|
||||
|
||||
// These three vectors have the same size
|
||||
const std::vector<size_t>& globalIndiciesToContributingEclipseCells() const;
|
||||
const std::vector<double>& contributingEclipseCellTransmissibilities() const;
|
||||
const std::vector<double>& contributingEclipseCellIntersectionAreas() const;
|
||||
const std::vector<double>& contributingEclipseCellPermeabilities() const;
|
||||
|
||||
double areaOpenForFlow() const;
|
||||
|
||||
const RigFractureCell& fractureCell() const;
|
||||
|
||||
static std::vector<QString> requiredResultNames();
|
||||
static std::vector<QString> optionalResultNames();
|
||||
|
||||
private:
|
||||
void calculateStimPlanCellsMatrixTransmissibility( const std::set<size_t>& reservoirCellIndicesOpenForFlow );
|
||||
std::vector<size_t> getPotentiallyFracturedCellsForPolygon( const std::vector<cvf::Vec3d>& polygon ) const;
|
||||
|
||||
static cvf::ref<RigResultAccessor> createResultAccessor( const RimEclipseCase* eclipseCase,
|
||||
const QString& uiResultName );
|
||||
|
||||
private:
|
||||
const RimEclipseCase* m_case;
|
||||
const RimFracture* m_fracture;
|
||||
|
||||
double m_cDarcy;
|
||||
double m_fractureSkinFactor;
|
||||
cvf::Mat4d m_fractureTransform;
|
||||
const RigFractureCell& m_stimPlanCell;
|
||||
|
||||
// These three vectors have the same size
|
||||
std::vector<size_t> m_globalIndiciesToContributingEclipseCells;
|
||||
std::vector<double> m_contributingEclipseCellTransmissibilities;
|
||||
std::vector<double> m_contributingEclipseCellAreas;
|
||||
std::vector<double> m_contributingEclipseCellPermeabilities;
|
||||
};
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017- Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigFractureTransmissibilityEquations.h"
|
||||
|
||||
#include "cvfMath.h"
|
||||
#include "cvfVector2.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
const double RigFractureTransmissibilityEquations::EPSILON = 1.0e-9;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigFractureTransmissibilityEquations::centerToCenterFractureCellTrans( double conductivityCell1,
|
||||
double sideLengthParallellTransCell1,
|
||||
double sideLengthNormalTransCell1,
|
||||
double conductivityCell2,
|
||||
double sideLengthParallellTransCell2,
|
||||
double sideLengthNormalTransCell2,
|
||||
double cDarcyForRelevantUnit )
|
||||
{
|
||||
double transCell1 = centerToEdgeFractureCellTrans( conductivityCell1,
|
||||
sideLengthParallellTransCell1,
|
||||
sideLengthNormalTransCell1,
|
||||
cDarcyForRelevantUnit );
|
||||
double transCell2 = centerToEdgeFractureCellTrans( conductivityCell2,
|
||||
sideLengthParallellTransCell2,
|
||||
sideLengthNormalTransCell2,
|
||||
cDarcyForRelevantUnit );
|
||||
|
||||
double totalTrans = 1 / ( ( 1 / transCell1 ) + ( 1 / transCell2 ) );
|
||||
|
||||
return totalTrans;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigFractureTransmissibilityEquations::fractureCellToWellRadialTrans( double fractureCellConductivity,
|
||||
double fractureCellSizeX,
|
||||
double fractureCellSizeZ,
|
||||
double wellRadius,
|
||||
double skinFactor,
|
||||
double cDarcyForRelevantUnit )
|
||||
{
|
||||
double ro = 0.14 * cvf::Math::sqrt( pow( fractureCellSizeX, 2.0 ) + pow( fractureCellSizeZ, 2 ) );
|
||||
|
||||
if ( ro < ( wellRadius * 1.01 ) )
|
||||
{
|
||||
ro = wellRadius * 1.01;
|
||||
}
|
||||
|
||||
double Tc = 2 * cvf::PI_D * cDarcyForRelevantUnit * fractureCellConductivity / ( log( ro / wellRadius ) + skinFactor );
|
||||
|
||||
CVF_TIGHT_ASSERT( Tc > 0 );
|
||||
return Tc;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigFractureTransmissibilityEquations::fractureCellToWellLinearTrans( double fractureConductivity,
|
||||
double fractureCellSizeX,
|
||||
double fractureCellSizeZ,
|
||||
double perforationLengthVertical,
|
||||
double perforationLengthHorizontal,
|
||||
double perforationEfficiency,
|
||||
double skinfactor,
|
||||
double cDarcyForRelevantUnit,
|
||||
double wellRadius )
|
||||
{
|
||||
const double invalidTrans = 1.0e9;
|
||||
const double epsilon = 1.0e-8;
|
||||
|
||||
double TcPrefix = 8 * cDarcyForRelevantUnit * fractureConductivity;
|
||||
|
||||
cvf::Vec2d wellOrientation = cvf::Vec2d( perforationLengthHorizontal, perforationLengthVertical ).getNormalized();
|
||||
cvf::Vec2d wellRadialVector = wellOrientation.perpendicularVector() * wellRadius;
|
||||
|
||||
double DzPerf = perforationLengthVertical * perforationEfficiency;
|
||||
double DxPerf = perforationLengthHorizontal * perforationEfficiency;
|
||||
|
||||
double TcZ = 0.0;
|
||||
if ( DzPerf > epsilon )
|
||||
{
|
||||
double effectiveFlowLengthHorizontal = fractureCellSizeX - 4.0 * std::abs( wellRadialVector.x() );
|
||||
double denominatorZ = effectiveFlowLengthHorizontal + skinfactor * DzPerf / cvf::PI_D;
|
||||
if ( denominatorZ < epsilon )
|
||||
{
|
||||
return invalidTrans;
|
||||
}
|
||||
TcZ = TcPrefix * DzPerf / denominatorZ;
|
||||
}
|
||||
|
||||
double TcX = 0.0;
|
||||
if ( DxPerf > epsilon )
|
||||
{
|
||||
double effectiveFlowLengthVertical = fractureCellSizeZ - 4.0 * std::abs( wellRadialVector.y() );
|
||||
double denominatorX = effectiveFlowLengthVertical + skinfactor * DxPerf / cvf::PI_D;
|
||||
if ( denominatorX < epsilon )
|
||||
{
|
||||
return invalidTrans;
|
||||
}
|
||||
TcX = TcPrefix * DxPerf / denominatorX;
|
||||
}
|
||||
|
||||
double Tc = cvf::Math::sqrt( pow( TcX, 2 ) + pow( TcZ, 2 ) );
|
||||
return Tc;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigFractureTransmissibilityEquations::matrixToFractureTrans( double perm,
|
||||
double NTG,
|
||||
double A,
|
||||
double cellSizeLength,
|
||||
double skinfactor,
|
||||
double fractureAreaWeightedlength,
|
||||
double cDarcy )
|
||||
{
|
||||
double transmissibility;
|
||||
|
||||
double slDivPi = 0.0;
|
||||
if ( cvf::Math::abs( skinfactor ) > EPSILON )
|
||||
{
|
||||
slDivPi = ( skinfactor * fractureAreaWeightedlength ) / cvf::PI_D;
|
||||
}
|
||||
|
||||
transmissibility = 8 * cDarcy * ( perm * NTG ) * A / ( cellSizeLength + slDivPi );
|
||||
|
||||
CVF_ASSERT( transmissibility == transmissibility );
|
||||
return transmissibility;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigFractureTransmissibilityEquations::effectiveInternalFractureToWellTransPDDHC( double sumScaledMatrixToFractureTrans,
|
||||
double scaledMatrixToWellTrans )
|
||||
{
|
||||
double divisor = sumScaledMatrixToFractureTrans - scaledMatrixToWellTrans;
|
||||
if ( cvf::Math::abs( divisor ) > EPSILON )
|
||||
{
|
||||
return ( sumScaledMatrixToFractureTrans * scaledMatrixToWellTrans ) / divisor;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigFractureTransmissibilityEquations::effectiveMatrixToWellTransPDDHC( double sumOriginalMatrixToFractureTrans,
|
||||
double effectiveInternalFractureToWellTrans )
|
||||
{
|
||||
double divisor = sumOriginalMatrixToFractureTrans + effectiveInternalFractureToWellTrans;
|
||||
if ( cvf::Math::abs( divisor ) > EPSILON )
|
||||
{
|
||||
return ( sumOriginalMatrixToFractureTrans * effectiveInternalFractureToWellTrans ) / divisor;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigFractureTransmissibilityEquations::matrixPermeability( double permx, double permy, double NTG )
|
||||
{
|
||||
double permxy = cvf::Math::sqrt( permx * permy );
|
||||
|
||||
return permxy * NTG;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigFractureTransmissibilityEquations::centerToEdgeFractureCellTrans( double conductivity,
|
||||
double sideLengthParallellTrans,
|
||||
double sideLengthNormalTrans,
|
||||
double cDarcyForRelevantUnit )
|
||||
{
|
||||
double transmissibility =
|
||||
cDarcyForRelevantUnit * conductivity * sideLengthNormalTrans / ( sideLengthParallellTrans / 2 );
|
||||
return transmissibility;
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017- Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class RigFractureTransmissibilityEquations
|
||||
{
|
||||
public:
|
||||
static double centerToCenterFractureCellTrans( double conductivityCell1,
|
||||
double sideLengthParallellTransCell1,
|
||||
double sideLengthNormalTransCell1,
|
||||
double conductivityCell2,
|
||||
double sideLengthParallellTransCell2,
|
||||
double sideLengthNormalTransCell2,
|
||||
double cDarcyForRelevantUnit );
|
||||
|
||||
static double fractureCellToWellRadialTrans( double fractureCellConductivity,
|
||||
double fractureCellSizeX,
|
||||
double fractureCellSizeZ,
|
||||
double wellRadius,
|
||||
double skinFactor,
|
||||
double cDarcyForRelevantUnit );
|
||||
|
||||
static double fractureCellToWellLinearTrans( double fractureConductivity,
|
||||
double fractureCellSizeX,
|
||||
double fractureCellSizeZ,
|
||||
double perforationLengthVertical,
|
||||
double perforationLengthHorizontal,
|
||||
double perforationEfficiency,
|
||||
double skinfactor,
|
||||
double cDarcyForRelevantUnit,
|
||||
double wellRadius );
|
||||
|
||||
static double matrixToFractureTrans( double permX,
|
||||
double NTG,
|
||||
double Ay,
|
||||
double dx,
|
||||
double skinfactor,
|
||||
double fractureAreaWeightedlength,
|
||||
double cDarcy );
|
||||
|
||||
static double effectiveInternalFractureToWellTransPDDHC( double sumScaledMatrixToFractureTrans,
|
||||
double scaledMatrixToWellTrans );
|
||||
|
||||
static double effectiveMatrixToWellTransPDDHC( double sumOriginalMatrixToFractureTrans,
|
||||
double effectiveInternalFractureToWellTrans );
|
||||
|
||||
static double matrixPermeability( double permx, double permy, double NTG );
|
||||
|
||||
private:
|
||||
static double centerToEdgeFractureCellTrans( double conductivity,
|
||||
double sideLengthParallellTrans,
|
||||
double sideLengthNormalTrans,
|
||||
double cDarcyForRelevantUnit );
|
||||
|
||||
private:
|
||||
static const double EPSILON;
|
||||
};
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017- Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigPerforationTransmissibilityEquations.h"
|
||||
|
||||
#include "cvfMath.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
const double RigPerforationTransmissibilityEquations::EPSILON = 1.0e-9;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigPerforationTransmissibilityEquations::betaFactor( double intertialCoefficient,
|
||||
double effectivePermeability,
|
||||
double permeabilityScalingFactor,
|
||||
double porosity,
|
||||
double porosityScalingFactor )
|
||||
{
|
||||
const double scaledEffectivePermeability = std::pow( effectivePermeability, permeabilityScalingFactor );
|
||||
const double scaledPorosity = std::pow( porosity, porosityScalingFactor );
|
||||
|
||||
return intertialCoefficient * scaledEffectivePermeability * scaledPorosity;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigPerforationTransmissibilityEquations::dFactor( double unitConstant,
|
||||
double betaFactor,
|
||||
double effectivePermeability,
|
||||
double perforationLengthInCell,
|
||||
double wellRadius,
|
||||
double gasDenity,
|
||||
double gasViscosity )
|
||||
{
|
||||
// clang-format off
|
||||
//
|
||||
// Ke 1 gasDensity
|
||||
// D = alpha * beta * -- * -- * ------------
|
||||
// h rw gasViscosity
|
||||
//
|
||||
// clang-format on
|
||||
|
||||
const double keOverH = effectivePermeability / perforationLengthInCell;
|
||||
const double oneOverRw = 1.0 / wellRadius;
|
||||
const double gasDensityOverGasViscosity = gasDenity / gasViscosity;
|
||||
|
||||
const double D = unitConstant * betaFactor * keOverH * oneOverRw * gasDensityOverGasViscosity;
|
||||
|
||||
return D;
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017- Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class RigPerforationTransmissibilityEquations
|
||||
{
|
||||
public:
|
||||
static double betaFactor( double intertialCoefficient,
|
||||
double effectivePermeability,
|
||||
double permeabilityScalingFactor,
|
||||
double porosity,
|
||||
double porosityScalingFactor );
|
||||
|
||||
static double dFactor( double unitConstant,
|
||||
double betaFactor,
|
||||
double effectivePermeability,
|
||||
double perforationLengthInCell,
|
||||
double wellRadius,
|
||||
double gasDensity,
|
||||
double gasViscosity );
|
||||
|
||||
private:
|
||||
static const double EPSILON;
|
||||
};
|
||||
@@ -0,0 +1,516 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 - Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigTransmissibilityCondenser.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
#include "RiaWeightedMeanCalculator.h"
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigFractureTransmissibilityEquations.h"
|
||||
|
||||
#include "cvfAssert.h"
|
||||
|
||||
#include "cvfMath.h"
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/LU>
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include <QDebug>
|
||||
#include <fstream>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigTransmissibilityCondenser::RigTransmissibilityCondenser()
|
||||
: m_transmissibilityThreshold( 1.0e-9 )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigTransmissibilityCondenser::RigTransmissibilityCondenser( const RigTransmissibilityCondenser& copyFrom )
|
||||
: m_neighborTransmissibilities( copyFrom.m_neighborTransmissibilities )
|
||||
, m_condensedTransmissibilities( copyFrom.m_condensedTransmissibilities )
|
||||
, m_externalCellAddrSet( copyFrom.m_externalCellAddrSet )
|
||||
, m_TiiInv( copyFrom.m_TiiInv )
|
||||
, m_Tie( copyFrom.m_Tie )
|
||||
, m_transmissibilityThreshold( copyFrom.m_transmissibilityThreshold )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigTransmissibilityCondenser& RigTransmissibilityCondenser::operator=( const RigTransmissibilityCondenser& rhs )
|
||||
{
|
||||
m_neighborTransmissibilities = rhs.m_neighborTransmissibilities;
|
||||
m_condensedTransmissibilities = rhs.m_condensedTransmissibilities;
|
||||
m_externalCellAddrSet = rhs.m_externalCellAddrSet;
|
||||
m_TiiInv = rhs.m_TiiInv;
|
||||
m_Tie = rhs.m_Tie;
|
||||
m_transmissibilityThreshold = rhs.m_transmissibilityThreshold;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigTransmissibilityCondenser::setTransmissibilityThreshold( double threshold )
|
||||
{
|
||||
m_transmissibilityThreshold = threshold;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigTransmissibilityCondenser::transmissibilityThreshold() const
|
||||
{
|
||||
return m_transmissibilityThreshold;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigTransmissibilityCondenser::addNeighborTransmissibility( CellAddress cell1, CellAddress cell2, double transmissibility )
|
||||
{
|
||||
if ( transmissibility < m_transmissibilityThreshold ) return;
|
||||
|
||||
m_condensedTransmissibilities.clear();
|
||||
m_externalCellAddrSet.clear();
|
||||
if ( cell1 < cell2 )
|
||||
m_neighborTransmissibilities[cell1][cell2] += transmissibility;
|
||||
else
|
||||
m_neighborTransmissibilities[cell2][cell1] += transmissibility;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::set<RigTransmissibilityCondenser::CellAddress> RigTransmissibilityCondenser::externalCells()
|
||||
{
|
||||
if ( m_externalCellAddrSet.empty() )
|
||||
{
|
||||
calculateCondensedTransmissibilities();
|
||||
}
|
||||
|
||||
return m_externalCellAddrSet;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigTransmissibilityCondenser::condensedTransmissibility( CellAddress externalCell1, CellAddress externalCell2 )
|
||||
{
|
||||
CAF_ASSERT( !( externalCell1 == externalCell2 ) );
|
||||
|
||||
if ( m_condensedTransmissibilities.empty() )
|
||||
{
|
||||
calculateCondensedTransmissibilities();
|
||||
}
|
||||
|
||||
if ( externalCell2 < externalCell1 ) std::swap( externalCell1, externalCell2 );
|
||||
|
||||
const auto& adrToAdrTransMapPair = m_condensedTransmissibilities.find( externalCell1 );
|
||||
if ( adrToAdrTransMapPair != m_condensedTransmissibilities.end() )
|
||||
{
|
||||
const auto& adrTransPair = adrToAdrTransMapPair->second.find( externalCell2 );
|
||||
if ( adrTransPair != adrToAdrTransMapPair->second.end() )
|
||||
{
|
||||
return adrTransPair->second;
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::map<size_t, double>
|
||||
RigTransmissibilityCondenser::scaleMatrixToFracTransByMatrixWellDP( const RigActiveCellInfo* actCellInfo,
|
||||
double currentWellPressure,
|
||||
const std::vector<double>& currentMatrixPressures,
|
||||
double* minPressureDrop,
|
||||
double* maxPressureDrop )
|
||||
{
|
||||
std::map<size_t, double> originalLumpedMatrixToFractureTrans; // Sum(T_mf)
|
||||
|
||||
double epsilonDeltaPressure = 1.0e-6;
|
||||
|
||||
double minNonZeroDeltaPressure = std::numeric_limits<double>::infinity();
|
||||
double maxNonZeroDeltaPressure = -std::numeric_limits<double>::infinity();
|
||||
|
||||
for ( auto it = m_neighborTransmissibilities.begin(); it != m_neighborTransmissibilities.end(); ++it )
|
||||
{
|
||||
if ( it->first.m_cellIndexSpace == CellAddress::STIMPLAN )
|
||||
{
|
||||
for ( auto jt = it->second.begin(); jt != it->second.end(); ++jt )
|
||||
{
|
||||
if ( jt->first.m_cellIndexSpace == CellAddress::ECLIPSE )
|
||||
{
|
||||
size_t globalMatrixCellIdx = jt->first.m_globalCellIdx;
|
||||
size_t eclipseResultIndex = actCellInfo->cellResultIndex( globalMatrixCellIdx );
|
||||
CVF_ASSERT( eclipseResultIndex < currentMatrixPressures.size() );
|
||||
double unsignedDeltaPressure =
|
||||
std::abs( currentMatrixPressures[eclipseResultIndex] - currentWellPressure );
|
||||
double nonZeroDeltaPressure = std::max( epsilonDeltaPressure, unsignedDeltaPressure );
|
||||
maxNonZeroDeltaPressure = std::max( maxNonZeroDeltaPressure, nonZeroDeltaPressure );
|
||||
minNonZeroDeltaPressure = std::min( minNonZeroDeltaPressure, nonZeroDeltaPressure );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( auto it = m_neighborTransmissibilities.begin(); it != m_neighborTransmissibilities.end(); ++it )
|
||||
{
|
||||
if ( it->first.m_cellIndexSpace == CellAddress::STIMPLAN )
|
||||
{
|
||||
for ( auto jt = it->second.begin(); jt != it->second.end(); ++jt )
|
||||
{
|
||||
if ( jt->first.m_cellIndexSpace == CellAddress::ECLIPSE )
|
||||
{
|
||||
size_t globalMatrixCellIdx = jt->first.m_globalCellIdx;
|
||||
size_t eclipseResultIndex = actCellInfo->cellResultIndex( globalMatrixCellIdx );
|
||||
CVF_ASSERT( eclipseResultIndex < currentMatrixPressures.size() );
|
||||
|
||||
originalLumpedMatrixToFractureTrans[globalMatrixCellIdx] += jt->second;
|
||||
|
||||
double unsignedDeltaPressure =
|
||||
std::abs( currentMatrixPressures[eclipseResultIndex] - currentWellPressure );
|
||||
double nonZeroDeltaPressure = std::max( epsilonDeltaPressure, unsignedDeltaPressure );
|
||||
|
||||
jt->second *= nonZeroDeltaPressure / maxNonZeroDeltaPressure;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( minPressureDrop && minNonZeroDeltaPressure != std::numeric_limits<double>::infinity() )
|
||||
{
|
||||
*minPressureDrop = minNonZeroDeltaPressure;
|
||||
}
|
||||
if ( maxPressureDrop && maxNonZeroDeltaPressure != std::numeric_limits<double>::infinity() )
|
||||
{
|
||||
*maxPressureDrop = maxNonZeroDeltaPressure;
|
||||
}
|
||||
|
||||
return originalLumpedMatrixToFractureTrans;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::map<size_t, double> RigTransmissibilityCondenser::calculateFicticiousFractureToWellTransmissibilities()
|
||||
{
|
||||
std::map<size_t, double> matrixToAllFracturesTrans;
|
||||
for ( auto it = m_neighborTransmissibilities.begin(); it != m_neighborTransmissibilities.end(); ++it )
|
||||
{
|
||||
if ( it->first.m_cellIndexSpace == CellAddress::STIMPLAN )
|
||||
{
|
||||
for ( auto jt = it->second.begin(); jt != it->second.end(); ++jt )
|
||||
{
|
||||
if ( jt->first.m_cellIndexSpace == CellAddress::ECLIPSE )
|
||||
{
|
||||
size_t globalMatrixCellIdx = jt->first.m_globalCellIdx;
|
||||
// T'_mf
|
||||
double matrixToFractureTrans = jt->second;
|
||||
// Sum(T'_mf)
|
||||
matrixToAllFracturesTrans[globalMatrixCellIdx] += matrixToFractureTrans;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::map<size_t, double> fictitiousFractureToWellTrans; // T'_fjw
|
||||
for ( const CellAddress& externalCell : m_externalCellAddrSet )
|
||||
{
|
||||
if ( externalCell.m_cellIndexSpace == CellAddress::ECLIPSE )
|
||||
{
|
||||
size_t globalMatrixCellIdx = externalCell.m_globalCellIdx;
|
||||
// Sum(T'_mf)
|
||||
double scaledMatrixToFractureTrans = matrixToAllFracturesTrans[globalMatrixCellIdx];
|
||||
// T'mw
|
||||
double scaledMatrixToWellTrans =
|
||||
condensedTransmissibility( externalCell, { true, RigTransmissibilityCondenser::CellAddress::WELL, 1 } );
|
||||
// T'_fjw
|
||||
fictitiousFractureToWellTrans[globalMatrixCellIdx] =
|
||||
RigFractureTransmissibilityEquations::effectiveInternalFractureToWellTransPDDHC( scaledMatrixToFractureTrans,
|
||||
scaledMatrixToWellTrans );
|
||||
}
|
||||
}
|
||||
return fictitiousFractureToWellTrans;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::map<size_t, double> RigTransmissibilityCondenser::calculateEffectiveMatrixToWellTransmissibilities(
|
||||
const std::map<size_t, double>& originalLumpedMatrixToFractureTrans,
|
||||
const std::map<size_t, double>& ficticuousFractureToWellTransMap )
|
||||
{
|
||||
std::map<size_t, double> effectiveMatrixToWellTrans;
|
||||
for ( const CellAddress& externalCell : m_externalCellAddrSet )
|
||||
{
|
||||
if ( externalCell.m_cellIndexSpace == CellAddress::ECLIPSE )
|
||||
{
|
||||
size_t globalMatrixCellIdx = externalCell.m_globalCellIdx;
|
||||
|
||||
auto matrixToFractureIt = originalLumpedMatrixToFractureTrans.find( globalMatrixCellIdx );
|
||||
CVF_ASSERT( matrixToFractureIt != originalLumpedMatrixToFractureTrans.end() );
|
||||
// Sum(T_mf)
|
||||
double lumpedOriginalMatrixToFractureT = matrixToFractureIt->second;
|
||||
// T'_fjw
|
||||
auto fictitiousFractureToWellIt = ficticuousFractureToWellTransMap.find( globalMatrixCellIdx );
|
||||
CVF_ASSERT( fictitiousFractureToWellIt != ficticuousFractureToWellTransMap.end() );
|
||||
double fictitiousFractureToWellTrans = fictitiousFractureToWellIt->second;
|
||||
|
||||
// T^dp_mw
|
||||
double transmissibilityValue =
|
||||
RigFractureTransmissibilityEquations::effectiveMatrixToWellTransPDDHC( lumpedOriginalMatrixToFractureT,
|
||||
fictitiousFractureToWellTrans );
|
||||
if ( transmissibilityValue > m_transmissibilityThreshold )
|
||||
{
|
||||
effectiveMatrixToWellTrans[globalMatrixCellIdx] = transmissibilityValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return effectiveMatrixToWellTrans;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigTransmissibilityCondenser::calculateCondensedTransmissibilities()
|
||||
{
|
||||
if ( m_neighborTransmissibilities.empty() ) return;
|
||||
|
||||
// Find all equations, and their total ordering
|
||||
|
||||
union
|
||||
{
|
||||
int idxToFirstExternalEquation;
|
||||
int internalEquationCount;
|
||||
};
|
||||
idxToFirstExternalEquation = -1;
|
||||
int totalEquationCount = -1;
|
||||
|
||||
std::map<CellAddress, int> cellAddressToEqIdxMap;
|
||||
std::vector<CellAddress> eqIdxToCellAddressMapping;
|
||||
{
|
||||
for ( const auto& adrEqIdxPair : m_neighborTransmissibilities )
|
||||
{
|
||||
cellAddressToEqIdxMap.insert( { adrEqIdxPair.first, -1 } );
|
||||
for ( const auto& adrTranspair : adrEqIdxPair.second )
|
||||
{
|
||||
cellAddressToEqIdxMap.insert( { adrTranspair.first, -1 } );
|
||||
}
|
||||
}
|
||||
|
||||
int currentEqIdx = 0;
|
||||
|
||||
for ( auto& adrEqIdxPair : cellAddressToEqIdxMap )
|
||||
{
|
||||
adrEqIdxPair.second = currentEqIdx;
|
||||
eqIdxToCellAddressMapping.push_back( adrEqIdxPair.first );
|
||||
|
||||
if ( idxToFirstExternalEquation == -1 && adrEqIdxPair.first.m_isExternal )
|
||||
{
|
||||
idxToFirstExternalEquation = currentEqIdx;
|
||||
}
|
||||
++currentEqIdx;
|
||||
}
|
||||
totalEquationCount = currentEqIdx;
|
||||
}
|
||||
|
||||
CAF_ASSERT( idxToFirstExternalEquation != -1 );
|
||||
|
||||
using namespace Eigen;
|
||||
|
||||
MatrixXd totalSystem = MatrixXd::Zero( totalEquationCount, totalEquationCount );
|
||||
|
||||
for ( const auto& adrToAdrTransMapPair : m_neighborTransmissibilities )
|
||||
{
|
||||
CAF_ASSERT( cellAddressToEqIdxMap.count( adrToAdrTransMapPair.first ) ); // Remove when stabilized
|
||||
int c1EquationIdx = cellAddressToEqIdxMap[adrToAdrTransMapPair.first];
|
||||
for ( const auto& adrTranspair : adrToAdrTransMapPair.second )
|
||||
{
|
||||
CAF_ASSERT( cellAddressToEqIdxMap.count( adrTranspair.first ) ); // Remove when stabilized
|
||||
|
||||
int c2EquationIdx = cellAddressToEqIdxMap[adrTranspair.first];
|
||||
|
||||
totalSystem( c1EquationIdx, c2EquationIdx ) += adrTranspair.second;
|
||||
totalSystem( c2EquationIdx, c1EquationIdx ) += adrTranspair.second;
|
||||
totalSystem( c1EquationIdx, c1EquationIdx ) -= adrTranspair.second;
|
||||
totalSystem( c2EquationIdx, c2EquationIdx ) -= adrTranspair.second;
|
||||
}
|
||||
|
||||
++c1EquationIdx;
|
||||
}
|
||||
|
||||
// std::cout << "T = " << std::endl << totalSystem << std::endl;
|
||||
|
||||
int externalEquationCount = totalEquationCount - internalEquationCount;
|
||||
|
||||
MatrixXd condensedSystem;
|
||||
MatrixXd Tee = totalSystem.bottomRightCorner( externalEquationCount, externalEquationCount );
|
||||
|
||||
if ( internalEquationCount == 0 )
|
||||
{
|
||||
condensedSystem = Tee;
|
||||
}
|
||||
else
|
||||
{
|
||||
MatrixXd Tei = totalSystem.bottomLeftCorner( externalEquationCount, internalEquationCount );
|
||||
MatrixXd Tii = totalSystem.topLeftCorner( internalEquationCount, internalEquationCount );
|
||||
|
||||
// std::ofstream outFileStream( "D:\\Data\\TestData\\TiiMatrix.txt" );
|
||||
// outFileStream << Tii;
|
||||
|
||||
Eigen::FullPivLU<MatrixXd> solver( Tii );
|
||||
|
||||
// outFileStream << std::endl;
|
||||
// outFileStream << "Rows x Cols: " << Tii.rows() << "x" << Tii.cols() << std::endl;
|
||||
// outFileStream << "Invertible: " << ( solver.isInvertible() ? "True" : "False" ) << std::endl;
|
||||
// outFileStream << "Condition: " << solver.rcond() << std::endl;
|
||||
// outFileStream << "Rank: " << solver.rank() << std::endl;
|
||||
|
||||
m_TiiInv = solver.inverse();
|
||||
m_Tie = totalSystem.topRightCorner( internalEquationCount, externalEquationCount );
|
||||
condensedSystem = Tee - Tei * m_TiiInv * m_Tie;
|
||||
}
|
||||
|
||||
// std::cout << "Te = " << std::endl << condensedSystem << std::endl << std::endl;
|
||||
|
||||
for ( int exEqIdx = 0; exEqIdx < externalEquationCount; ++exEqIdx )
|
||||
{
|
||||
for ( int exColIdx = exEqIdx + 1; exColIdx < externalEquationCount; ++exColIdx )
|
||||
{
|
||||
double T = condensedSystem( exEqIdx, exColIdx );
|
||||
// if (T != 0.0)
|
||||
{
|
||||
CellAddress cell1 = eqIdxToCellAddressMapping[exEqIdx + internalEquationCount];
|
||||
CellAddress cell2 = eqIdxToCellAddressMapping[exColIdx + internalEquationCount];
|
||||
if ( cell1 < cell2 )
|
||||
m_condensedTransmissibilities[cell1][cell2] = T;
|
||||
else
|
||||
m_condensedTransmissibilities[cell2][cell1] = T;
|
||||
|
||||
m_externalCellAddrSet.insert( cell1 );
|
||||
m_externalCellAddrSet.insert( cell2 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "RigFractureCell.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RimStimPlanFractureTemplate.h"
|
||||
|
||||
void printCellAddress( std::stringstream& str,
|
||||
const RigMainGrid* mainGrid,
|
||||
const RigFractureGrid* fractureGrid,
|
||||
RigTransmissibilityCondenser::CellAddress cellAddr )
|
||||
{
|
||||
using CellAddress = RigTransmissibilityCondenser::CellAddress;
|
||||
|
||||
str << ( cellAddr.m_isExternal ? "E " : "I " );
|
||||
|
||||
switch ( cellAddr.m_cellIndexSpace )
|
||||
{
|
||||
case CellAddress::ECLIPSE:
|
||||
{
|
||||
if ( cellAddr.m_globalCellIdx > mainGrid->cellCount() )
|
||||
{
|
||||
str << "ECL - LGR CELL ";
|
||||
}
|
||||
else
|
||||
{
|
||||
str << "ECL ";
|
||||
size_t i, j, k;
|
||||
mainGrid->ijkFromCellIndex( cellAddr.m_globalCellIdx, &i, &j, &k );
|
||||
str << std::setw( 5 ) << i + 1 << std::setw( 5 ) << j + 1 << std::setw( 5 ) << k + 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CellAddress::STIMPLAN:
|
||||
{
|
||||
str << "STP ";
|
||||
const RigFractureCell& stpCell = fractureGrid->cellFromIndex( cellAddr.m_globalCellIdx );
|
||||
str << std::setw( 5 ) << stpCell.getI() + 1 << std::setw( 5 ) << stpCell.getJ() + 1 << std::setw( 5 ) << " ";
|
||||
}
|
||||
break;
|
||||
|
||||
case CellAddress::WELL:
|
||||
{
|
||||
str << "WEL ";
|
||||
str << std::setw( 5 ) << cellAddr.m_globalCellIdx << std::setw( 5 ) << " " << std::setw( 5 ) << " ";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
str << " ";
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::string RigTransmissibilityCondenser::neighborTransDebugOutput( const RigMainGrid* mainGrid,
|
||||
const RigFractureGrid* fractureGrid )
|
||||
{
|
||||
std::stringstream debugText;
|
||||
for ( const auto& adrEqIdxPair : m_neighborTransmissibilities )
|
||||
{
|
||||
for ( const auto& adrTransPair : adrEqIdxPair.second )
|
||||
{
|
||||
debugText << "-- ";
|
||||
printCellAddress( debugText, mainGrid, fractureGrid, adrEqIdxPair.first );
|
||||
printCellAddress( debugText, mainGrid, fractureGrid, adrTransPair.first );
|
||||
|
||||
debugText << " Trans: " << std::setprecision( 10 ) << std::fixed << adrTransPair.second;
|
||||
debugText << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return debugText.str();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::string RigTransmissibilityCondenser::condensedTransDebugOutput( const RigMainGrid* mainGrid,
|
||||
const RigFractureGrid* fractureGrid )
|
||||
{
|
||||
std::stringstream debugText;
|
||||
for ( const auto& adrEqIdxPair : m_condensedTransmissibilities )
|
||||
{
|
||||
for ( const auto& adrTransPair : adrEqIdxPair.second )
|
||||
{
|
||||
debugText << "-- ";
|
||||
printCellAddress( debugText, mainGrid, fractureGrid, adrEqIdxPair.first );
|
||||
printCellAddress( debugText, mainGrid, fractureGrid, adrTransPair.first );
|
||||
|
||||
debugText << " Trans: " << std::setprecision( 10 ) << std::fixed << adrTransPair.second;
|
||||
debugText << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return debugText.str();
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 - Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cafAssert.h"
|
||||
|
||||
#include <Eigen/Core>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
class RigActiveCellInfo;
|
||||
class RigMainGrid;
|
||||
class RimStimPlanFractureTemplate;
|
||||
class RigFractureGrid;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class RigTransmissibilityCondenser
|
||||
{
|
||||
public:
|
||||
RigTransmissibilityCondenser();
|
||||
RigTransmissibilityCondenser( const RigTransmissibilityCondenser& copyFrom );
|
||||
RigTransmissibilityCondenser& operator=( const RigTransmissibilityCondenser& rhs );
|
||||
|
||||
class CellAddress
|
||||
{
|
||||
public:
|
||||
enum CellIndexSpace
|
||||
{
|
||||
ECLIPSE,
|
||||
STIMPLAN,
|
||||
WELL
|
||||
};
|
||||
|
||||
CellAddress()
|
||||
: m_isExternal( false )
|
||||
, m_cellIndexSpace( STIMPLAN )
|
||||
, m_globalCellIdx( -1 )
|
||||
{
|
||||
}
|
||||
CellAddress( bool isExternal, CellIndexSpace cellType, size_t globalCellIdx )
|
||||
: m_isExternal( isExternal )
|
||||
, m_cellIndexSpace( cellType )
|
||||
, m_globalCellIdx( globalCellIdx )
|
||||
{
|
||||
}
|
||||
|
||||
bool m_isExternal;
|
||||
CellIndexSpace m_cellIndexSpace;
|
||||
size_t m_globalCellIdx;
|
||||
|
||||
bool operator==( const CellAddress& o )
|
||||
{
|
||||
return ( m_isExternal == o.m_isExternal ) && ( m_cellIndexSpace == o.m_cellIndexSpace ) &&
|
||||
( m_globalCellIdx == o.m_globalCellIdx );
|
||||
}
|
||||
|
||||
// Ordering external after internal is important for the matrix order internally
|
||||
|
||||
bool operator<( const CellAddress& other ) const
|
||||
{
|
||||
if ( m_isExternal != other.m_isExternal ) return !m_isExternal; // Internal cells < External cells
|
||||
if ( m_cellIndexSpace != other.m_cellIndexSpace )
|
||||
return m_cellIndexSpace < other.m_cellIndexSpace; // Eclipse < StimPlan
|
||||
if ( m_globalCellIdx != other.m_globalCellIdx ) return m_globalCellIdx < other.m_globalCellIdx;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void setTransmissibilityThreshold( double threshold );
|
||||
double transmissibilityThreshold() const;
|
||||
|
||||
void addNeighborTransmissibility( CellAddress cell1, CellAddress cell2, double transmissibility );
|
||||
|
||||
std::set<CellAddress> externalCells();
|
||||
|
||||
double condensedTransmissibility( CellAddress externalCell1, CellAddress externalCell2 );
|
||||
|
||||
std::string neighborTransDebugOutput( const RigMainGrid* mainGrid, const RigFractureGrid* fractureGrid );
|
||||
std::string condensedTransDebugOutput( const RigMainGrid* mainGrid, const RigFractureGrid* fractureGrid );
|
||||
|
||||
std::map<size_t, double> scaleMatrixToFracTransByMatrixWellDP( const RigActiveCellInfo* actCellInfo,
|
||||
double currentWellPressure,
|
||||
const std::vector<double>& currentMatrixPressures,
|
||||
double* minPressureDrop,
|
||||
double* maxPressureDrop );
|
||||
|
||||
std::map<size_t, double> calculateFicticiousFractureToWellTransmissibilities();
|
||||
std::map<size_t, double> calculateEffectiveMatrixToWellTransmissibilities(
|
||||
const std::map<size_t, double>& originalLumpedMatrixToFractureTrans,
|
||||
const std::map<size_t, double>& ficticuousFractureToWellTransMap );
|
||||
|
||||
void calculateCondensedTransmissibilities();
|
||||
|
||||
protected:
|
||||
typedef std::pair<CellAddress, std::map<CellAddress, double>> ConnectionTransmissibility;
|
||||
typedef std::map<CellAddress, std::map<CellAddress, double>> ConnectionTransmissibilities;
|
||||
|
||||
ConnectionTransmissibilities m_neighborTransmissibilities;
|
||||
ConnectionTransmissibilities m_condensedTransmissibilities;
|
||||
|
||||
std::set<CellAddress> m_externalCellAddrSet;
|
||||
|
||||
Eigen::MatrixXd m_TiiInv;
|
||||
Eigen::MatrixXd m_Tie;
|
||||
|
||||
private:
|
||||
double m_transmissibilityThreshold;
|
||||
};
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigVirtualPerforationTransmissibilities.h"
|
||||
|
||||
#include "RigStatisticsMath.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
CompletionDataFrame::CompletionDataFrame()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void CompletionDataFrame::setCompletionData( const std::vector<RigCompletionData>& completions )
|
||||
{
|
||||
for ( auto& completion : completions )
|
||||
{
|
||||
auto it = m_multipleCompletionsPerEclipseCell.find( completion.completionDataGridCell().globalCellIndex() );
|
||||
if ( it != m_multipleCompletionsPerEclipseCell.end() )
|
||||
{
|
||||
it->second.push_back( completion );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_multipleCompletionsPerEclipseCell.insert(
|
||||
std::pair<size_t, std::vector<RigCompletionData>>( completion.completionDataGridCell().globalCellIndex(),
|
||||
std::vector<RigCompletionData>{ completion } ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::map<size_t, std::vector<RigCompletionData>>& CompletionDataFrame::multipleCompletionsPerEclipseCell() const
|
||||
{
|
||||
return m_multipleCompletionsPerEclipseCell;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigVirtualPerforationTransmissibilities::RigVirtualPerforationTransmissibilities()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigVirtualPerforationTransmissibilities::~RigVirtualPerforationTransmissibilities()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigVirtualPerforationTransmissibilities::setCompletionDataForWellPath(
|
||||
const RimWellPath* wellPath,
|
||||
const std::vector<std::vector<RigCompletionData>>& completionsPerTimeStep )
|
||||
{
|
||||
CVF_ASSERT( m_mapFromWellToCompletionData.find( wellPath ) == m_mapFromWellToCompletionData.end() );
|
||||
|
||||
{
|
||||
std::vector<CompletionDataFrame> values;
|
||||
|
||||
for ( const auto& c : completionsPerTimeStep )
|
||||
{
|
||||
CompletionDataFrame oneTimeStep;
|
||||
oneTimeStep.setCompletionData( c );
|
||||
values.push_back( oneTimeStep );
|
||||
}
|
||||
|
||||
auto pair = std::pair<const RimWellPath*, std::vector<CompletionDataFrame>>( wellPath, values );
|
||||
|
||||
m_mapFromWellToCompletionData.insert( pair );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::map<size_t, std::vector<RigCompletionData>>&
|
||||
RigVirtualPerforationTransmissibilities::multipleCompletionsPerEclipseCell( const RimWellPath* wellPath,
|
||||
size_t timeStepIndex ) const
|
||||
{
|
||||
static std::map<size_t, std::vector<RigCompletionData>> dummy;
|
||||
|
||||
auto item = m_mapFromWellToCompletionData.find( wellPath );
|
||||
if ( item != m_mapFromWellToCompletionData.end() )
|
||||
{
|
||||
size_t indexToUse = timeStepIndex;
|
||||
if ( item->second.size() == 1 )
|
||||
{
|
||||
indexToUse = 0;
|
||||
}
|
||||
|
||||
return item->second[indexToUse].multipleCompletionsPerEclipseCell();
|
||||
}
|
||||
|
||||
return dummy;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigVirtualPerforationTransmissibilities::setCompletionDataForSimWell(
|
||||
const RigSimWellData* simWellData,
|
||||
const std::vector<std::vector<RigCompletionData>>& completionsPerTimeStep )
|
||||
{
|
||||
m_mapFromSimWellToCompletionData[simWellData] = completionsPerTimeStep;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<RigCompletionData>&
|
||||
RigVirtualPerforationTransmissibilities::completionsForSimWell( const RigSimWellData* simWellData,
|
||||
size_t timeStepIndex ) const
|
||||
{
|
||||
static std::vector<RigCompletionData> dummayVector;
|
||||
|
||||
auto item = m_mapFromSimWellToCompletionData.find( simWellData );
|
||||
if ( item != m_mapFromSimWellToCompletionData.end() )
|
||||
{
|
||||
return item->second[timeStepIndex];
|
||||
}
|
||||
|
||||
return dummayVector;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigVirtualPerforationTransmissibilities::computeMinMax( double* minValue,
|
||||
double* maxValue,
|
||||
double* posClosestToZero,
|
||||
double* negClosestToZero ) const
|
||||
{
|
||||
MinMaxAccumulator minMaxAccumulator;
|
||||
PosNegAccumulator posNegAccumulator;
|
||||
|
||||
for ( const auto& item : m_mapFromWellToCompletionData )
|
||||
{
|
||||
auto dataForWellPath = item.second;
|
||||
|
||||
for ( const auto& timeStepFrame : dataForWellPath )
|
||||
{
|
||||
for ( const auto& allCompletionsForWell : timeStepFrame.multipleCompletionsPerEclipseCell() )
|
||||
{
|
||||
for ( const auto& completionData : allCompletionsForWell.second )
|
||||
{
|
||||
double transmissibility = completionData.transmissibility();
|
||||
|
||||
minMaxAccumulator.addValue( transmissibility );
|
||||
posNegAccumulator.addValue( transmissibility );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( const auto& item : m_mapFromSimWellToCompletionData )
|
||||
{
|
||||
auto dataForSimWell = item.second;
|
||||
|
||||
for ( const auto& timeStepFrame : dataForSimWell )
|
||||
{
|
||||
for ( const auto& completionData : timeStepFrame )
|
||||
{
|
||||
double transmissibility = completionData.transmissibility();
|
||||
|
||||
minMaxAccumulator.addValue( transmissibility );
|
||||
posNegAccumulator.addValue( transmissibility );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( *minValue ) *minValue = minMaxAccumulator.min;
|
||||
if ( *maxValue ) *maxValue = minMaxAccumulator.max;
|
||||
if ( *posClosestToZero ) *posClosestToZero = posNegAccumulator.pos;
|
||||
if ( *negClosestToZero ) *negClosestToZero = posNegAccumulator.neg;
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RigCompletionData.h"
|
||||
|
||||
#include "cvfObject.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
class RigCompletionData;
|
||||
class RigCompletionDataGridCell;
|
||||
class RigSimWellData;
|
||||
class RimWellPath;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class CompletionDataFrame
|
||||
{
|
||||
public:
|
||||
CompletionDataFrame();
|
||||
|
||||
void setCompletionData( const std::vector<RigCompletionData>& completions );
|
||||
|
||||
const std::map<size_t, std::vector<RigCompletionData>>& multipleCompletionsPerEclipseCell() const;
|
||||
|
||||
private:
|
||||
std::map<size_t, std::vector<RigCompletionData>> m_multipleCompletionsPerEclipseCell;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class RigVirtualPerforationTransmissibilities : public cvf::Object
|
||||
{
|
||||
public:
|
||||
RigVirtualPerforationTransmissibilities();
|
||||
~RigVirtualPerforationTransmissibilities() override;
|
||||
|
||||
void setCompletionDataForWellPath( const RimWellPath* wellPath,
|
||||
const std::vector<std::vector<RigCompletionData>>& completionsPerTimeStep );
|
||||
|
||||
const std::map<size_t, std::vector<RigCompletionData>>&
|
||||
multipleCompletionsPerEclipseCell( const RimWellPath* wellPath, size_t timeStepIndex ) const;
|
||||
|
||||
void setCompletionDataForSimWell( const RigSimWellData* simWellData,
|
||||
const std::vector<std::vector<RigCompletionData>>& completionsPerTimeStep );
|
||||
|
||||
const std::vector<RigCompletionData>& completionsForSimWell( const RigSimWellData* simWellData,
|
||||
size_t timeStepIndex ) const;
|
||||
|
||||
void computeMinMax( double* minValue, double* maxValue, double* posClosestToZero, double* negClosestToZero ) const;
|
||||
|
||||
private:
|
||||
std::map<const RimWellPath*, std::vector<CompletionDataFrame>> m_mapFromWellToCompletionData;
|
||||
std::map<const RigSimWellData*, std::vector<std::vector<RigCompletionData>>> m_mapFromSimWellToCompletionData;
|
||||
};
|
||||
@@ -0,0 +1,238 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017- Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigWellPathStimplanIntersector.h"
|
||||
|
||||
#include "RigCellGeometryTools.h"
|
||||
#include "RigFractureCell.h"
|
||||
#include "RigFractureGrid.h"
|
||||
#include "RigWellPath.h"
|
||||
|
||||
#include "RimFracture.h"
|
||||
#include "RimFractureTemplate.h"
|
||||
#include "RimSimWellFracture.h"
|
||||
#include "RimStimPlanFractureTemplate.h"
|
||||
|
||||
#include "cvfMath.h"
|
||||
#include "cvfMatrix4.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigWellPathStimplanIntersector::RigWellPathStimplanIntersector( gsl::not_null<const RigWellPath*> wellPathGeom,
|
||||
gsl::not_null<const RimFracture*> rimFracture )
|
||||
{
|
||||
std::vector<cvf::Vec3d> wellPathPoints =
|
||||
wellPathGeom->wellPathPointsIncludingInterpolatedIntersectionPoint( rimFracture->fractureMD() );
|
||||
cvf::Mat4d fractureXf = rimFracture->transformMatrix();
|
||||
double wellRadius = rimFracture->wellRadius();
|
||||
|
||||
std::vector<std::vector<cvf::Vec3d>> fractureGridCellPolygons;
|
||||
{
|
||||
RimFractureTemplate* fractureTemplate = rimFracture->fractureTemplate();
|
||||
|
||||
if ( fractureTemplate && fractureTemplate->fractureGrid() )
|
||||
{
|
||||
const std::vector<RigFractureCell>& stpCells = fractureTemplate->fractureGrid()->fractureCells();
|
||||
for ( const auto& stpCell : stpCells )
|
||||
{
|
||||
fractureGridCellPolygons.push_back( stpCell.getPolygon() );
|
||||
}
|
||||
}
|
||||
}
|
||||
double perforationLength = rimFracture->perforationLength();
|
||||
|
||||
calculate( fractureXf,
|
||||
wellPathPoints,
|
||||
wellRadius,
|
||||
perforationLength,
|
||||
fractureGridCellPolygons,
|
||||
m_stimPlanCellIdxToIntersectionInfoMap );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::map<size_t, RigWellPathStimplanIntersector::RigWellPathStimplanIntersector::WellCellIntersection>&
|
||||
RigWellPathStimplanIntersector::intersections() const
|
||||
{
|
||||
return m_stimPlanCellIdxToIntersectionInfoMap;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigWellPathStimplanIntersector::calculate( const cvf::Mat4d& fractureXf,
|
||||
const std::vector<cvf::Vec3d>& wellPathPointsDomainCoords,
|
||||
double wellRadius,
|
||||
double perforationLength,
|
||||
const std::vector<std::vector<cvf::Vec3d>>& fractureGridCellPolygons,
|
||||
std::map<size_t, WellCellIntersection>& m_stimPlanCellIdxToIntersectionInfoMap )
|
||||
{
|
||||
cvf::Mat4d toFractureXf = fractureXf.getInverted();
|
||||
|
||||
std::vector<cvf::Vec3d> perforationLengthBoundingBoxPolygon;
|
||||
{
|
||||
double cicleRadius = perforationLength / 2;
|
||||
int pointsInCirclePolygon = 20;
|
||||
|
||||
for ( int i = 0; i < pointsInCirclePolygon; i++ )
|
||||
{
|
||||
double x = cicleRadius * cvf::Math::cos( i * ( 2 * cvf::PI_D / pointsInCirclePolygon ) );
|
||||
double y = cicleRadius * cvf::Math::sin( i * ( 2 * cvf::PI_D / pointsInCirclePolygon ) );
|
||||
perforationLengthBoundingBoxPolygon.push_back( cvf::Vec3d( x, y, 0 ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Convert well path to fracture template system
|
||||
|
||||
std::vector<cvf::Vec3d> fractureRelativeWellPathPoints;
|
||||
for ( const auto& wellPPoint : wellPathPointsDomainCoords )
|
||||
{
|
||||
fractureRelativeWellPathPoints.push_back( wellPPoint.getTransformedPoint( toFractureXf ) );
|
||||
}
|
||||
|
||||
// Clip well path to fracture domain
|
||||
|
||||
std::vector<std::vector<cvf::Vec3d>> wellPathPartsWithinFracture =
|
||||
RigCellGeometryTools::clipPolylineByPolygon( fractureRelativeWellPathPoints,
|
||||
perforationLengthBoundingBoxPolygon,
|
||||
RigCellGeometryTools::INTERPOLATE_LINE_Z );
|
||||
|
||||
// Remove the part of the well path that is more than well radius away from the fracture plane
|
||||
|
||||
std::vector<std::vector<cvf::Vec3d>> intersectingWellPathParts;
|
||||
|
||||
for ( const auto& part : wellPathPartsWithinFracture )
|
||||
{
|
||||
std::vector<cvf::Vec3d> currentIntersectingWpPart;
|
||||
for ( size_t vxIdx = 0; vxIdx < part.size() - 1; ++vxIdx )
|
||||
{
|
||||
double thisAbsZ = fabs( part[vxIdx].z() );
|
||||
double nextAbsZ = fabs( part[vxIdx + 1].z() );
|
||||
double thisZ = part[vxIdx].z();
|
||||
double nextZ = part[vxIdx + 1].z();
|
||||
|
||||
if ( thisAbsZ >= wellRadius && nextAbsZ >= wellRadius )
|
||||
{
|
||||
if ( ( thisZ >= 0 && nextZ >= 0 ) || ( thisZ <= 0 && nextZ <= 0 ) )
|
||||
{
|
||||
continue; // Outside
|
||||
}
|
||||
else // In and out
|
||||
{
|
||||
{
|
||||
double wellRadiusDistFromPlane = thisZ > 0 ? wellRadius : -wellRadius;
|
||||
|
||||
double fraction = ( wellRadiusDistFromPlane - thisZ ) / ( nextZ - thisZ );
|
||||
|
||||
cvf::Vec3d intersectPoint = part[vxIdx] + fraction * ( part[vxIdx + 1] - part[vxIdx] );
|
||||
currentIntersectingWpPart.push_back( intersectPoint );
|
||||
}
|
||||
{
|
||||
double wellRadiusDistFromPlane = nextZ > 0 ? wellRadius : -wellRadius;
|
||||
|
||||
double fraction = ( wellRadiusDistFromPlane - thisZ ) / ( nextZ - thisZ );
|
||||
|
||||
cvf::Vec3d intersectPoint = part[vxIdx] + fraction * ( part[vxIdx + 1] - part[vxIdx] );
|
||||
currentIntersectingWpPart.push_back( intersectPoint );
|
||||
|
||||
intersectingWellPathParts.push_back( currentIntersectingWpPart );
|
||||
currentIntersectingWpPart.clear();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ( thisAbsZ < wellRadius && nextAbsZ < wellRadius ) // Inside
|
||||
{
|
||||
currentIntersectingWpPart.push_back( part[vxIdx] );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( thisAbsZ < wellRadius && nextAbsZ >= wellRadius ) // Going out
|
||||
{
|
||||
currentIntersectingWpPart.push_back( part[vxIdx] );
|
||||
|
||||
double wellRadiusDistFromPlane = nextZ > 0 ? wellRadius : -wellRadius;
|
||||
|
||||
double fraction = ( wellRadiusDistFromPlane - thisZ ) / ( nextZ - thisZ );
|
||||
|
||||
cvf::Vec3d intersectPoint = part[vxIdx] + fraction * ( part[vxIdx + 1] - part[vxIdx] );
|
||||
currentIntersectingWpPart.push_back( intersectPoint );
|
||||
|
||||
intersectingWellPathParts.push_back( currentIntersectingWpPart );
|
||||
currentIntersectingWpPart.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( thisAbsZ >= wellRadius && nextAbsZ < wellRadius ) // Going in
|
||||
{
|
||||
double wellRadiusDistFromPlane = thisZ > 0 ? wellRadius : -wellRadius;
|
||||
|
||||
double fraction = ( wellRadiusDistFromPlane - thisZ ) / ( nextZ - thisZ );
|
||||
|
||||
cvf::Vec3d intersectPoint = part[vxIdx] + fraction * ( part[vxIdx + 1] - part[vxIdx] );
|
||||
currentIntersectingWpPart.push_back( intersectPoint );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Add last point if it is within the radius
|
||||
|
||||
if ( part.size() > 1 && fabs( part.back().z() ) < wellRadius )
|
||||
{
|
||||
currentIntersectingWpPart.push_back( part.back() );
|
||||
}
|
||||
|
||||
if ( !currentIntersectingWpPart.empty() )
|
||||
{
|
||||
intersectingWellPathParts.push_back( currentIntersectingWpPart );
|
||||
}
|
||||
}
|
||||
|
||||
// Find the StimPlan cells touched by the intersecting well path parts
|
||||
|
||||
for ( size_t cIdx = 0; cIdx < fractureGridCellPolygons.size(); ++cIdx )
|
||||
{
|
||||
const std::vector<cvf::Vec3d>& cellPolygon = fractureGridCellPolygons[cIdx];
|
||||
for ( const auto& wellpathPart : intersectingWellPathParts )
|
||||
{
|
||||
std::vector<std::vector<cvf::Vec3d>> wellPathPartsInPolygon =
|
||||
RigCellGeometryTools::clipPolylineByPolygon( wellpathPart, cellPolygon, RigCellGeometryTools::USE_HUGEVAL );
|
||||
for ( const auto& wellPathPartInCell : wellPathPartsInPolygon )
|
||||
{
|
||||
if ( !wellPathPartInCell.empty() )
|
||||
{
|
||||
int endpointCount = 0;
|
||||
if ( wellPathPartInCell.front().z() != HUGE_VAL ) ++endpointCount;
|
||||
if ( wellPathPartInCell.back().z() != HUGE_VAL ) ++endpointCount;
|
||||
|
||||
cvf::Vec3d intersectionLength = ( wellPathPartInCell.back() - wellPathPartInCell.front() );
|
||||
double xLengthInCell = fabs( intersectionLength.x() );
|
||||
double yLengthInCell = fabs( intersectionLength.y() );
|
||||
|
||||
m_stimPlanCellIdxToIntersectionInfoMap[cIdx].endpointCount += endpointCount;
|
||||
m_stimPlanCellIdxToIntersectionInfoMap[cIdx].hlength += xLengthInCell;
|
||||
m_stimPlanCellIdxToIntersectionInfoMap[cIdx].vlength += yLengthInCell;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017- Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cvfMatrix4.h"
|
||||
|
||||
#include <gsl/gsl>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
class RigWellPath;
|
||||
class RimFracture;
|
||||
class RigWellPathStimplanIntersectorTester;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class RigWellPathStimplanIntersector
|
||||
{
|
||||
public:
|
||||
struct WellCellIntersection
|
||||
{
|
||||
WellCellIntersection()
|
||||
: hlength( 0.0 )
|
||||
, vlength( 0.0 )
|
||||
, endpointCount( 0 )
|
||||
{
|
||||
}
|
||||
double hlength;
|
||||
double vlength;
|
||||
int endpointCount;
|
||||
|
||||
double computeLength() const { return cvf::Math::sqrt( hlength * hlength + vlength * vlength ); }
|
||||
};
|
||||
|
||||
RigWellPathStimplanIntersector( gsl::not_null<const RigWellPath*> wellpathGeom,
|
||||
gsl::not_null<const RimFracture*> rimFracture );
|
||||
|
||||
const std::map<size_t, WellCellIntersection>& intersections() const;
|
||||
|
||||
private:
|
||||
friend class RigWellPathStimplanIntersectorTester;
|
||||
static void calculate( const cvf::Mat4d& fractureXf,
|
||||
const std::vector<cvf::Vec3d>& wellPathPoints,
|
||||
double wellRadius,
|
||||
double perforationLength,
|
||||
const std::vector<std::vector<cvf::Vec3d>>& stpCellPolygons,
|
||||
std::map<size_t, WellCellIntersection>& stimPlanCellIdxToIntersectionInfoMap );
|
||||
|
||||
std::map<size_t, WellCellIntersection> m_stimPlanCellIdxToIntersectionInfoMap;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class RigWellPathStimplanIntersectorTester
|
||||
{
|
||||
public:
|
||||
static void testCalculate(
|
||||
const cvf::Mat4d& fractureXf,
|
||||
const std::vector<cvf::Vec3d>& wellPathPoints,
|
||||
double wellRadius,
|
||||
double perforationLength,
|
||||
const std::vector<std::vector<cvf::Vec3d>>& stpCellPolygons,
|
||||
std::map<size_t, RigWellPathStimplanIntersector::WellCellIntersection>& stimPlanCellIdxToIntersectionInfoMap )
|
||||
{
|
||||
RigWellPathStimplanIntersector::calculate( fractureXf,
|
||||
wellPathPoints,
|
||||
wellRadius,
|
||||
perforationLength,
|
||||
stpCellPolygons,
|
||||
stimPlanCellIdxToIntersectionInfoMap );
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,936 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigAccWellFlowCalculator.h"
|
||||
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigFlowDiagResults.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigSimWellData.h"
|
||||
#include "RigSimulationWellCoordsAndMD.h"
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
///
|
||||
//==================================================================================================
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigEclCellIndexCalculator::resultCellIndex( size_t gridIndex, size_t gridCellIndex ) const
|
||||
{
|
||||
const RigGridBase* grid = m_mainGrid->gridByIndex( gridIndex );
|
||||
size_t reservoirCellIndex = grid->reservoirCellIndex( gridCellIndex );
|
||||
|
||||
return m_activeCellInfo->cellResultIndex( reservoirCellIndex );
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
///
|
||||
//==================================================================================================
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// The pipeBranchesWellResultPoints are describing the lines between the points, starting with the first line
|
||||
// and is thus expected to be one less than the number of centerline points
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigAccWellFlowCalculator::RigAccWellFlowCalculator(
|
||||
const std::vector<std::vector<cvf::Vec3d>>& pipeBranchesCLCoords,
|
||||
const std::vector<std::vector<RigWellResultPoint>>& pipeBranchesWellResultPoints,
|
||||
const std::map<QString, const std::vector<double>*>& tracerCellFractionValues,
|
||||
const RigEclCellIndexCalculator& cellIndexCalculator,
|
||||
double smallContribThreshold,
|
||||
bool isProducer )
|
||||
: m_pipeBranchesCLCoords( pipeBranchesCLCoords )
|
||||
, m_pipeBranchesWellResultPoints( pipeBranchesWellResultPoints )
|
||||
, m_tracerCellFractionValues( &tracerCellFractionValues )
|
||||
, m_cellIndexCalculator( cellIndexCalculator )
|
||||
, m_smallContributionsThreshold( smallContribThreshold )
|
||||
, m_isProducer( isProducer )
|
||||
, m_useTotalWellPhaseRateOnly( false )
|
||||
{
|
||||
m_connectionFlowPrBranch.resize( m_pipeBranchesWellResultPoints.size() );
|
||||
m_pseudoLengthFlowPrBranch.resize( m_pipeBranchesWellResultPoints.size() );
|
||||
|
||||
for ( const auto& it : ( *m_tracerCellFractionValues ) )
|
||||
m_tracerNames.push_back( it.first );
|
||||
|
||||
m_tracerNames.push_back( RIG_RESERVOIR_TRACER_NAME );
|
||||
|
||||
initializePipeBranchesMeasuredDepths();
|
||||
calculateAccumulatedFlowPrConnection( 0, 0 );
|
||||
calculateFlowPrPseudoLength( 0, 0.0 );
|
||||
sortTracers();
|
||||
groupSmallContributions();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigAccWellFlowCalculator::RigAccWellFlowCalculator( const std::vector<std::vector<cvf::Vec3d>>& pipeBranchesCLCoords,
|
||||
const std::vector<std::vector<RigWellResultPoint>>& pipeBranchesWellResultPoints,
|
||||
double smallContribThreshold )
|
||||
: m_pipeBranchesCLCoords( pipeBranchesCLCoords )
|
||||
, m_pipeBranchesWellResultPoints( pipeBranchesWellResultPoints )
|
||||
, m_tracerCellFractionValues( nullptr )
|
||||
, m_cellIndexCalculator( RigEclCellIndexCalculator( nullptr, nullptr ) )
|
||||
, m_smallContributionsThreshold( smallContribThreshold )
|
||||
, m_isProducer( true )
|
||||
, m_useTotalWellPhaseRateOnly( false )
|
||||
{
|
||||
m_connectionFlowPrBranch.resize( m_pipeBranchesWellResultPoints.size() );
|
||||
m_pseudoLengthFlowPrBranch.resize( m_pipeBranchesWellResultPoints.size() );
|
||||
|
||||
if ( !m_useTotalWellPhaseRateOnly )
|
||||
{
|
||||
m_tracerNames.push_back( RIG_FLOW_OIL_NAME );
|
||||
m_tracerNames.push_back( RIG_FLOW_GAS_NAME );
|
||||
m_tracerNames.push_back( RIG_FLOW_WATER_NAME );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tracerNames.push_back( RIG_FLOW_TOTAL_NAME );
|
||||
}
|
||||
|
||||
initializePipeBranchesMeasuredDepths();
|
||||
calculateAccumulatedFlowPrConnection( 0, 0 );
|
||||
calculateFlowPrPseudoLength( 0, 0.0 );
|
||||
|
||||
if ( !m_useTotalWellPhaseRateOnly ) sortTracers();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigAccWellFlowCalculator::RigAccWellFlowCalculator( const std::vector<cvf::Vec3d>& pipeBranchCLCoords,
|
||||
const std::vector<RigWellResultPoint>& pipeBranchesWellResultPoints,
|
||||
const std::vector<double>& pipeBranchMeasuredDepths,
|
||||
bool totalFlowOnly )
|
||||
: m_tracerCellFractionValues( nullptr )
|
||||
, m_cellIndexCalculator( RigEclCellIndexCalculator( nullptr, nullptr ) )
|
||||
, m_smallContributionsThreshold( 0.0 )
|
||||
, m_isProducer( true )
|
||||
, m_useTotalWellPhaseRateOnly( totalFlowOnly )
|
||||
{
|
||||
m_pipeBranchesCLCoords.push_back( pipeBranchCLCoords );
|
||||
m_pipeBranchesWellResultPoints.push_back( pipeBranchesWellResultPoints );
|
||||
m_pipeBranchesMeasuredDepths.push_back( pipeBranchMeasuredDepths );
|
||||
|
||||
m_connectionFlowPrBranch.resize( m_pipeBranchesWellResultPoints.size() );
|
||||
m_pseudoLengthFlowPrBranch.resize( m_pipeBranchesWellResultPoints.size() );
|
||||
|
||||
if ( !m_useTotalWellPhaseRateOnly )
|
||||
{
|
||||
m_tracerNames.push_back( RIG_FLOW_OIL_NAME );
|
||||
m_tracerNames.push_back( RIG_FLOW_GAS_NAME );
|
||||
m_tracerNames.push_back( RIG_FLOW_WATER_NAME );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tracerNames.push_back( RIG_FLOW_TOTAL_NAME );
|
||||
}
|
||||
|
||||
initializePipeBranchesMeasuredDepths();
|
||||
calculateAccumulatedFlowPrConnection( 0, 0 );
|
||||
calculateFlowPrPseudoLength( 0, 0.0 );
|
||||
|
||||
if ( !m_useTotalWellPhaseRateOnly ) sortTracers();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigAccWellFlowCalculator::initializePipeBranchesMeasuredDepths()
|
||||
{
|
||||
for ( const auto& branchClPoints : m_pipeBranchesCLCoords )
|
||||
{
|
||||
RigSimulationWellCoordsAndMD mdCalculator( branchClPoints );
|
||||
m_pipeBranchesMeasuredDepths.push_back( mdCalculator.measuredDepths() );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<double>& RigAccWellFlowCalculator::connectionNumbersFromTop( size_t branchIdx ) const
|
||||
{
|
||||
return m_connectionFlowPrBranch[branchIdx].depthValuesFromTop;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<double>& RigAccWellFlowCalculator::accumulatedTracerFlowPrConnection( const QString& tracerName,
|
||||
size_t branchIdx ) const
|
||||
{
|
||||
auto flowPrTracerIt = m_connectionFlowPrBranch[branchIdx].accFlowPrTracer.find( tracerName );
|
||||
if ( flowPrTracerIt != m_connectionFlowPrBranch[branchIdx].accFlowPrTracer.end() )
|
||||
{
|
||||
return flowPrTracerIt->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
CVF_ASSERT( false );
|
||||
static std::vector<double> dummy;
|
||||
return dummy;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<double>& RigAccWellFlowCalculator::tracerFlowPrConnection( const QString& tracerName, size_t branchIdx ) const
|
||||
{
|
||||
auto flowPrTracerIt = m_connectionFlowPrBranch[branchIdx].flowPrTracer.find( tracerName );
|
||||
if ( flowPrTracerIt != m_connectionFlowPrBranch[branchIdx].flowPrTracer.end() )
|
||||
{
|
||||
return flowPrTracerIt->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
CVF_ASSERT( false );
|
||||
static std::vector<double> dummy;
|
||||
return dummy;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<double>& RigAccWellFlowCalculator::pseudoLengthFromTop( size_t branchIdx ) const
|
||||
{
|
||||
return m_pseudoLengthFlowPrBranch[branchIdx].depthValuesFromTop;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<double>& RigAccWellFlowCalculator::trueVerticalDepth( size_t branchIdx ) const
|
||||
{
|
||||
return m_pseudoLengthFlowPrBranch[branchIdx].trueVerticalDepth;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<double>& RigAccWellFlowCalculator::accumulatedTracerFlowPrPseudoLength( const QString& tracerName,
|
||||
size_t branchIdx ) const
|
||||
{
|
||||
auto flowPrTracerIt = m_pseudoLengthFlowPrBranch[branchIdx].accFlowPrTracer.find( tracerName );
|
||||
if ( flowPrTracerIt != m_pseudoLengthFlowPrBranch[branchIdx].accFlowPrTracer.end() )
|
||||
{
|
||||
return flowPrTracerIt->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
CVF_ASSERT( false );
|
||||
static std::vector<double> dummy;
|
||||
return dummy;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<double>& RigAccWellFlowCalculator::tracerFlowPrPseudoLength( const QString& tracerName,
|
||||
size_t branchIdx ) const
|
||||
{
|
||||
auto flowPrTracerIt = m_pseudoLengthFlowPrBranch[branchIdx].flowPrTracer.find( tracerName );
|
||||
if ( flowPrTracerIt != m_pseudoLengthFlowPrBranch[branchIdx].flowPrTracer.end() )
|
||||
{
|
||||
return flowPrTracerIt->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
CVF_ASSERT( false );
|
||||
static std::vector<double> dummy;
|
||||
return dummy;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<std::pair<QString, double>> RigAccWellFlowCalculator::totalWellFlowPrTracer() const
|
||||
{
|
||||
std::vector<QString> tracerNames = this->tracerNames();
|
||||
std::vector<std::pair<QString, double>> tracerWithValues;
|
||||
|
||||
for ( const QString& tracerName : tracerNames )
|
||||
{
|
||||
const std::vector<double>& accFlow = this->accumulatedTracerFlowPrConnection( tracerName, 0 );
|
||||
tracerWithValues.push_back( std::make_pair( tracerName, accFlow.back() ) );
|
||||
}
|
||||
|
||||
return tracerWithValues;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<std::pair<QString, double>> RigAccWellFlowCalculator::totalTracerFractions() const
|
||||
{
|
||||
std::vector<std::pair<QString, double>> totalFlows = totalWellFlowPrTracer();
|
||||
|
||||
float sumTracerFlows = 0.0f;
|
||||
for ( const auto& tracerVal : totalFlows )
|
||||
{
|
||||
sumTracerFlows += tracerVal.second;
|
||||
}
|
||||
|
||||
if ( sumTracerFlows == 0.0 ) totalFlows.clear();
|
||||
|
||||
for ( auto& tracerPair : totalFlows )
|
||||
{
|
||||
tracerPair.second = tracerPair.second / sumTracerFlows;
|
||||
}
|
||||
|
||||
return totalFlows;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigAccWellFlowCalculator::isWellFlowConsistent() const
|
||||
{
|
||||
bool isConsistent = true;
|
||||
for ( const std::vector<RigWellResultPoint>& branch : m_pipeBranchesWellResultPoints )
|
||||
{
|
||||
for ( const RigWellResultPoint& wrp : branch )
|
||||
{
|
||||
isConsistent = isFlowRateConsistent( wrp.flowRate() );
|
||||
|
||||
if ( !isConsistent ) break;
|
||||
}
|
||||
if ( !isConsistent ) break;
|
||||
}
|
||||
return isConsistent;
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
std::vector<double>
|
||||
RigAccWellFlowCalculator::calculateAccumulatedFractions( const std::vector<double>& accumulatedFlowPrTracer ) const
|
||||
{
|
||||
double totalFlow = 0.0;
|
||||
for ( double tracerFlow : accumulatedFlowPrTracer )
|
||||
{
|
||||
totalFlow += tracerFlow;
|
||||
}
|
||||
|
||||
std::vector<double> flowFractionsPrTracer( accumulatedFlowPrTracer.size(), 0.0 );
|
||||
|
||||
if ( totalFlow == 0.0 || !isFlowRateConsistent( totalFlow ) ) // If we have no accumulated flow, we set all the flow
|
||||
// associated to the last tracer, which is the reservoir
|
||||
{
|
||||
flowFractionsPrTracer.back() = 1.0;
|
||||
return flowFractionsPrTracer;
|
||||
}
|
||||
|
||||
for ( size_t tIdx = 0; tIdx < accumulatedFlowPrTracer.size(); ++tIdx )
|
||||
{
|
||||
double tracerFlow = accumulatedFlowPrTracer[tIdx];
|
||||
flowFractionsPrTracer[tIdx] = tracerFlow / totalFlow;
|
||||
}
|
||||
|
||||
return flowFractionsPrTracer;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigAccWellFlowCalculator::isConnectionFlowConsistent( const RigWellResultPoint& wellCell ) const
|
||||
{
|
||||
if ( !m_tracerCellFractionValues ) return true; // No flow diagnostics.
|
||||
|
||||
return isFlowRateConsistent( wellCell.flowRate() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigAccWellFlowCalculator::isFlowRateConsistent( double flowRate ) const
|
||||
{
|
||||
if ( !m_tracerCellFractionValues ) return true; // No flow diagnostics.
|
||||
|
||||
return ( flowRate >= 0.0 && m_isProducer ) || ( flowRate <= 0.0 && !m_isProducer );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigAccWellFlowCalculator::calculateAccumulatedFlowPrConnection( size_t branchIdx, size_t startConnectionNumberFromTop )
|
||||
{
|
||||
const std::vector<RigWellResultPoint>& branchCells = m_pipeBranchesWellResultPoints[branchIdx];
|
||||
|
||||
std::vector<size_t> resPointUniqueIndexFromBottom = wrpToUniqueWrpIndexFromBottom( branchCells );
|
||||
|
||||
size_t prevConnIndx = -1;
|
||||
int clSegIdx = static_cast<int>( branchCells.size() ) - 1;
|
||||
|
||||
std::vector<double> accFlowPrTracer( m_tracerNames.size(), 0.0 );
|
||||
|
||||
while ( clSegIdx >= 0 )
|
||||
{
|
||||
// Skip point if referring to the same cell as the previous centerline segment did
|
||||
{
|
||||
if ( resPointUniqueIndexFromBottom[clSegIdx] == prevConnIndx )
|
||||
{
|
||||
--clSegIdx;
|
||||
continue;
|
||||
}
|
||||
|
||||
prevConnIndx = resPointUniqueIndexFromBottom[clSegIdx];
|
||||
}
|
||||
|
||||
// Accumulate the connection-cell's fraction flows
|
||||
|
||||
const RigWellResultPoint& wellCell = branchCells[clSegIdx];
|
||||
|
||||
std::vector<double> flowPrTracer = calculateWellCellFlowPrTracer( wellCell, accFlowPrTracer );
|
||||
|
||||
addDownStreamBranchFlow( &accFlowPrTracer, flowPrTracer );
|
||||
|
||||
if ( !isConnectionFlowConsistent( wellCell ) )
|
||||
{
|
||||
// Associate all the flow with the reservoir tracer for inconsistent flow direction
|
||||
flowPrTracer = std::vector<double>( flowPrTracer.size(), 0.0 );
|
||||
flowPrTracer.back() = wellCell.flowRate();
|
||||
}
|
||||
|
||||
// Add the total accumulated (fraction) flows from any branches connected to this cell
|
||||
|
||||
size_t connNumFromTop = connectionIndexFromTop( resPointUniqueIndexFromBottom, clSegIdx ) +
|
||||
startConnectionNumberFromTop;
|
||||
|
||||
std::vector<size_t> downStreamBranchIndices = findDownStreamBranchIdxs( branchCells[clSegIdx] );
|
||||
for ( size_t dsBidx : downStreamBranchIndices )
|
||||
{
|
||||
BranchFlow& downStreamBranchFlow = m_connectionFlowPrBranch[dsBidx];
|
||||
if ( dsBidx != branchIdx && downStreamBranchFlow.depthValuesFromTop.size() == 0 ) // Not this branch or
|
||||
// already calculated
|
||||
{
|
||||
calculateAccumulatedFlowPrConnection( dsBidx, connNumFromTop );
|
||||
std::vector<double> accBranchFlowPrTracer = accumulatedDsBranchFlowPrTracer( downStreamBranchFlow );
|
||||
addDownStreamBranchFlow( &accFlowPrTracer, accBranchFlowPrTracer );
|
||||
if ( m_pipeBranchesWellResultPoints[dsBidx].size() <= 3 )
|
||||
{
|
||||
// Short branch. Will not be visible. Show branch flow as addition to this connections direct flow
|
||||
addDownStreamBranchFlow( &flowPrTracer, accBranchFlowPrTracer );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Push back the accumulated result into the storage
|
||||
|
||||
BranchFlow& branchFlow = m_connectionFlowPrBranch[branchIdx];
|
||||
|
||||
storeFlowOnDepth( &branchFlow, connNumFromTop, accFlowPrTracer, flowPrTracer );
|
||||
|
||||
--clSegIdx;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigAccWellFlowCalculator::calculateFlowPrPseudoLength( size_t branchIdx, double startPseudoLengthFromTop )
|
||||
{
|
||||
const std::vector<RigWellResultPoint>& branchCells = m_pipeBranchesWellResultPoints[branchIdx];
|
||||
const std::vector<cvf::Vec3d>& branchClPoints = m_pipeBranchesCLCoords[branchIdx];
|
||||
const std::vector<double>& branchMDs = m_pipeBranchesMeasuredDepths[branchIdx];
|
||||
|
||||
int clSegIdx = static_cast<int>( branchCells.size() ) - 1;
|
||||
|
||||
std::vector<double> accFlowPrTracer( m_tracerNames.size(), 0.0 );
|
||||
|
||||
BranchFlow& branchFlow = m_pseudoLengthFlowPrBranch[branchIdx];
|
||||
|
||||
RigWellResultPoint previousResultPoint;
|
||||
|
||||
while ( clSegIdx >= 0 )
|
||||
{
|
||||
int cellBottomPointIndex = -1;
|
||||
int cellUpperPointIndex = -1;
|
||||
int currentSegmentIndex = -1;
|
||||
|
||||
// Find the complete cell span
|
||||
{
|
||||
cellBottomPointIndex = clSegIdx + 1;
|
||||
|
||||
previousResultPoint = branchCells[clSegIdx];
|
||||
--clSegIdx;
|
||||
while ( clSegIdx >= 0 && previousResultPoint.isEqual( branchCells[clSegIdx] ) )
|
||||
{
|
||||
--clSegIdx;
|
||||
}
|
||||
|
||||
cellUpperPointIndex = clSegIdx + 1;
|
||||
currentSegmentIndex = cellUpperPointIndex;
|
||||
}
|
||||
const RigWellResultPoint& wellCell = branchCells[currentSegmentIndex];
|
||||
std::vector<double> flowPrTracerToAccumulate = calculateWellCellFlowPrTracer( wellCell, accFlowPrTracer );
|
||||
|
||||
double pseudoLengthFromTop_lower = branchMDs[cellBottomPointIndex] + startPseudoLengthFromTop;
|
||||
double tvd_lower = -branchClPoints[cellBottomPointIndex][2];
|
||||
|
||||
// Push back the new start-of-cell flow, with the previously accumulated result into the storage
|
||||
|
||||
std::vector<double> flowPrTracer;
|
||||
if ( !isConnectionFlowConsistent( wellCell ) )
|
||||
{
|
||||
// Associate all the flow with the reservoir tracer for inconsistent flow direction
|
||||
flowPrTracer = std::vector<double>( flowPrTracerToAccumulate.size(), 0.0 );
|
||||
flowPrTracer.back() = wellCell.flowRate();
|
||||
}
|
||||
else
|
||||
{
|
||||
flowPrTracer = flowPrTracerToAccumulate;
|
||||
}
|
||||
|
||||
storeFlowOnDepthWTvd( &branchFlow, pseudoLengthFromTop_lower, tvd_lower, accFlowPrTracer, flowPrTracer );
|
||||
|
||||
// Accumulate the connection-cell's fraction flows
|
||||
|
||||
addDownStreamBranchFlow( &accFlowPrTracer, flowPrTracerToAccumulate );
|
||||
|
||||
double pseudoLengthFromTop_upper = branchMDs[cellUpperPointIndex] + startPseudoLengthFromTop;
|
||||
double tvd_upper = -branchClPoints[cellUpperPointIndex][2];
|
||||
|
||||
// Push back the accumulated result into the storage
|
||||
|
||||
storeFlowOnDepthWTvd( &branchFlow, pseudoLengthFromTop_upper, tvd_upper, accFlowPrTracer, flowPrTracer );
|
||||
|
||||
// Add the total accumulated (fraction) flows from any branches connected to this cell
|
||||
|
||||
std::vector<size_t> downStreamBranchIndices = findDownStreamBranchIdxs( branchCells[cellUpperPointIndex] );
|
||||
for ( size_t dsBidx : downStreamBranchIndices )
|
||||
{
|
||||
BranchFlow& downStreamBranchFlow = m_pseudoLengthFlowPrBranch[dsBidx];
|
||||
if ( dsBidx != branchIdx && downStreamBranchFlow.depthValuesFromTop.size() == 0 ) // Not this branch or
|
||||
// already calculated
|
||||
{
|
||||
calculateFlowPrPseudoLength( dsBidx, pseudoLengthFromTop_upper );
|
||||
std::vector<double> accBranchFlowPrTracer = accumulatedDsBranchFlowPrTracer( downStreamBranchFlow );
|
||||
addDownStreamBranchFlow( &accFlowPrTracer, accBranchFlowPrTracer );
|
||||
if ( m_pipeBranchesWellResultPoints[dsBidx].size() <= 3 )
|
||||
{
|
||||
// Short branch. Will not be visible. Show branch flow as addition to this connections direct flow
|
||||
addDownStreamBranchFlow( &flowPrTracer, accBranchFlowPrTracer );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Push back the accumulated result after adding the branch result into the storage
|
||||
|
||||
if ( downStreamBranchIndices.size() )
|
||||
storeFlowOnDepthWTvd( &branchFlow, pseudoLengthFromTop_upper, tvd_upper, accFlowPrTracer, flowPrTracer );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigAccWellFlowCalculator::addDownStreamBranchFlow( std::vector<double>* accFlowPrTracer,
|
||||
const std::vector<double>& accBranchFlowPrTracer ) const
|
||||
{
|
||||
double totalThisBranchFlow = 0.0;
|
||||
for ( double tracerFlow : *accFlowPrTracer )
|
||||
{
|
||||
totalThisBranchFlow += tracerFlow;
|
||||
}
|
||||
|
||||
double totalDsBranchFlow = 0.0;
|
||||
for ( double tracerFlow : accBranchFlowPrTracer )
|
||||
{
|
||||
totalDsBranchFlow += tracerFlow;
|
||||
}
|
||||
|
||||
bool isAccumulationConsistent = isFlowRateConsistent( totalThisBranchFlow ); // If inconsistent, is it always only
|
||||
// the Reservoir tracer that has the
|
||||
// flow ?
|
||||
bool isBranchConsistent = isFlowRateConsistent( totalDsBranchFlow );
|
||||
|
||||
if ( isAccumulationConsistent == isBranchConsistent )
|
||||
{
|
||||
for ( size_t tracerIdx = 0; tracerIdx < ( *accFlowPrTracer ).size(); ++tracerIdx )
|
||||
{
|
||||
( *accFlowPrTracer )[tracerIdx] += accBranchFlowPrTracer[tracerIdx];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
double totalAccFlow = totalThisBranchFlow + totalDsBranchFlow;
|
||||
|
||||
if ( !isFlowRateConsistent( totalAccFlow ) )
|
||||
{
|
||||
// Reset the accumulated values, as everything must be moved to the "Reservoir" tracer.
|
||||
for ( double& val : ( *accFlowPrTracer ) )
|
||||
val = 0.0;
|
||||
|
||||
// Put all flow into the Reservoir tracer
|
||||
accFlowPrTracer->back() = totalThisBranchFlow + totalDsBranchFlow;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We will end up with a consistent accumulated flow, and need to keep the accumulated distribution in this branch
|
||||
// or to use the ds branch distribution
|
||||
|
||||
std::vector<double> accFractionsPrTracer;
|
||||
|
||||
if ( !isAccumulationConsistent && isBranchConsistent )
|
||||
{
|
||||
accFractionsPrTracer = calculateAccumulatedFractions( accBranchFlowPrTracer );
|
||||
}
|
||||
else if ( isAccumulationConsistent && !isBranchConsistent )
|
||||
{
|
||||
accFractionsPrTracer = calculateAccumulatedFractions( *accFlowPrTracer );
|
||||
}
|
||||
|
||||
// Set the accumulated values to the totalFlow times the tracer fraction selected.
|
||||
|
||||
for ( size_t tIdx = 0; tIdx < accFlowPrTracer->size(); ++tIdx )
|
||||
{
|
||||
( *accFlowPrTracer )[tIdx] = accFractionsPrTracer[tIdx] * ( totalAccFlow );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigAccWellFlowCalculator::storeFlowOnDepth( BranchFlow* branchFlow,
|
||||
double depthValue,
|
||||
const std::vector<double>& accFlowPrTracer,
|
||||
const std::vector<double>& flowPrTracer )
|
||||
{
|
||||
size_t tracerIdx = 0;
|
||||
for ( const auto& tracerName : m_tracerNames )
|
||||
{
|
||||
branchFlow->accFlowPrTracer[tracerName].push_back( accFlowPrTracer[tracerIdx] );
|
||||
branchFlow->flowPrTracer[tracerName].push_back( flowPrTracer[tracerIdx] );
|
||||
tracerIdx++;
|
||||
}
|
||||
|
||||
branchFlow->depthValuesFromTop.push_back( depthValue );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigAccWellFlowCalculator::storeFlowOnDepthWTvd( BranchFlow* branchFlow,
|
||||
double depthValue,
|
||||
double trueVerticalDepth,
|
||||
const std::vector<double>& accFlowPrTracer,
|
||||
const std::vector<double>& flowPrTracer )
|
||||
{
|
||||
size_t tracerIdx = 0;
|
||||
for ( const auto& tracerName : m_tracerNames )
|
||||
{
|
||||
branchFlow->accFlowPrTracer[tracerName].push_back( accFlowPrTracer[tracerIdx] );
|
||||
branchFlow->flowPrTracer[tracerName].push_back( flowPrTracer[tracerIdx] );
|
||||
tracerIdx++;
|
||||
}
|
||||
|
||||
branchFlow->depthValuesFromTop.push_back( depthValue );
|
||||
branchFlow->trueVerticalDepth.push_back( trueVerticalDepth );
|
||||
}
|
||||
|
||||
std::vector<double> RigAccWellFlowCalculator::accumulatedDsBranchFlowPrTracer( const BranchFlow& downStreamBranchFlow ) const
|
||||
{
|
||||
std::vector<double> accBranchFlowPrTracer( m_tracerNames.size(), 0.0 );
|
||||
|
||||
size_t tracerIdx = 0;
|
||||
for ( const auto& tracerName : m_tracerNames )
|
||||
{
|
||||
const auto trNameAccFlowsPair = downStreamBranchFlow.accFlowPrTracer.find( tracerName );
|
||||
if ( trNameAccFlowsPair != downStreamBranchFlow.accFlowPrTracer.end() )
|
||||
{
|
||||
accBranchFlowPrTracer[tracerIdx] = trNameAccFlowsPair->second.back();
|
||||
}
|
||||
tracerIdx++;
|
||||
}
|
||||
|
||||
return accBranchFlowPrTracer;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Calculate the flow pr tracer. If inconsistent flow, keep the existing fractions constant
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<double>
|
||||
RigAccWellFlowCalculator::calculateWellCellFlowPrTracer( const RigWellResultPoint& wellCell,
|
||||
const std::vector<double>& currentAccumulatedFlowPrTracer ) const
|
||||
{
|
||||
std::vector<double> flowPrTracer( m_tracerNames.size(), 0.0 );
|
||||
|
||||
if ( !isConnectionFlowConsistent( wellCell ) )
|
||||
{
|
||||
double flowRate = wellCell.flowRate();
|
||||
flowPrTracer = calculateAccumulatedFractions( currentAccumulatedFlowPrTracer );
|
||||
for ( double& accFraction : flowPrTracer )
|
||||
{
|
||||
accFraction *= flowRate;
|
||||
}
|
||||
|
||||
return flowPrTracer;
|
||||
}
|
||||
|
||||
if ( m_tracerCellFractionValues )
|
||||
{
|
||||
if ( wellCell.isCell() && wellCell.m_isOpen )
|
||||
{
|
||||
size_t resCellIndex = m_cellIndexCalculator.resultCellIndex( wellCell.m_gridIndex, wellCell.m_gridCellIndex );
|
||||
size_t tracerIdx = 0;
|
||||
double totalTracerFractionInCell = 0.0;
|
||||
for ( const auto& tracerFractionValsPair : ( *m_tracerCellFractionValues ) )
|
||||
{
|
||||
const std::vector<double>* fractionVals = tracerFractionValsPair.second;
|
||||
if ( fractionVals )
|
||||
{
|
||||
double cellTracerFraction = ( *fractionVals )[resCellIndex];
|
||||
if ( cellTracerFraction != HUGE_VAL && cellTracerFraction == cellTracerFraction )
|
||||
{
|
||||
double tracerFlow = cellTracerFraction * wellCell.flowRate();
|
||||
flowPrTracer[tracerIdx] = tracerFlow;
|
||||
|
||||
totalTracerFractionInCell += cellTracerFraction;
|
||||
}
|
||||
}
|
||||
tracerIdx++;
|
||||
}
|
||||
|
||||
double reservoirFraction = 1.0 - totalTracerFractionInCell;
|
||||
double reservoirTracerFlow = reservoirFraction * wellCell.flowRate();
|
||||
flowPrTracer[tracerIdx] = reservoirTracerFlow;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !m_useTotalWellPhaseRateOnly )
|
||||
{
|
||||
flowPrTracer[0] = wellCell.oilRate();
|
||||
flowPrTracer[1] = wellCell.gasRate();
|
||||
flowPrTracer[2] = wellCell.waterRate();
|
||||
}
|
||||
else
|
||||
{
|
||||
flowPrTracer[0] = wellCell.flowRate();
|
||||
}
|
||||
}
|
||||
|
||||
return flowPrTracer;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<size_t>
|
||||
RigAccWellFlowCalculator::wrpToUniqueWrpIndexFromBottom( const std::vector<RigWellResultPoint>& branchCells ) const
|
||||
{
|
||||
std::vector<size_t> resPointToConnectionIndexFromBottom;
|
||||
resPointToConnectionIndexFromBottom.resize( branchCells.size(), -1 );
|
||||
|
||||
size_t connIdxFromBottom = 0;
|
||||
int clSegIdx = static_cast<int>( branchCells.size() ) - 1;
|
||||
|
||||
if ( clSegIdx < 0 ) return resPointToConnectionIndexFromBottom;
|
||||
|
||||
size_t prevGridIdx = branchCells[clSegIdx].m_gridIndex;
|
||||
size_t prevGridCellIdx = branchCells[clSegIdx].m_gridCellIndex;
|
||||
int prevErtSegId = branchCells[clSegIdx].m_ertSegmentId;
|
||||
int prevErtBranchId = branchCells[clSegIdx].m_ertBranchId;
|
||||
|
||||
while ( clSegIdx >= 0 )
|
||||
{
|
||||
if ( branchCells[clSegIdx].isValid() && ( branchCells[clSegIdx].m_gridIndex != prevGridIdx ||
|
||||
branchCells[clSegIdx].m_gridCellIndex != prevGridCellIdx ||
|
||||
branchCells[clSegIdx].m_ertSegmentId != prevErtSegId ||
|
||||
branchCells[clSegIdx].m_ertBranchId != prevErtBranchId ) )
|
||||
{
|
||||
++connIdxFromBottom;
|
||||
|
||||
prevGridIdx = branchCells[clSegIdx].m_gridIndex;
|
||||
prevGridCellIdx = branchCells[clSegIdx].m_gridCellIndex;
|
||||
prevErtSegId = branchCells[clSegIdx].m_ertSegmentId;
|
||||
prevErtBranchId = branchCells[clSegIdx].m_ertBranchId;
|
||||
}
|
||||
|
||||
resPointToConnectionIndexFromBottom[clSegIdx] = connIdxFromBottom;
|
||||
|
||||
--clSegIdx;
|
||||
}
|
||||
|
||||
return resPointToConnectionIndexFromBottom;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigAccWellFlowCalculator::connectionIndexFromTop( const std::vector<size_t>& resPointToConnectionIndexFromBottom,
|
||||
size_t clSegIdx )
|
||||
{
|
||||
return resPointToConnectionIndexFromBottom.front() - resPointToConnectionIndexFromBottom[clSegIdx];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<size_t> RigAccWellFlowCalculator::findDownStreamBranchIdxs( const RigWellResultPoint& connectionPoint ) const
|
||||
{
|
||||
std::vector<size_t> downStreamBranchIdxs;
|
||||
|
||||
for ( size_t bIdx = 0; bIdx < m_pipeBranchesWellResultPoints.size(); ++bIdx )
|
||||
{
|
||||
if ( m_pipeBranchesWellResultPoints[bIdx][0].m_gridIndex == connectionPoint.m_gridIndex &&
|
||||
m_pipeBranchesWellResultPoints[bIdx][0].m_gridCellIndex == connectionPoint.m_gridCellIndex &&
|
||||
m_pipeBranchesWellResultPoints[bIdx][0].m_ertBranchId == connectionPoint.m_ertBranchId &&
|
||||
m_pipeBranchesWellResultPoints[bIdx][0].m_ertSegmentId == connectionPoint.m_ertSegmentId )
|
||||
{
|
||||
downStreamBranchIdxs.push_back( bIdx );
|
||||
}
|
||||
}
|
||||
return downStreamBranchIdxs;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
void RigAccWellFlowCalculator::sortTracers()
|
||||
{
|
||||
std::multimap<double, QString> sortedTracers;
|
||||
for ( const QString& tracerName : m_tracerNames )
|
||||
{
|
||||
const std::vector<double>& mainBranchAccFlow = accumulatedTracerFlowPrConnection( tracerName, 0 );
|
||||
|
||||
double totalFlow = 0.0;
|
||||
|
||||
if ( mainBranchAccFlow.size() )
|
||||
totalFlow = -fabs( mainBranchAccFlow.back() ); // Based on size in reverse order (biggest to least)
|
||||
|
||||
sortedTracers.insert( { totalFlow, tracerName } );
|
||||
}
|
||||
|
||||
m_tracerNames.clear();
|
||||
for ( const auto& tracerPair : sortedTracers )
|
||||
{
|
||||
m_tracerNames.push_back( tracerPair.second );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Concatenate small tracers into an "Other" group
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigAccWellFlowCalculator::groupSmallContributions()
|
||||
{
|
||||
if ( !( m_smallContributionsThreshold > 0.0 ) ) return;
|
||||
|
||||
// Find the tracers we need to group
|
||||
|
||||
std::vector<QString> tracersToGroup;
|
||||
{
|
||||
bool hasConsistentWellFlow = isWellFlowConsistent();
|
||||
|
||||
std::vector<std::pair<QString, double>> totalTracerFractions = this->totalTracerFractions();
|
||||
|
||||
if ( totalTracerFractions.size() < 5 ) return; // No grouping for few legend items
|
||||
|
||||
for ( const auto& tracerPair : totalTracerFractions )
|
||||
{
|
||||
if ( fabs( tracerPair.second ) <= m_smallContributionsThreshold &&
|
||||
( hasConsistentWellFlow || tracerPair.first != RIG_RESERVOIR_TRACER_NAME ) ) // Do not group the
|
||||
// Reservoir tracer if the
|
||||
// well flow is
|
||||
// inconsistent, because
|
||||
// cross flow is shown as
|
||||
// the reservoir fraction
|
||||
{
|
||||
tracersToGroup.push_back( tracerPair.first );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( tracersToGroup.size() < 2 ) return; // Must at least group two ...
|
||||
|
||||
// Concatenate the values for each branch, erasing the tracers being grouped, replaced with the concatenated values
|
||||
|
||||
for ( BranchFlow& brRes : m_connectionFlowPrBranch )
|
||||
{
|
||||
groupSmallTracers( &brRes.accFlowPrTracer, tracersToGroup );
|
||||
groupSmallTracers( &brRes.flowPrTracer, tracersToGroup );
|
||||
}
|
||||
|
||||
for ( BranchFlow& brRes : m_pseudoLengthFlowPrBranch )
|
||||
{
|
||||
groupSmallTracers( &brRes.accFlowPrTracer, tracersToGroup );
|
||||
groupSmallTracers( &brRes.flowPrTracer, tracersToGroup );
|
||||
}
|
||||
|
||||
// Remove the grouped tracer names from the tracerName list, and replace with the "Others" name
|
||||
|
||||
std::vector<QString> filteredTracernames;
|
||||
for ( const QString& tracerName : m_tracerNames )
|
||||
{
|
||||
bool isDeleted = false;
|
||||
for ( const QString& deletedTracerName : tracersToGroup )
|
||||
{
|
||||
if ( tracerName == deletedTracerName )
|
||||
{
|
||||
isDeleted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !isDeleted ) filteredTracernames.push_back( tracerName );
|
||||
}
|
||||
|
||||
m_tracerNames.swap( filteredTracernames );
|
||||
m_tracerNames.push_back( RIG_TINY_TRACER_GROUP_NAME );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigAccWellFlowCalculator::groupSmallTracers( std::map<QString, std::vector<double>>* branchFlowSet,
|
||||
const std::vector<QString>& tracersToGroup )
|
||||
{
|
||||
if ( branchFlowSet->empty() ) return;
|
||||
|
||||
size_t depthCount = branchFlowSet->begin()->second.size();
|
||||
std::vector<double> groupedAccFlowValues( depthCount, 0.0 );
|
||||
|
||||
for ( const QString& tracername : tracersToGroup )
|
||||
{
|
||||
auto it = branchFlowSet->find( tracername );
|
||||
|
||||
if ( it != branchFlowSet->end() )
|
||||
{
|
||||
const std::vector<double>& tracerVals = it->second;
|
||||
for ( size_t cIdx = 0; cIdx < groupedAccFlowValues.size(); ++cIdx )
|
||||
{
|
||||
groupedAccFlowValues[cIdx] += tracerVals[cIdx];
|
||||
}
|
||||
}
|
||||
|
||||
branchFlowSet->erase( it );
|
||||
}
|
||||
|
||||
( *branchFlowSet )[RIG_TINY_TRACER_GROUP_NAME] = groupedAccFlowValues;
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <vector>
|
||||
//==================================================================================================
|
||||
///
|
||||
///
|
||||
//==================================================================================================
|
||||
|
||||
class RigMainGrid;
|
||||
class RigActiveCellInfo;
|
||||
|
||||
class RigEclCellIndexCalculator
|
||||
{
|
||||
public:
|
||||
RigEclCellIndexCalculator( const RigMainGrid* mainGrid, const RigActiveCellInfo* activeCellInfo )
|
||||
: m_mainGrid( mainGrid )
|
||||
, m_activeCellInfo( activeCellInfo )
|
||||
{
|
||||
}
|
||||
|
||||
size_t resultCellIndex( size_t gridIndex, size_t gridCellIndex ) const;
|
||||
|
||||
private:
|
||||
const RigMainGrid* m_mainGrid;
|
||||
const RigActiveCellInfo* m_activeCellInfo;
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
///
|
||||
//==================================================================================================
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "cvfVector3.h"
|
||||
|
||||
struct RigWellResultPoint;
|
||||
|
||||
class RigAccWellFlowCalculator
|
||||
{
|
||||
public:
|
||||
RigAccWellFlowCalculator( const std::vector<std::vector<cvf::Vec3d>>& pipeBranchesCLCoords,
|
||||
const std::vector<std::vector<RigWellResultPoint>>& pipeBranchesCellIds,
|
||||
const std::map<QString, const std::vector<double>*>& tracerCellFractionValues,
|
||||
const RigEclCellIndexCalculator& cellIndexCalculator,
|
||||
double smallContribThreshold,
|
||||
bool isProducer );
|
||||
|
||||
RigAccWellFlowCalculator( const std::vector<std::vector<cvf::Vec3d>>& pipeBranchesCLCoords,
|
||||
const std::vector<std::vector<RigWellResultPoint>>& pipeBranchesCellIds,
|
||||
double smallContribThreshold );
|
||||
|
||||
RigAccWellFlowCalculator( const std::vector<cvf::Vec3d>& pipeBranchCLCoords,
|
||||
const std::vector<RigWellResultPoint>& pipeBranchCellIds,
|
||||
const std::vector<double>& pipeBranchMeasuredDepths,
|
||||
bool totalFlowOnly );
|
||||
|
||||
const std::vector<double>& connectionNumbersFromTop( size_t branchIdx ) const;
|
||||
const std::vector<double>& accumulatedTracerFlowPrConnection( const QString& tracerName, size_t branchIdx ) const;
|
||||
const std::vector<double>& tracerFlowPrConnection( const QString& tracerName, size_t branchIdx ) const;
|
||||
|
||||
const std::vector<double>& pseudoLengthFromTop( size_t branchIdx ) const;
|
||||
const std::vector<double>& trueVerticalDepth( size_t branchIdx ) const;
|
||||
const std::vector<double>& accumulatedTracerFlowPrPseudoLength( const QString& tracerName, size_t branchIdx ) const;
|
||||
const std::vector<double>& tracerFlowPrPseudoLength( const QString& tracerName, size_t branchIdx ) const;
|
||||
|
||||
const std::vector<QString>& tracerNames() const { return m_tracerNames; }
|
||||
|
||||
std::vector<std::pair<QString, double>> totalTracerFractions() const;
|
||||
|
||||
private:
|
||||
void initializePipeBranchesMeasuredDepths();
|
||||
|
||||
bool isConnectionFlowConsistent( const RigWellResultPoint& wellCell ) const;
|
||||
bool isFlowRateConsistent( double flowRate ) const;
|
||||
|
||||
void calculateAccumulatedFlowPrConnection( size_t branchIdx, size_t startConnectionNumberFromTop );
|
||||
void calculateFlowPrPseudoLength( size_t branchIdx, double startPseudoLengthFromTop );
|
||||
|
||||
std::vector<double> calculateWellCellFlowPrTracer( const RigWellResultPoint& wellCell,
|
||||
const std::vector<double>& currentAccumulatedFlowPrTracer ) const;
|
||||
void sortTracers();
|
||||
void groupSmallContributions();
|
||||
|
||||
void groupSmallTracers( std::map<QString, std::vector<double>>* branchFlowSet,
|
||||
const std::vector<QString>& tracersToGroup );
|
||||
|
||||
bool isWellFlowConsistent() const;
|
||||
std::vector<double> calculateAccumulatedFractions( const std::vector<double>& accumulatedFlowPrTracer ) const;
|
||||
std::vector<size_t> wrpToUniqueWrpIndexFromBottom( const std::vector<RigWellResultPoint>& branchCells ) const;
|
||||
static size_t connectionIndexFromTop( const std::vector<size_t>& resPointToConnectionIndexFromBottom, size_t clSegIdx );
|
||||
std::vector<size_t> findDownStreamBranchIdxs( const RigWellResultPoint& connectionPoint ) const;
|
||||
|
||||
std::vector<std::pair<QString, double>> totalWellFlowPrTracer() const;
|
||||
|
||||
std::vector<std::vector<cvf::Vec3d>> m_pipeBranchesCLCoords;
|
||||
std::vector<std::vector<RigWellResultPoint>> m_pipeBranchesWellResultPoints;
|
||||
std::vector<std::vector<double>> m_pipeBranchesMeasuredDepths;
|
||||
|
||||
const std::map<QString, const std::vector<double>*>* m_tracerCellFractionValues;
|
||||
RigEclCellIndexCalculator m_cellIndexCalculator;
|
||||
std::vector<QString> m_tracerNames;
|
||||
double m_smallContributionsThreshold;
|
||||
bool m_isProducer;
|
||||
bool m_useTotalWellPhaseRateOnly;
|
||||
|
||||
struct BranchFlow
|
||||
{
|
||||
std::vector<double> depthValuesFromTop;
|
||||
std::vector<double> trueVerticalDepth;
|
||||
std::map<QString, std::vector<double>> accFlowPrTracer;
|
||||
std::map<QString, std::vector<double>> flowPrTracer;
|
||||
};
|
||||
|
||||
void storeFlowOnDepth( BranchFlow* branchFlow,
|
||||
double depthValue,
|
||||
const std::vector<double>& accFlowPrTracer,
|
||||
const std::vector<double>& flowPrTracer );
|
||||
void storeFlowOnDepthWTvd( BranchFlow* branchFlow,
|
||||
double depthValue,
|
||||
double trueVerticalDepth,
|
||||
const std::vector<double>& accFlowPrTracer,
|
||||
const std::vector<double>& flowPrTracer );
|
||||
|
||||
std::vector<double> accumulatedDsBranchFlowPrTracer( const BranchFlow& downStreamBranchFlow ) const;
|
||||
void addDownStreamBranchFlow( std::vector<double>* accFlowPrTracer,
|
||||
const std::vector<double>& accBranchFlowPrTracer ) const;
|
||||
|
||||
std::vector<BranchFlow> m_connectionFlowPrBranch;
|
||||
std::vector<BranchFlow> m_pseudoLengthFlowPrBranch;
|
||||
};
|
||||
@@ -0,0 +1,250 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2011- Statoil ASA
|
||||
// Copyright (C) 2013- Ceetron Solutions AS
|
||||
// Copyright (C) 2011-2012 Ceetron AS
|
||||
//
|
||||
// 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 "RigActiveCellInfo.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigActiveCellInfo::RigActiveCellInfo()
|
||||
: m_reservoirActiveCellCount( 0 )
|
||||
, m_reservoirCellResultCount( 0 )
|
||||
, m_activeCellPositionMin( cvf::Vec3d::ZERO )
|
||||
, m_activeCellPositionMax( cvf::Vec3d::ZERO )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigActiveCellInfo::setReservoirCellCount( size_t reservoirCellCount )
|
||||
{
|
||||
m_cellIndexToResultIndex.resize( reservoirCellCount, cvf::UNDEFINED_SIZE_T );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigActiveCellInfo::reservoirCellCount() const
|
||||
{
|
||||
return m_cellIndexToResultIndex.size();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigActiveCellInfo::reservoirCellResultCount() const
|
||||
{
|
||||
return m_reservoirCellResultCount;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigActiveCellInfo::isActive( size_t reservoirCellIndex ) const
|
||||
{
|
||||
if ( m_cellIndexToResultIndex.size() == 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
CVF_TIGHT_ASSERT( reservoirCellIndex < m_cellIndexToResultIndex.size() );
|
||||
|
||||
return m_cellIndexToResultIndex[reservoirCellIndex] != cvf::UNDEFINED_SIZE_T;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigActiveCellInfo::cellResultIndex( size_t reservoirCellIndex ) const
|
||||
{
|
||||
if ( m_cellIndexToResultIndex.size() == 0 )
|
||||
{
|
||||
return reservoirCellIndex;
|
||||
}
|
||||
|
||||
CVF_TIGHT_ASSERT( reservoirCellIndex < m_cellIndexToResultIndex.size() );
|
||||
|
||||
return m_cellIndexToResultIndex[reservoirCellIndex];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigActiveCellInfo::setCellResultIndex( size_t reservoirCellIndex, size_t reservoirCellResultIndex )
|
||||
{
|
||||
CVF_TIGHT_ASSERT( reservoirCellResultIndex < m_cellIndexToResultIndex.size() );
|
||||
|
||||
m_cellIndexToResultIndex[reservoirCellIndex] = reservoirCellResultIndex;
|
||||
|
||||
#pragma omp critical
|
||||
{
|
||||
if ( reservoirCellResultIndex >= m_reservoirCellResultCount )
|
||||
{
|
||||
m_reservoirCellResultCount = reservoirCellResultIndex + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigActiveCellInfo::setGridCount( size_t gridCount )
|
||||
{
|
||||
m_perGridActiveCellInfo.resize( gridCount );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigActiveCellInfo::setGridActiveCellCounts( size_t gridIndex, size_t activeCellCount )
|
||||
{
|
||||
CVF_ASSERT( gridIndex < m_perGridActiveCellInfo.size() );
|
||||
|
||||
m_perGridActiveCellInfo[gridIndex].setActiveCellCount( activeCellCount );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigActiveCellInfo::computeDerivedData()
|
||||
{
|
||||
m_reservoirActiveCellCount = 0;
|
||||
|
||||
for ( size_t i = 0; i < m_perGridActiveCellInfo.size(); i++ )
|
||||
{
|
||||
m_reservoirActiveCellCount += m_perGridActiveCellInfo[i].activeCellCount();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigActiveCellInfo::reservoirActiveCellCount() const
|
||||
{
|
||||
return m_reservoirActiveCellCount;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigActiveCellInfo::setIJKBoundingBox( const cvf::Vec3st& min, const cvf::Vec3st& max )
|
||||
{
|
||||
m_activeCellPositionMin = min;
|
||||
m_activeCellPositionMax = max;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigActiveCellInfo::IJKBoundingBox( cvf::Vec3st& min, cvf::Vec3st& max ) const
|
||||
{
|
||||
min = m_activeCellPositionMin;
|
||||
max = m_activeCellPositionMax;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigActiveCellInfo::gridActiveCellCounts( size_t gridIndex, size_t& activeCellCount ) const
|
||||
{
|
||||
activeCellCount = m_perGridActiveCellInfo[gridIndex].activeCellCount();
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::BoundingBox RigActiveCellInfo::geometryBoundingBox() const
|
||||
{
|
||||
return m_activeCellsBoundingBox;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigActiveCellInfo::setGeometryBoundingBox( cvf::BoundingBox bb )
|
||||
{
|
||||
m_activeCellsBoundingBox = bb;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigActiveCellInfo::clear()
|
||||
{
|
||||
m_perGridActiveCellInfo.clear();
|
||||
m_cellIndexToResultIndex.clear();
|
||||
m_reservoirActiveCellCount = 0;
|
||||
m_activeCellPositionMin = cvf::Vec3st( 0, 0, 0 );
|
||||
m_activeCellPositionMax = cvf::Vec3st( 0, 0, 0 );
|
||||
m_activeCellsBoundingBox.reset();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigActiveCellInfo::addLgr( size_t cellCount )
|
||||
{
|
||||
size_t currentGridCount = m_perGridActiveCellInfo.size();
|
||||
size_t currentActiveCellCount = reservoirActiveCellCount();
|
||||
size_t currentReservoirCellCount = reservoirCellCount();
|
||||
|
||||
setGridCount( currentGridCount + 1 );
|
||||
setGridActiveCellCounts( currentGridCount, cellCount );
|
||||
setReservoirCellCount( currentReservoirCellCount + cellCount );
|
||||
|
||||
computeDerivedData();
|
||||
|
||||
for ( size_t i = 0; i < cellCount; i++ )
|
||||
{
|
||||
setCellResultIndex( currentReservoirCellCount + i, currentActiveCellCount + i );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigActiveCellInfo::isCoarseningActive() const
|
||||
{
|
||||
return m_reservoirCellResultCount != m_reservoirActiveCellCount;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigActiveCellInfo::GridActiveCellCounts::GridActiveCellCounts()
|
||||
: m_activeCellCount( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigActiveCellInfo::GridActiveCellCounts::activeCellCount() const
|
||||
{
|
||||
return m_activeCellCount;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigActiveCellInfo::GridActiveCellCounts::setActiveCellCount( size_t activeCellCount )
|
||||
{
|
||||
m_activeCellCount = activeCellCount;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2011- Statoil ASA
|
||||
// Copyright (C) 2013- Ceetron Solutions AS
|
||||
// Copyright (C) 2011-2012 Ceetron AS
|
||||
//
|
||||
// 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 "cvfBoundingBox.h"
|
||||
#include "cvfObject.h"
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class RigActiveCellInfo : public cvf::Object
|
||||
{
|
||||
public:
|
||||
RigActiveCellInfo();
|
||||
|
||||
void setReservoirCellCount( size_t reservoirCellCount );
|
||||
size_t reservoirCellCount() const;
|
||||
size_t reservoirActiveCellCount() const;
|
||||
size_t reservoirCellResultCount() const;
|
||||
bool isCoarseningActive() const;
|
||||
|
||||
bool isActive( size_t reservoirCellIndex ) const;
|
||||
size_t cellResultIndex( size_t reservoirCellIndex ) const;
|
||||
void setCellResultIndex( size_t reservoirCellIndex, size_t globalResultCellIndex );
|
||||
|
||||
void setGridCount( size_t gridCount );
|
||||
void setGridActiveCellCounts( size_t gridIndex, size_t activeCellCount );
|
||||
void gridActiveCellCounts( size_t gridIndex, size_t& activeCellCount ) const;
|
||||
void computeDerivedData();
|
||||
|
||||
void setIJKBoundingBox( const cvf::Vec3st& min, const cvf::Vec3st& max );
|
||||
void IJKBoundingBox( cvf::Vec3st& min, cvf::Vec3st& max ) const;
|
||||
|
||||
cvf::BoundingBox geometryBoundingBox() const;
|
||||
void setGeometryBoundingBox( cvf::BoundingBox bb );
|
||||
|
||||
void clear();
|
||||
|
||||
void addLgr( size_t cellCount );
|
||||
|
||||
private:
|
||||
class GridActiveCellCounts
|
||||
{
|
||||
public:
|
||||
GridActiveCellCounts();
|
||||
|
||||
size_t activeCellCount() const;
|
||||
void setActiveCellCount( size_t activeCellCount );
|
||||
|
||||
private:
|
||||
size_t m_activeCellCount;
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<GridActiveCellCounts> m_perGridActiveCellInfo;
|
||||
|
||||
std::vector<size_t> m_cellIndexToResultIndex;
|
||||
|
||||
size_t m_reservoirActiveCellCount;
|
||||
size_t m_reservoirCellResultCount;
|
||||
|
||||
cvf::Vec3st m_activeCellPositionMin;
|
||||
cvf::Vec3st m_activeCellPositionMax;
|
||||
|
||||
cvf::BoundingBox m_activeCellsBoundingBox;
|
||||
};
|
||||
@@ -0,0 +1,89 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigActiveCellsResultAccessor.h"
|
||||
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigGridBase.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
RigActiveCellsResultAccessor::RigActiveCellsResultAccessor( const RigGridBase* grid,
|
||||
const std::vector<double>* reservoirResultValues,
|
||||
const RigActiveCellInfo* activeCellInfo )
|
||||
: m_activeCellInfo( activeCellInfo )
|
||||
, m_grid( grid )
|
||||
, m_reservoirResultValues( reservoirResultValues )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigActiveCellsResultAccessor::cellScalar( size_t gridLocalCellIndex ) const
|
||||
{
|
||||
if ( m_reservoirResultValues == nullptr || m_reservoirResultValues->size() == 0 ) return HUGE_VAL;
|
||||
|
||||
size_t reservoirCellIndex = m_grid->reservoirCellIndex( gridLocalCellIndex );
|
||||
size_t resultValueIndex = m_activeCellInfo->cellResultIndex( reservoirCellIndex );
|
||||
if ( resultValueIndex == cvf::UNDEFINED_SIZE_T ) return HUGE_VAL;
|
||||
|
||||
if ( resultValueIndex < m_reservoirResultValues->size() ) return m_reservoirResultValues->at( resultValueIndex );
|
||||
|
||||
CVF_TIGHT_ASSERT( resultValueIndex < m_activeCellInfo->reservoirActiveCellCount() ); // Because some static results
|
||||
// might lack LGR data
|
||||
|
||||
return HUGE_VAL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigActiveCellsResultAccessor::cellFaceScalar( size_t gridLocalCellIndex,
|
||||
cvf::StructGridInterface::FaceType faceId ) const
|
||||
{
|
||||
return cellScalar( gridLocalCellIndex );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigActiveCellsResultAccessor::cellScalarGlobIdx( size_t reservoirCellIndex ) const
|
||||
{
|
||||
if ( m_reservoirResultValues == nullptr || m_reservoirResultValues->size() == 0 ) return HUGE_VAL;
|
||||
|
||||
size_t resultValueIndex = m_activeCellInfo->cellResultIndex( reservoirCellIndex );
|
||||
if ( resultValueIndex == cvf::UNDEFINED_SIZE_T ) return HUGE_VAL;
|
||||
|
||||
if ( resultValueIndex < m_reservoirResultValues->size() ) return m_reservoirResultValues->at( resultValueIndex );
|
||||
|
||||
CVF_TIGHT_ASSERT( resultValueIndex < m_activeCellInfo->reservoirActiveCellCount() ); // Because some static results
|
||||
// might lack LGR data
|
||||
|
||||
return HUGE_VAL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigActiveCellsResultAccessor::cellFaceScalarGlobIdx( size_t globCellIndex,
|
||||
cvf::StructGridInterface::FaceType faceId ) const
|
||||
{
|
||||
return cellScalarGlobIdx( globCellIndex );
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigResultAccessor.h"
|
||||
|
||||
class RigGridBase;
|
||||
class RigActiveCellInfo;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigActiveCellsResultAccessor : public RigResultAccessor
|
||||
{
|
||||
public:
|
||||
RigActiveCellsResultAccessor( const RigGridBase* grid,
|
||||
const std::vector<double>* reservoirResultValues,
|
||||
const RigActiveCellInfo* activeCellInfo );
|
||||
|
||||
double cellScalar( size_t gridLocalCellIndex ) const override;
|
||||
double cellFaceScalar( size_t gridLocalCellIndex, cvf::StructGridInterface::FaceType faceId ) const override;
|
||||
|
||||
double cellScalarGlobIdx( size_t globCellIndex ) const override;
|
||||
double cellFaceScalarGlobIdx( size_t globCellIndex, cvf::StructGridInterface::FaceType faceId ) const override;
|
||||
|
||||
private:
|
||||
const RigActiveCellInfo* m_activeCellInfo;
|
||||
const RigGridBase* m_grid;
|
||||
const std::vector<double>* m_reservoirResultValues;
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigAllGridCellsResultAccessor.h"
|
||||
|
||||
#include "RigGridBase.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigAllGridCellsResultAccessor::RigAllGridCellsResultAccessor( const RigGridBase* grid,
|
||||
const std::vector<double>* reservoirResultValues )
|
||||
: m_grid( grid )
|
||||
, m_reservoirResultValues( reservoirResultValues )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigAllGridCellsResultAccessor::cellScalar( size_t gridLocalCellIndex ) const
|
||||
{
|
||||
if ( m_reservoirResultValues->size() == 0 ) return HUGE_VAL;
|
||||
|
||||
size_t reservoirCellIndex = m_grid->reservoirCellIndex( gridLocalCellIndex );
|
||||
CVF_TIGHT_ASSERT( reservoirCellIndex < m_reservoirResultValues->size() );
|
||||
|
||||
return m_reservoirResultValues->at( reservoirCellIndex );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigAllGridCellsResultAccessor::cellFaceScalar( size_t gridLocalCellIndex,
|
||||
cvf::StructGridInterface::FaceType faceId ) const
|
||||
{
|
||||
return cellScalar( gridLocalCellIndex );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigAllGridCellsResultAccessor::cellScalarGlobIdx( size_t globCellIndex ) const
|
||||
{
|
||||
if ( m_reservoirResultValues->size() == 0 ) return HUGE_VAL;
|
||||
|
||||
CVF_TIGHT_ASSERT( globCellIndex < m_reservoirResultValues->size() );
|
||||
|
||||
return m_reservoirResultValues->at( globCellIndex );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigAllGridCellsResultAccessor::cellFaceScalarGlobIdx( size_t globCellIndex,
|
||||
cvf::StructGridInterface::FaceType faceId ) const
|
||||
{
|
||||
return cellScalarGlobIdx( globCellIndex );
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigActiveCellsResultAccessor.h"
|
||||
|
||||
class RigGridBase;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigAllGridCellsResultAccessor : public RigResultAccessor
|
||||
{
|
||||
public:
|
||||
RigAllGridCellsResultAccessor( const RigGridBase* grid, const std::vector<double>* reservoirResultValues );
|
||||
|
||||
double cellScalar( size_t gridLocalCellIndex ) const override;
|
||||
double cellFaceScalar( size_t gridLocalCellIndex, cvf::StructGridInterface::FaceType faceId ) const override;
|
||||
double cellScalarGlobIdx( size_t globCellIndex ) const override;
|
||||
double cellFaceScalarGlobIdx( size_t globCellIndex, cvf::StructGridInterface::FaceType faceId ) const override;
|
||||
|
||||
private:
|
||||
const RigGridBase* m_grid;
|
||||
const std::vector<double>* m_reservoirResultValues;
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigAllanDiagramData.h"
|
||||
|
||||
#include "RigCaseCellResultsData.h"
|
||||
#include "RigFormationNames.h"
|
||||
#include "RigMainGrid.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigAllanDiagramData::RigAllanDiagramData()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigAllanDiagramData::~RigAllanDiagramData()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cvfObject.h"
|
||||
#include <QString>
|
||||
|
||||
#include <map>
|
||||
|
||||
class RigAllanDiagramData : public cvf::Object
|
||||
{
|
||||
public:
|
||||
RigAllanDiagramData();
|
||||
~RigAllanDiagramData() override;
|
||||
|
||||
const std::map<std::pair<int, int>, int>& formationCombinationToCategory()
|
||||
{
|
||||
return m_formationCombinationToCategory;
|
||||
}
|
||||
|
||||
std::pair<int, int> formationIndexCombinationFromCategory( int category )
|
||||
{
|
||||
for ( auto it : m_formationCombinationToCategory )
|
||||
{
|
||||
if ( it.second == category )
|
||||
{
|
||||
return it.first;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair( -1, -1 );
|
||||
}
|
||||
|
||||
void setFormationCombinationToCategorymap( const std::map<std::pair<int, int>, int>& mapping )
|
||||
{
|
||||
m_formationCombinationToCategory = mapping;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<std::pair<int, int>, int> m_formationCombinationToCategory;
|
||||
};
|
||||
@@ -0,0 +1,183 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2019- 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 "RigCaseCellResultCalculator.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "RigCaseCellResultsData.h"
|
||||
#include "RigEclipseCaseData.h"
|
||||
#include "RigEclipseResultAddress.h"
|
||||
#include "RigGridManager.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigResultAccessorFactory.h"
|
||||
#include "RigResultModifier.h"
|
||||
#include "RigResultModifierFactory.h"
|
||||
|
||||
#include "RimEclipseCase.h"
|
||||
#include "RimProject.h"
|
||||
|
||||
#include "cvfAssert.h"
|
||||
|
||||
#include <algorithm>
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigCaseCellResultCalculator::computeDifference( RigEclipseCaseData* sourceCase,
|
||||
RiaDefines::PorosityModelType porosityModel,
|
||||
const RigEclipseResultAddress& address )
|
||||
{
|
||||
CVF_ASSERT( address.isValid() );
|
||||
CVF_ASSERT( address.hasDifferenceCase() || address.isTimeLapse() );
|
||||
|
||||
// Assume at this stage that data for the case is available
|
||||
// It is up to the caller to make sure the case is read from file
|
||||
|
||||
RigEclipseCaseData* baseCase = sourceCase;
|
||||
|
||||
if ( address.hasDifferenceCase() )
|
||||
{
|
||||
{
|
||||
auto eclipseCases = RimProject::current()->eclipseCases();
|
||||
for ( RimEclipseCase* c : eclipseCases )
|
||||
{
|
||||
if ( c && c->caseId() == address.m_differenceCaseId && c->eclipseCaseData() )
|
||||
{
|
||||
baseCase = c->eclipseCaseData();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !baseCase || !sourceCase )
|
||||
{
|
||||
RiaLogging::error( "Missing input case for difference calculator" );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
RigMainGrid* sourceMainGrid = sourceCase->mainGrid();
|
||||
RigMainGrid* baseMainGrid = baseCase->mainGrid();
|
||||
|
||||
if ( !RigGridManager::isMainGridDimensionsEqual( sourceMainGrid, baseMainGrid ) )
|
||||
{
|
||||
RiaLogging::error( "Case difference : Grid cases do not match" );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
RigCaseCellResultsData* baseCaseResults = baseCase->results( porosityModel );
|
||||
RigCaseCellResultsData* sourceCaseResults = sourceCase->results( porosityModel );
|
||||
|
||||
if ( !baseCaseResults || !sourceCaseResults )
|
||||
{
|
||||
RiaLogging::error( "Missing result data for difference calculator" );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
RigEclipseResultAddress nativeAddress( address );
|
||||
nativeAddress.m_differenceCaseId = RigEclipseResultAddress::noCaseDiffValue();
|
||||
nativeAddress.m_timeLapseBaseFrameIdx = RigEclipseResultAddress::noTimeLapseValue();
|
||||
if ( !sourceCaseResults->ensureKnownResultLoaded( nativeAddress ) )
|
||||
{
|
||||
RiaLogging::error( "Failed to load destination diff result" );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !baseCaseResults->ensureKnownResultLoaded( nativeAddress ) )
|
||||
{
|
||||
RiaLogging::error( "Failed to load difference result" );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize difference result with infinity for correct number of time steps and values per time step
|
||||
{
|
||||
const std::vector<std::vector<double>>& srcFrames = sourceCaseResults->cellScalarResults( nativeAddress );
|
||||
std::vector<std::vector<double>>* diffResultFrames =
|
||||
sourceCaseResults->modifiableCellScalarResultTimesteps( address );
|
||||
diffResultFrames->resize( srcFrames.size() );
|
||||
for ( size_t fIdx = 0; fIdx < srcFrames.size(); ++fIdx )
|
||||
{
|
||||
const std::vector<double>& srcVals = srcFrames[fIdx];
|
||||
std::vector<double>& dstVals = diffResultFrames->at( fIdx );
|
||||
|
||||
// Clear the values, and resize with infinity as default value
|
||||
dstVals.clear();
|
||||
dstVals.resize( srcVals.size(), std::numeric_limits<double>::infinity() );
|
||||
}
|
||||
}
|
||||
|
||||
size_t baseFrameCount = baseCaseResults->cellScalarResults( nativeAddress ).size();
|
||||
size_t sourceFrameCount = sourceCaseResults->cellScalarResults( nativeAddress ).size();
|
||||
size_t maxFrameCount = 0;
|
||||
if ( address.isTimeLapse() )
|
||||
{
|
||||
// We have one defined time step for base case, loop over all source time steps
|
||||
maxFrameCount = sourceFrameCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We compare cases, diff is computed time index by time index. Use minimum frame count.
|
||||
maxFrameCount = std::min( baseFrameCount, sourceFrameCount );
|
||||
}
|
||||
|
||||
size_t maxGridCount = std::min( baseMainGrid->gridCount(), sourceMainGrid->gridCount() );
|
||||
|
||||
for ( size_t gridIdx = 0; gridIdx < maxGridCount; ++gridIdx )
|
||||
{
|
||||
auto grid = sourceMainGrid->gridByIndex( gridIdx );
|
||||
const RigActiveCellInfo* activeCellInfo = sourceCaseResults->activeCellInfo();
|
||||
|
||||
for ( size_t fIdx = 0; fIdx < maxFrameCount; ++fIdx )
|
||||
{
|
||||
cvf::ref<RigResultAccessor> sourceResultAccessor =
|
||||
RigResultAccessorFactory::createFromResultAddress( sourceCase, gridIdx, porosityModel, fIdx, nativeAddress );
|
||||
|
||||
cvf::ref<RigResultModifier> resultModifier =
|
||||
RigResultModifierFactory::createResultModifier( sourceCase, gridIdx, porosityModel, fIdx, address );
|
||||
|
||||
size_t baseFrameIdx = fIdx;
|
||||
if ( address.isTimeLapse() )
|
||||
{
|
||||
baseFrameIdx = address.m_timeLapseBaseFrameIdx;
|
||||
}
|
||||
|
||||
cvf::ref<RigResultAccessor> baseResultAccessor =
|
||||
RigResultAccessorFactory::createFromResultAddress( baseCase, gridIdx, porosityModel, baseFrameIdx, nativeAddress );
|
||||
|
||||
for ( size_t localGridCellIdx = 0; localGridCellIdx < grid->cellCount(); localGridCellIdx++ )
|
||||
{
|
||||
size_t reservoirCellIndex = grid->reservoirCellIndex( localGridCellIdx );
|
||||
if ( activeCellInfo->isActive( reservoirCellIndex ) )
|
||||
{
|
||||
double sourceVal = sourceResultAccessor->cellScalar( localGridCellIdx );
|
||||
double baseVal = baseResultAccessor->cellScalar( localGridCellIdx );
|
||||
|
||||
double difference = sourceVal - baseVal;
|
||||
|
||||
resultModifier->setCellScalar( localGridCellIdx, difference );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2019- 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 "RiaPorosityModel.h"
|
||||
|
||||
class RigEclipseCaseData;
|
||||
class RigEclipseResultAddress;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigCaseCellResultCalculator
|
||||
{
|
||||
public:
|
||||
static bool computeDifference( RigEclipseCaseData* destination,
|
||||
RiaDefines::PorosityModelType porosityModel,
|
||||
const RigEclipseResultAddress& address );
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,212 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2011- Statoil ASA
|
||||
// Copyright (C) 2013- Ceetron Solutions AS
|
||||
// Copyright (C) 2011-2012 Ceetron AS
|
||||
//
|
||||
// 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 "RiaDefines.h"
|
||||
#include "RiaPorosityModel.h"
|
||||
|
||||
#include "RigEclipseResultAddress.h"
|
||||
|
||||
#include "cvfCollection.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
class RifReaderInterface;
|
||||
class RigActiveCellInfo;
|
||||
class RigMainGrid;
|
||||
class RigEclipseResultInfo;
|
||||
class RigStatisticsDataCache;
|
||||
class RigEclipseTimeStepInfo;
|
||||
class RigEclipseCaseData;
|
||||
class RigFormationNames;
|
||||
class RigAllanDiagramData;
|
||||
|
||||
class RimEclipseCase;
|
||||
|
||||
//==================================================================================================
|
||||
/// Class containing the results for the complete number of active cells. Both main grid and LGR's
|
||||
//==================================================================================================
|
||||
class RigCaseCellResultsData : public cvf::Object
|
||||
{
|
||||
public:
|
||||
explicit RigCaseCellResultsData( RigEclipseCaseData* ownerCaseData, RiaDefines::PorosityModelType porosityModel );
|
||||
|
||||
// Initialization
|
||||
|
||||
void setReaderInterface( RifReaderInterface* readerInterface );
|
||||
const RifReaderInterface* readerInterface() const;
|
||||
void setHdf5Filename( const QString& hdf5SourSimFilename );
|
||||
void setActiveFormationNames( RigFormationNames* activeFormationNames );
|
||||
const RigFormationNames* activeFormationNames() const;
|
||||
RigAllanDiagramData* allanDiagramData();
|
||||
|
||||
void setMainGrid( RigMainGrid* ownerGrid );
|
||||
void setActiveCellInfo( RigActiveCellInfo* activeCellInfo );
|
||||
RigActiveCellInfo* activeCellInfo();
|
||||
const RigActiveCellInfo* activeCellInfo() const;
|
||||
|
||||
// Access the results data
|
||||
|
||||
const std::vector<std::vector<double>>& cellScalarResults( const RigEclipseResultAddress& resVarAddr ) const;
|
||||
const std::vector<double>& cellScalarResults( const RigEclipseResultAddress& resVarAddr, size_t timeStepIndex ) const;
|
||||
std::vector<std::vector<double>>* modifiableCellScalarResultTimesteps( const RigEclipseResultAddress& resVarAddr );
|
||||
std::vector<double>* modifiableCellScalarResult( const RigEclipseResultAddress& resVarAddr, size_t timeStepIndex );
|
||||
|
||||
bool isUsingGlobalActiveIndex( const RigEclipseResultAddress& resVarAddr ) const;
|
||||
|
||||
static const std::vector<double>* getResultIndexableStaticResult( RigActiveCellInfo* actCellInfo,
|
||||
RigCaseCellResultsData* gridCellResults,
|
||||
QString porvResultName,
|
||||
std::vector<double>& activeCellsResultsTempContainer );
|
||||
// Statistic values of the results
|
||||
|
||||
void recalculateStatistics( const RigEclipseResultAddress& resVarAddr );
|
||||
void minMaxCellScalarValues( const RigEclipseResultAddress& resVarAddr, double& min, double& max );
|
||||
void minMaxCellScalarValues( const RigEclipseResultAddress& resVarAddr, size_t timeStepIndex, double& min, double& max );
|
||||
void posNegClosestToZero( const RigEclipseResultAddress& resVarAddr, double& pos, double& neg );
|
||||
void posNegClosestToZero( const RigEclipseResultAddress& resVarAddr, size_t timeStepIndex, double& pos, double& neg );
|
||||
const std::vector<size_t>& cellScalarValuesHistogram( const RigEclipseResultAddress& resVarAddr );
|
||||
const std::vector<size_t>& cellScalarValuesHistogram( const RigEclipseResultAddress& resVarAddr, size_t timeStepIndex );
|
||||
void p10p90CellScalarValues( const RigEclipseResultAddress& resVarAddr, double& p10, double& p90 );
|
||||
void p10p90CellScalarValues( const RigEclipseResultAddress& resVarAddr, size_t timeStepIndex, double& p10, double& p90 );
|
||||
void meanCellScalarValues( const RigEclipseResultAddress& resVarAddr, double& meanValue );
|
||||
void meanCellScalarValues( const RigEclipseResultAddress& resVarAddr, size_t timeStepIndex, double& meanValue );
|
||||
const std::vector<int>& uniqueCellScalarValues( const RigEclipseResultAddress& resVarAddr );
|
||||
void sumCellScalarValues( const RigEclipseResultAddress& resVarAddr, double& sumValue );
|
||||
void sumCellScalarValues( const RigEclipseResultAddress& resVarAddr, size_t timeStepIndex, double& sumValue );
|
||||
void mobileVolumeWeightedMean( const RigEclipseResultAddress& resVarAddr, double& meanValue );
|
||||
void mobileVolumeWeightedMean( const RigEclipseResultAddress& resVarAddr, size_t timeStepIndex, double& meanValue );
|
||||
|
||||
// Access meta-information about the results
|
||||
|
||||
size_t timeStepCount( const RigEclipseResultAddress& resVarAddr ) const;
|
||||
size_t maxTimeStepCount( RigEclipseResultAddress* resultAddressWithMostTimeSteps = nullptr ) const;
|
||||
|
||||
std::vector<QDateTime> allTimeStepDatesFromEclipseReader() const;
|
||||
std::vector<QDateTime> timeStepDates() const;
|
||||
std::vector<QDateTime> timeStepDates( const RigEclipseResultAddress& resVarAddr ) const;
|
||||
std::vector<double> daysSinceSimulationStart() const;
|
||||
std::vector<double> daysSinceSimulationStart( const RigEclipseResultAddress& resVarAddr ) const;
|
||||
int reportStepNumber( const RigEclipseResultAddress& resVarAddr, size_t timeStepIndex ) const;
|
||||
|
||||
std::vector<RigEclipseTimeStepInfo> timeStepInfos( const RigEclipseResultAddress& resVarAddr ) const;
|
||||
void setTimeStepInfos( const RigEclipseResultAddress& resVarAddr,
|
||||
const std::vector<RigEclipseTimeStepInfo>& timeStepInfos );
|
||||
|
||||
void clearScalarResult( RiaDefines::ResultCatType type, const QString& resultName );
|
||||
void clearScalarResult( const RigEclipseResultAddress& resultAddress );
|
||||
void clearAllResults();
|
||||
void freeAllocatedResultsData();
|
||||
void eraseAllSourSimData();
|
||||
|
||||
QStringList resultNames( RiaDefines::ResultCatType type ) const;
|
||||
std::vector<RigEclipseResultAddress> existingResults() const;
|
||||
const RigEclipseResultInfo* resultInfo( const RigEclipseResultAddress& resVarAddr ) const;
|
||||
bool updateResultName( RiaDefines::ResultCatType resultType, const QString& oldName, const QString& newName );
|
||||
QString makeResultNameUnique( const QString& resultNameProposal ) const;
|
||||
|
||||
void ensureKnownResultLoadedForTimeStep( const RigEclipseResultAddress& resultAddress, size_t timeStepIndex );
|
||||
bool ensureKnownResultLoaded( const RigEclipseResultAddress& resultAddress );
|
||||
|
||||
bool findAndLoadResultByName( const QString& resultName,
|
||||
const std::vector<RiaDefines::ResultCatType>& resultCategorySearchOrder );
|
||||
|
||||
bool hasResultEntry( const RigEclipseResultAddress& resultAddress ) const;
|
||||
bool isResultLoaded( const RigEclipseResultAddress& resultAddress ) const;
|
||||
void createResultEntry( const RigEclipseResultAddress& resultAddress, bool needsToBeStored );
|
||||
void createPlaceholderResultEntries();
|
||||
void computeDepthRelatedResults();
|
||||
void computeCellVolumes();
|
||||
bool hasFlowDiagUsableFluxes() const;
|
||||
|
||||
static void copyResultsMetaDataFromMainCase( RigEclipseCaseData* mainCaseResultsData,
|
||||
RiaDefines::PorosityModelType poroModel,
|
||||
std::vector<RimEclipseCase*> destinationCases );
|
||||
|
||||
private:
|
||||
size_t findOrLoadKnownScalarResult( const RigEclipseResultAddress& resVarAddr );
|
||||
size_t findOrLoadKnownScalarResultByResultTypeOrder( const RigEclipseResultAddress& resVarAddr,
|
||||
const std::vector<RiaDefines::ResultCatType>& resultCategorySearchOrder );
|
||||
|
||||
size_t findOrLoadKnownScalarResultForTimeStep( const RigEclipseResultAddress& resVarAddr, size_t timeStepIndex );
|
||||
size_t findOrCreateScalarResultIndex( const RigEclipseResultAddress& resVarAddr, bool needsToBeStored );
|
||||
|
||||
size_t findScalarResultIndexFromAddress( const RigEclipseResultAddress& resVarAddr ) const;
|
||||
|
||||
size_t addStaticScalarResult( RiaDefines::ResultCatType type,
|
||||
const QString& resultName,
|
||||
bool needsToBeStored,
|
||||
size_t resultValueCount );
|
||||
|
||||
const std::vector<RigEclipseResultInfo>& infoForEachResultIndex();
|
||||
size_t resultCount() const;
|
||||
|
||||
bool mustBeCalculated( size_t scalarResultIndex ) const;
|
||||
void setMustBeCalculated( size_t scalarResultIndex );
|
||||
|
||||
void computeSOILForTimeStep( size_t timeStepIndex );
|
||||
void testAndComputeSgasForTimeStep( size_t timeStepIndex );
|
||||
|
||||
bool hasCompleteTransmissibilityResults() const;
|
||||
|
||||
void computeRiTransComponent( const QString& riTransComponentResultName );
|
||||
void computeNncCombRiTrans();
|
||||
|
||||
void computeRiMULTComponent( const QString& riMultCompName );
|
||||
void computeNncCombRiMULT();
|
||||
void computeRiTRANSbyAreaComponent( const QString& riTransByAreaCompResultName );
|
||||
void computeNncCombRiTRANSbyArea();
|
||||
|
||||
void computeCompletionTypeForTimeStep( size_t timeStep );
|
||||
double darchysValue();
|
||||
|
||||
void computeOilVolumes();
|
||||
void computeMobilePV();
|
||||
|
||||
bool isDataPresent( size_t scalarResultIndex ) const;
|
||||
|
||||
void assignValuesToTemporaryLgrs( const QString& resultName, std::vector<double>& values );
|
||||
|
||||
RigStatisticsDataCache* statistics( const RigEclipseResultAddress& resVarAddr );
|
||||
|
||||
static void
|
||||
computeAllanResults( RigCaseCellResultsData* cellResultsData, RigMainGrid* mainGrid, bool includeInactiveCells );
|
||||
|
||||
private:
|
||||
cvf::ref<RifReaderInterface> m_readerInterface;
|
||||
cvf::cref<RigFormationNames> m_activeFormationNamesData;
|
||||
cvf::ref<RigAllanDiagramData> m_allanDiagramData;
|
||||
|
||||
std::vector<std::vector<std::vector<double>>> m_cellScalarResults; ///< Scalar results on the complete reservoir for
|
||||
///< each Result index (ResultVariable) and timestep
|
||||
cvf::Collection<RigStatisticsDataCache> m_statisticsDataCache;
|
||||
std::vector<RigEclipseResultInfo> m_resultInfos;
|
||||
std::map<RigEclipseResultAddress, size_t> m_addressToResultIndexMap;
|
||||
|
||||
RigMainGrid* m_ownerMainGrid;
|
||||
RigEclipseCaseData* m_ownerCaseData;
|
||||
RigActiveCellInfo* m_activeCellInfo;
|
||||
RiaDefines::PorosityModelType m_porosityModel;
|
||||
};
|
||||
@@ -0,0 +1,220 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2016- Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigCaseRealizationParameters.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCaseRealizationParameters::Value::Value()
|
||||
: m_valueType( TYPE_NONE )
|
||||
, m_numericValue( std::numeric_limits<double>::infinity() )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCaseRealizationParameters::Value::Value( double value )
|
||||
: Value()
|
||||
{
|
||||
setValue( value );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCaseRealizationParameters::Value::Value( const QString& value )
|
||||
: Value()
|
||||
{
|
||||
setValue( value );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCaseRealizationParameters::Value::setValue( double value )
|
||||
{
|
||||
m_valueType = TYPE_NUMERIC;
|
||||
m_numericValue = value;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCaseRealizationParameters::Value::setValue( const QString& value )
|
||||
{
|
||||
m_valueType = TYPE_TEXT;
|
||||
m_textValue = value;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCaseRealizationParameters::Value::numericValue() const
|
||||
{
|
||||
return m_numericValue;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const QString& RigCaseRealizationParameters::Value::textValue() const
|
||||
{
|
||||
return m_textValue;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCaseRealizationParameters::addParameter( const QString& name, double value )
|
||||
{
|
||||
m_parameters[name].setValue( value );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCaseRealizationParameters::addParameter( const QString& name, const QString& value )
|
||||
{
|
||||
m_parameters[name].setValue( value );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCaseRealizationParameters::Value RigCaseRealizationParameters::parameterValue( const QString& name )
|
||||
{
|
||||
if ( m_parameters.count( name ) == 0 ) return Value();
|
||||
return m_parameters[name];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::map<QString, RigCaseRealizationParameters::Value> RigCaseRealizationParameters::parameters() const
|
||||
{
|
||||
return m_parameters;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::set<QString> RigCaseRealizationParameters::parameterNames() const
|
||||
{
|
||||
std::set<QString> names;
|
||||
for ( auto& par : parameters() )
|
||||
names.insert( par.first );
|
||||
return names;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigCaseRealizationParameters::parameterHash( const QString& name ) const
|
||||
{
|
||||
auto itr = m_parameters.find( name );
|
||||
if ( itr == m_parameters.end() || !itr->second.isValid() ) return 0;
|
||||
|
||||
std::hash<std::string> stringHasher;
|
||||
std::hash<double> doubleHasher;
|
||||
size_t nameHash;
|
||||
size_t valueHash = 0;
|
||||
|
||||
nameHash = stringHasher( name.toStdString() );
|
||||
|
||||
auto value = itr->second;
|
||||
if ( value.isNumeric() )
|
||||
{
|
||||
valueHash = doubleHasher( value.numericValue() );
|
||||
}
|
||||
else if ( value.isText() )
|
||||
{
|
||||
valueHash = stringHasher( value.textValue().toStdString() );
|
||||
}
|
||||
|
||||
QString s = QString::number( nameHash ) + QString::number( valueHash );
|
||||
|
||||
return stringHasher( s.toStdString() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigCaseRealizationParameters::parametersHash()
|
||||
{
|
||||
if ( m_parametersHash == 0 ) calculateParametersHash();
|
||||
return m_parametersHash;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
int RigCaseRealizationParameters::realizationNumber() const
|
||||
{
|
||||
return m_realizationNumber;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCaseRealizationParameters::setRealizationNumber( int realization )
|
||||
{
|
||||
m_realizationNumber = realization;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCaseRealizationParameters::clearParametersHash()
|
||||
{
|
||||
m_parametersHash = 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCaseRealizationParameters::calculateParametersHash( const std::set<QString>& paramNames /*= std::set<QString>()*/ )
|
||||
{
|
||||
QStringList hashes;
|
||||
|
||||
if ( paramNames.empty() )
|
||||
{
|
||||
for ( auto param : m_parameters )
|
||||
{
|
||||
hashes.push_back( QString::number( parameterHash( param.first ) ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( const auto& paramName : paramNames )
|
||||
{
|
||||
if ( m_parameters.find( paramName ) == m_parameters.end() ) return;
|
||||
hashes.push_back( QString::number( parameterHash( paramName ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
std::hash<std::string> stringHasher;
|
||||
m_parametersHash = stringHasher( hashes.join( "" ).toStdString() );
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2016- Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cvfObject.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
//==================================================================================================
|
||||
//
|
||||
//
|
||||
//==================================================================================================
|
||||
class RigCaseRealizationParameters
|
||||
{
|
||||
public:
|
||||
// Internal class
|
||||
class Value
|
||||
{
|
||||
enum ValueType
|
||||
{
|
||||
TYPE_NONE,
|
||||
TYPE_NUMERIC,
|
||||
TYPE_TEXT
|
||||
};
|
||||
|
||||
public:
|
||||
Value();
|
||||
Value( double value );
|
||||
Value( const QString& value );
|
||||
|
||||
void setValue( double value );
|
||||
void setValue( const QString& value );
|
||||
|
||||
bool isValid() const { return m_valueType != TYPE_NONE; }
|
||||
bool isNumeric() const { return m_valueType == TYPE_NUMERIC; }
|
||||
bool isText() const { return m_valueType == TYPE_TEXT; }
|
||||
|
||||
double numericValue() const;
|
||||
const QString& textValue() const;
|
||||
|
||||
private:
|
||||
ValueType m_valueType;
|
||||
double m_numericValue;
|
||||
QString m_textValue;
|
||||
};
|
||||
|
||||
RigCaseRealizationParameters()
|
||||
: m_parametersHash( 0 )
|
||||
, m_realizationNumber( -1 )
|
||||
{
|
||||
}
|
||||
|
||||
void addParameter( const QString& name, double value );
|
||||
void addParameter( const QString& name, const QString& value );
|
||||
Value parameterValue( const QString& name );
|
||||
|
||||
std::map<QString, Value> parameters() const;
|
||||
std::set<QString> parameterNames() const;
|
||||
|
||||
size_t parameterHash( const QString& name ) const;
|
||||
size_t parametersHash();
|
||||
|
||||
int realizationNumber() const;
|
||||
void setRealizationNumber( int realization );
|
||||
|
||||
void clearParametersHash();
|
||||
void calculateParametersHash( const std::set<QString>& paramNames = std::set<QString>() );
|
||||
|
||||
private:
|
||||
std::map<QString, Value> m_parameters;
|
||||
size_t m_parametersHash;
|
||||
int m_realizationNumber;
|
||||
};
|
||||
@@ -0,0 +1,194 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2015- Statoil ASA
|
||||
// Copyright (C) 2015- Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigCaseToCaseCellMapper.h"
|
||||
#include "RigCaseToCaseCellMapperTools.h"
|
||||
|
||||
#include "RigFemPart.h"
|
||||
#include "RigFemPartGrid.h"
|
||||
#include "RigMainGrid.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCaseToCaseCellMapper::RigCaseToCaseCellMapper( RigMainGrid* masterEclGrid, RigMainGrid* dependentEclGrid )
|
||||
: m_masterGrid( masterEclGrid )
|
||||
, m_dependentGrid( dependentEclGrid )
|
||||
, m_masterFemPart( nullptr )
|
||||
, m_dependentFemPart( nullptr )
|
||||
{
|
||||
m_masterCellOrIntervalIndex.resize( dependentEclGrid->globalCellArray().size(), cvf::UNDEFINED_INT );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCaseToCaseCellMapper::RigCaseToCaseCellMapper( RigFemPart* masterFemPart, RigMainGrid* dependentEclGrid )
|
||||
: m_masterGrid( nullptr )
|
||||
, m_dependentGrid( dependentEclGrid )
|
||||
, m_masterFemPart( masterFemPart )
|
||||
, m_dependentFemPart( nullptr )
|
||||
{
|
||||
m_masterCellOrIntervalIndex.resize( dependentEclGrid->globalCellArray().size(), cvf::UNDEFINED_INT );
|
||||
this->calculateEclToGeomCellMapping( dependentEclGrid, masterFemPart, false );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCaseToCaseCellMapper::RigCaseToCaseCellMapper( RigFemPart* masterFemPart, RigFemPart* dependentFemPart )
|
||||
: m_masterGrid( nullptr )
|
||||
, m_dependentGrid( nullptr )
|
||||
, m_masterFemPart( masterFemPart )
|
||||
, m_dependentFemPart( dependentFemPart )
|
||||
{
|
||||
m_masterCellOrIntervalIndex.resize( dependentFemPart->elementCount(), cvf::UNDEFINED_INT );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCaseToCaseCellMapper::RigCaseToCaseCellMapper( RigMainGrid* masterEclGrid, RigFemPart* dependentFemPart )
|
||||
: m_masterGrid( masterEclGrid )
|
||||
, m_dependentGrid( nullptr )
|
||||
, m_masterFemPart( dependentFemPart )
|
||||
, m_dependentFemPart( nullptr )
|
||||
{
|
||||
m_masterCellOrIntervalIndex.resize( dependentFemPart->elementCount(), cvf::UNDEFINED_INT );
|
||||
this->calculateEclToGeomCellMapping( masterEclGrid, dependentFemPart, true );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const int* RigCaseToCaseCellMapper::masterCaseCellIndices( int dependentCaseReservoirCellIndex,
|
||||
int* masterCaseCellIndexCount ) const
|
||||
{
|
||||
int seriesIndex = m_masterCellOrIntervalIndex[dependentCaseReservoirCellIndex];
|
||||
|
||||
if ( seriesIndex == cvf::UNDEFINED_INT )
|
||||
{
|
||||
( *masterCaseCellIndexCount ) = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ( seriesIndex < 0 )
|
||||
{
|
||||
( *masterCaseCellIndexCount ) = static_cast<int>( m_masterCellIndexSeries[-seriesIndex].size() );
|
||||
return &( m_masterCellIndexSeries[-seriesIndex][0] );
|
||||
}
|
||||
else
|
||||
{
|
||||
( *masterCaseCellIndexCount ) = 1;
|
||||
return &( m_masterCellOrIntervalIndex[dependentCaseReservoirCellIndex] );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCaseToCaseCellMapper::addMapping( int depCaseCellIdx, int masterCaseMatchingCell )
|
||||
{
|
||||
int mcOrSeriesIdx = m_masterCellOrIntervalIndex[depCaseCellIdx];
|
||||
if ( mcOrSeriesIdx == cvf::UNDEFINED_INT )
|
||||
{
|
||||
m_masterCellOrIntervalIndex[depCaseCellIdx] = masterCaseMatchingCell;
|
||||
}
|
||||
else if ( mcOrSeriesIdx >= 0 )
|
||||
{
|
||||
int newSeriesIdx = static_cast<int>( m_masterCellIndexSeries.size() );
|
||||
m_masterCellIndexSeries.push_back( std::vector<int>() );
|
||||
m_masterCellIndexSeries.back().push_back( mcOrSeriesIdx );
|
||||
m_masterCellIndexSeries.back().push_back( masterCaseMatchingCell );
|
||||
m_masterCellOrIntervalIndex[depCaseCellIdx] = -newSeriesIdx;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_masterCellIndexSeries[-mcOrSeriesIdx].push_back( masterCaseMatchingCell );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCaseToCaseCellMapper::calculateEclToGeomCellMapping( RigMainGrid* masterEclGrid,
|
||||
RigFemPart* dependentFemPart,
|
||||
bool eclipseIsMaster )
|
||||
{
|
||||
// Find tolerance
|
||||
|
||||
double cellSizeI, cellSizeJ, cellSizeK;
|
||||
masterEclGrid->characteristicCellSizes( &cellSizeI, &cellSizeJ, &cellSizeK );
|
||||
|
||||
double xyTolerance = cellSizeI * 0.4;
|
||||
double zTolerance = cellSizeK * 0.4;
|
||||
|
||||
bool isEclFaceNormalsOutwards = masterEclGrid->isFaceNormalsOutwards();
|
||||
|
||||
cvf::Vec3d elmCorners[8];
|
||||
|
||||
size_t cellCount = masterEclGrid->cellCount();
|
||||
|
||||
for ( size_t cellIdx = 0; cellIdx < cellCount; ++cellIdx )
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
{
|
||||
// For debugging
|
||||
size_t i, j, k;
|
||||
masterEclGrid->ijkFromCellIndex( cellIdx, &i, &j, &k ); // Will not work when LGR present
|
||||
}
|
||||
#endif
|
||||
|
||||
cvf::Vec3d geoMechConvertedEclCell[8];
|
||||
RigCaseToCaseCellMapperTools::estimatedFemCellFromEclCell( masterEclGrid, cellIdx, geoMechConvertedEclCell );
|
||||
|
||||
cvf::BoundingBox elmBBox;
|
||||
for ( int i = 0; i < 8; ++i )
|
||||
elmBBox.add( geoMechConvertedEclCell[i] );
|
||||
|
||||
std::vector<size_t> closeElements;
|
||||
dependentFemPart->findIntersectingCells( elmBBox, &closeElements );
|
||||
|
||||
for ( size_t ccIdx = 0; ccIdx < closeElements.size(); ++ccIdx )
|
||||
{
|
||||
int elmIdx = static_cast<int>( closeElements[ccIdx] );
|
||||
|
||||
RigCaseToCaseCellMapperTools::elementCorners( dependentFemPart, elmIdx, elmCorners );
|
||||
|
||||
RigCaseToCaseCellMapperTools::rotateCellTopologicallyToMatchBaseCell( geoMechConvertedEclCell,
|
||||
isEclFaceNormalsOutwards,
|
||||
elmCorners );
|
||||
|
||||
bool isMatching = RigCaseToCaseCellMapperTools::isEclFemCellsMatching( geoMechConvertedEclCell,
|
||||
elmCorners,
|
||||
xyTolerance,
|
||||
zTolerance );
|
||||
|
||||
if ( isMatching )
|
||||
{
|
||||
if ( eclipseIsMaster )
|
||||
addMapping( elmIdx, static_cast<int>( cellIdx ) );
|
||||
else
|
||||
addMapping( static_cast<int>( cellIdx ), elmIdx );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2015- Statoil ASA
|
||||
// Copyright (C) 2015- Ceetron Solutions AS
|
||||
//
|
||||
// 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 "cvfMath.h"
|
||||
#include "cvfObject.h"
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class RigMainGrid;
|
||||
class RigFemPart;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigCaseToCaseCellMapper : public cvf::Object
|
||||
{
|
||||
public:
|
||||
RigCaseToCaseCellMapper( RigMainGrid* masterEclGrid, RigFemPart* dependentFemPart );
|
||||
RigCaseToCaseCellMapper( RigMainGrid* masterEclGrid, RigMainGrid* dependentEclGrid );
|
||||
RigCaseToCaseCellMapper( RigFemPart* masterFemPart, RigMainGrid* dependentEclGrid );
|
||||
RigCaseToCaseCellMapper( RigFemPart* masterFemPart, RigFemPart* dependentFemPart );
|
||||
|
||||
const int* masterCaseCellIndices( int dependentCaseReservoirCellIndex, int* masterCaseCellIndexCount ) const;
|
||||
|
||||
const RigMainGrid* masterGrid() const { return m_masterGrid; }
|
||||
const RigMainGrid* dependentGrid() const { return m_dependentGrid; }
|
||||
const RigFemPart* masterFemPart() const { return m_masterFemPart; }
|
||||
const RigFemPart* dependentFemPart() const { return m_dependentFemPart; }
|
||||
|
||||
private:
|
||||
void addMapping( int depCaseCellIdx, int masterCaseMatchingCell );
|
||||
void calculateEclToGeomCellMapping( RigMainGrid* masterEclGrid, RigFemPart* dependentFemPart, bool eclipseIsMaster );
|
||||
|
||||
std::vector<int> m_masterCellOrIntervalIndex;
|
||||
std::vector<std::vector<int>> m_masterCellIndexSeries;
|
||||
|
||||
RigMainGrid* m_masterGrid;
|
||||
RigMainGrid* m_dependentGrid;
|
||||
RigFemPart* m_masterFemPart;
|
||||
RigFemPart* m_dependentFemPart;
|
||||
};
|
||||
@@ -0,0 +1,502 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2015- Statoil ASA
|
||||
// Copyright (C) 2015- Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigCaseToCaseCellMapperTools.h"
|
||||
#include "RigCaseToCaseCellMapper.h"
|
||||
|
||||
#include "RigFemPart.h"
|
||||
#include "RigFemPartGrid.h"
|
||||
#include "RigMainGrid.h"
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
|
||||
class RigNeighborCornerFinder
|
||||
{
|
||||
public:
|
||||
RigNeighborCornerFinder( const RigMainGrid* mainGrid, size_t baseI, size_t baseJ, size_t baseK )
|
||||
: m_mainGrid( mainGrid )
|
||||
, m_baseI( baseI )
|
||||
, m_baseJ( baseJ )
|
||||
, m_baseK( baseK )
|
||||
{
|
||||
}
|
||||
|
||||
const std::array<size_t, 8>* neighborIndices( int offsetI, int offsetJ, int offsetK )
|
||||
{
|
||||
if ( offsetI < 0 && m_baseI == 0 ) return nullptr;
|
||||
if ( offsetJ < 0 && m_baseJ == 0 ) return nullptr;
|
||||
if ( offsetK < 0 && m_baseK == 0 ) return nullptr;
|
||||
if ( offsetI > 0 && m_baseI == m_mainGrid->cellCountI() - 1 ) return nullptr;
|
||||
if ( offsetJ > 0 && m_baseJ == m_mainGrid->cellCountJ() - 1 ) return nullptr;
|
||||
if ( offsetK > 0 && m_baseK == m_mainGrid->cellCountK() - 1 ) return nullptr;
|
||||
|
||||
size_t gridLocalCellIndex = m_mainGrid->cellIndexFromIJK( m_baseI + offsetI, m_baseJ + offsetJ, m_baseK + offsetK );
|
||||
const RigCell& cell = m_mainGrid->globalCellArray()[gridLocalCellIndex];
|
||||
return &( cell.cornerIndices() );
|
||||
}
|
||||
|
||||
private:
|
||||
const RigMainGrid* m_mainGrid;
|
||||
size_t m_baseI;
|
||||
size_t m_baseJ;
|
||||
size_t m_baseK;
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Average of neighbor corresponding nodes
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCaseToCaseCellMapperTools::estimatedFemCellFromEclCell( const RigMainGrid* eclGrid,
|
||||
size_t reservoirCellIndex,
|
||||
cvf::Vec3d estimatedElmCorners[8] )
|
||||
{
|
||||
CVF_TIGHT_ASSERT( reservoirCellIndex < eclGrid->cellCount() ); // Assume reservoirCellIdx == localGridCellIdx for
|
||||
// maingrid
|
||||
|
||||
const std::vector<cvf::Vec3d>& eclNodes = eclGrid->nodes();
|
||||
|
||||
size_t I, J, K;
|
||||
eclGrid->ijkFromCellIndex( reservoirCellIndex, &I, &J, &K );
|
||||
RigNeighborCornerFinder nbFinder( eclGrid, I, J, K );
|
||||
|
||||
// Cell corner Averaging mapping: Local cell index in neighbor matching specific corner of this cell
|
||||
// N - Negative P - positive
|
||||
// 0 <- NI[1] NINJ[2] NJ[3] NK[4] NINK[5] NINJNK[6] NJNK[7]
|
||||
// 1 <- NJ[2] PINJ[3] PI[0] NK[5] NJNK[6] PINJNK[7] PINK[4]
|
||||
// 2 <- PI[3] PIPJ[0] PJ[1] NK[6] PINK[7] PIPJNK[4] PJNK[5]
|
||||
// 3 <- PJ[0] NIPJ[1] NI[2] NK[7] PJNK[4] NIPJNK[5] NINK[6]
|
||||
// 4 <- NI[5] NINJ[6] NJ[7] PK[0] NIPK[1] NINJPK[2] NJPK[3]
|
||||
// 5 <- NJ[6] PINJ[7] PI[4] PK[1] NJPK[2] PINJPK[3] PIPK[0]
|
||||
// 6 <- PI[7] PIPJ[4] PJ[5] PK[2] PIPK[3] PIPJPK[0] PJPK[1]
|
||||
// 7 <- PJ[4] NIPJ[5] NI[6] PK[3] PJPK[0] NIPJPK[1] NIPK[2]
|
||||
|
||||
const std::array<size_t, 8>* IJK = nbFinder.neighborIndices( 0, 0, 0 );
|
||||
const std::array<size_t, 8>* NI = nbFinder.neighborIndices( -1, 0, 0 );
|
||||
const std::array<size_t, 8>* NJ = nbFinder.neighborIndices( 0, -1, 0 );
|
||||
const std::array<size_t, 8>* PI = nbFinder.neighborIndices( 1, 0, 0 );
|
||||
const std::array<size_t, 8>* PJ = nbFinder.neighborIndices( 0, 1, 0 );
|
||||
const std::array<size_t, 8>* NK = nbFinder.neighborIndices( 0, 0, -1 );
|
||||
const std::array<size_t, 8>* PK = nbFinder.neighborIndices( 0, 0, 1 );
|
||||
const std::array<size_t, 8>* NINJ = nbFinder.neighborIndices( -1, -1, 0 );
|
||||
const std::array<size_t, 8>* PINJ = nbFinder.neighborIndices( 1, -1, 0 );
|
||||
|
||||
const std::array<size_t, 8>* PIPJ = nbFinder.neighborIndices( 1, 1, 0 );
|
||||
const std::array<size_t, 8>* NIPJ = nbFinder.neighborIndices( -1, 1, 0 );
|
||||
const std::array<size_t, 8>* NINK = nbFinder.neighborIndices( -1, 0, -1 );
|
||||
const std::array<size_t, 8>* NJNK = nbFinder.neighborIndices( 0, -1, -1 );
|
||||
const std::array<size_t, 8>* PINK = nbFinder.neighborIndices( 1, 0, -1 );
|
||||
const std::array<size_t, 8>* PJNK = nbFinder.neighborIndices( 0, 1, -1 );
|
||||
const std::array<size_t, 8>* NIPK = nbFinder.neighborIndices( -1, 0, 1 );
|
||||
const std::array<size_t, 8>* NJPK = nbFinder.neighborIndices( 0, -1, 1 );
|
||||
const std::array<size_t, 8>* PIPK = nbFinder.neighborIndices( 1, 0, 1 );
|
||||
|
||||
const std::array<size_t, 8>* PJPK = nbFinder.neighborIndices( 0, 1, 1 );
|
||||
const std::array<size_t, 8>* NINJNK = nbFinder.neighborIndices( -1, -1, -1 );
|
||||
const std::array<size_t, 8>* PINJNK = nbFinder.neighborIndices( 1, -1, -1 );
|
||||
const std::array<size_t, 8>* PIPJNK = nbFinder.neighborIndices( 1, 1, -1 );
|
||||
const std::array<size_t, 8>* NIPJNK = nbFinder.neighborIndices( -1, 1, -1 );
|
||||
const std::array<size_t, 8>* NINJPK = nbFinder.neighborIndices( -1, -1, 1 );
|
||||
const std::array<size_t, 8>* PINJPK = nbFinder.neighborIndices( 1, -1, 1 );
|
||||
const std::array<size_t, 8>* PIPJPK = nbFinder.neighborIndices( 1, 1, 1 );
|
||||
const std::array<size_t, 8>* NIPJPK = nbFinder.neighborIndices( -1, 1, 1 );
|
||||
|
||||
std::vector<size_t> contributingNodeIndicesPrCellCorner[8];
|
||||
|
||||
if ( IJK ) contributingNodeIndicesPrCellCorner[0].push_back( ( *IJK )[0] );
|
||||
if ( NI ) contributingNodeIndicesPrCellCorner[0].push_back( ( *NI )[1] );
|
||||
if ( NINJ ) contributingNodeIndicesPrCellCorner[0].push_back( ( *NINJ )[2] );
|
||||
if ( NJ ) contributingNodeIndicesPrCellCorner[0].push_back( ( *NJ )[3] );
|
||||
if ( NK ) contributingNodeIndicesPrCellCorner[0].push_back( ( *NK )[4] );
|
||||
if ( NINK ) contributingNodeIndicesPrCellCorner[0].push_back( ( *NINK )[5] );
|
||||
if ( NINJNK ) contributingNodeIndicesPrCellCorner[0].push_back( ( *NINJNK )[6] );
|
||||
if ( NJNK ) contributingNodeIndicesPrCellCorner[0].push_back( ( *NJNK )[7] );
|
||||
|
||||
if ( IJK ) contributingNodeIndicesPrCellCorner[1].push_back( ( *IJK )[1] );
|
||||
if ( NJ ) contributingNodeIndicesPrCellCorner[1].push_back( ( *NJ )[2] );
|
||||
if ( PINJ ) contributingNodeIndicesPrCellCorner[1].push_back( ( *PINJ )[3] );
|
||||
if ( PI ) contributingNodeIndicesPrCellCorner[1].push_back( ( *PI )[0] );
|
||||
if ( NK ) contributingNodeIndicesPrCellCorner[1].push_back( ( *NK )[5] );
|
||||
if ( NJNK ) contributingNodeIndicesPrCellCorner[1].push_back( ( *NJNK )[6] );
|
||||
if ( PINJNK ) contributingNodeIndicesPrCellCorner[1].push_back( ( *PINJNK )[7] );
|
||||
if ( PINK ) contributingNodeIndicesPrCellCorner[1].push_back( ( *PINK )[4] );
|
||||
|
||||
if ( IJK ) contributingNodeIndicesPrCellCorner[2].push_back( ( *IJK )[2] );
|
||||
if ( PI ) contributingNodeIndicesPrCellCorner[2].push_back( ( *PI )[3] );
|
||||
if ( PIPJ ) contributingNodeIndicesPrCellCorner[2].push_back( ( *PIPJ )[0] );
|
||||
if ( PJ ) contributingNodeIndicesPrCellCorner[2].push_back( ( *PJ )[1] );
|
||||
if ( NK ) contributingNodeIndicesPrCellCorner[2].push_back( ( *NK )[6] );
|
||||
if ( PINK ) contributingNodeIndicesPrCellCorner[2].push_back( ( *PINK )[7] );
|
||||
if ( PIPJNK ) contributingNodeIndicesPrCellCorner[2].push_back( ( *PIPJNK )[4] );
|
||||
if ( PJNK ) contributingNodeIndicesPrCellCorner[2].push_back( ( *PJNK )[5] );
|
||||
|
||||
if ( IJK ) contributingNodeIndicesPrCellCorner[3].push_back( ( *IJK )[3] );
|
||||
if ( PJ ) contributingNodeIndicesPrCellCorner[3].push_back( ( *PJ )[0] );
|
||||
if ( NIPJ ) contributingNodeIndicesPrCellCorner[3].push_back( ( *NIPJ )[1] );
|
||||
if ( NI ) contributingNodeIndicesPrCellCorner[3].push_back( ( *NI )[2] );
|
||||
if ( NK ) contributingNodeIndicesPrCellCorner[3].push_back( ( *NK )[7] );
|
||||
if ( PJNK ) contributingNodeIndicesPrCellCorner[3].push_back( ( *PJNK )[4] );
|
||||
if ( NIPJNK ) contributingNodeIndicesPrCellCorner[3].push_back( ( *NIPJNK )[5] );
|
||||
if ( NINK ) contributingNodeIndicesPrCellCorner[3].push_back( ( *NINK )[6] );
|
||||
|
||||
// 4 <- NI[5] NINJ[6] NJ[7] PK[0] NIPK[1] NINJPK[2] NJPK[3]
|
||||
|
||||
if ( IJK ) contributingNodeIndicesPrCellCorner[4].push_back( ( *IJK )[4] );
|
||||
if ( NI ) contributingNodeIndicesPrCellCorner[4].push_back( ( *NI )[5] );
|
||||
if ( NINJ ) contributingNodeIndicesPrCellCorner[4].push_back( ( *NINJ )[6] );
|
||||
if ( NJ ) contributingNodeIndicesPrCellCorner[4].push_back( ( *NJ )[7] );
|
||||
if ( PK ) contributingNodeIndicesPrCellCorner[4].push_back( ( *PK )[0] );
|
||||
if ( NIPK ) contributingNodeIndicesPrCellCorner[4].push_back( ( *NIPK )[1] );
|
||||
if ( NINJPK ) contributingNodeIndicesPrCellCorner[4].push_back( ( *NINJPK )[2] );
|
||||
if ( NJPK ) contributingNodeIndicesPrCellCorner[4].push_back( ( *NJPK )[3] );
|
||||
|
||||
if ( IJK ) contributingNodeIndicesPrCellCorner[5].push_back( ( *IJK )[5] );
|
||||
if ( NJ ) contributingNodeIndicesPrCellCorner[5].push_back( ( *NJ )[6] );
|
||||
if ( PINJ ) contributingNodeIndicesPrCellCorner[5].push_back( ( *PINJ )[7] );
|
||||
if ( PI ) contributingNodeIndicesPrCellCorner[5].push_back( ( *PI )[4] );
|
||||
if ( PK ) contributingNodeIndicesPrCellCorner[5].push_back( ( *PK )[1] );
|
||||
if ( NJPK ) contributingNodeIndicesPrCellCorner[5].push_back( ( *NJPK )[2] );
|
||||
if ( PINJPK ) contributingNodeIndicesPrCellCorner[5].push_back( ( *PINJPK )[3] );
|
||||
if ( PIPK ) contributingNodeIndicesPrCellCorner[5].push_back( ( *PIPK )[0] );
|
||||
|
||||
// 6 <- PI[7] PIPJ[4] PJ[5] PK[2] PIPK[3] PIPJPK[0] PJPK[1]
|
||||
|
||||
if ( IJK ) contributingNodeIndicesPrCellCorner[6].push_back( ( *IJK )[6] );
|
||||
if ( PI ) contributingNodeIndicesPrCellCorner[6].push_back( ( *PI )[7] );
|
||||
if ( PIPJ ) contributingNodeIndicesPrCellCorner[6].push_back( ( *PIPJ )[4] );
|
||||
if ( PJ ) contributingNodeIndicesPrCellCorner[6].push_back( ( *PJ )[5] );
|
||||
if ( PK ) contributingNodeIndicesPrCellCorner[6].push_back( ( *PK )[2] );
|
||||
if ( PIPK ) contributingNodeIndicesPrCellCorner[6].push_back( ( *PIPK )[3] );
|
||||
if ( PIPJPK ) contributingNodeIndicesPrCellCorner[6].push_back( ( *PIPJPK )[0] );
|
||||
if ( PJPK ) contributingNodeIndicesPrCellCorner[6].push_back( ( *PJPK )[1] );
|
||||
|
||||
if ( IJK ) contributingNodeIndicesPrCellCorner[7].push_back( ( *IJK )[7] );
|
||||
if ( PJ ) contributingNodeIndicesPrCellCorner[7].push_back( ( *PJ )[4] );
|
||||
if ( NIPJ ) contributingNodeIndicesPrCellCorner[7].push_back( ( *NIPJ )[5] );
|
||||
if ( NI ) contributingNodeIndicesPrCellCorner[7].push_back( ( *NI )[6] );
|
||||
if ( PK ) contributingNodeIndicesPrCellCorner[7].push_back( ( *PK )[3] );
|
||||
if ( PJPK ) contributingNodeIndicesPrCellCorner[7].push_back( ( *PJPK )[0] );
|
||||
if ( NIPJPK ) contributingNodeIndicesPrCellCorner[7].push_back( ( *NIPJPK )[1] );
|
||||
if ( NIPK ) contributingNodeIndicesPrCellCorner[7].push_back( ( *NIPK )[2] );
|
||||
|
||||
// Average the nodes
|
||||
for ( size_t cornIdx = 0; cornIdx < 8; ++cornIdx )
|
||||
{
|
||||
estimatedElmCorners[cornIdx] = cvf::Vec3d::ZERO;
|
||||
size_t contribCount = contributingNodeIndicesPrCellCorner[cornIdx].size();
|
||||
for ( size_t ctnIdx = 0; ctnIdx < contribCount; ++ctnIdx )
|
||||
{
|
||||
estimatedElmCorners[cornIdx] += eclNodes[contributingNodeIndicesPrCellCorner[cornIdx][ctnIdx]];
|
||||
}
|
||||
estimatedElmCorners[cornIdx] /= contribCount;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCaseToCaseCellMapperTools::rotateQuad( cvf::Vec3d quad[4], int idxToNewStart )
|
||||
{
|
||||
if ( idxToNewStart == 0 ) return;
|
||||
cvf::Vec3d tmpQuad[4];
|
||||
tmpQuad[0] = quad[0];
|
||||
tmpQuad[1] = quad[1];
|
||||
tmpQuad[2] = quad[2];
|
||||
tmpQuad[3] = quad[3];
|
||||
|
||||
quad[0] = tmpQuad[idxToNewStart];
|
||||
++idxToNewStart;
|
||||
if ( idxToNewStart > 3 ) idxToNewStart = 0;
|
||||
quad[1] = tmpQuad[idxToNewStart];
|
||||
++idxToNewStart;
|
||||
if ( idxToNewStart > 3 ) idxToNewStart = 0;
|
||||
quad[2] = tmpQuad[idxToNewStart];
|
||||
++idxToNewStart;
|
||||
if ( idxToNewStart > 3 ) idxToNewStart = 0;
|
||||
quad[3] = tmpQuad[idxToNewStart];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCaseToCaseCellMapperTools::flipQuadWinding( cvf::Vec3d quad[4] )
|
||||
{
|
||||
cvf::Vec3d temp = quad[1];
|
||||
quad[1] = quad[3];
|
||||
quad[3] = temp;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
int RigCaseToCaseCellMapperTools::quadVxClosestToXYOfPoint( const cvf::Vec3d point, const cvf::Vec3d quad[4] )
|
||||
{
|
||||
double minSqDist = HUGE_VAL;
|
||||
int quadVxIdxClosestToPoint = cvf::UNDEFINED_INT;
|
||||
|
||||
for ( int i = 0; i < 4; ++i )
|
||||
{
|
||||
cvf::Vec3d diff = quad[i] - point;
|
||||
diff[2] = 0.0;
|
||||
|
||||
double sqDist = diff.lengthSquared();
|
||||
if ( sqDist < minSqDist )
|
||||
{
|
||||
minSqDist = sqDist;
|
||||
quadVxIdxClosestToPoint = i;
|
||||
}
|
||||
}
|
||||
|
||||
return quadVxIdxClosestToPoint;
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigCaseToCaseCellMapperTools::elementCorners( const RigFemPart* femPart, int elmIdx, cvf::Vec3d elmCorners[8] )
|
||||
{
|
||||
RigElementType elmType = femPart->elementType( elmIdx );
|
||||
if ( !( elmType == HEX8 || elmType == HEX8P ) ) return false;
|
||||
|
||||
const std::vector<cvf::Vec3f>& nodeCoords = femPart->nodes().coordinates;
|
||||
const int* cornerIndices = femPart->connectivities( elmIdx );
|
||||
|
||||
elmCorners[0] = cvf::Vec3d( nodeCoords[cornerIndices[0]] );
|
||||
elmCorners[1] = cvf::Vec3d( nodeCoords[cornerIndices[1]] );
|
||||
elmCorners[2] = cvf::Vec3d( nodeCoords[cornerIndices[2]] );
|
||||
elmCorners[3] = cvf::Vec3d( nodeCoords[cornerIndices[3]] );
|
||||
elmCorners[4] = cvf::Vec3d( nodeCoords[cornerIndices[4]] );
|
||||
elmCorners[5] = cvf::Vec3d( nodeCoords[cornerIndices[5]] );
|
||||
elmCorners[6] = cvf::Vec3d( nodeCoords[cornerIndices[6]] );
|
||||
elmCorners[7] = cvf::Vec3d( nodeCoords[cornerIndices[7]] );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
int RigCaseToCaseCellMapperTools::findMatchingPOSKFaceIdx( const cvf::Vec3d baseCell[8],
|
||||
bool isBaseCellNormalsOutwards,
|
||||
const cvf::Vec3d c2[8] )
|
||||
{
|
||||
int faceNodeCount;
|
||||
const int* posKFace =
|
||||
RigFemTypes::localElmNodeIndicesForFace( HEX8, (int)( cvf::StructGridInterface::POS_K ), &faceNodeCount );
|
||||
|
||||
double sign = isBaseCellNormalsOutwards ? 1.0 : -1.0;
|
||||
|
||||
cvf::Vec3d posKnormal = sign * ( baseCell[posKFace[2]] - baseCell[posKFace[0]] ) ^
|
||||
( baseCell[posKFace[3]] - baseCell[posKFace[1]] );
|
||||
posKnormal.normalize();
|
||||
|
||||
double minDiff = HUGE_VAL;
|
||||
int bestFace = -1;
|
||||
for ( int faceIdx = 5; faceIdx >= 0; --faceIdx ) // Backwards. might hit earlier more often
|
||||
{
|
||||
const int* face = RigFemTypes::localElmNodeIndicesForFace( HEX8, faceIdx, &faceNodeCount );
|
||||
cvf::Vec3d normal = ( c2[face[2]] - c2[face[0]] ) ^ ( c2[face[3]] - c2[face[1]] );
|
||||
normal.normalize();
|
||||
double sqDiff = ( posKnormal - normal ).lengthSquared();
|
||||
if ( sqDiff < minDiff )
|
||||
{
|
||||
minDiff = sqDiff;
|
||||
bestFace = faceIdx;
|
||||
if ( minDiff < 0.1 * 0.1 ) break; // This must be the one. Do not search further
|
||||
}
|
||||
}
|
||||
|
||||
return bestFace;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigCaseToCaseCellMapperTools::isEclFemCellsMatching( const cvf::Vec3d baseCell[8],
|
||||
cvf::Vec3d cell[8],
|
||||
double xyTolerance,
|
||||
double zTolerance )
|
||||
{
|
||||
bool isMatching = true;
|
||||
|
||||
for ( int i = 0; i < 4; ++i )
|
||||
{
|
||||
cvf::Vec3d diff = cell[i] - baseCell[i];
|
||||
|
||||
if ( !( fabs( diff.x() ) < xyTolerance && fabs( diff.y() ) < xyTolerance && fabs( diff.z() ) < zTolerance ) )
|
||||
{
|
||||
isMatching = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return isMatching;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCaseToCaseCellMapperTools::rotateCellTopologicallyToMatchBaseCell( const cvf::Vec3d* baseCell,
|
||||
bool baseCellFaceNormalsIsOutwards,
|
||||
cvf::Vec3d* cell )
|
||||
{
|
||||
int femDeepZFaceIdx = findMatchingPOSKFaceIdx( baseCell, baseCellFaceNormalsIsOutwards, cell );
|
||||
|
||||
{
|
||||
cvf::Vec3d tmpFemCorners[8];
|
||||
tmpFemCorners[0] = cell[0];
|
||||
tmpFemCorners[1] = cell[1];
|
||||
tmpFemCorners[2] = cell[2];
|
||||
tmpFemCorners[3] = cell[3];
|
||||
tmpFemCorners[4] = cell[4];
|
||||
tmpFemCorners[5] = cell[5];
|
||||
tmpFemCorners[6] = cell[6];
|
||||
tmpFemCorners[7] = cell[7];
|
||||
|
||||
int femShallowZFaceIdx = RigFemTypes::oppositeFace( HEX8, femDeepZFaceIdx );
|
||||
|
||||
int faceNodeCount;
|
||||
const int* localElmNodeIndicesForPOSKFace =
|
||||
RigFemTypes::localElmNodeIndicesForFace( HEX8, femDeepZFaceIdx, &faceNodeCount );
|
||||
const int* localElmNodeIndicesForNEGKFace =
|
||||
RigFemTypes::localElmNodeIndicesForFace( HEX8, femShallowZFaceIdx, &faceNodeCount );
|
||||
|
||||
cell[0] = tmpFemCorners[localElmNodeIndicesForNEGKFace[0]];
|
||||
cell[1] = tmpFemCorners[localElmNodeIndicesForNEGKFace[1]];
|
||||
cell[2] = tmpFemCorners[localElmNodeIndicesForNEGKFace[2]];
|
||||
cell[3] = tmpFemCorners[localElmNodeIndicesForNEGKFace[3]];
|
||||
cell[4] = tmpFemCorners[localElmNodeIndicesForPOSKFace[0]];
|
||||
cell[5] = tmpFemCorners[localElmNodeIndicesForPOSKFace[1]];
|
||||
cell[6] = tmpFemCorners[localElmNodeIndicesForPOSKFace[2]];
|
||||
cell[7] = tmpFemCorners[localElmNodeIndicesForPOSKFace[3]];
|
||||
}
|
||||
|
||||
cvf::Vec3d* femDeepestQuad = &( cell[4] );
|
||||
cvf::Vec3d* femShallowQuad = &( cell[0] );
|
||||
|
||||
// Now the top/bottom have opposite winding. To make the comparisons and index rotations simpler
|
||||
// flip the winding of the top or bottom face depending on whether the eclipse grid is inside-out
|
||||
|
||||
if ( baseCellFaceNormalsIsOutwards )
|
||||
{
|
||||
flipQuadWinding( femShallowQuad );
|
||||
}
|
||||
else
|
||||
{
|
||||
flipQuadWinding( femDeepestQuad );
|
||||
}
|
||||
|
||||
// We now need to rotate the fem quads to be aligned with the ecl quads
|
||||
// Since the start point of the quad always is aligned with the opposite face-quad start
|
||||
// we can find the rotation for the top, and apply it to both top and bottom
|
||||
|
||||
int femQuadStartIdx = quadVxClosestToXYOfPoint( baseCell[0], femShallowQuad );
|
||||
rotateQuad( femDeepestQuad, femQuadStartIdx );
|
||||
rotateQuad( femShallowQuad, femQuadStartIdx );
|
||||
}
|
||||
|
||||
#if 0 // Inside Bounding box test
|
||||
cvf::BoundingBox cellBBox;
|
||||
for (int i = 0; i < 8 ; ++i) cellBBox.add(cellCorners[i]);
|
||||
|
||||
cvf::Vec3d cs = cellBBox.min();
|
||||
cvf::Vec3d cl = cellBBox.max();
|
||||
cvf::Vec3d es = elmBBox.min();
|
||||
cvf::Vec3d el = elmBBox.max();
|
||||
|
||||
if ( ( (cs.x() + xyTolerance) >= es.x() && (cl.x() - xyTolerance) <= el.x())
|
||||
&& ( (cs.y() + xyTolerance) >= es.y() && (cl.y() - xyTolerance) <= el.y())
|
||||
&& ( (cs.z() + zTolerance ) >= es.z() && (cl.z() - zTolerance ) <= el.z()) )
|
||||
{
|
||||
// Cell bb equal or inside Elm bb
|
||||
isMatching = true;
|
||||
}
|
||||
|
||||
if ( ( (es.x() + xyTolerance) >= cs.x() && (el.x() - xyTolerance) <= cl.x())
|
||||
&& ( (es.y() + xyTolerance) >= cs.y() && (el.y() - xyTolerance) <= cl.y())
|
||||
&& ( (es.z() + zTolerance ) >= cs.z() && (el.z() - zTolerance ) <= cl.z()) )
|
||||
{
|
||||
// Elm bb equal or inside Cell bb
|
||||
isMatching = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
{
|
||||
const std::vector<cvf::Vec3d>& eclNodes = eclGrid->nodes();
|
||||
const RigCell& cell = eclGrid->cells()[reservoirCellIndex];
|
||||
const std::array<size_t, 8>& cornerIndices = cell.cornerIndices();
|
||||
int faceNodeCount;
|
||||
const int* localElmNodeIndicesForTopZFace = RigFemTypes::localElmNodeIndicesForFace(HEX8, 4, &faceNodeCount);
|
||||
const int* localElmNodeIndicesForBotZFace = RigFemTypes::localElmNodeIndicesForFace(HEX8, 5, &faceNodeCount);
|
||||
|
||||
eclDeepestQuad[0] = eclNodes[cornerIndices[localElmNodeIndicesForTopZFace[0]]];
|
||||
eclDeepestQuad[1] = eclNodes[cornerIndices[localElmNodeIndicesForTopZFace[1]]];
|
||||
eclDeepestQuad[2] = eclNodes[cornerIndices[localElmNodeIndicesForTopZFace[2]]];
|
||||
eclDeepestQuad[3] = eclNodes[cornerIndices[localElmNodeIndicesForTopZFace[3]]];
|
||||
|
||||
eclShallowQuad[0] = eclNodes[cornerIndices[localElmNodeIndicesForBotZFace[0]]];
|
||||
eclShallowQuad[1] = eclNodes[cornerIndices[localElmNodeIndicesForBotZFace[1]]];
|
||||
eclShallowQuad[2] = eclNodes[cornerIndices[localElmNodeIndicesForBotZFace[2]]];
|
||||
eclShallowQuad[3] = eclNodes[cornerIndices[localElmNodeIndicesForBotZFace[3]]];
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// First search K=1 diagonally for a seed cell; A cell without collapsings, and without faults
|
||||
|
||||
size_t minIJCount = masterEclGrid->cellCountI();
|
||||
if (minIJCount > masterEclGrid->cellCountJ())
|
||||
minIJCount = masterEclGrid->cellCountJ();
|
||||
|
||||
for (size_t ij = 0; ij < minIJCount; ++ij )
|
||||
{
|
||||
size_t localCellIdx = masterEclGrid->cellIndexFromIJK(ij, ij, 0);
|
||||
size_t reservoirCellIdx = masterEclGrid->reservoirCellIndex(localCellIdx);
|
||||
|
||||
cvf::Vec3d vertices[8];
|
||||
masterEclGrid->cellCornerVertices(localCellIdx, vertices);
|
||||
if (!isCellNormal(vertices))
|
||||
continue;
|
||||
|
||||
const RigFault* fault = masterEclGrid->findFaultFromCellIndexAndCellFace(reservoirCellIdx, cvf::StructGridInterface::POS_I);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Vec3d RigCaseToCaseCellMapperTools::calculateCellCenter( cvf::Vec3d elmCorners[8] )
|
||||
{
|
||||
cvf::Vec3d avg( cvf::Vec3d::ZERO );
|
||||
|
||||
size_t i;
|
||||
for ( i = 0; i < 8; i++ )
|
||||
{
|
||||
avg += elmCorners[i];
|
||||
}
|
||||
|
||||
avg /= 8.0;
|
||||
|
||||
return avg;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2015- Statoil ASA
|
||||
// Copyright (C) 2015- Ceetron Solutions AS
|
||||
//
|
||||
// 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 "cvfMath.h"
|
||||
#include "cvfObject.h"
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class RigMainGrid;
|
||||
class RigFemPart;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
|
||||
class RigCaseToCaseCellMapperTools
|
||||
{
|
||||
public:
|
||||
static void estimatedFemCellFromEclCell( const RigMainGrid* eclGrid,
|
||||
size_t reservoirCellIndex,
|
||||
cvf::Vec3d estimatedElmCorners[8] );
|
||||
static void rotateCellTopologicallyToMatchBaseCell( const cvf::Vec3d* baseCell,
|
||||
bool baseCellFaceNormalsIsOutwards,
|
||||
cvf::Vec3d* cell );
|
||||
static cvf::Vec3d calculateCellCenter( cvf::Vec3d elmCorners[8] );
|
||||
static bool elementCorners( const RigFemPart* femPart, int elmIdx, cvf::Vec3d elmCorners[8] );
|
||||
static bool
|
||||
isEclFemCellsMatching( const cvf::Vec3d baseCell[8], cvf::Vec3d cell[8], double xyTolerance, double zTolerance );
|
||||
|
||||
private:
|
||||
static void rotateQuad( cvf::Vec3d quad[4], int idxToNewStart );
|
||||
static void flipQuadWinding( cvf::Vec3d quad[4] );
|
||||
static int quadVxClosestToXYOfPoint( const cvf::Vec3d point, const cvf::Vec3d quad[4] );
|
||||
static int
|
||||
findMatchingPOSKFaceIdx( const cvf::Vec3d baseCell[8], bool isBaseCellNormalsOutwards, const cvf::Vec3d c2[8] );
|
||||
};
|
||||
@@ -0,0 +1,544 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2015- Statoil ASA
|
||||
// Copyright (C) 2015- Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigCaseToCaseRangeFilterMapper.h"
|
||||
#include "RigCaseToCaseCellMapper.h"
|
||||
#include "RigCaseToCaseCellMapperTools.h"
|
||||
|
||||
#include "RigFemPart.h"
|
||||
#include "RigFemPartGrid.h"
|
||||
#include "RigMainGrid.h"
|
||||
|
||||
#include "RimCellRangeFilter.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCaseToCaseRangeFilterMapper::convertRangeFilterEclToFem( RimCellRangeFilter* srcFilter,
|
||||
const RigMainGrid* srcEclGrid,
|
||||
RimCellRangeFilter* dstFilter,
|
||||
const RigFemPart* dstFemPart )
|
||||
{
|
||||
convertRangeFilter( srcFilter, dstFilter, srcEclGrid, dstFemPart, true );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCaseToCaseRangeFilterMapper::convertRangeFilterFemToEcl( RimCellRangeFilter* srcFilter,
|
||||
const RigFemPart* srcFemPart,
|
||||
RimCellRangeFilter* dstFilter,
|
||||
const RigMainGrid* dstEclGrid )
|
||||
{
|
||||
convertRangeFilter( srcFilter, dstFilter, dstEclGrid, srcFemPart, false );
|
||||
}
|
||||
|
||||
struct RigRangeEndPoints
|
||||
{
|
||||
RigRangeEndPoints()
|
||||
: StartI( cvf::UNDEFINED_SIZE_T )
|
||||
, StartJ( cvf::UNDEFINED_SIZE_T )
|
||||
, StartK( cvf::UNDEFINED_SIZE_T )
|
||||
, EndI( cvf::UNDEFINED_SIZE_T )
|
||||
, EndJ( cvf::UNDEFINED_SIZE_T )
|
||||
, EndK( cvf::UNDEFINED_SIZE_T )
|
||||
{
|
||||
}
|
||||
|
||||
size_t StartI;
|
||||
size_t StartJ;
|
||||
size_t StartK;
|
||||
size_t EndI;
|
||||
size_t EndJ;
|
||||
size_t EndK;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
void RigCaseToCaseRangeFilterMapper::convertRangeFilter( const RimCellRangeFilter* srcFilter,
|
||||
RimCellRangeFilter* dstFilter,
|
||||
const RigMainGrid* eclGrid,
|
||||
const RigFemPart* femPart,
|
||||
bool femIsDestination )
|
||||
{
|
||||
CVF_ASSERT( srcFilter && eclGrid && dstFilter && femPart );
|
||||
CVF_ASSERT( srcFilter->gridIndex() == 0 ); // LGR not supported yet
|
||||
|
||||
RigRangeEndPoints src;
|
||||
// Convert the (start, count) range filter vars to end point cell ijk
|
||||
{
|
||||
src.StartI = srcFilter->startIndexI() - 1;
|
||||
src.StartJ = srcFilter->startIndexJ() - 1;
|
||||
src.StartK = srcFilter->startIndexK() - 1;
|
||||
|
||||
// Needs to subtract one more to have the end idx being
|
||||
// the last cell in the selection, not the first outside
|
||||
src.EndI = src.StartI + srcFilter->cellCountI() - 1;
|
||||
src.EndJ = src.StartJ + srcFilter->cellCountJ() - 1;
|
||||
src.EndK = src.StartK + srcFilter->cellCountK() - 1;
|
||||
}
|
||||
|
||||
// Clamp the src end points to be inside the src model
|
||||
{
|
||||
size_t maxIIndex;
|
||||
size_t maxJIndex;
|
||||
size_t maxKIndex;
|
||||
|
||||
// Clamp end
|
||||
if ( femIsDestination )
|
||||
{
|
||||
maxIIndex = eclGrid->cellCountI() - 1;
|
||||
maxJIndex = eclGrid->cellCountJ() - 1;
|
||||
maxKIndex = eclGrid->cellCountK() - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxIIndex = femPart->getOrCreateStructGrid()->cellCountI() - 1;
|
||||
maxJIndex = femPart->getOrCreateStructGrid()->cellCountJ() - 1;
|
||||
maxKIndex = femPart->getOrCreateStructGrid()->cellCountK() - 1;
|
||||
}
|
||||
src.EndI = std::min( src.EndI, maxIIndex );
|
||||
src.EndJ = std::min( src.EndJ, maxJIndex );
|
||||
src.EndK = std::min( src.EndK, maxKIndex );
|
||||
}
|
||||
|
||||
// When using femPart as source we need to clamp the fem srcRange filter
|
||||
// to the extents of the ecl grid within the fem part before
|
||||
// doing the mapping. If not, the range filter corners will most likely be outside
|
||||
// the ecl grid, resulting in an undefined conversion.
|
||||
if ( !femIsDestination )
|
||||
{
|
||||
RigRangeEndPoints eclMaxMin;
|
||||
eclMaxMin.StartI = 0;
|
||||
eclMaxMin.StartJ = 0;
|
||||
eclMaxMin.StartK = 0;
|
||||
eclMaxMin.EndI = eclGrid->cellCountI() - 1;
|
||||
eclMaxMin.EndJ = eclGrid->cellCountJ() - 1;
|
||||
eclMaxMin.EndK = eclGrid->cellCountK() - 1;
|
||||
RigRangeEndPoints eclExtInFem;
|
||||
|
||||
convertRangeFilterEndPoints( eclMaxMin, eclExtInFem, eclGrid, femPart, true );
|
||||
|
||||
src.StartI = std::max( src.StartI, eclExtInFem.StartI );
|
||||
src.StartJ = std::max( src.StartJ, eclExtInFem.StartJ );
|
||||
src.StartK = std::max( src.StartK, eclExtInFem.StartK );
|
||||
src.EndI = std::min( src.EndI, eclExtInFem.EndI );
|
||||
src.EndJ = std::min( src.EndJ, eclExtInFem.EndJ );
|
||||
src.EndK = std::min( src.EndK, eclExtInFem.EndK );
|
||||
}
|
||||
|
||||
RigRangeEndPoints dst;
|
||||
|
||||
convertRangeFilterEndPoints( src, dst, eclGrid, femPart, femIsDestination );
|
||||
|
||||
// Populate the dst range filter with new data
|
||||
|
||||
if ( dst.StartI != cvf::UNDEFINED_SIZE_T && dst.StartJ != cvf::UNDEFINED_SIZE_T && dst.StartK != cvf::UNDEFINED_SIZE_T &&
|
||||
dst.EndI != cvf::UNDEFINED_SIZE_T && dst.EndJ != cvf::UNDEFINED_SIZE_T && dst.EndK != cvf::UNDEFINED_SIZE_T )
|
||||
{
|
||||
dstFilter->startIndexI = static_cast<int>( dst.StartI + 1 );
|
||||
dstFilter->startIndexJ = static_cast<int>( dst.StartJ + 1 );
|
||||
dstFilter->startIndexK = static_cast<int>( dst.StartK + 1 );
|
||||
|
||||
dstFilter->cellCountI = static_cast<int>( dst.EndI - ( dst.StartI - 1 ) );
|
||||
dstFilter->cellCountJ = static_cast<int>( dst.EndJ - ( dst.StartJ - 1 ) );
|
||||
dstFilter->cellCountK = static_cast<int>( dst.EndK - ( dst.StartK - 1 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
dstFilter->startIndexI = 1;
|
||||
dstFilter->startIndexJ = 1;
|
||||
dstFilter->startIndexK = 1;
|
||||
|
||||
dstFilter->cellCountI = 0;
|
||||
dstFilter->cellCountJ = 0;
|
||||
dstFilter->cellCountK = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCaseToCaseRangeFilterMapper::convertRangeFilterEndPoints( const RigRangeEndPoints& src,
|
||||
RigRangeEndPoints& dst,
|
||||
const RigMainGrid* eclGrid,
|
||||
const RigFemPart* femPart,
|
||||
bool femIsDestination )
|
||||
{
|
||||
{
|
||||
struct RangeFilterCorner
|
||||
{
|
||||
RangeFilterCorner()
|
||||
: cellMatchType( APPROX_ON_COLLAPSED )
|
||||
{
|
||||
}
|
||||
cvf::Vec3st ijk;
|
||||
CellMatchType cellMatchType;
|
||||
};
|
||||
|
||||
RangeFilterCorner rangeFilterMatches[8];
|
||||
|
||||
cvf::Vec3st srcRangeCube[8];
|
||||
srcRangeCube[0] = cvf::Vec3st( src.StartI, src.StartJ, src.StartK );
|
||||
srcRangeCube[1] = cvf::Vec3st( src.EndI, src.StartJ, src.StartK );
|
||||
srcRangeCube[2] = cvf::Vec3st( src.EndI, src.EndJ, src.StartK );
|
||||
srcRangeCube[3] = cvf::Vec3st( src.StartI, src.EndJ, src.StartK );
|
||||
srcRangeCube[4] = cvf::Vec3st( src.StartI, src.StartJ, src.EndK );
|
||||
srcRangeCube[5] = cvf::Vec3st( src.EndI, src.StartJ, src.EndK );
|
||||
srcRangeCube[6] = cvf::Vec3st( src.EndI, src.EndJ, src.EndK );
|
||||
srcRangeCube[7] = cvf::Vec3st( src.StartI, src.EndJ, src.EndK );
|
||||
|
||||
bool foundExactMatch = false;
|
||||
int cornerIdx = 0;
|
||||
int diagIdx = 6; // Index to diagonal corner
|
||||
|
||||
for ( cornerIdx = 0; cornerIdx < 4; ++cornerIdx )
|
||||
{
|
||||
diagIdx = ( cornerIdx < 2 ) ? cornerIdx + 6 : cornerIdx + 2;
|
||||
|
||||
if ( femIsDestination )
|
||||
{
|
||||
rangeFilterMatches[cornerIdx].cellMatchType =
|
||||
findBestFemCellFromEclCell( eclGrid,
|
||||
srcRangeCube[cornerIdx][0],
|
||||
srcRangeCube[cornerIdx][1],
|
||||
srcRangeCube[cornerIdx][2],
|
||||
femPart,
|
||||
&( rangeFilterMatches[cornerIdx].ijk[0] ),
|
||||
&( rangeFilterMatches[cornerIdx].ijk[1] ),
|
||||
&( rangeFilterMatches[cornerIdx].ijk[2] ) );
|
||||
|
||||
rangeFilterMatches[diagIdx].cellMatchType =
|
||||
findBestFemCellFromEclCell( eclGrid,
|
||||
srcRangeCube[diagIdx][0],
|
||||
srcRangeCube[diagIdx][1],
|
||||
srcRangeCube[diagIdx][2],
|
||||
femPart,
|
||||
&( rangeFilterMatches[diagIdx].ijk[0] ),
|
||||
&( rangeFilterMatches[diagIdx].ijk[1] ),
|
||||
&( rangeFilterMatches[diagIdx].ijk[2] ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
rangeFilterMatches[cornerIdx].cellMatchType =
|
||||
findBestEclCellFromFemCell( femPart,
|
||||
srcRangeCube[cornerIdx][0],
|
||||
srcRangeCube[cornerIdx][1],
|
||||
srcRangeCube[cornerIdx][2],
|
||||
eclGrid,
|
||||
&( rangeFilterMatches[cornerIdx].ijk[0] ),
|
||||
&( rangeFilterMatches[cornerIdx].ijk[1] ),
|
||||
&( rangeFilterMatches[cornerIdx].ijk[2] ) );
|
||||
|
||||
rangeFilterMatches[diagIdx].cellMatchType =
|
||||
findBestEclCellFromFemCell( femPart,
|
||||
srcRangeCube[diagIdx][0],
|
||||
srcRangeCube[diagIdx][1],
|
||||
srcRangeCube[diagIdx][2],
|
||||
eclGrid,
|
||||
&( rangeFilterMatches[diagIdx].ijk[0] ),
|
||||
&( rangeFilterMatches[diagIdx].ijk[1] ),
|
||||
&( rangeFilterMatches[diagIdx].ijk[2] ) );
|
||||
}
|
||||
|
||||
if ( rangeFilterMatches[cornerIdx].cellMatchType == EXACT && rangeFilterMatches[diagIdx].cellMatchType == EXACT )
|
||||
{
|
||||
foundExactMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the start and end IJK from the matched corners
|
||||
if ( foundExactMatch )
|
||||
{
|
||||
// Populate dst range filter from the diagonal that matches exact
|
||||
dst.StartI = std::min( rangeFilterMatches[cornerIdx].ijk[0], rangeFilterMatches[diagIdx].ijk[0] );
|
||||
dst.StartJ = std::min( rangeFilterMatches[cornerIdx].ijk[1], rangeFilterMatches[diagIdx].ijk[1] );
|
||||
dst.StartK = std::min( rangeFilterMatches[cornerIdx].ijk[2], rangeFilterMatches[diagIdx].ijk[2] );
|
||||
dst.EndI = std::max( rangeFilterMatches[cornerIdx].ijk[0], rangeFilterMatches[diagIdx].ijk[0] );
|
||||
dst.EndJ = std::max( rangeFilterMatches[cornerIdx].ijk[1], rangeFilterMatches[diagIdx].ijk[1] );
|
||||
dst.EndK = std::max( rangeFilterMatches[cornerIdx].ijk[2], rangeFilterMatches[diagIdx].ijk[2] );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Look at the matches for each "face" of the range filter cube,
|
||||
// and use first exact match to determine the position of that "face"
|
||||
size_t faceIJKs[6] = { cvf::UNDEFINED_SIZE_T,
|
||||
cvf::UNDEFINED_SIZE_T,
|
||||
cvf::UNDEFINED_SIZE_T,
|
||||
cvf::UNDEFINED_SIZE_T,
|
||||
cvf::UNDEFINED_SIZE_T,
|
||||
cvf::UNDEFINED_SIZE_T };
|
||||
for ( int faceIdx = 0; faceIdx < 6; ++faceIdx )
|
||||
{
|
||||
auto gridAxis = cvf::StructGridInterface::gridAxisFromFace( cvf::StructGridInterface::FaceType( faceIdx ) );
|
||||
|
||||
int ijOrk = 0;
|
||||
if ( gridAxis == cvf::StructGridInterface::GridAxisType::AXIS_I ) ijOrk = 0;
|
||||
if ( gridAxis == cvf::StructGridInterface::GridAxisType::AXIS_J ) ijOrk = 1;
|
||||
if ( gridAxis == cvf::StructGridInterface::GridAxisType::AXIS_K ) ijOrk = 2;
|
||||
|
||||
cvf::ubyte surfCorners[4];
|
||||
cvf::StructGridInterface::cellFaceVertexIndices( (cvf::StructGridInterface::FaceType)faceIdx, surfCorners );
|
||||
bool foundAcceptedMatch = false;
|
||||
for ( int cIdx = 0; cIdx < 4; ++cIdx )
|
||||
{
|
||||
if ( rangeFilterMatches[surfCorners[cIdx]].cellMatchType == EXACT )
|
||||
{
|
||||
foundAcceptedMatch = true;
|
||||
|
||||
faceIJKs[faceIdx] = rangeFilterMatches[surfCorners[cIdx]].ijk[ijOrk];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !foundAcceptedMatch )
|
||||
{
|
||||
// Take first match that is not related to a collapsed eclipse cell
|
||||
for ( int cIdx = 0; cIdx < 4; ++cIdx )
|
||||
{
|
||||
if ( rangeFilterMatches[surfCorners[cIdx]].cellMatchType == APPROX )
|
||||
{
|
||||
foundAcceptedMatch = true;
|
||||
|
||||
faceIJKs[faceIdx] = rangeFilterMatches[surfCorners[cIdx]].ijk[ijOrk];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !foundAcceptedMatch )
|
||||
{
|
||||
// Only collapsed cell hits in this "face"
|
||||
// Todo: then use opposite face - range filter thickness
|
||||
// For now, just select the first
|
||||
faceIJKs[faceIdx] = rangeFilterMatches[surfCorners[0]].ijk[ijOrk];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
for ( int faceIdx = 0; faceIdx < 6; ++faceIdx )
|
||||
{
|
||||
CVF_TIGHT_ASSERT( faceIJKs[faceIdx] != cvf::UNDEFINED_SIZE_T );
|
||||
}
|
||||
#endif
|
||||
|
||||
dst.EndI = faceIJKs[cvf::StructGridInterface::POS_I];
|
||||
dst.StartI = faceIJKs[cvf::StructGridInterface::NEG_I];
|
||||
dst.EndJ = faceIJKs[cvf::StructGridInterface::POS_J];
|
||||
dst.StartJ = faceIJKs[cvf::StructGridInterface::NEG_J];
|
||||
dst.EndK = faceIJKs[cvf::StructGridInterface::POS_K];
|
||||
dst.StartK = faceIJKs[cvf::StructGridInterface::NEG_K];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Return 0 for collapsed cell 1 for
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCaseToCaseRangeFilterMapper::CellMatchType
|
||||
RigCaseToCaseRangeFilterMapper::findBestFemCellFromEclCell( const RigMainGrid* masterEclGrid,
|
||||
size_t ei,
|
||||
size_t ej,
|
||||
size_t ek,
|
||||
const RigFemPart* dependentFemPart,
|
||||
size_t* fi,
|
||||
size_t* fj,
|
||||
size_t* fk )
|
||||
{
|
||||
// Find tolerance
|
||||
|
||||
double cellSizeI, cellSizeJ, cellSizeK;
|
||||
masterEclGrid->characteristicCellSizes( &cellSizeI, &cellSizeJ, &cellSizeK );
|
||||
|
||||
double xyTolerance = cellSizeI * 0.01;
|
||||
double zTolerance = cellSizeK * 0.01;
|
||||
|
||||
bool isEclFaceNormalsOutwards = masterEclGrid->isFaceNormalsOutwards();
|
||||
|
||||
size_t cellIdx = masterEclGrid->cellIndexFromIJK( ei, ej, ek );
|
||||
|
||||
bool isCollapsedCell = masterEclGrid->globalCellArray()[cellIdx].isCollapsedCell();
|
||||
|
||||
cvf::Vec3d geoMechConvertedEclCell[8];
|
||||
RigCaseToCaseCellMapperTools::estimatedFemCellFromEclCell( masterEclGrid, cellIdx, geoMechConvertedEclCell );
|
||||
|
||||
cvf::BoundingBox elmBBox;
|
||||
for ( int i = 0; i < 8; ++i )
|
||||
elmBBox.add( geoMechConvertedEclCell[i] );
|
||||
|
||||
std::vector<size_t> closeElements;
|
||||
dependentFemPart->findIntersectingCells( elmBBox, &closeElements );
|
||||
|
||||
cvf::Vec3d elmCorners[8];
|
||||
int elmIdxToBestMatch = -1;
|
||||
double sqDistToClosestElmCenter = HUGE_VAL;
|
||||
cvf::Vec3d convEclCellCenter = RigCaseToCaseCellMapperTools::calculateCellCenter( geoMechConvertedEclCell );
|
||||
|
||||
bool foundExactMatch = false;
|
||||
|
||||
for ( size_t ccIdx = 0; ccIdx < closeElements.size(); ++ccIdx )
|
||||
{
|
||||
int elmIdx = static_cast<int>( closeElements[ccIdx] );
|
||||
|
||||
RigCaseToCaseCellMapperTools::elementCorners( dependentFemPart, elmIdx, elmCorners );
|
||||
|
||||
cvf::Vec3d cellCenter = RigCaseToCaseCellMapperTools::calculateCellCenter( elmCorners );
|
||||
double sqDist = ( cellCenter - convEclCellCenter ).lengthSquared();
|
||||
if ( sqDist < sqDistToClosestElmCenter )
|
||||
{
|
||||
elmIdxToBestMatch = elmIdx;
|
||||
sqDistToClosestElmCenter = sqDist;
|
||||
}
|
||||
|
||||
RigCaseToCaseCellMapperTools::rotateCellTopologicallyToMatchBaseCell( geoMechConvertedEclCell,
|
||||
isEclFaceNormalsOutwards,
|
||||
elmCorners );
|
||||
|
||||
foundExactMatch = RigCaseToCaseCellMapperTools::isEclFemCellsMatching( geoMechConvertedEclCell,
|
||||
elmCorners,
|
||||
xyTolerance,
|
||||
zTolerance );
|
||||
|
||||
if ( foundExactMatch )
|
||||
{
|
||||
elmIdxToBestMatch = elmIdx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( elmIdxToBestMatch != -1 )
|
||||
{
|
||||
bool validIndex = dependentFemPart->getOrCreateStructGrid()->ijkFromCellIndex( elmIdxToBestMatch, fi, fj, fk );
|
||||
CVF_ASSERT( validIndex );
|
||||
}
|
||||
else
|
||||
{
|
||||
( *fi ) = cvf::UNDEFINED_SIZE_T;
|
||||
( *fj ) = cvf::UNDEFINED_SIZE_T;
|
||||
( *fk ) = cvf::UNDEFINED_SIZE_T;
|
||||
}
|
||||
|
||||
if ( foundExactMatch ) return EXACT;
|
||||
if ( isCollapsedCell ) return APPROX_ON_COLLAPSED;
|
||||
return APPROX;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCaseToCaseRangeFilterMapper::CellMatchType
|
||||
RigCaseToCaseRangeFilterMapper::findBestEclCellFromFemCell( const RigFemPart* dependentFemPart,
|
||||
size_t fi,
|
||||
size_t fj,
|
||||
size_t fk,
|
||||
const RigMainGrid* masterEclGrid,
|
||||
size_t* ei,
|
||||
size_t* ej,
|
||||
size_t* ek )
|
||||
{
|
||||
// Find tolerance
|
||||
|
||||
double cellSizeI, cellSizeJ, cellSizeK;
|
||||
masterEclGrid->characteristicCellSizes( &cellSizeI, &cellSizeJ, &cellSizeK );
|
||||
|
||||
double xyTolerance = cellSizeI * 0.4;
|
||||
double zTolerance = cellSizeK * 0.4;
|
||||
|
||||
bool isEclFaceNormalsOutwards = masterEclGrid->isFaceNormalsOutwards();
|
||||
|
||||
int elementIdx = static_cast<int>( dependentFemPart->getOrCreateStructGrid()->cellIndexFromIJK( fi, fj, fk ) );
|
||||
|
||||
cvf::Vec3d elmCorners[8];
|
||||
RigCaseToCaseCellMapperTools::elementCorners( dependentFemPart, elementIdx, elmCorners );
|
||||
|
||||
cvf::BoundingBox elmBBox;
|
||||
for ( int i = 0; i < 8; ++i )
|
||||
elmBBox.add( elmCorners[i] );
|
||||
|
||||
std::vector<size_t> closeCells;
|
||||
masterEclGrid->findIntersectingCells( elmBBox,
|
||||
&closeCells ); // This might actually miss the exact one, but we have no other
|
||||
// alternative yet.
|
||||
|
||||
size_t globCellIdxToBestMatch = cvf::UNDEFINED_SIZE_T;
|
||||
double sqDistToClosestCellCenter = HUGE_VAL;
|
||||
cvf::Vec3d elmCenter = RigCaseToCaseCellMapperTools::calculateCellCenter( elmCorners );
|
||||
|
||||
bool foundExactMatch = false;
|
||||
cvf::Vec3d rotatedElm[8];
|
||||
|
||||
for ( size_t ccIdx = 0; ccIdx < closeCells.size(); ++ccIdx )
|
||||
{
|
||||
size_t cellIdx = closeCells[ccIdx];
|
||||
cvf::Vec3d geoMechConvertedEclCell[8];
|
||||
RigCaseToCaseCellMapperTools::estimatedFemCellFromEclCell( masterEclGrid, cellIdx, geoMechConvertedEclCell );
|
||||
|
||||
cvf::Vec3d cellCenter = RigCaseToCaseCellMapperTools::calculateCellCenter( geoMechConvertedEclCell );
|
||||
double sqDist = ( cellCenter - elmCenter ).lengthSquared();
|
||||
if ( sqDist < sqDistToClosestCellCenter )
|
||||
{
|
||||
globCellIdxToBestMatch = cellIdx;
|
||||
sqDistToClosestCellCenter = sqDist;
|
||||
}
|
||||
|
||||
rotatedElm[0] = elmCorners[0];
|
||||
rotatedElm[1] = elmCorners[1];
|
||||
rotatedElm[2] = elmCorners[2];
|
||||
rotatedElm[3] = elmCorners[3];
|
||||
rotatedElm[4] = elmCorners[4];
|
||||
rotatedElm[5] = elmCorners[5];
|
||||
rotatedElm[6] = elmCorners[6];
|
||||
rotatedElm[7] = elmCorners[7];
|
||||
|
||||
RigCaseToCaseCellMapperTools::rotateCellTopologicallyToMatchBaseCell( geoMechConvertedEclCell,
|
||||
isEclFaceNormalsOutwards,
|
||||
rotatedElm );
|
||||
|
||||
foundExactMatch = RigCaseToCaseCellMapperTools::isEclFemCellsMatching( geoMechConvertedEclCell,
|
||||
rotatedElm,
|
||||
xyTolerance,
|
||||
zTolerance );
|
||||
|
||||
if ( foundExactMatch )
|
||||
{
|
||||
globCellIdxToBestMatch = cellIdx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool isCollapsedCell = false;
|
||||
if ( globCellIdxToBestMatch != cvf::UNDEFINED_SIZE_T )
|
||||
{
|
||||
masterEclGrid->ijkFromCellIndex( globCellIdxToBestMatch, ei, ej, ek );
|
||||
isCollapsedCell = masterEclGrid->globalCellArray()[globCellIdxToBestMatch].isCollapsedCell();
|
||||
}
|
||||
else
|
||||
{
|
||||
( *ei ) = cvf::UNDEFINED_SIZE_T;
|
||||
( *ej ) = cvf::UNDEFINED_SIZE_T;
|
||||
( *ek ) = cvf::UNDEFINED_SIZE_T;
|
||||
}
|
||||
|
||||
if ( foundExactMatch ) return EXACT;
|
||||
if ( isCollapsedCell ) return APPROX_ON_COLLAPSED;
|
||||
return APPROX;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2015- Statoil ASA
|
||||
// Copyright (C) 2015- Ceetron Solutions AS
|
||||
//
|
||||
// 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 <cstddef>
|
||||
|
||||
class RimCellRangeFilter;
|
||||
class RigMainGrid;
|
||||
class RigFemPart;
|
||||
struct RigRangeEndPoints;
|
||||
|
||||
class RigCaseToCaseRangeFilterMapper
|
||||
{
|
||||
public:
|
||||
static void convertRangeFilterEclToFem( RimCellRangeFilter* srcFilter,
|
||||
const RigMainGrid* srcEclGrid,
|
||||
RimCellRangeFilter* dstFilter,
|
||||
const RigFemPart* dstFemPart );
|
||||
static void convertRangeFilterFemToEcl( RimCellRangeFilter* srcFilter,
|
||||
const RigFemPart* srcFemPart,
|
||||
RimCellRangeFilter* dstFilter,
|
||||
const RigMainGrid* dstEclGrid );
|
||||
|
||||
private:
|
||||
static void convertRangeFilter( const RimCellRangeFilter* srcFilter,
|
||||
RimCellRangeFilter* dstFilter,
|
||||
const RigMainGrid* eclGrid,
|
||||
const RigFemPart* femPart,
|
||||
bool femIsDestination );
|
||||
|
||||
static void convertRangeFilterEndPoints( const RigRangeEndPoints& src,
|
||||
RigRangeEndPoints& dst,
|
||||
const RigMainGrid* eclGrid,
|
||||
const RigFemPart* femPart,
|
||||
bool femIsDestination );
|
||||
|
||||
enum CellMatchType
|
||||
{
|
||||
APPROX_ON_COLLAPSED,
|
||||
APPROX,
|
||||
EXACT
|
||||
};
|
||||
static CellMatchType findBestFemCellFromEclCell( const RigMainGrid* masterEclGrid,
|
||||
size_t ei,
|
||||
size_t ej,
|
||||
size_t ek,
|
||||
const RigFemPart* dependentFemPart,
|
||||
size_t* fi,
|
||||
size_t* fj,
|
||||
size_t* fk );
|
||||
|
||||
static CellMatchType findBestEclCellFromFemCell( const RigFemPart* dependentFemPart,
|
||||
size_t fi,
|
||||
size_t fj,
|
||||
size_t fk,
|
||||
const RigMainGrid* masterEclGrid,
|
||||
size_t* ei,
|
||||
size_t* ej,
|
||||
size_t* ek );
|
||||
};
|
||||
@@ -0,0 +1,401 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2011- Statoil ASA
|
||||
// Copyright (C) 2013- Ceetron Solutions AS
|
||||
// Copyright (C) 2011-2012 Ceetron AS
|
||||
//
|
||||
// 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 "RigCell.h"
|
||||
#include "RigCellGeometryTools.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "cvfPlane.h"
|
||||
#include "cvfRay.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
static size_t undefinedCornersArray[8] = { cvf::UNDEFINED_SIZE_T,
|
||||
cvf::UNDEFINED_SIZE_T,
|
||||
cvf::UNDEFINED_SIZE_T,
|
||||
cvf::UNDEFINED_SIZE_T,
|
||||
cvf::UNDEFINED_SIZE_T,
|
||||
cvf::UNDEFINED_SIZE_T,
|
||||
cvf::UNDEFINED_SIZE_T,
|
||||
cvf::UNDEFINED_SIZE_T };
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCell::RigCell()
|
||||
: m_gridLocalCellIndex( cvf::UNDEFINED_SIZE_T )
|
||||
, m_hostGrid( nullptr )
|
||||
, m_subGrid( nullptr )
|
||||
, m_parentCellIndex( cvf::UNDEFINED_SIZE_T )
|
||||
, m_mainGridCellIndex( cvf::UNDEFINED_SIZE_T )
|
||||
, m_coarseningBoxIndex( cvf::UNDEFINED_SIZE_T )
|
||||
, m_isInvalid( false )
|
||||
{
|
||||
memcpy( m_cornerIndices.data(), undefinedCornersArray, 8 * sizeof( size_t ) );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCell::~RigCell()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Vec3d RigCell::center() const
|
||||
{
|
||||
cvf::Vec3d avg( cvf::Vec3d::ZERO );
|
||||
|
||||
size_t i;
|
||||
for ( i = 0; i < 8; i++ )
|
||||
{
|
||||
avg += m_hostGrid->mainGrid()->nodes()[m_cornerIndices[i]];
|
||||
}
|
||||
|
||||
avg /= 8.0;
|
||||
|
||||
return avg;
|
||||
}
|
||||
|
||||
bool isNear( const cvf::Vec3d& p1, const cvf::Vec3d& p2, double tolerance )
|
||||
{
|
||||
if ( cvf::Math::abs( p1[0] - p2[0] ) < tolerance && cvf::Math::abs( p1[1] - p2[1] ) < tolerance &&
|
||||
cvf::Math::abs( p1[2] - p2[2] ) < tolerance )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigCell::isLongPyramidCell( double maxHeightFactor, double nodeNearTolerance ) const
|
||||
{
|
||||
cvf::ubyte faceVertexIndices[4];
|
||||
double squaredMaxHeightFactor = maxHeightFactor * maxHeightFactor;
|
||||
|
||||
const std::vector<cvf::Vec3d>& nodes = m_hostGrid->mainGrid()->nodes();
|
||||
|
||||
int face;
|
||||
for ( face = 0; face < 6; ++face )
|
||||
{
|
||||
cvf::StructGridInterface::cellFaceVertexIndices( static_cast<cvf::StructGridInterface::FaceType>( face ),
|
||||
faceVertexIndices );
|
||||
int zeroLengthEdgeCount = 0;
|
||||
|
||||
const cvf::Vec3d& c0 = nodes[m_cornerIndices[faceVertexIndices[0]]];
|
||||
const cvf::Vec3d& c1 = nodes[m_cornerIndices[faceVertexIndices[1]]];
|
||||
const cvf::Vec3d& c2 = nodes[m_cornerIndices[faceVertexIndices[2]]];
|
||||
const cvf::Vec3d& c3 = nodes[m_cornerIndices[faceVertexIndices[3]]];
|
||||
|
||||
if ( isNear( c0, c1, nodeNearTolerance ) )
|
||||
{
|
||||
++zeroLengthEdgeCount;
|
||||
}
|
||||
if ( isNear( c1, c2, nodeNearTolerance ) )
|
||||
{
|
||||
++zeroLengthEdgeCount;
|
||||
}
|
||||
if ( isNear( c2, c3, nodeNearTolerance ) )
|
||||
{
|
||||
++zeroLengthEdgeCount;
|
||||
}
|
||||
|
||||
if ( zeroLengthEdgeCount == 3 )
|
||||
{
|
||||
return true;
|
||||
|
||||
#if 0 // More advanced checks turned off since the start. Why did I do that ?
|
||||
// Collapse of a complete face is detected. This is possibly the top of a pyramid
|
||||
|
||||
// "face" has the index to the collapsed face. We need the size of the opposite face
|
||||
// to compare it with the pyramid "roof" length.
|
||||
|
||||
cvf::StructGridInterface::FaceType oppositeFace = cvf::StructGridInterface::POS_I;
|
||||
switch (face)
|
||||
{
|
||||
case cvf::StructGridInterface::POS_I:
|
||||
oppositeFace = cvf::StructGridInterface::NEG_I;
|
||||
break;
|
||||
case cvf::StructGridInterface::POS_J:
|
||||
oppositeFace = cvf::StructGridInterface::NEG_J;
|
||||
break;
|
||||
case cvf::StructGridInterface::POS_K:
|
||||
oppositeFace = cvf::StructGridInterface::NEG_K;
|
||||
break;
|
||||
case cvf::StructGridInterface::NEG_I:
|
||||
oppositeFace = cvf::StructGridInterface::POS_I;
|
||||
break;
|
||||
case cvf::StructGridInterface::NEG_J:
|
||||
oppositeFace = cvf::StructGridInterface::POS_J;
|
||||
break;
|
||||
case cvf::StructGridInterface::NEG_K:
|
||||
oppositeFace = cvf::StructGridInterface::POS_K;
|
||||
break;
|
||||
default:
|
||||
CVF_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
cvf::StructGridInterface::cellFaceVertexIndices(oppositeFace, faceVertexIndices);
|
||||
|
||||
|
||||
const cvf::Vec3d& c0opp = nodes[m_cornerIndices[faceVertexIndices[0]]];
|
||||
const cvf::Vec3d& c1opp = nodes[m_cornerIndices[faceVertexIndices[1]]];
|
||||
const cvf::Vec3d& c2opp = nodes[m_cornerIndices[faceVertexIndices[2]]];
|
||||
const cvf::Vec3d& c3opp = nodes[m_cornerIndices[faceVertexIndices[3]]];
|
||||
|
||||
// Check if any of the opposite face vertexes are also degenerated to the pyramid top
|
||||
|
||||
int okVertexCount = 0;
|
||||
cvf::Vec3d okVxs[4];
|
||||
if (!isNear(c0opp, c0, nodeNearTolerance)) { okVxs[okVertexCount] = c0opp; ++okVertexCount; }
|
||||
if (!isNear(c1opp, c0, nodeNearTolerance)) { okVxs[okVertexCount] = c1opp; ++okVertexCount; }
|
||||
if (!isNear(c2opp, c0, nodeNearTolerance)) { okVxs[okVertexCount] = c2opp; ++okVertexCount; }
|
||||
if (!isNear(c3opp, c0, nodeNearTolerance)) { okVxs[okVertexCount] = c3opp; ++okVertexCount; }
|
||||
|
||||
if (okVertexCount < 2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the good vertices to calculate a face size that can be compared to the pyramid height:
|
||||
double typicalSquaredEdgeLength = 0;
|
||||
for (int i = 1; i < okVertexCount; ++i)
|
||||
{
|
||||
typicalSquaredEdgeLength += (okVxs[i-1] - okVxs[i]).lengthSquared();
|
||||
}
|
||||
typicalSquaredEdgeLength /= okVertexCount;
|
||||
double pyramidHeightSquared = (okVxs[0] - c0).lengthSquared();
|
||||
|
||||
if (pyramidHeightSquared > squaredMaxHeightFactor*typicalSquaredEdgeLength)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Check the ratio of the length of opposite edges.
|
||||
// both ratios have to be above threshold to detect a pyramid-ish cell
|
||||
// Only test this if we have all nonzero edge lengths.
|
||||
else if ( zeroLengthEdgeCount == 0 ) // If the four first faces are ok, the two last must be as well
|
||||
{
|
||||
double e0SquareLength = ( c1 - c0 ).lengthSquared();
|
||||
double e2SquareLength = ( c3 - c2 ).lengthSquared();
|
||||
if ( e0SquareLength / e2SquareLength > squaredMaxHeightFactor ||
|
||||
e2SquareLength / e0SquareLength > squaredMaxHeightFactor )
|
||||
{
|
||||
double e1SquareLength = ( c2 - c1 ).lengthSquared();
|
||||
double e3SquareLength = ( c0 - c3 ).lengthSquared();
|
||||
|
||||
if ( e1SquareLength / e3SquareLength > squaredMaxHeightFactor ||
|
||||
e3SquareLength / e1SquareLength > squaredMaxHeightFactor )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigCell::isCollapsedCell( double nodeNearTolerance ) const
|
||||
{
|
||||
const std::vector<cvf::Vec3d>& nodes = m_hostGrid->mainGrid()->nodes();
|
||||
|
||||
cvf::ubyte faceVertexIndices[4];
|
||||
cvf::ubyte oppFaceVertexIndices[4];
|
||||
|
||||
int face;
|
||||
for ( face = 0; face < 6; face += 2 )
|
||||
{
|
||||
cvf::StructGridInterface::cellFaceVertexIndices( static_cast<cvf::StructGridInterface::FaceType>( face ),
|
||||
faceVertexIndices );
|
||||
cvf::StructGridInterface::cellFaceVertexIndices( cvf::StructGridInterface::oppositeFace(
|
||||
static_cast<cvf::StructGridInterface::FaceType>( face ) ),
|
||||
oppFaceVertexIndices );
|
||||
|
||||
cvf::Vec3d c0 = nodes[m_cornerIndices[faceVertexIndices[0]]];
|
||||
cvf::Vec3d c1 = nodes[m_cornerIndices[faceVertexIndices[1]]];
|
||||
cvf::Vec3d c2 = nodes[m_cornerIndices[faceVertexIndices[2]]];
|
||||
cvf::Vec3d c3 = nodes[m_cornerIndices[faceVertexIndices[3]]];
|
||||
|
||||
cvf::Vec3d oc0 = nodes[m_cornerIndices[oppFaceVertexIndices[0]]];
|
||||
cvf::Vec3d oc1 = nodes[m_cornerIndices[oppFaceVertexIndices[1]]];
|
||||
cvf::Vec3d oc2 = nodes[m_cornerIndices[oppFaceVertexIndices[2]]];
|
||||
cvf::Vec3d oc3 = nodes[m_cornerIndices[oppFaceVertexIndices[3]]];
|
||||
|
||||
int zeroLengthEdgeCount = 0;
|
||||
if ( isNear( c0, oc0, nodeNearTolerance ) )
|
||||
{
|
||||
++zeroLengthEdgeCount;
|
||||
}
|
||||
if ( isNear( c1, oc3, nodeNearTolerance ) )
|
||||
{
|
||||
++zeroLengthEdgeCount;
|
||||
}
|
||||
if ( isNear( c2, oc2, nodeNearTolerance ) )
|
||||
{
|
||||
++zeroLengthEdgeCount;
|
||||
}
|
||||
if ( isNear( c3, oc1, nodeNearTolerance ) )
|
||||
{
|
||||
++zeroLengthEdgeCount;
|
||||
}
|
||||
|
||||
if ( zeroLengthEdgeCount >= 4 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Vec3d RigCell::faceCenter( cvf::StructGridInterface::FaceType face ) const
|
||||
{
|
||||
cvf::Vec3d avg( cvf::Vec3d::ZERO );
|
||||
|
||||
cvf::ubyte faceVertexIndices[4];
|
||||
cvf::StructGridInterface::cellFaceVertexIndices( face, faceVertexIndices );
|
||||
|
||||
const std::vector<cvf::Vec3d>& nodeCoords = m_hostGrid->mainGrid()->nodes();
|
||||
|
||||
size_t i;
|
||||
for ( i = 0; i < 4; i++ )
|
||||
{
|
||||
avg += nodeCoords[m_cornerIndices[faceVertexIndices[i]]];
|
||||
}
|
||||
|
||||
avg /= 4.0;
|
||||
|
||||
return avg;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Returns an area vector for the cell face. The direction is the face normal, and the length is
|
||||
/// equal to the face area (projected to the plane represented by the diagonal in case of warp)
|
||||
/// The components of this area vector are equal to the area of the face projection onto
|
||||
/// the corresponding plane.
|
||||
/// See http://geomalgorithms.com/a01-_area.html
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Vec3d RigCell::faceNormalWithAreaLength( cvf::StructGridInterface::FaceType face ) const
|
||||
{
|
||||
cvf::ubyte faceVertexIndices[4];
|
||||
cvf::StructGridInterface::cellFaceVertexIndices( face, faceVertexIndices );
|
||||
const std::vector<cvf::Vec3d>& nodeCoords = m_hostGrid->mainGrid()->nodes();
|
||||
|
||||
return 0.5 * ( nodeCoords[m_cornerIndices[faceVertexIndices[2]]] - nodeCoords[m_cornerIndices[faceVertexIndices[0]]] ) ^
|
||||
( nodeCoords[m_cornerIndices[faceVertexIndices[3]]] - nodeCoords[m_cornerIndices[faceVertexIndices[1]]] );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCell::volume() const
|
||||
{
|
||||
const std::vector<cvf::Vec3d>& nodeCoords = m_hostGrid->mainGrid()->nodes();
|
||||
|
||||
std::array<cvf::Vec3d, 8> hexCorners;
|
||||
for ( size_t i = 0; i < 8; ++i )
|
||||
{
|
||||
hexCorners[i] = nodeCoords.at( m_cornerIndices[i] );
|
||||
}
|
||||
return RigCellGeometryTools::calculateCellVolume( hexCorners );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Find the intersection between the cell and the ray. The point closest to the ray origin is returned
|
||||
/// in \a intersectionPoint, while the return value is the total number of intersections with the 24 triangles
|
||||
/// the cell is interpreted as.
|
||||
/// If no intersection is found, the intersection point is untouched.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
int RigCell::firstIntersectionPoint( const cvf::Ray& ray, cvf::Vec3d* intersectionPoint ) const
|
||||
{
|
||||
CVF_ASSERT( intersectionPoint != nullptr );
|
||||
|
||||
cvf::ubyte faceVertexIndices[4];
|
||||
int face;
|
||||
const std::vector<cvf::Vec3d>& nodes = m_hostGrid->mainGrid()->nodes();
|
||||
|
||||
cvf::Vec3d firstIntersection( cvf::Vec3d::ZERO );
|
||||
double minLsq = HUGE_VAL;
|
||||
int intersectionCount = 0;
|
||||
|
||||
for ( face = 0; face < 6; ++face )
|
||||
{
|
||||
cvf::StructGridInterface::cellFaceVertexIndices( static_cast<cvf::StructGridInterface::FaceType>( face ),
|
||||
faceVertexIndices );
|
||||
cvf::Vec3d intersection;
|
||||
cvf::Vec3d faceCenter = this->faceCenter( static_cast<cvf::StructGridInterface::FaceType>( face ) );
|
||||
|
||||
for ( size_t i = 0; i < 4; ++i )
|
||||
{
|
||||
size_t next = i < 3 ? i + 1 : 0;
|
||||
if ( ray.triangleIntersect( nodes[m_cornerIndices[faceVertexIndices[i]]],
|
||||
nodes[m_cornerIndices[faceVertexIndices[next]]],
|
||||
faceCenter,
|
||||
&intersection ) )
|
||||
{
|
||||
intersectionCount++;
|
||||
double lsq = ( intersection - ray.origin() ).lengthSquared();
|
||||
if ( lsq < minLsq )
|
||||
{
|
||||
firstIntersection = intersection;
|
||||
minLsq = lsq;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( intersectionCount > 0 )
|
||||
{
|
||||
*intersectionPoint = firstIntersection;
|
||||
}
|
||||
|
||||
return intersectionCount;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCell::faceIndices( cvf::StructGridInterface::FaceType face, std::array<size_t, 4>* indices ) const
|
||||
{
|
||||
cvf::ubyte faceVertexIndices[4];
|
||||
cvf::StructGridInterface::cellFaceVertexIndices( face, faceVertexIndices );
|
||||
|
||||
( *indices )[0] = m_cornerIndices[faceVertexIndices[0]];
|
||||
( *indices )[1] = m_cornerIndices[faceVertexIndices[1]];
|
||||
( *indices )[2] = m_cornerIndices[faceVertexIndices[2]];
|
||||
( *indices )[3] = m_cornerIndices[faceVertexIndices[3]];
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2011- Statoil ASA
|
||||
// Copyright (C) 2013- Ceetron Solutions AS
|
||||
// Copyright (C) 2011-2012 Ceetron AS
|
||||
//
|
||||
// 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 "RigLocalGrid.h"
|
||||
|
||||
#include "cvfStructGrid.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace cvf
|
||||
{
|
||||
class Ray;
|
||||
}
|
||||
|
||||
class RigCell
|
||||
{
|
||||
public:
|
||||
RigCell();
|
||||
~RigCell(); // Not virtual, to save space. Do not inherit from this class
|
||||
|
||||
std::array<size_t, 8>& cornerIndices() { return m_cornerIndices; }
|
||||
const std::array<size_t, 8>& cornerIndices() const { return m_cornerIndices; }
|
||||
|
||||
void faceIndices( cvf::StructGridInterface::FaceType face, std::array<size_t, 4>* faceIndices ) const;
|
||||
|
||||
bool isInvalid() const { return m_isInvalid; }
|
||||
void setInvalid( bool val ) { m_isInvalid = val; }
|
||||
|
||||
size_t gridLocalCellIndex() const { return m_gridLocalCellIndex; }
|
||||
void setGridLocalCellIndex( size_t val ) { m_gridLocalCellIndex = val; }
|
||||
|
||||
RigLocalGrid* subGrid() const { return m_subGrid; }
|
||||
void setSubGrid( RigLocalGrid* subGrid ) { m_subGrid = subGrid; }
|
||||
void removeSubGrid( RigLocalGrid* subGrid ) { m_subGrid = nullptr; }
|
||||
|
||||
RigGridBase* hostGrid() const { return m_hostGrid; }
|
||||
void setHostGrid( RigGridBase* hostGrid ) { m_hostGrid = hostGrid; }
|
||||
|
||||
size_t parentCellIndex() const { return m_parentCellIndex; }
|
||||
void setParentCellIndex( size_t parentCellIndex ) { m_parentCellIndex = parentCellIndex; }
|
||||
size_t mainGridCellIndex() const { return m_mainGridCellIndex; }
|
||||
void setMainGridCellIndex( size_t mainGridCellContainingThisCell )
|
||||
{
|
||||
m_mainGridCellIndex = mainGridCellContainingThisCell;
|
||||
}
|
||||
|
||||
size_t coarseningBoxIndex() const { return m_coarseningBoxIndex; }
|
||||
void setCoarseningBoxIndex( size_t coarseningBoxIndex ) { m_coarseningBoxIndex = coarseningBoxIndex; }
|
||||
|
||||
cvf::Vec3d center() const;
|
||||
cvf::Vec3d faceCenter( cvf::StructGridInterface::FaceType face ) const;
|
||||
cvf::Vec3d faceNormalWithAreaLength( cvf::StructGridInterface::FaceType face ) const;
|
||||
double volume() const;
|
||||
|
||||
int firstIntersectionPoint( const cvf::Ray& ray, cvf::Vec3d* intersectionPoint ) const;
|
||||
bool isLongPyramidCell( double maxHeightFactor = 5, double nodeNearTolerance = 1e-3 ) const;
|
||||
bool isCollapsedCell( double nodeNearTolerance = 1e-3 ) const;
|
||||
|
||||
private:
|
||||
std::array<size_t, 8> m_cornerIndices;
|
||||
|
||||
size_t m_gridLocalCellIndex; ///< This cells index in the grid it belongs to.
|
||||
RigGridBase* m_hostGrid;
|
||||
RigLocalGrid* m_subGrid;
|
||||
|
||||
size_t m_parentCellIndex; ///< Grid cell index of the cell in the parent grid containing this cell
|
||||
size_t m_mainGridCellIndex;
|
||||
|
||||
size_t m_coarseningBoxIndex; ///< If defined, index into list of coarsening boxes in RigGridBase
|
||||
|
||||
bool m_isInvalid;
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigCellEdgeResultAccessor.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCellEdgeResultAccessor::RigCellEdgeResultAccessor()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCellEdgeResultAccessor::setDataAccessObjectForFace( cvf::StructGridInterface::FaceType faceId,
|
||||
RigResultAccessor* resultAccessObject )
|
||||
{
|
||||
m_resultAccessObjects[faceId] = resultAccessObject;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCellEdgeResultAccessor::cellScalar( size_t gridLocalCellIndex ) const
|
||||
{
|
||||
// TODO: How to handle when we get here?
|
||||
CVF_ASSERT( false );
|
||||
|
||||
return cvf::UNDEFINED_DOUBLE;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCellEdgeResultAccessor::cellFaceScalar( size_t gridLocalCellIndex, cvf::StructGridInterface::FaceType faceId ) const
|
||||
{
|
||||
const RigResultAccessor* resultAccessObj = m_resultAccessObjects[faceId].p();
|
||||
if ( resultAccessObj != nullptr )
|
||||
{
|
||||
return resultAccessObj->cellFaceScalar( gridLocalCellIndex, faceId );
|
||||
}
|
||||
|
||||
return HUGE_VAL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCellEdgeResultAccessor::cellScalarGlobIdx( size_t globCellIndex ) const
|
||||
{
|
||||
// TODO: How to handle when we get here?
|
||||
CVF_ASSERT( false );
|
||||
|
||||
return cvf::UNDEFINED_DOUBLE;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCellEdgeResultAccessor::cellFaceScalarGlobIdx( size_t globCellIndex, cvf::StructGridInterface::FaceType faceId ) const
|
||||
{
|
||||
const RigResultAccessor* resultAccessObj = m_resultAccessObjects[faceId].p();
|
||||
if ( resultAccessObj != nullptr )
|
||||
{
|
||||
return resultAccessObj->cellFaceScalarGlobIdx( globCellIndex, faceId );
|
||||
}
|
||||
|
||||
return HUGE_VAL;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigResultAccessor.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigCellEdgeResultAccessor : public RigResultAccessor
|
||||
{
|
||||
public:
|
||||
RigCellEdgeResultAccessor();
|
||||
|
||||
void setDataAccessObjectForFace( cvf::StructGridInterface::FaceType faceId, RigResultAccessor* resultAccessObject );
|
||||
|
||||
double cellScalar( size_t gridLocalCellIndex ) const override;
|
||||
double cellFaceScalar( size_t gridLocalCellIndex, cvf::StructGridInterface::FaceType faceId ) const override;
|
||||
|
||||
double cellScalarGlobIdx( size_t globCellIndex ) const override;
|
||||
double cellFaceScalarGlobIdx( size_t globCellIndex, cvf::StructGridInterface::FaceType faceId ) const override;
|
||||
|
||||
private:
|
||||
std::array<cvf::ref<RigResultAccessor>, 6> m_resultAccessObjects;
|
||||
};
|
||||
@@ -0,0 +1,360 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2020 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 "RigCellFaceGeometryTools.h"
|
||||
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigCell.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigNncConnection.h"
|
||||
|
||||
#include "cvfGeometryTools.h"
|
||||
|
||||
#include "cafAssert.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::StructGridInterface::FaceType
|
||||
RigCellFaceGeometryTools::calculateCellFaceOverlap( const RigCell& c1,
|
||||
const RigCell& c2,
|
||||
const RigMainGrid& mainGrid,
|
||||
std::vector<size_t>* connectionPolygon,
|
||||
std::vector<cvf::Vec3d>* connectionIntersections )
|
||||
{
|
||||
// Try to find the shared face
|
||||
|
||||
bool isPossibleNeighborInDirection[6] = { true, true, true, true, true, true };
|
||||
|
||||
if ( c1.hostGrid() == c2.hostGrid() )
|
||||
{
|
||||
char hasNeighbourInAnyDirection = 0;
|
||||
|
||||
size_t i1, j1, k1;
|
||||
c1.hostGrid()->ijkFromCellIndex( c1.gridLocalCellIndex(), &i1, &j1, &k1 );
|
||||
size_t i2, j2, k2;
|
||||
c2.hostGrid()->ijkFromCellIndex( c2.gridLocalCellIndex(), &i2, &j2, &k2 );
|
||||
|
||||
isPossibleNeighborInDirection[cvf::StructGridInterface::POS_I] = ( ( i1 + 1 ) == i2 );
|
||||
isPossibleNeighborInDirection[cvf::StructGridInterface::NEG_I] = ( ( i2 + 1 ) == i1 );
|
||||
isPossibleNeighborInDirection[cvf::StructGridInterface::POS_J] = ( ( j1 + 1 ) == j2 );
|
||||
isPossibleNeighborInDirection[cvf::StructGridInterface::NEG_J] = ( ( j2 + 1 ) == j1 );
|
||||
isPossibleNeighborInDirection[cvf::StructGridInterface::POS_K] = ( ( k1 + 1 ) == k2 );
|
||||
isPossibleNeighborInDirection[cvf::StructGridInterface::NEG_K] = ( ( k2 + 1 ) == k1 );
|
||||
|
||||
hasNeighbourInAnyDirection = isPossibleNeighborInDirection[cvf::StructGridInterface::POS_I] +
|
||||
isPossibleNeighborInDirection[cvf::StructGridInterface::NEG_I] +
|
||||
isPossibleNeighborInDirection[cvf::StructGridInterface::POS_J] +
|
||||
isPossibleNeighborInDirection[cvf::StructGridInterface::NEG_J] +
|
||||
isPossibleNeighborInDirection[cvf::StructGridInterface::POS_K] +
|
||||
isPossibleNeighborInDirection[cvf::StructGridInterface::NEG_K];
|
||||
|
||||
// If cell 2 is not adjancent with respect to any of the six ijk directions,
|
||||
// assume that we have no overlapping area.
|
||||
|
||||
if ( !hasNeighbourInAnyDirection )
|
||||
{
|
||||
// Add to search map
|
||||
// m_cellIdxToFaceToConnectionIdxMap[m_connections[cnIdx].m_c1GlobIdx][cvf::StructGridInterface::NO_FACE].push_back(cnIdx);
|
||||
// m_cellIdxToFaceToConnectionIdxMap[m_connections[cnIdx].m_c2GlobIdx][cvf::StructGridInterface::NO_FACE].push_back(cnIdx);
|
||||
|
||||
// cvf::Trace::show("NNC: No direct neighbors : C1: " + cvf::String((int)m_connections[cnIdx].m_c1GlobIdx) +
|
||||
// " C2: " + cvf::String((int)m_connections[cnIdx].m_c2GlobIdx));
|
||||
return cvf::StructGridInterface::NO_FACE;
|
||||
}
|
||||
}
|
||||
|
||||
for ( unsigned char fIdx = 0; fIdx < 6; ++fIdx )
|
||||
{
|
||||
if ( !isPossibleNeighborInDirection[fIdx] )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate connection polygon
|
||||
|
||||
std::vector<size_t> polygon;
|
||||
std::vector<cvf::Vec3d> intersections;
|
||||
std::array<size_t, 4> face1;
|
||||
std::array<size_t, 4> face2;
|
||||
c1.faceIndices( ( cvf::StructGridInterface::FaceType )( fIdx ), &face1 );
|
||||
c2.faceIndices( cvf::StructGridInterface::oppositeFace( ( cvf::StructGridInterface::FaceType )( fIdx ) ), &face2 );
|
||||
|
||||
bool foundOverlap =
|
||||
cvf::GeometryTools::calculateOverlapPolygonOfTwoQuads( &polygon,
|
||||
&intersections,
|
||||
(cvf::EdgeIntersectStorage<size_t>*)nullptr,
|
||||
cvf::wrapArrayConst( &mainGrid.nodes() ),
|
||||
face1.data(),
|
||||
face2.data(),
|
||||
1e-6 );
|
||||
|
||||
if ( foundOverlap )
|
||||
{
|
||||
if ( connectionPolygon ) ( *connectionPolygon ) = polygon;
|
||||
if ( connectionIntersections ) ( *connectionIntersections ) = intersections;
|
||||
return ( cvf::StructGridInterface::FaceType )( fIdx );
|
||||
}
|
||||
}
|
||||
|
||||
return cvf::StructGridInterface::NO_FACE;
|
||||
}
|
||||
|
||||
void assignThreadConnections( RigConnectionContainer& allConnections, RigConnectionContainer& threadConnections )
|
||||
{
|
||||
#pragma omp critical
|
||||
{
|
||||
allConnections.push_back( threadConnections );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigConnectionContainer RigCellFaceGeometryTools::computeOtherNncs( const RigMainGrid* mainGrid,
|
||||
const RigConnectionContainer& nativeConnections,
|
||||
const RigActiveCellInfo* activeCellInfo,
|
||||
bool includeInactiveCells )
|
||||
{
|
||||
// Compute Non-Neighbor Connections (NNC) not reported by Eclipse. NNCs with zero transmissibility are not reported
|
||||
// by Eclipse. Use faults as basis for subset of cells to find NNC connection for. The imported connections from
|
||||
// Eclipse are located at the beginning of the connections vector.
|
||||
|
||||
std::set<std::pair<unsigned, unsigned>> nativeCellPairs;
|
||||
|
||||
for ( size_t i = 0; i < nativeConnections.size(); ++i )
|
||||
{
|
||||
RigConnection c = nativeConnections[i];
|
||||
nativeCellPairs.emplace( static_cast<unsigned>( c.c1GlobIdx() ), static_cast<unsigned>( c.c2GlobIdx() ) );
|
||||
}
|
||||
|
||||
if ( nativeConnections.size() != nativeCellPairs.size() )
|
||||
{
|
||||
QString message = QString( "Nnc connection imported from Eclipse are not unique\nNNC count : %1\nUnique : %2" )
|
||||
.arg( nativeConnections.size() )
|
||||
.arg( nativeCellPairs.size() );
|
||||
qDebug() << message;
|
||||
}
|
||||
|
||||
const cvf::Collection<RigFault>& faults = mainGrid->faults();
|
||||
|
||||
RigConnectionContainer otherConnections;
|
||||
|
||||
for ( int faultIdx = 0; faultIdx < (int)faults.size(); faultIdx++ )
|
||||
{
|
||||
const RigFault* fault = faults.at( faultIdx );
|
||||
|
||||
const std::vector<RigFault::FaultFace>& faultFaces = fault->faultFaces();
|
||||
|
||||
// Build a vector of active face indices so we don't have to have this check inside the parallel loop.
|
||||
// This makes the load balancing much better in the loop.
|
||||
std::vector<size_t> activeFaceIndices;
|
||||
activeFaceIndices.reserve( faultFaces.size() );
|
||||
for ( size_t faceIdx = 0; faceIdx < faultFaces.size(); ++faceIdx )
|
||||
{
|
||||
const RigFault::FaultFace& f = faultFaces[faceIdx];
|
||||
|
||||
bool atLeastOneCellActive = true;
|
||||
if ( !includeInactiveCells && activeCellInfo && activeCellInfo->reservoirActiveCellCount() > 0u )
|
||||
{
|
||||
atLeastOneCellActive = activeCellInfo->isActive( f.m_nativeReservoirCellIndex ) ||
|
||||
activeCellInfo->isActive( f.m_oppositeReservoirCellIndex );
|
||||
}
|
||||
|
||||
if ( atLeastOneCellActive ) activeFaceIndices.push_back( faceIdx );
|
||||
}
|
||||
|
||||
size_t totalNumberOfConnections = 0u;
|
||||
#pragma omp parallel
|
||||
{
|
||||
RigConnectionContainer threadConnections;
|
||||
#pragma omp for schedule( guided ) reduction( + : totalNumberOfConnections )
|
||||
for ( int activeFaceIdx = 0; activeFaceIdx < static_cast<int>( activeFaceIndices.size() ); activeFaceIdx++ )
|
||||
{
|
||||
size_t faceIdx = activeFaceIndices[activeFaceIdx];
|
||||
extractConnectionsForFace( faultFaces[faceIdx], mainGrid, nativeCellPairs, threadConnections );
|
||||
}
|
||||
#pragma omp barrier
|
||||
otherConnections.reserve( otherConnections.size() + totalNumberOfConnections );
|
||||
|
||||
// Merge together connections per thread
|
||||
assignThreadConnections( otherConnections, threadConnections );
|
||||
} // end parallel region
|
||||
}
|
||||
|
||||
otherConnections.remove_duplicates();
|
||||
return otherConnections;
|
||||
}
|
||||
|
||||
void RigCellFaceGeometryTools::extractConnectionsForFace( const RigFault::FaultFace& face,
|
||||
const RigMainGrid* mainGrid,
|
||||
const std::set<std::pair<unsigned, unsigned>>& nativeCellPairs,
|
||||
RigConnectionContainer& connections )
|
||||
{
|
||||
size_t sourceReservoirCellIndex = face.m_nativeReservoirCellIndex;
|
||||
cvf::StructGridInterface::FaceType sourceCellFace = face.m_nativeFace;
|
||||
|
||||
if ( sourceReservoirCellIndex >= mainGrid->cellCount() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const std::vector<cvf::Vec3d>& mainGridNodes = mainGrid->nodes();
|
||||
|
||||
cvf::BoundingBox bb;
|
||||
std::array<size_t, 4> sourceFaceIndices;
|
||||
mainGrid->globalCellArray()[sourceReservoirCellIndex].faceIndices( sourceCellFace, &sourceFaceIndices );
|
||||
|
||||
bb.add( mainGridNodes[sourceFaceIndices[0]] );
|
||||
bb.add( mainGridNodes[sourceFaceIndices[1]] );
|
||||
bb.add( mainGridNodes[sourceFaceIndices[2]] );
|
||||
bb.add( mainGridNodes[sourceFaceIndices[3]] );
|
||||
|
||||
std::vector<size_t> closeCells;
|
||||
mainGrid->findIntersectingCells( bb, &closeCells );
|
||||
|
||||
cvf::StructGridInterface::FaceType candidateFace = cvf::StructGridInterface::oppositeFace( sourceCellFace );
|
||||
|
||||
size_t neighborCellIndex = std::numeric_limits<size_t>::max();
|
||||
size_t ni = std::numeric_limits<size_t>::max();
|
||||
size_t nj = std::numeric_limits<size_t>::max();
|
||||
size_t nk = std::numeric_limits<size_t>::max();
|
||||
|
||||
{
|
||||
size_t i;
|
||||
size_t j;
|
||||
size_t k;
|
||||
mainGrid->ijkFromCellIndexUnguarded( sourceReservoirCellIndex, &i, &j, &k );
|
||||
|
||||
mainGrid->neighborIJKAtCellFace( i, j, k, sourceCellFace, &ni, &nj, &nk );
|
||||
|
||||
if ( mainGrid->isCellValid( ni, nj, nk ) )
|
||||
{
|
||||
neighborCellIndex = mainGrid->cellIndexFromIJK( ni, nj, nk );
|
||||
}
|
||||
}
|
||||
|
||||
for ( size_t candidateCellIndex : closeCells )
|
||||
{
|
||||
if ( candidateCellIndex == sourceReservoirCellIndex )
|
||||
{
|
||||
// Exclude cellIndex for source cell
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( candidateCellIndex >= mainGrid->cellCount() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( candidateCellIndex == neighborCellIndex )
|
||||
{
|
||||
// Exclude direct neighbor
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( neighborCellIndex != std::numeric_limits<size_t>::max() )
|
||||
{
|
||||
// Find target IJK index based on source cell and cell face
|
||||
// Exclude cells not matching destination target index
|
||||
|
||||
size_t ci = std::numeric_limits<size_t>::max();
|
||||
size_t cj = std::numeric_limits<size_t>::max();
|
||||
size_t ck = std::numeric_limits<size_t>::max();
|
||||
mainGrid->ijkFromCellIndexUnguarded( candidateCellIndex, &ci, &cj, &ck );
|
||||
|
||||
auto gridAxis = cvf::StructGridInterface::gridAxisFromFace( sourceCellFace );
|
||||
if ( gridAxis == cvf::StructGridInterface::GridAxisType::AXIS_I )
|
||||
{
|
||||
if ( ni != ci )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if ( gridAxis == cvf::StructGridInterface::GridAxisType::AXIS_J )
|
||||
{
|
||||
if ( nj != cj )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if ( gridAxis == cvf::StructGridInterface::GridAxisType::AXIS_K )
|
||||
{
|
||||
if ( nk != ck )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<unsigned, unsigned> candidate( static_cast<unsigned>( sourceReservoirCellIndex ),
|
||||
static_cast<unsigned>( candidateCellIndex ) );
|
||||
|
||||
if ( nativeCellPairs.count( candidate ) > 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<size_t> polygon;
|
||||
std::vector<cvf::Vec3d> intersections;
|
||||
|
||||
std::array<size_t, 4> candidateFaceIndices;
|
||||
mainGrid->globalCellArray()[candidateCellIndex].faceIndices( candidateFace, &candidateFaceIndices );
|
||||
|
||||
bool foundOverlap =
|
||||
cvf::GeometryTools::calculateOverlapPolygonOfTwoQuads( &polygon,
|
||||
&intersections,
|
||||
(cvf::EdgeIntersectStorage<size_t>*)nullptr,
|
||||
cvf::wrapArrayConst( &mainGridNodes ),
|
||||
sourceFaceIndices.data(),
|
||||
candidateFaceIndices.data(),
|
||||
1e-6 );
|
||||
|
||||
if ( foundOverlap )
|
||||
{
|
||||
RigConnection conn( sourceReservoirCellIndex,
|
||||
candidateCellIndex,
|
||||
sourceCellFace,
|
||||
RigCellFaceGeometryTools::extractPolygon( mainGridNodes, polygon, intersections ) );
|
||||
|
||||
connections.push_back( conn );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<cvf::Vec3f> RigCellFaceGeometryTools::extractPolygon( const std::vector<cvf::Vec3d>& nativeNodes,
|
||||
const std::vector<size_t>& connectionPolygon,
|
||||
const std::vector<cvf::Vec3d>& connectionIntersections )
|
||||
{
|
||||
std::vector<cvf::Vec3f> allPolygonNodes;
|
||||
|
||||
for ( size_t polygonIndex : connectionPolygon )
|
||||
{
|
||||
if ( polygonIndex < nativeNodes.size() )
|
||||
allPolygonNodes.push_back( cvf::Vec3f( nativeNodes[polygonIndex] ) );
|
||||
else
|
||||
allPolygonNodes.push_back( cvf::Vec3f( connectionIntersections[polygonIndex - nativeNodes.size()] ) );
|
||||
}
|
||||
|
||||
return allPolygonNodes;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2020 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 "RigFault.h"
|
||||
#include "RigNncConnection.h"
|
||||
|
||||
#include "cvfCollection.h"
|
||||
#include "cvfStructGrid.h"
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class RigActiveCellInfo;
|
||||
class RigCell;
|
||||
class RigMainGrid;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigCellFaceGeometryTools
|
||||
{
|
||||
public:
|
||||
static cvf::StructGridInterface::FaceType calculateCellFaceOverlap( const RigCell& c1,
|
||||
const RigCell& c2,
|
||||
const RigMainGrid& mainGrid,
|
||||
std::vector<size_t>* connectionPolygon,
|
||||
std::vector<cvf::Vec3d>* connectionIntersections );
|
||||
|
||||
static RigConnectionContainer computeOtherNncs( const RigMainGrid* mainGrid,
|
||||
const RigConnectionContainer& nativeConnections,
|
||||
const RigActiveCellInfo* activeCellInfo,
|
||||
bool includeInactiveCells );
|
||||
|
||||
static void extractConnectionsForFace( const RigFault::FaultFace& face,
|
||||
const RigMainGrid* mainGrid,
|
||||
const std::set<std::pair<unsigned, unsigned>>& nativeCellPairs,
|
||||
RigConnectionContainer& connections );
|
||||
static std::vector<cvf::Vec3f> extractPolygon( const std::vector<cvf::Vec3d>& nativeNodes,
|
||||
const std::vector<size_t>& connectionPolygon,
|
||||
const std::vector<cvf::Vec3d>& connectionIntersections );
|
||||
};
|
||||
@@ -0,0 +1,789 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigCellGeometryTools.h"
|
||||
#include "cvfGeometryTools.h"
|
||||
#include "cvfStructGrid.h"
|
||||
|
||||
#include "cafHexGridIntersectionTools/cafHexGridIntersectionTools.h"
|
||||
#include "cvfBoundingBox.h"
|
||||
#include "cvfMatrix3.h"
|
||||
|
||||
#include "clipper/clipper.hpp"
|
||||
|
||||
#include "cvfMath.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Efficient Computation of Volume of Hexahedral Cells
|
||||
/// Jeffrey Grandy, Lawrence Livermore National Laboratory
|
||||
/// https://www.osti.gov/servlets/purl/632793/
|
||||
///
|
||||
/// Note that in the paper the following vertex numbering is used
|
||||
/// 6---------7
|
||||
/// /| /| |k
|
||||
/// / | / | | /j
|
||||
/// 4---------5 | |/
|
||||
/// | 2------|--3 *---i
|
||||
/// | / | /
|
||||
/// |/ |/
|
||||
/// 0---------1
|
||||
///
|
||||
/// While in ResInsight, this is the numbering. Thus we need to swap 2<->3, 6<->7 in the equations.
|
||||
/// Note the negative k! This causes an additional set of 0<->4, 1<->5, etc. index swaps.
|
||||
/// 7---------6
|
||||
/// /| /| |-k
|
||||
/// / | / | | /j
|
||||
/// 4---------5 | |/
|
||||
/// | 3------|--2 *---i
|
||||
/// | / | /
|
||||
/// |/ |/
|
||||
/// 0---------1
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCellGeometryTools::calculateCellVolume( const std::array<cvf::Vec3d, 8>& x )
|
||||
{
|
||||
// 6 * 3 flops = 18 flops
|
||||
|
||||
// Perform index swap when retrieving corners but keep indices in variable names matching paper.
|
||||
cvf::Vec3d x3mx0 = x[6] - x[4]; // Swap 3->2, then negate z by 2->6 and 0->4
|
||||
cvf::Vec3d x5mx0 = x[1] - x[4]; // Negate z by Swap 5->1 and 0->4
|
||||
cvf::Vec3d x6mx0 = x[3] - x[4]; // Swap 6->7, then negate z by 7->3 and 0->4
|
||||
cvf::Vec3d x7mx1 = x[2] - x[5]; // Swap 7->6, then negate z by 6->2 and 1->5
|
||||
cvf::Vec3d x7mx2 = x[2] - x[7]; // Swap 7->6, 2->3, then negate z by 6->2 and 3->7
|
||||
cvf::Vec3d x7mx4 = x[2] - x[0]; // Swap 7->6 then negate z by 6->2 and 4->0
|
||||
|
||||
// 3 flops for summation + 5 for dot product + 9 flops for cross product = 17 flops
|
||||
double det1 = ( x7mx1 + x6mx0 ) * ( x7mx2 ^ x3mx0 );
|
||||
// 3 flops for summation + 5 for dot product + 9 flops for cross product = 17 flops
|
||||
double det2 = x6mx0 * ( ( x7mx2 + x5mx0 ) ^ x7mx4 );
|
||||
// 3 flops for summation + 5 for dot product + 9 flops for cross product = 17 flops
|
||||
double det3 = x7mx1 * ( x5mx0 ^ ( x7mx4 + x3mx0 ) );
|
||||
|
||||
// 2 flops for summation + 1 for division = 3 flops
|
||||
double volume = ( det1 + det2 + det3 ) / 12.0;
|
||||
|
||||
// In order for this to work in any rotation of the cell, we need the absolute value. 1 flop.
|
||||
return std::abs( volume ); // Altogether 18 + 3*17 + 3 + 1 flops = 73 flops.
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// A reasonable approximation to the overlap volume
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigCellGeometryTools::estimateHexOverlapWithBoundingBox( const std::array<cvf::Vec3d, 8>& hexCorners,
|
||||
const cvf::BoundingBox& boundingBox,
|
||||
std::array<cvf::Vec3d, 8>* overlapElement,
|
||||
cvf::BoundingBox* overlapBoundingBox )
|
||||
{
|
||||
CVF_ASSERT( overlapElement && overlapBoundingBox );
|
||||
*overlapBoundingBox = cvf::BoundingBox();
|
||||
|
||||
std::vector<cvf::Vec3d> uniqueTopPoints = { hexCorners[0], hexCorners[1], hexCorners[2], hexCorners[3] };
|
||||
auto uniqueTopEnd = std::unique( uniqueTopPoints.begin(), uniqueTopPoints.end() );
|
||||
|
||||
if ( uniqueTopEnd - uniqueTopPoints.begin() < 3u ) return false;
|
||||
|
||||
cvf::Plane topPlane;
|
||||
if ( !topPlane.setFromPoints( uniqueTopPoints[0], uniqueTopPoints[1], uniqueTopPoints[2] ) ) return false;
|
||||
|
||||
std::vector<cvf::Vec3d> uniqueBottomPoints = { hexCorners[4], hexCorners[5], hexCorners[6], hexCorners[7] };
|
||||
auto uniqueBottomEnd = std::unique( uniqueBottomPoints.begin(), uniqueBottomPoints.end() );
|
||||
if ( uniqueBottomEnd - uniqueBottomPoints.begin() < 3u ) return false;
|
||||
|
||||
cvf::Plane bottomPlane;
|
||||
if ( !bottomPlane.setFromPoints( uniqueBottomPoints[0], uniqueBottomPoints[1], uniqueBottomPoints[2] ) )
|
||||
return false;
|
||||
|
||||
const cvf::Vec3d& boundingMin = boundingBox.min();
|
||||
const cvf::Vec3d& boundingMax = boundingBox.max();
|
||||
|
||||
for ( size_t i = 0; i < 4; ++i )
|
||||
{
|
||||
const cvf::Vec3d& hexCorner = hexCorners[i];
|
||||
double x = cvf::Math::clamp( hexCorner.x(), boundingMin.x(), boundingMax.x() );
|
||||
double y = cvf::Math::clamp( hexCorner.y(), boundingMin.y(), boundingMax.y() );
|
||||
cvf::Vec3d corner;
|
||||
cvf::Vec3d maxZCorner( x, y, boundingMax.z() );
|
||||
cvf::Vec3d minZCorner( x, y, boundingMin.z() );
|
||||
if ( topPlane.intersect( minZCorner, maxZCorner, &corner ) )
|
||||
{
|
||||
overlapBoundingBox->add( corner );
|
||||
std::swap( ( *overlapElement )[i], corner );
|
||||
}
|
||||
else
|
||||
{
|
||||
double z = cvf::Math::clamp( hexCorner.z(), boundingMin.z(), boundingMax.z() );
|
||||
cvf::Vec3d clampedCorner( x, y, z );
|
||||
overlapBoundingBox->add( clampedCorner );
|
||||
( *overlapElement )[i] = clampedCorner;
|
||||
}
|
||||
}
|
||||
for ( size_t i = 4; i < 8; ++i )
|
||||
{
|
||||
const cvf::Vec3d& hexCorner = hexCorners[i];
|
||||
double x = cvf::Math::clamp( hexCorner.x(), boundingMin.x(), boundingMax.x() );
|
||||
double y = cvf::Math::clamp( hexCorner.y(), boundingMin.y(), boundingMax.y() );
|
||||
cvf::Vec3d corner;
|
||||
cvf::Vec3d maxZCorner( x, y, boundingMax.z() );
|
||||
cvf::Vec3d minZCorner( x, y, boundingMin.z() );
|
||||
if ( bottomPlane.intersect( minZCorner, maxZCorner, &corner ) )
|
||||
{
|
||||
overlapBoundingBox->add( corner );
|
||||
std::swap( ( *overlapElement )[i], corner );
|
||||
}
|
||||
else
|
||||
{
|
||||
double z = cvf::Math::clamp( hexCorner.z(), boundingMin.z(), boundingMax.z() );
|
||||
cvf::Vec3d clampedCorner( x, y, z );
|
||||
overlapBoundingBox->add( clampedCorner );
|
||||
( *overlapElement )[i] = clampedCorner;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCellGeometryTools::createPolygonFromLineSegments( std::list<std::pair<cvf::Vec3d, cvf::Vec3d>>& intersectionLineSegments,
|
||||
std::vector<std::vector<cvf::Vec3d>>& polygons,
|
||||
double tolerance )
|
||||
{
|
||||
bool startNewPolygon = true;
|
||||
while ( !intersectionLineSegments.empty() )
|
||||
{
|
||||
if ( startNewPolygon )
|
||||
{
|
||||
std::vector<cvf::Vec3d> polygon;
|
||||
// Add first line segments to polygon and remove from list
|
||||
std::pair<cvf::Vec3d, cvf::Vec3d> linesegment = intersectionLineSegments.front();
|
||||
polygon.push_back( linesegment.first );
|
||||
polygon.push_back( linesegment.second );
|
||||
intersectionLineSegments.remove( linesegment );
|
||||
polygons.push_back( polygon );
|
||||
startNewPolygon = false;
|
||||
}
|
||||
|
||||
std::vector<cvf::Vec3d>& polygon = polygons.back();
|
||||
|
||||
// Search remaining list for next point...
|
||||
|
||||
bool isFound = false;
|
||||
|
||||
for ( std::list<std::pair<cvf::Vec3d, cvf::Vec3d>>::iterator lIt = intersectionLineSegments.begin();
|
||||
lIt != intersectionLineSegments.end();
|
||||
lIt++ )
|
||||
{
|
||||
cvf::Vec3d lineSegmentStart = lIt->first;
|
||||
cvf::Vec3d lineSegmentEnd = lIt->second;
|
||||
cvf::Vec3d polygonEnd = polygon.back();
|
||||
|
||||
double lineSegmentLength = ( lineSegmentStart - lineSegmentEnd ).lengthSquared();
|
||||
if ( lineSegmentLength < tolerance * tolerance )
|
||||
{
|
||||
intersectionLineSegments.erase( lIt );
|
||||
isFound = true;
|
||||
break;
|
||||
}
|
||||
|
||||
double lineSegmentStartDiff = ( lineSegmentStart - polygonEnd ).lengthSquared();
|
||||
if ( lineSegmentStartDiff < tolerance * tolerance )
|
||||
{
|
||||
polygon.push_back( lIt->second );
|
||||
intersectionLineSegments.erase( lIt );
|
||||
isFound = true;
|
||||
break;
|
||||
}
|
||||
|
||||
double lineSegmentEndDiff = ( lineSegmentEnd - polygonEnd ).lengthSquared();
|
||||
if ( lineSegmentEndDiff < tolerance * tolerance )
|
||||
{
|
||||
polygon.push_back( lIt->first );
|
||||
intersectionLineSegments.erase( lIt );
|
||||
isFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isFound )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
startNewPolygon = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Ramer-Douglas-Peucker simplification algorithm
|
||||
///
|
||||
/// https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCellGeometryTools::simplifyPolygon( std::vector<cvf::Vec3d>* vertices, double epsilon )
|
||||
{
|
||||
CVF_ASSERT( vertices );
|
||||
if ( vertices->size() < 3 ) return;
|
||||
|
||||
std::pair<size_t, double> maxDistPoint( 0u, 0.0 );
|
||||
|
||||
for ( size_t i = 1; i < vertices->size() - 1; ++i )
|
||||
{
|
||||
cvf::Vec3d v = vertices->at( i );
|
||||
double u;
|
||||
cvf::Vec3d v_proj = cvf::GeometryTools::projectPointOnLine( vertices->front(), vertices->back(), v, &u );
|
||||
double distance = ( v_proj - v ).length();
|
||||
if ( distance > maxDistPoint.second )
|
||||
{
|
||||
maxDistPoint = std::make_pair( i, distance );
|
||||
}
|
||||
}
|
||||
|
||||
if ( maxDistPoint.second > epsilon )
|
||||
{
|
||||
std::vector<cvf::Vec3d> newVertices1( vertices->begin(), vertices->begin() + maxDistPoint.first + 1 );
|
||||
std::vector<cvf::Vec3d> newVertices2( vertices->begin() + maxDistPoint.first, vertices->end() );
|
||||
|
||||
// Recurse
|
||||
simplifyPolygon( &newVertices1, epsilon );
|
||||
simplifyPolygon( &newVertices2, epsilon );
|
||||
|
||||
std::vector<cvf::Vec3d> newVertices( newVertices1.begin(), newVertices1.end() - 1 );
|
||||
newVertices.insert( newVertices.end(), newVertices2.begin(), newVertices2.end() );
|
||||
*vertices = newVertices;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<cvf::Vec3d> newVertices = { vertices->front(), vertices->back() };
|
||||
*vertices = newVertices;
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
void RigCellGeometryTools::findCellLocalXYZ( const std::array<cvf::Vec3d, 8>& hexCorners,
|
||||
cvf::Vec3d& localXdirection,
|
||||
cvf::Vec3d& localYdirection,
|
||||
cvf::Vec3d& localZdirection )
|
||||
{
|
||||
cvf::ubyte faceVertexIndices[4];
|
||||
cvf::StructGridInterface::FaceEnum face;
|
||||
|
||||
face = cvf::StructGridInterface::NEG_I;
|
||||
cvf::StructGridInterface::cellFaceVertexIndices( face, faceVertexIndices );
|
||||
cvf::Vec3d faceCenterNegI = cvf::GeometryTools::computeFaceCenter( hexCorners[faceVertexIndices[0]],
|
||||
hexCorners[faceVertexIndices[1]],
|
||||
hexCorners[faceVertexIndices[2]],
|
||||
hexCorners[faceVertexIndices[3]] );
|
||||
// TODO: Should we use face centroids instead of face centers?
|
||||
|
||||
face = cvf::StructGridInterface::POS_I;
|
||||
cvf::StructGridInterface::cellFaceVertexIndices( face, faceVertexIndices );
|
||||
cvf::Vec3d faceCenterPosI = cvf::GeometryTools::computeFaceCenter( hexCorners[faceVertexIndices[0]],
|
||||
hexCorners[faceVertexIndices[1]],
|
||||
hexCorners[faceVertexIndices[2]],
|
||||
hexCorners[faceVertexIndices[3]] );
|
||||
|
||||
face = cvf::StructGridInterface::NEG_J;
|
||||
cvf::StructGridInterface::cellFaceVertexIndices( face, faceVertexIndices );
|
||||
cvf::Vec3d faceCenterNegJ = cvf::GeometryTools::computeFaceCenter( hexCorners[faceVertexIndices[0]],
|
||||
hexCorners[faceVertexIndices[1]],
|
||||
hexCorners[faceVertexIndices[2]],
|
||||
hexCorners[faceVertexIndices[3]] );
|
||||
|
||||
face = cvf::StructGridInterface::POS_J;
|
||||
cvf::StructGridInterface::cellFaceVertexIndices( face, faceVertexIndices );
|
||||
cvf::Vec3d faceCenterPosJ = cvf::GeometryTools::computeFaceCenter( hexCorners[faceVertexIndices[0]],
|
||||
hexCorners[faceVertexIndices[1]],
|
||||
hexCorners[faceVertexIndices[2]],
|
||||
hexCorners[faceVertexIndices[3]] );
|
||||
|
||||
cvf::Vec3d faceCenterCenterVectorI = faceCenterPosI - faceCenterNegI;
|
||||
cvf::Vec3d faceCenterCenterVectorJ = faceCenterPosJ - faceCenterNegJ;
|
||||
|
||||
localZdirection.cross( faceCenterCenterVectorI, faceCenterCenterVectorJ );
|
||||
localZdirection.normalize();
|
||||
|
||||
cvf::Vec3d crossPoductJZ;
|
||||
crossPoductJZ.cross( faceCenterCenterVectorJ, localZdirection );
|
||||
localXdirection = faceCenterCenterVectorI + crossPoductJZ;
|
||||
localXdirection.normalize();
|
||||
|
||||
cvf::Vec3d crossPoductIZ;
|
||||
crossPoductIZ.cross( faceCenterCenterVectorI, localZdirection );
|
||||
localYdirection = faceCenterCenterVectorJ - crossPoductIZ;
|
||||
localYdirection.normalize();
|
||||
|
||||
// TODO: Check if we end up with 0-vectors, and handle this case...
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCellGeometryTools::polygonLengthInLocalXdirWeightedByArea( const std::vector<cvf::Vec3d>& polygonToCalcLengthOf )
|
||||
{
|
||||
// Find bounding box
|
||||
cvf::BoundingBox polygonBBox;
|
||||
for ( cvf::Vec3d nodeCoord : polygonToCalcLengthOf )
|
||||
polygonBBox.add( nodeCoord );
|
||||
cvf::Vec3d bboxCorners[8];
|
||||
polygonBBox.cornerVertices( bboxCorners );
|
||||
|
||||
// Split bounding box in multiple polygons (2D)
|
||||
int resolutionOfLengthCalc = 20;
|
||||
double widthOfPolygon = polygonBBox.extent().y() / resolutionOfLengthCalc;
|
||||
|
||||
std::vector<double> areasOfPolygonContributions;
|
||||
std::vector<double> lengthOfPolygonContributions;
|
||||
|
||||
cvf::Vec3d directionOfLength( 1, 0, 0 );
|
||||
|
||||
for ( int i = 0; i < resolutionOfLengthCalc; i++ )
|
||||
{
|
||||
cvf::Vec3d pointOnLine1( bboxCorners[0].x(), bboxCorners[0].y() + i * widthOfPolygon, 0 );
|
||||
cvf::Vec3d pointOnLine2( bboxCorners[0].x(), bboxCorners[0].y() + ( i + 1 ) * widthOfPolygon, 0 );
|
||||
|
||||
std::pair<cvf::Vec3d, cvf::Vec3d> line1 = getLineThroughBoundingBox( directionOfLength, polygonBBox, pointOnLine1 );
|
||||
std::pair<cvf::Vec3d, cvf::Vec3d> line2 = getLineThroughBoundingBox( directionOfLength, polygonBBox, pointOnLine2 );
|
||||
|
||||
std::vector<cvf::Vec3d> polygon;
|
||||
polygon.push_back( line1.first );
|
||||
polygon.push_back( line1.second );
|
||||
polygon.push_back( line2.second );
|
||||
polygon.push_back( line2.first );
|
||||
|
||||
// Use clipper to find overlap between bbpolygon and fracture
|
||||
std::vector<std::vector<cvf::Vec3d>> clippedPolygons = intersectionWithPolygon( polygonToCalcLengthOf, polygon );
|
||||
|
||||
double area = 0;
|
||||
double length = 0;
|
||||
cvf::Vec3d areaVector = cvf::Vec3d::ZERO;
|
||||
|
||||
// Calculate length (max-min) and area
|
||||
for ( std::vector<cvf::Vec3d> clippedPolygon : clippedPolygons )
|
||||
{
|
||||
areaVector = cvf::GeometryTools::polygonAreaNormal3D( clippedPolygon );
|
||||
area += areaVector.length();
|
||||
length += ( getLengthOfPolygonAlongLine( line1, clippedPolygon ) +
|
||||
getLengthOfPolygonAlongLine( line2, clippedPolygon ) ) /
|
||||
2;
|
||||
}
|
||||
areasOfPolygonContributions.push_back( area );
|
||||
lengthOfPolygonContributions.push_back( length );
|
||||
}
|
||||
|
||||
// Calculate area-weighted length average.
|
||||
double totalArea = 0.0;
|
||||
double totalAreaXlength = 0.0;
|
||||
|
||||
for ( size_t i = 0; i < areasOfPolygonContributions.size(); i++ )
|
||||
{
|
||||
totalArea += areasOfPolygonContributions[i];
|
||||
totalAreaXlength += ( areasOfPolygonContributions[i] * lengthOfPolygonContributions[i] );
|
||||
}
|
||||
|
||||
double areaWeightedLength = totalAreaXlength / totalArea;
|
||||
return areaWeightedLength;
|
||||
}
|
||||
|
||||
double clipperConversionFactor = 10000; // For transform to clipper int
|
||||
|
||||
ClipperLib::IntPoint toClipperPoint( const cvf::Vec3d& cvfPoint )
|
||||
{
|
||||
int xInt = cvfPoint.x() * clipperConversionFactor;
|
||||
int yInt = cvfPoint.y() * clipperConversionFactor;
|
||||
int zInt = cvfPoint.z() * clipperConversionFactor;
|
||||
return ClipperLib::IntPoint( xInt, yInt, zInt );
|
||||
}
|
||||
|
||||
cvf::Vec3d fromClipperPoint( const ClipperLib::IntPoint& clipPoint )
|
||||
{
|
||||
double zDValue;
|
||||
|
||||
if ( clipPoint.Z == std::numeric_limits<int>::max() )
|
||||
{
|
||||
zDValue = HUGE_VAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
zDValue = clipPoint.Z;
|
||||
}
|
||||
|
||||
return cvf::Vec3d( clipPoint.X, clipPoint.Y, zDValue ) / clipperConversionFactor;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<std::vector<cvf::Vec3d>>
|
||||
RigCellGeometryTools::intersectionWithPolygons( const std::vector<cvf::Vec3d>& polygon1,
|
||||
const std::vector<std::vector<cvf::Vec3d>>& polygonToIntersectWith )
|
||||
{
|
||||
std::vector<std::vector<cvf::Vec3d>> clippedPolygons;
|
||||
|
||||
// Convert to int for clipper library and store as clipper "path"
|
||||
ClipperLib::Clipper clpr;
|
||||
{
|
||||
ClipperLib::Path polygon1path;
|
||||
for ( const cvf::Vec3d& v : polygon1 )
|
||||
{
|
||||
polygon1path.push_back( toClipperPoint( v ) );
|
||||
}
|
||||
clpr.AddPath( polygon1path, ClipperLib::ptSubject, true );
|
||||
}
|
||||
|
||||
for ( const auto& path : polygonToIntersectWith )
|
||||
{
|
||||
ClipperLib::Path polygon2path;
|
||||
for ( const auto& v : path )
|
||||
{
|
||||
polygon2path.push_back( toClipperPoint( v ) );
|
||||
}
|
||||
|
||||
clpr.AddPath( polygon2path, ClipperLib::ptClip, true );
|
||||
}
|
||||
|
||||
ClipperLib::Paths solution;
|
||||
clpr.Execute( ClipperLib::ctIntersection, solution, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd );
|
||||
|
||||
// Convert back to std::vector<std::vector<cvf::Vec3d> >
|
||||
for ( ClipperLib::Path pathInSol : solution )
|
||||
{
|
||||
std::vector<cvf::Vec3d> clippedPolygon;
|
||||
for ( ClipperLib::IntPoint IntPosition : pathInSol )
|
||||
{
|
||||
clippedPolygon.push_back( fromClipperPoint( IntPosition ) );
|
||||
}
|
||||
clippedPolygons.push_back( clippedPolygon );
|
||||
}
|
||||
|
||||
return clippedPolygons;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<std::vector<cvf::Vec3d>>
|
||||
RigCellGeometryTools::intersectionWithPolygon( const std::vector<cvf::Vec3d>& polygon1,
|
||||
const std::vector<cvf::Vec3d>& polygon2 )
|
||||
{
|
||||
return intersectionWithPolygons( polygon1, { polygon2 } );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<std::vector<cvf::Vec3d>>
|
||||
RigCellGeometryTools::subtractPolygons( const std::vector<cvf::Vec3d>& sourcePolygon,
|
||||
const std::vector<std::vector<cvf::Vec3d>>& polygonsToSubtract )
|
||||
{
|
||||
ClipperLib::Clipper clpr;
|
||||
|
||||
{
|
||||
// Convert to int for clipper library and store as clipper "path"
|
||||
ClipperLib::Path polygon1path;
|
||||
for ( const auto& v : sourcePolygon )
|
||||
{
|
||||
polygon1path.push_back( toClipperPoint( v ) );
|
||||
}
|
||||
clpr.AddPath( polygon1path, ClipperLib::ptSubject, true );
|
||||
}
|
||||
|
||||
for ( const auto& path : polygonsToSubtract )
|
||||
{
|
||||
ClipperLib::Path polygon2path;
|
||||
for ( const auto& v : path )
|
||||
{
|
||||
polygon2path.push_back( toClipperPoint( v ) );
|
||||
}
|
||||
|
||||
clpr.AddPath( polygon2path, ClipperLib::ptClip, true );
|
||||
}
|
||||
|
||||
ClipperLib::Paths solution;
|
||||
clpr.Execute( ClipperLib::ctDifference, solution, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd );
|
||||
|
||||
std::vector<std::vector<cvf::Vec3d>> clippedPolygons;
|
||||
|
||||
// Convert back to std::vector<std::vector<cvf::Vec3d> >
|
||||
for ( ClipperLib::Path pathInSol : solution )
|
||||
{
|
||||
std::vector<cvf::Vec3d> clippedPolygon;
|
||||
for ( ClipperLib::IntPoint IntPosition : pathInSol )
|
||||
{
|
||||
clippedPolygon.push_back( fromClipperPoint( IntPosition ) );
|
||||
}
|
||||
|
||||
clippedPolygons.push_back( clippedPolygon );
|
||||
}
|
||||
|
||||
return clippedPolygons;
|
||||
}
|
||||
|
||||
std::vector<std::vector<cvf::Vec3d>> RigCellGeometryTools::subtractPolygon( const std::vector<cvf::Vec3d>& sourcePolygon,
|
||||
const std::vector<cvf::Vec3d>& polygonToSubtract )
|
||||
{
|
||||
return subtractPolygons( sourcePolygon, { polygonToSubtract } );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Note for cppcheck : First four parameter cannot be const to match the signature of the receiver
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void fillInterpolatedSubjectZ( ClipperLib::IntPoint& e1bot,
|
||||
ClipperLib::IntPoint& e1top,
|
||||
ClipperLib::IntPoint& e2bot,
|
||||
ClipperLib::IntPoint& e2top,
|
||||
ClipperLib::IntPoint& pt )
|
||||
{
|
||||
ClipperLib::IntPoint ePLbot;
|
||||
ClipperLib::IntPoint ePLtop;
|
||||
|
||||
if ( e1top.Z == std::numeric_limits<int>::max() )
|
||||
{
|
||||
ePLtop = e2top;
|
||||
ePLbot = e2bot;
|
||||
}
|
||||
else
|
||||
{
|
||||
ePLtop = e1top;
|
||||
ePLbot = e1bot;
|
||||
}
|
||||
|
||||
double ePLXRange = ( ePLtop.X - ePLbot.X );
|
||||
double ePLYRange = ( ePLtop.Y - ePLbot.Y );
|
||||
|
||||
double ePLLength = sqrt( ePLXRange * ePLXRange + ePLYRange * ePLYRange );
|
||||
|
||||
if ( ePLLength <= 1 )
|
||||
{
|
||||
pt.Z = ePLbot.Z;
|
||||
return;
|
||||
}
|
||||
|
||||
double ePLBotPtXRange = pt.X - ePLbot.X;
|
||||
double ePLBotPtYRange = pt.Y - ePLbot.Y;
|
||||
|
||||
double ePLBotPtLength = sqrt( ePLBotPtXRange * ePLBotPtXRange + ePLBotPtYRange * ePLBotPtYRange );
|
||||
|
||||
double fraction = ePLBotPtLength / ePLLength;
|
||||
|
||||
pt.Z = std::nearbyint( ePLbot.Z + fraction * ( ePLtop.Z - ePLbot.Z ) );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void fillUndefinedZ( ClipperLib::IntPoint& e1bot,
|
||||
ClipperLib::IntPoint& e1top,
|
||||
ClipperLib::IntPoint& e2bot,
|
||||
ClipperLib::IntPoint& e2top,
|
||||
ClipperLib::IntPoint& pt )
|
||||
{
|
||||
pt.Z = std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Assumes x.y plane polygon. Polyline might have a Z, and the returned Z is the polyline Z, interpolated if it is
|
||||
/// clipped.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<std::vector<cvf::Vec3d>> RigCellGeometryTools::clipPolylineByPolygon( const std::vector<cvf::Vec3d>& polyLine,
|
||||
const std::vector<cvf::Vec3d>& polygon,
|
||||
ZInterpolationType interpolType )
|
||||
{
|
||||
std::vector<std::vector<cvf::Vec3d>> clippedPolyline;
|
||||
|
||||
// Adjusting polygon to avoid clipper issue with interpolating z-values when lines crosses though polygon vertecies
|
||||
std::vector<cvf::Vec3d> adjustedPolygon = ajustPolygonToAvoidIntersectionsAtVertex( polyLine, polygon );
|
||||
|
||||
// Convert to int for clipper library and store as clipper "path"
|
||||
ClipperLib::Path polyLinePath;
|
||||
for ( const cvf::Vec3d& v : polyLine )
|
||||
{
|
||||
polyLinePath.push_back( toClipperPoint( v ) );
|
||||
}
|
||||
|
||||
ClipperLib::Path polygonPath;
|
||||
for ( const cvf::Vec3d& v : adjustedPolygon )
|
||||
{
|
||||
ClipperLib::IntPoint intp = toClipperPoint( v );
|
||||
intp.Z = std::numeric_limits<int>::max();
|
||||
polygonPath.push_back( intp );
|
||||
}
|
||||
|
||||
ClipperLib::Clipper clpr;
|
||||
clpr.AddPath( polyLinePath, ClipperLib::ptSubject, false );
|
||||
clpr.AddPath( polygonPath, ClipperLib::ptClip, true );
|
||||
|
||||
if ( interpolType == INTERPOLATE_LINE_Z )
|
||||
{
|
||||
clpr.ZFillFunction( &fillInterpolatedSubjectZ );
|
||||
}
|
||||
else if ( interpolType == USE_HUGEVAL )
|
||||
{
|
||||
clpr.ZFillFunction( &fillUndefinedZ );
|
||||
}
|
||||
|
||||
ClipperLib::PolyTree solution;
|
||||
clpr.Execute( ClipperLib::ctIntersection, solution, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd );
|
||||
|
||||
// We only expect open paths from this method (unless the polyline is self intersecting, a condition we do not handle)
|
||||
ClipperLib::Paths solutionPaths;
|
||||
ClipperLib::OpenPathsFromPolyTree( solution, solutionPaths );
|
||||
|
||||
// Convert back to std::vector<std::vector<cvf::Vec3d> >
|
||||
for ( ClipperLib::Path pathInSol : solutionPaths )
|
||||
{
|
||||
std::vector<cvf::Vec3d> clippedPolygon;
|
||||
for ( ClipperLib::IntPoint IntPosition : pathInSol )
|
||||
{
|
||||
clippedPolygon.push_back( fromClipperPoint( IntPosition ) );
|
||||
}
|
||||
clippedPolyline.push_back( clippedPolygon );
|
||||
}
|
||||
|
||||
return clippedPolyline;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::pair<cvf::Vec3d, cvf::Vec3d> RigCellGeometryTools::getLineThroughBoundingBox( const cvf::Vec3d& lineDirection,
|
||||
const cvf::BoundingBox& polygonBBox,
|
||||
const cvf::Vec3d& pointOnLine )
|
||||
{
|
||||
cvf::Vec3d bboxCorners[8];
|
||||
polygonBBox.cornerVertices( bboxCorners );
|
||||
|
||||
cvf::Vec3d startPoint = pointOnLine;
|
||||
cvf::Vec3d endPoint = pointOnLine;
|
||||
cvf::Vec3d lineDir = lineDirection;
|
||||
|
||||
// To avoid doing many iterations in loops below linedirection should be quite large.
|
||||
lineDir.normalize();
|
||||
lineDir = lineDir * polygonBBox.extent().length() / 5;
|
||||
|
||||
// Extend line in positive direction
|
||||
while ( polygonBBox.contains( startPoint ) )
|
||||
{
|
||||
startPoint = startPoint + lineDir;
|
||||
}
|
||||
// Extend line in negative direction
|
||||
while ( polygonBBox.contains( endPoint ) )
|
||||
{
|
||||
endPoint = endPoint - lineDir;
|
||||
}
|
||||
|
||||
std::pair<cvf::Vec3d, cvf::Vec3d> line;
|
||||
line = { startPoint, endPoint };
|
||||
return line;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCellGeometryTools::getLengthOfPolygonAlongLine( const std::pair<cvf::Vec3d, cvf::Vec3d>& line,
|
||||
const std::vector<cvf::Vec3d>& polygon )
|
||||
{
|
||||
cvf::BoundingBox lineBoundingBox;
|
||||
|
||||
for ( cvf::Vec3d polygonPoint : polygon )
|
||||
{
|
||||
cvf::Vec3d pointOnLine = cvf::GeometryTools::projectPointOnLine( line.first, line.second, polygonPoint, nullptr );
|
||||
lineBoundingBox.add( pointOnLine );
|
||||
}
|
||||
|
||||
double length = lineBoundingBox.extent().length();
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<cvf::Vec3d> RigCellGeometryTools::unionOfPolygons( const std::vector<std::vector<cvf::Vec3d>>& polygons )
|
||||
{
|
||||
// Convert to int for clipper library and store as clipper "path"
|
||||
std::vector<ClipperLib::Path> polygonPaths;
|
||||
for ( const std::vector<cvf::Vec3d>& polygon : polygons )
|
||||
{
|
||||
polygonPaths.emplace_back();
|
||||
auto& p = polygonPaths.back();
|
||||
for ( const cvf::Vec3d& pp : polygon )
|
||||
{
|
||||
p.push_back( toClipperPoint( pp ) );
|
||||
}
|
||||
}
|
||||
|
||||
ClipperLib::Clipper clpr;
|
||||
clpr.AddPaths( polygonPaths, ClipperLib::ptSubject, true );
|
||||
|
||||
ClipperLib::Paths solution;
|
||||
clpr.Execute( ClipperLib::ctUnion, solution, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd );
|
||||
|
||||
// Convert back to std::vector<std::vector<cvf::Vec3d> >
|
||||
std::vector<cvf::Vec3d> unionPolygon;
|
||||
for ( ClipperLib::Path pathInSol : solution )
|
||||
{
|
||||
for ( ClipperLib::IntPoint IntPosition : pathInSol )
|
||||
{
|
||||
unionPolygon.push_back( fromClipperPoint( IntPosition ) );
|
||||
}
|
||||
}
|
||||
|
||||
return unionPolygon;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<cvf::Vec3d>
|
||||
RigCellGeometryTools::ajustPolygonToAvoidIntersectionsAtVertex( const std::vector<cvf::Vec3d>& polyLine,
|
||||
const std::vector<cvf::Vec3d>& polygon )
|
||||
{
|
||||
std::vector<cvf::Vec3d> adjustedPolygon;
|
||||
|
||||
double treshold = ( 1.0 / 10000.0 ) * 5; // 5 times polygonScaleFactor for converting to int for clipper
|
||||
|
||||
for ( cvf::Vec3d polygonPoint : polygon )
|
||||
{
|
||||
for ( size_t i = 0; i < polyLine.size() - 1; i++ )
|
||||
{
|
||||
cvf::Vec3d linePoint1( polyLine[i].x(), polyLine[i].y(), 0.0 );
|
||||
cvf::Vec3d linePoint2( polyLine[i + 1].x(), polyLine[i + 1].y(), 0.0 );
|
||||
|
||||
double pointDistanceFromLine = cvf::GeometryTools::linePointSquareDist( linePoint1, linePoint2, polygonPoint );
|
||||
if ( pointDistanceFromLine < treshold )
|
||||
{
|
||||
// calculate new polygonPoint
|
||||
cvf::Vec3d directionOfLineSegment = linePoint2 - linePoint1;
|
||||
// finding normal to the direction of the line segment in the XY plane (z=0)
|
||||
cvf::Vec3d normalToLine( -directionOfLineSegment.y(), directionOfLineSegment.x(), 0.0 );
|
||||
normalToLine.normalize();
|
||||
polygonPoint = polygonPoint + normalToLine * 0.005;
|
||||
}
|
||||
}
|
||||
adjustedPolygon.push_back( polygonPoint );
|
||||
}
|
||||
|
||||
return adjustedPolygon;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include "cvfBoundingBox.h"
|
||||
#include "cvfPlane.h"
|
||||
|
||||
#include <array>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
class RigCellGeometryTools
|
||||
{
|
||||
public:
|
||||
static double calculateCellVolume( const std::array<cvf::Vec3d, 8>& hexCorners );
|
||||
static bool estimateHexOverlapWithBoundingBox( const std::array<cvf::Vec3d, 8>& hexCorners,
|
||||
const cvf::BoundingBox& boundingBox2dExtrusion,
|
||||
std::array<cvf::Vec3d, 8>* overlapCorners,
|
||||
cvf::BoundingBox* overlapBoundingBox );
|
||||
|
||||
static void createPolygonFromLineSegments( std::list<std::pair<cvf::Vec3d, cvf::Vec3d>>& intersectionLineSegments,
|
||||
std::vector<std::vector<cvf::Vec3d>>& polygons,
|
||||
double tolerance = 1.0e-4 );
|
||||
static void simplifyPolygon( std::vector<cvf::Vec3d>* vertices, double epsilon );
|
||||
|
||||
static void findCellLocalXYZ( const std::array<cvf::Vec3d, 8>& hexCorners,
|
||||
cvf::Vec3d& localXdirection,
|
||||
cvf::Vec3d& localYdirection,
|
||||
cvf::Vec3d& localZdirection );
|
||||
|
||||
static double polygonLengthInLocalXdirWeightedByArea( const std::vector<cvf::Vec3d>& polygon2d );
|
||||
|
||||
static std::vector<std::vector<cvf::Vec3d>>
|
||||
intersectionWithPolygons( const std::vector<cvf::Vec3d>& polygon1,
|
||||
const std::vector<std::vector<cvf::Vec3d>>& polygonToIntersectWith );
|
||||
|
||||
static std::vector<std::vector<cvf::Vec3d>> intersectionWithPolygon( const std::vector<cvf::Vec3d>& polygon1,
|
||||
const std::vector<cvf::Vec3d>& polygon2 );
|
||||
|
||||
static std::vector<std::vector<cvf::Vec3d>>
|
||||
subtractPolygons( const std::vector<cvf::Vec3d>& sourcePolygon,
|
||||
const std::vector<std::vector<cvf::Vec3d>>& polygonsToSubtract );
|
||||
static std::vector<std::vector<cvf::Vec3d>> subtractPolygon( const std::vector<cvf::Vec3d>& sourcePolygon,
|
||||
const std::vector<cvf::Vec3d>& polygonToSubtract );
|
||||
|
||||
enum ZInterpolationType
|
||||
{
|
||||
INTERPOLATE_LINE_Z,
|
||||
USE_HUGEVAL,
|
||||
USE_ZERO
|
||||
};
|
||||
static std::vector<std::vector<cvf::Vec3d>> clipPolylineByPolygon( const std::vector<cvf::Vec3d>& polyLine,
|
||||
const std::vector<cvf::Vec3d>& polygon,
|
||||
ZInterpolationType interpolType = USE_ZERO );
|
||||
|
||||
static std::pair<cvf::Vec3d, cvf::Vec3d> getLineThroughBoundingBox( const cvf::Vec3d& lineDirection,
|
||||
const cvf::BoundingBox& polygonBBox,
|
||||
const cvf::Vec3d& pointOnLine );
|
||||
|
||||
static double getLengthOfPolygonAlongLine( const std::pair<cvf::Vec3d, cvf::Vec3d>& line,
|
||||
const std::vector<cvf::Vec3d>& polygon );
|
||||
|
||||
static std::vector<cvf::Vec3d> unionOfPolygons( const std::vector<std::vector<cvf::Vec3d>>& polygons );
|
||||
|
||||
private:
|
||||
static std::vector<cvf::Vec3d> ajustPolygonToAvoidIntersectionsAtVertex( const std::vector<cvf::Vec3d>& polyLine,
|
||||
const std::vector<cvf::Vec3d>& polygon );
|
||||
};
|
||||
@@ -0,0 +1,173 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigCombMultResultAccessor.h"
|
||||
|
||||
#include "RigGridBase.h"
|
||||
|
||||
#include "RigCell.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include <cmath>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCombMultResultAccessor::RigCombMultResultAccessor( const RigGridBase* grid )
|
||||
: m_grid( grid )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCombMultResultAccessor::setMultResultAccessors( RigResultAccessor* multXPosAccessor,
|
||||
RigResultAccessor* multXNegAccessor,
|
||||
RigResultAccessor* multYPosAccessor,
|
||||
RigResultAccessor* multYNegAccessor,
|
||||
RigResultAccessor* multZPosAccessor,
|
||||
RigResultAccessor* multZNegAccessor )
|
||||
{
|
||||
m_multXPosAccessor = multXPosAccessor;
|
||||
m_multXNegAccessor = multXNegAccessor;
|
||||
m_multYPosAccessor = multYPosAccessor;
|
||||
m_multYNegAccessor = multYNegAccessor;
|
||||
m_multZPosAccessor = multZPosAccessor;
|
||||
m_multZNegAccessor = multZNegAccessor;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCombMultResultAccessor::cellScalar( size_t gridLocalCellIndex ) const
|
||||
{
|
||||
CVF_TIGHT_ASSERT( false );
|
||||
|
||||
return HUGE_VAL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCombMultResultAccessor::cellFaceScalar( size_t gridLocalCellIndex, cvf::StructGridInterface::FaceType faceId ) const
|
||||
{
|
||||
size_t i, j, k, neighborGridCellIdx;
|
||||
m_grid->ijkFromCellIndex( gridLocalCellIndex, &i, &j, &k );
|
||||
|
||||
double faceScalarThisCell = nativeMultScalar( gridLocalCellIndex, faceId );
|
||||
|
||||
double faceScalarNeighborCell = 1.0;
|
||||
if ( m_grid->cellIJKNeighbor( i, j, k, faceId, &neighborGridCellIdx ) )
|
||||
{
|
||||
faceScalarNeighborCell = nativeMultScalar( neighborGridCellIdx, cvf::StructGridInterface::oppositeFace( faceId ) );
|
||||
}
|
||||
|
||||
return faceScalarThisCell * faceScalarNeighborCell;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCombMultResultAccessor::nativeMultScalar( size_t gridLocalCellIndex, cvf::StructGridInterface::FaceType faceId ) const
|
||||
{
|
||||
double faceScalar = 1.0;
|
||||
|
||||
switch ( faceId )
|
||||
{
|
||||
case cvf::StructGridInterface::POS_I:
|
||||
{
|
||||
if ( m_multXPosAccessor.notNull() )
|
||||
{
|
||||
faceScalar = m_multXPosAccessor->cellScalar( gridLocalCellIndex );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cvf::StructGridInterface::NEG_I:
|
||||
{
|
||||
if ( m_multXNegAccessor.notNull() )
|
||||
{
|
||||
faceScalar = m_multXNegAccessor->cellScalar( gridLocalCellIndex );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case cvf::StructGridInterface::POS_J:
|
||||
{
|
||||
if ( m_multYPosAccessor.notNull() )
|
||||
{
|
||||
faceScalar = m_multYPosAccessor->cellScalar( gridLocalCellIndex );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cvf::StructGridInterface::NEG_J:
|
||||
{
|
||||
if ( m_multYNegAccessor.notNull() )
|
||||
{
|
||||
faceScalar = m_multYNegAccessor->cellScalar( gridLocalCellIndex );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case cvf::StructGridInterface::POS_K:
|
||||
{
|
||||
if ( m_multZPosAccessor.notNull() )
|
||||
{
|
||||
faceScalar = m_multZPosAccessor->cellScalar( gridLocalCellIndex );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cvf::StructGridInterface::NEG_K:
|
||||
{
|
||||
if ( m_multZNegAccessor.notNull() )
|
||||
{
|
||||
faceScalar = m_multZNegAccessor->cellScalar( gridLocalCellIndex );
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// FaceScalar with value HUGE_VAL means value outside valid IJK-range. Clamp to 1.0 as this means no change in MULT
|
||||
// factor.
|
||||
if ( faceScalar == HUGE_VAL )
|
||||
{
|
||||
faceScalar = 1.0;
|
||||
}
|
||||
|
||||
return faceScalar;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCombMultResultAccessor::cellScalarGlobIdx( size_t globCellIndex ) const
|
||||
{
|
||||
CVF_TIGHT_ASSERT( false );
|
||||
|
||||
return HUGE_VAL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCombMultResultAccessor::cellFaceScalarGlobIdx( size_t globCellIndex, cvf::StructGridInterface::FaceType faceId ) const
|
||||
{
|
||||
size_t gridLocalCellIndex = m_grid->mainGrid()->cell( globCellIndex ).gridLocalCellIndex();
|
||||
return this->cellFaceScalar( gridLocalCellIndex, faceId );
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigResultAccessor.h"
|
||||
|
||||
#include "cvfCollection.h"
|
||||
|
||||
class RigGridBase;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigCombMultResultAccessor : public RigResultAccessor
|
||||
{
|
||||
public:
|
||||
explicit RigCombMultResultAccessor( const RigGridBase* grid );
|
||||
|
||||
void setMultResultAccessors( RigResultAccessor* multXPosAccessor,
|
||||
RigResultAccessor* multXNegAccessor,
|
||||
RigResultAccessor* multYPosAccessor,
|
||||
RigResultAccessor* multYNegAccessor,
|
||||
RigResultAccessor* multZPosAccessor,
|
||||
RigResultAccessor* multZNegAccessor );
|
||||
|
||||
double cellScalar( size_t gridLocalCellIndex ) const override;
|
||||
double cellFaceScalar( size_t gridLocalCellIndex, cvf::StructGridInterface::FaceType faceId ) const override;
|
||||
double cellScalarGlobIdx( size_t globCellIndex ) const override;
|
||||
double cellFaceScalarGlobIdx( size_t globCellIndex, cvf::StructGridInterface::FaceType faceId ) const override;
|
||||
|
||||
private:
|
||||
double nativeMultScalar( size_t gridLocalCellIndex, cvf::StructGridInterface::FaceType faceId ) const;
|
||||
|
||||
private:
|
||||
cvf::ref<RigResultAccessor> m_multXPosAccessor;
|
||||
cvf::ref<RigResultAccessor> m_multXNegAccessor;
|
||||
cvf::ref<RigResultAccessor> m_multYPosAccessor;
|
||||
cvf::ref<RigResultAccessor> m_multYNegAccessor;
|
||||
cvf::ref<RigResultAccessor> m_multZPosAccessor;
|
||||
cvf::ref<RigResultAccessor> m_multZNegAccessor;
|
||||
|
||||
const RigGridBase* m_grid;
|
||||
};
|
||||
@@ -0,0 +1,162 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigCombTransResultAccessor.h"
|
||||
|
||||
#include "RigGridBase.h"
|
||||
|
||||
#include "RigCell.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include <cmath>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCombTransResultAccessor::RigCombTransResultAccessor( const RigGridBase* grid )
|
||||
: m_grid( grid )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Only sensible to provide the positive values, as the negative ones will never be used.
|
||||
/// The negative faces gets their value from the neighbor cell in that direction
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigCombTransResultAccessor::setTransResultAccessors( RigResultAccessor* xTransAccessor,
|
||||
RigResultAccessor* yTransAccessor,
|
||||
RigResultAccessor* zTransAccessor )
|
||||
|
||||
{
|
||||
m_xTransAccessor = xTransAccessor;
|
||||
m_yTransAccessor = yTransAccessor;
|
||||
m_zTransAccessor = zTransAccessor;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCombTransResultAccessor::cellScalar( size_t gridLocalCellIndex ) const
|
||||
{
|
||||
CVF_TIGHT_ASSERT( false );
|
||||
|
||||
return HUGE_VAL;
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Get tran value from neighbor cell. Return 0.0 on active/inactive cell borders and end of grid
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCombTransResultAccessor::neighborCellTran( size_t gridLocalCellIndex,
|
||||
cvf::StructGridInterface::FaceType faceId,
|
||||
const RigResultAccessor* transAccessor ) const
|
||||
{
|
||||
if ( transAccessor != nullptr )
|
||||
{
|
||||
size_t i, j, k, neighborGridCellIdx;
|
||||
m_grid->ijkFromCellIndex( gridLocalCellIndex, &i, &j, &k );
|
||||
|
||||
if ( m_grid->cellIJKNeighbor( i, j, k, faceId, &neighborGridCellIdx ) )
|
||||
{
|
||||
double neighborCellValue = transAccessor->cellScalar( neighborGridCellIdx );
|
||||
if ( neighborCellValue == HUGE_VAL && transAccessor->cellScalar( gridLocalCellIndex ) != HUGE_VAL )
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return neighborCellValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
return HUGE_VAL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCombTransResultAccessor::cellFaceScalar( size_t gridLocalCellIndex, cvf::StructGridInterface::FaceType faceId ) const
|
||||
{
|
||||
switch ( faceId )
|
||||
{
|
||||
case cvf::StructGridInterface::POS_I:
|
||||
{
|
||||
if ( m_xTransAccessor.notNull() )
|
||||
{
|
||||
return m_xTransAccessor->cellScalar( gridLocalCellIndex );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case cvf::StructGridInterface::NEG_I:
|
||||
{
|
||||
return this->neighborCellTran( gridLocalCellIndex, cvf::StructGridInterface::NEG_I, m_xTransAccessor.p() );
|
||||
}
|
||||
break;
|
||||
case cvf::StructGridInterface::POS_J:
|
||||
{
|
||||
if ( m_yTransAccessor.notNull() )
|
||||
{
|
||||
return m_yTransAccessor->cellScalar( gridLocalCellIndex );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case cvf::StructGridInterface::NEG_J:
|
||||
{
|
||||
return this->neighborCellTran( gridLocalCellIndex, cvf::StructGridInterface::NEG_J, m_yTransAccessor.p() );
|
||||
}
|
||||
break;
|
||||
case cvf::StructGridInterface::POS_K:
|
||||
{
|
||||
if ( m_zTransAccessor.notNull() )
|
||||
{
|
||||
return m_zTransAccessor->cellScalar( gridLocalCellIndex );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case cvf::StructGridInterface::NEG_K:
|
||||
{
|
||||
return this->neighborCellTran( gridLocalCellIndex, cvf::StructGridInterface::NEG_K, m_zTransAccessor.p() );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return HUGE_VAL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCombTransResultAccessor::cellScalarGlobIdx( size_t globCellIndex ) const
|
||||
{
|
||||
CVF_TIGHT_ASSERT( false );
|
||||
|
||||
return HUGE_VAL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigCombTransResultAccessor::cellFaceScalarGlobIdx( size_t globCellIndex,
|
||||
cvf::StructGridInterface::FaceType faceId ) const
|
||||
{
|
||||
size_t gridLocalCellIndex = m_grid->mainGrid()->cell( globCellIndex ).gridLocalCellIndex();
|
||||
return this->cellFaceScalar( gridLocalCellIndex, faceId );
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigResultAccessor.h"
|
||||
|
||||
#include "cvfCollection.h"
|
||||
|
||||
class RigGridBase;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigCombTransResultAccessor : public RigResultAccessor
|
||||
{
|
||||
public:
|
||||
explicit RigCombTransResultAccessor( const RigGridBase* grid );
|
||||
|
||||
void setTransResultAccessors( RigResultAccessor* xTransAccessor,
|
||||
RigResultAccessor* yTransAccessor,
|
||||
RigResultAccessor* zTransAccessor );
|
||||
|
||||
double cellScalar( size_t gridLocalCellIndex ) const override;
|
||||
double cellFaceScalar( size_t gridLocalCellIndex, cvf::StructGridInterface::FaceType faceId ) const override;
|
||||
double cellScalarGlobIdx( size_t globCellIndex ) const override;
|
||||
double cellFaceScalarGlobIdx( size_t globCellIndex, cvf::StructGridInterface::FaceType faceId ) const override;
|
||||
|
||||
private:
|
||||
double neighborCellTran( size_t gridLocalCellIndex,
|
||||
cvf::StructGridInterface::FaceType faceId,
|
||||
const RigResultAccessor* transAccessor ) const;
|
||||
|
||||
cvf::ref<RigResultAccessor> m_xTransAccessor;
|
||||
cvf::ref<RigResultAccessor> m_yTransAccessor;
|
||||
cvf::ref<RigResultAccessor> m_zTransAccessor;
|
||||
|
||||
const RigGridBase* m_grid;
|
||||
};
|
||||
@@ -0,0 +1,105 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2015- Statoil ASA
|
||||
// Copyright (C) 2015- Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigEclipseAllanFaultsStatCalc.h"
|
||||
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigCaseCellResultsData.h"
|
||||
#include "RigNNCData.h"
|
||||
#include "RigStatisticsMath.h"
|
||||
#include "RigWeightedMeanCalc.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigEclipseAllanFaultsStatCalc::RigEclipseAllanFaultsStatCalc( RigNNCData* cellResultsData,
|
||||
const RigEclipseResultAddress& scalarResultIndex )
|
||||
: m_caseData( cellResultsData )
|
||||
, m_resultAddress( scalarResultIndex )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseAllanFaultsStatCalc::minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max )
|
||||
{
|
||||
MinMaxAccumulator acc( min, max );
|
||||
traverseCells( acc, timeStepIndex );
|
||||
min = acc.min;
|
||||
max = acc.max;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseAllanFaultsStatCalc::posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg )
|
||||
{
|
||||
PosNegAccumulator acc( pos, neg );
|
||||
traverseCells( acc, timeStepIndex );
|
||||
pos = acc.pos;
|
||||
neg = acc.neg;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseAllanFaultsStatCalc::valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount )
|
||||
{
|
||||
SumCountAccumulator acc( valueSum, sampleCount );
|
||||
traverseCells( acc, timeStepIndex );
|
||||
valueSum = acc.valueSum;
|
||||
sampleCount = acc.sampleCount;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseAllanFaultsStatCalc::addDataToHistogramCalculator( size_t timeStepIndex,
|
||||
RigHistogramCalculator& histogramCalculator )
|
||||
{
|
||||
traverseCells( histogramCalculator, timeStepIndex );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseAllanFaultsStatCalc::uniqueValues( size_t timeStepIndex, std::set<int>& values )
|
||||
{
|
||||
UniqueValueAccumulator acc;
|
||||
traverseCells( acc, timeStepIndex );
|
||||
values = acc.uniqueValues;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigEclipseAllanFaultsStatCalc::timeStepCount()
|
||||
{
|
||||
return (size_t)1;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseAllanFaultsStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex, double& result )
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2015- Statoil ASA
|
||||
// Copyright (C) 2015- Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigStatisticsCalculator.h"
|
||||
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigCaseCellResultsData.h"
|
||||
#include "RigNNCData.h"
|
||||
|
||||
#include "cvfArray.h"
|
||||
|
||||
class RigEclipseAllanFaultsStatCalc : public RigStatisticsCalculator
|
||||
{
|
||||
public:
|
||||
RigEclipseAllanFaultsStatCalc( RigNNCData* cellResultsData, const RigEclipseResultAddress& scalarResultIndex );
|
||||
|
||||
void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override;
|
||||
void posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg ) override;
|
||||
void valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount ) override;
|
||||
void addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator ) override;
|
||||
void uniqueValues( size_t timeStepIndex, std::set<int>& values ) override;
|
||||
size_t timeStepCount() override;
|
||||
void mobileVolumeWeightedMean( size_t timeStepIndex, double& result ) override;
|
||||
|
||||
private:
|
||||
RigNNCData* m_caseData;
|
||||
RigEclipseResultAddress m_resultAddress;
|
||||
|
||||
template <typename StatisticsAccumulator>
|
||||
void traverseCells( StatisticsAccumulator& accumulator, size_t timeStepIndex )
|
||||
{
|
||||
const std::vector<double>* values = m_caseData->staticConnectionScalarResult( m_resultAddress );
|
||||
|
||||
if ( values && !values->empty() )
|
||||
{
|
||||
for ( const auto& v : *values )
|
||||
{
|
||||
accumulator.addValue( v );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,776 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2011- Statoil ASA
|
||||
// Copyright (C) 2013- Ceetron Solutions AS
|
||||
// Copyright (C) 2011-2012 Ceetron AS
|
||||
//
|
||||
// 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 "RigEclipseCaseData.h"
|
||||
|
||||
#include "RifReaderEclipseOutput.h"
|
||||
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigCaseCellResultsData.h"
|
||||
#include "RigEquil.h"
|
||||
#include "RigFormationNames.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigResultAccessorFactory.h"
|
||||
#include "RigSimWellData.h"
|
||||
#include "RigSimulationWellCenterLineCalculator.h"
|
||||
#include "RigSimulationWellCoordsAndMD.h"
|
||||
#include "RigVirtualPerforationTransmissibilities.h"
|
||||
#include "RigWellPath.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigEclipseCaseData::RigEclipseCaseData( RimEclipseCase* ownerCase )
|
||||
: m_hasParsedDeckForEquilData( false )
|
||||
{
|
||||
m_mainGrid = new RigMainGrid();
|
||||
m_ownerCase = ownerCase;
|
||||
|
||||
m_matrixModelResults = new RigCaseCellResultsData( this, RiaDefines::PorosityModelType::MATRIX_MODEL );
|
||||
m_fractureModelResults = new RigCaseCellResultsData( this, RiaDefines::PorosityModelType::FRACTURE_MODEL );
|
||||
|
||||
m_activeCellInfo = new RigActiveCellInfo;
|
||||
m_fractureActiveCellInfo = new RigActiveCellInfo;
|
||||
|
||||
m_matrixModelResults->setActiveCellInfo( m_activeCellInfo.p() );
|
||||
m_fractureModelResults->setActiveCellInfo( m_fractureActiveCellInfo.p() );
|
||||
|
||||
m_unitsType = RiaEclipseUnitTools::UnitSystem::UNITS_METRIC;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigEclipseCaseData::~RigEclipseCaseData()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigMainGrid* RigEclipseCaseData::mainGrid()
|
||||
{
|
||||
return m_mainGrid.p();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const RigMainGrid* RigEclipseCaseData::mainGrid() const
|
||||
{
|
||||
return m_mainGrid.p();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseCaseData::setMainGrid( RigMainGrid* mainGrid )
|
||||
{
|
||||
m_mainGrid = mainGrid;
|
||||
|
||||
m_matrixModelResults->setMainGrid( m_mainGrid.p() );
|
||||
m_fractureModelResults->setMainGrid( m_mainGrid.p() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseCaseData::allGrids( std::vector<RigGridBase*>* grids )
|
||||
{
|
||||
CVF_ASSERT( grids );
|
||||
|
||||
if ( m_mainGrid.isNull() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
for ( i = 0; i < m_mainGrid->gridCount(); i++ )
|
||||
{
|
||||
grids->push_back( m_mainGrid->gridByIndex( i ) );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseCaseData::allGrids( std::vector<const RigGridBase*>* grids ) const
|
||||
{
|
||||
CVF_ASSERT( grids );
|
||||
|
||||
if ( m_mainGrid.isNull() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
for ( i = 0; i < m_mainGrid->gridCount(); i++ )
|
||||
{
|
||||
grids->push_back( m_mainGrid->gridByIndex( i ) );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Get grid by index. The main grid has index 0, so the first lgr has index 1
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const RigGridBase* RigEclipseCaseData::grid( size_t index ) const
|
||||
{
|
||||
CVF_ASSERT( m_mainGrid.notNull() );
|
||||
return m_mainGrid->gridByIndex( index );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Get grid by index. The main grid has index 0, so the first lgr has index 1
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigGridBase* RigEclipseCaseData::grid( size_t index )
|
||||
{
|
||||
CVF_ASSERT( m_mainGrid.notNull() );
|
||||
return m_mainGrid->gridByIndex( index );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const RigGridBase* RigEclipseCaseData::grid( const QString& gridName ) const
|
||||
{
|
||||
if ( m_mainGrid.isNull() )
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ( gridName.isEmpty() )
|
||||
{
|
||||
return m_mainGrid.p();
|
||||
}
|
||||
|
||||
size_t i;
|
||||
for ( i = 0; i < m_mainGrid->gridCount(); i++ )
|
||||
{
|
||||
const RigGridBase* grid = m_mainGrid->gridByIndex( i );
|
||||
if ( QString::fromStdString( grid->gridName() ) == gridName ) return grid;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigEclipseCaseData::gridCount() const
|
||||
{
|
||||
CVF_ASSERT( m_mainGrid.notNull() );
|
||||
return m_mainGrid->gridCount();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseCaseData::computeWellCellsPrGrid()
|
||||
{
|
||||
// If we have computed this already, return
|
||||
if ( m_wellCellsInGrid.size() ) return;
|
||||
|
||||
std::vector<RigGridBase*> grids;
|
||||
this->allGrids( &grids );
|
||||
|
||||
// Debug code used to display grid names and grid sizes
|
||||
/*
|
||||
size_t totCellCount = 0;
|
||||
for (auto g : grids)
|
||||
{
|
||||
qDebug() << g->gridName().data();
|
||||
qDebug() << g->cellCountI() << " " << g->cellCountJ() << " " << g->cellCountK() << " ";
|
||||
|
||||
size_t cellCount = g->cellCount();
|
||||
totCellCount += cellCount;
|
||||
qDebug() << cellCount;
|
||||
|
||||
qDebug() << "\n";
|
||||
}
|
||||
|
||||
qDebug() << "\nTotal cell count " << totCellCount;
|
||||
*/
|
||||
|
||||
size_t gIdx;
|
||||
|
||||
// Allocate and initialize the arrays
|
||||
|
||||
m_wellCellsInGrid.resize( grids.size() );
|
||||
m_gridCellToResultWellIndex.resize( grids.size() );
|
||||
|
||||
for ( gIdx = 0; gIdx < grids.size(); ++gIdx )
|
||||
{
|
||||
if ( m_wellCellsInGrid[gIdx].isNull() || m_wellCellsInGrid[gIdx]->size() != grids[gIdx]->cellCount() )
|
||||
{
|
||||
m_wellCellsInGrid[gIdx] = new cvf::UByteArray;
|
||||
m_wellCellsInGrid[gIdx]->resize( grids[gIdx]->cellCount() );
|
||||
|
||||
m_gridCellToResultWellIndex[gIdx] = new cvf::UIntArray;
|
||||
m_gridCellToResultWellIndex[gIdx]->resize( grids[gIdx]->cellCount() );
|
||||
}
|
||||
m_wellCellsInGrid[gIdx]->setAll( false );
|
||||
m_gridCellToResultWellIndex[gIdx]->setAll( cvf::UNDEFINED_UINT );
|
||||
}
|
||||
|
||||
// Fill arrays with data
|
||||
size_t wIdx;
|
||||
for ( wIdx = 0; wIdx < m_simWellData.size(); ++wIdx )
|
||||
{
|
||||
size_t tIdx;
|
||||
for ( tIdx = 0; tIdx < m_simWellData[wIdx]->m_wellCellsTimeSteps.size(); ++tIdx )
|
||||
{
|
||||
RigWellResultFrame& wellCells = m_simWellData[wIdx]->m_wellCellsTimeSteps[tIdx];
|
||||
|
||||
// Well result branches
|
||||
for ( size_t sIdx = 0; sIdx < wellCells.m_wellResultBranches.size(); ++sIdx )
|
||||
{
|
||||
RigWellResultBranch& wellSegment = wellCells.m_wellResultBranches[sIdx];
|
||||
|
||||
size_t cdIdx;
|
||||
for ( cdIdx = 0; cdIdx < wellSegment.m_branchResultPoints.size(); ++cdIdx )
|
||||
{
|
||||
size_t gridIndex = wellSegment.m_branchResultPoints[cdIdx].m_gridIndex;
|
||||
size_t gridCellIndex = wellSegment.m_branchResultPoints[cdIdx].m_gridCellIndex;
|
||||
|
||||
if ( gridIndex < m_wellCellsInGrid.size() && gridCellIndex < m_wellCellsInGrid[gridIndex]->size() )
|
||||
{
|
||||
// NOTE : We do not check if the grid cell is active as we do for well head.
|
||||
// If we add test for active cell, thorough testing and verification of the new behaviour must
|
||||
// be adressed
|
||||
|
||||
m_wellCellsInGrid[gridIndex]->set( gridCellIndex, true );
|
||||
m_gridCellToResultWellIndex[gridIndex]->set( gridCellIndex, static_cast<cvf::uint>( wIdx ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseCaseData::setSimWellData( const cvf::Collection<RigSimWellData>& data )
|
||||
{
|
||||
m_simWellData = data;
|
||||
m_wellCellsInGrid.clear();
|
||||
m_gridCellToResultWellIndex.clear();
|
||||
|
||||
computeWellCellsPrGrid();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::set<QString> RigEclipseCaseData::findSortedWellNames() const
|
||||
{
|
||||
std::set<QString> sortedWellNames;
|
||||
|
||||
const cvf::Collection<RigSimWellData>& simWellData = wellResults();
|
||||
|
||||
for ( size_t wIdx = 0; wIdx < simWellData.size(); ++wIdx )
|
||||
{
|
||||
sortedWellNames.insert( simWellData[wIdx]->m_wellName );
|
||||
}
|
||||
|
||||
return sortedWellNames;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const RigSimWellData* RigEclipseCaseData::findSimWellData( QString wellName ) const
|
||||
{
|
||||
for ( size_t wIdx = 0; wIdx < m_simWellData.size(); ++wIdx )
|
||||
{
|
||||
if ( m_simWellData[wIdx]->m_wellName == wellName )
|
||||
{
|
||||
return m_simWellData[wIdx].p();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const cvf::UByteArray* RigEclipseCaseData::wellCellsInGrid( size_t gridIndex )
|
||||
{
|
||||
computeWellCellsPrGrid();
|
||||
CVF_ASSERT( gridIndex < m_wellCellsInGrid.size() );
|
||||
|
||||
return m_wellCellsInGrid[gridIndex].p();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const cvf::UIntArray* RigEclipseCaseData::gridCellToResultWellIndex( size_t gridIndex )
|
||||
{
|
||||
computeWellCellsPrGrid();
|
||||
CVF_ASSERT( gridIndex < m_gridCellToResultWellIndex.size() );
|
||||
|
||||
return m_gridCellToResultWellIndex[gridIndex].p();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const RigCell& RigEclipseCaseData::cellFromWellResultCell( const RigWellResultPoint& wellResultCell ) const
|
||||
{
|
||||
CVF_ASSERT( wellResultCell.isCell() );
|
||||
|
||||
size_t gridIndex = wellResultCell.m_gridIndex;
|
||||
size_t gridCellIndex = wellResultCell.m_gridCellIndex;
|
||||
|
||||
std::vector<const RigGridBase*> grids;
|
||||
allGrids( &grids );
|
||||
|
||||
return grids[gridIndex]->cell( gridCellIndex );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigEclipseCaseData::findSharedSourceFace( cvf::StructGridInterface::FaceType& sharedSourceFace,
|
||||
const RigWellResultPoint& sourceWellCellResult,
|
||||
const RigWellResultPoint& otherWellCellResult ) const
|
||||
{
|
||||
size_t gridIndex = sourceWellCellResult.m_gridIndex;
|
||||
size_t gridCellIndex = sourceWellCellResult.m_gridCellIndex;
|
||||
|
||||
size_t otherGridIndex = otherWellCellResult.m_gridIndex;
|
||||
size_t otherGridCellIndex = otherWellCellResult.m_gridCellIndex;
|
||||
|
||||
if ( gridIndex != otherGridIndex ) return false;
|
||||
|
||||
std::vector<const RigGridBase*> grids;
|
||||
allGrids( &grids );
|
||||
|
||||
const RigGridBase* grid = grids[gridIndex];
|
||||
size_t i, j, k;
|
||||
grid->ijkFromCellIndex( gridCellIndex, &i, &j, &k );
|
||||
|
||||
size_t faceIdx;
|
||||
for ( faceIdx = 0; faceIdx < 6; faceIdx++ )
|
||||
{
|
||||
cvf::StructGridInterface::FaceType sourceFace = static_cast<cvf::StructGridInterface::FaceType>( faceIdx );
|
||||
|
||||
size_t ni, nj, nk;
|
||||
grid->neighborIJKAtCellFace( i, j, k, sourceFace, &ni, &nj, &nk );
|
||||
|
||||
if ( grid->isCellValid( ni, nj, nk ) )
|
||||
{
|
||||
size_t neighborCellIndex = grid->cellIndexFromIJK( ni, nj, nk );
|
||||
|
||||
if ( neighborCellIndex == otherGridCellIndex )
|
||||
{
|
||||
sharedSourceFace = sourceFace;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Helper class used to find min/max range for valid and active cells
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class CellRangeBB
|
||||
{
|
||||
public:
|
||||
CellRangeBB()
|
||||
: m_min( cvf::UNDEFINED_SIZE_T, cvf::UNDEFINED_SIZE_T, cvf::UNDEFINED_SIZE_T )
|
||||
, m_max( cvf::Vec3st::ZERO )
|
||||
{
|
||||
}
|
||||
|
||||
void add( size_t i, size_t j, size_t k )
|
||||
{
|
||||
if ( i < m_min.x() ) m_min.x() = i;
|
||||
if ( j < m_min.y() ) m_min.y() = j;
|
||||
if ( k < m_min.z() ) m_min.z() = k;
|
||||
|
||||
if ( i > m_max.x() ) m_max.x() = i;
|
||||
if ( j > m_max.y() ) m_max.y() = j;
|
||||
if ( k > m_max.z() ) m_max.z() = k;
|
||||
}
|
||||
|
||||
public:
|
||||
cvf::Vec3st m_min;
|
||||
cvf::Vec3st m_max;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseCaseData::computeActiveCellIJKBBox()
|
||||
{
|
||||
if ( m_mainGrid.notNull() && m_activeCellInfo.notNull() && m_fractureActiveCellInfo.notNull() )
|
||||
{
|
||||
CellRangeBB matrixModelActiveBB;
|
||||
CellRangeBB fractureModelActiveBB;
|
||||
|
||||
size_t idx;
|
||||
for ( idx = 0; idx < m_mainGrid->cellCount(); idx++ )
|
||||
{
|
||||
size_t i, j, k;
|
||||
m_mainGrid->ijkFromCellIndex( idx, &i, &j, &k );
|
||||
|
||||
if ( m_activeCellInfo->isActive( idx ) )
|
||||
{
|
||||
matrixModelActiveBB.add( i, j, k );
|
||||
}
|
||||
|
||||
if ( m_fractureActiveCellInfo->isActive( idx ) )
|
||||
{
|
||||
fractureModelActiveBB.add( i, j, k );
|
||||
}
|
||||
}
|
||||
m_activeCellInfo->setIJKBoundingBox( matrixModelActiveBB.m_min, matrixModelActiveBB.m_max );
|
||||
m_fractureActiveCellInfo->setIJKBoundingBox( fractureModelActiveBB.m_min, fractureModelActiveBB.m_max );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseCaseData::computeActiveCellBoundingBoxes()
|
||||
{
|
||||
computeActiveCellIJKBBox();
|
||||
computeActiveCellsGeometryBoundingBox();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<QString> RigEclipseCaseData::simulationWellNames() const
|
||||
{
|
||||
std::vector<QString> wellNames;
|
||||
for ( const auto& wellResult : wellResults() )
|
||||
{
|
||||
wellNames.push_back( wellResult->m_wellName );
|
||||
}
|
||||
return wellNames;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigEclipseCaseData::hasSimulationWell( const QString& simWellName ) const
|
||||
{
|
||||
const auto wellNames = simulationWellNames();
|
||||
return std::find( wellNames.begin(), wellNames.end(), simWellName ) != wellNames.end();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<const RigWellPath*> RigEclipseCaseData::simulationWellBranches( const QString& simWellName,
|
||||
bool includeAllCellCenters,
|
||||
bool useAutoDetectionOfBranches ) const
|
||||
{
|
||||
std::vector<const RigWellPath*> branches;
|
||||
|
||||
if ( simWellName.isEmpty() || simWellName.toUpper() == "NONE" )
|
||||
{
|
||||
return branches;
|
||||
}
|
||||
|
||||
const RigSimWellData* simWellData = findSimWellData( simWellName );
|
||||
if ( !simWellData ) return branches;
|
||||
|
||||
std::tuple<QString, bool, bool> simWellSeachItem =
|
||||
std::make_tuple( simWellName, includeAllCellCenters, useAutoDetectionOfBranches );
|
||||
|
||||
if ( m_simWellBranchCache.find( simWellSeachItem ) == m_simWellBranchCache.end() )
|
||||
{
|
||||
std::vector<std::vector<cvf::Vec3d>> pipeBranchesCLCoords;
|
||||
std::vector<std::vector<RigWellResultPoint>> pipeBranchesCellIds;
|
||||
|
||||
RigSimulationWellCenterLineCalculator::calculateWellPipeCenterlineFromWellFrame( this,
|
||||
simWellData,
|
||||
-1,
|
||||
useAutoDetectionOfBranches,
|
||||
includeAllCellCenters,
|
||||
pipeBranchesCLCoords,
|
||||
pipeBranchesCellIds );
|
||||
|
||||
m_simWellBranchCache.insert( std::make_pair( simWellSeachItem, cvf::Collection<RigWellPath>() ) );
|
||||
|
||||
for ( size_t brIdx = 0; brIdx < pipeBranchesCLCoords.size(); ++brIdx )
|
||||
{
|
||||
auto wellMdCalculator = RigSimulationWellCoordsAndMD( pipeBranchesCLCoords[brIdx] );
|
||||
|
||||
cvf::ref<RigWellPath> newWellPath =
|
||||
new RigWellPath( wellMdCalculator.wellPathPoints(), wellMdCalculator.measuredDepths() );
|
||||
|
||||
m_simWellBranchCache[simWellSeachItem].push_back( newWellPath.p() );
|
||||
}
|
||||
}
|
||||
|
||||
for ( const auto& branch : m_simWellBranchCache[simWellSeachItem] )
|
||||
{
|
||||
branches.push_back( branch.p() );
|
||||
}
|
||||
|
||||
return branches;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseCaseData::setVirtualPerforationTransmissibilities(
|
||||
RigVirtualPerforationTransmissibilities* virtualPerforationTransmissibilities )
|
||||
{
|
||||
m_virtualPerforationTransmissibilities = virtualPerforationTransmissibilities;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const RigVirtualPerforationTransmissibilities* RigEclipseCaseData::virtualPerforationTransmissibilities() const
|
||||
{
|
||||
return m_virtualPerforationTransmissibilities.p();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseCaseData::ensureDeckIsParsedForEquilData( const QString& dataDeckFile,
|
||||
const QString& includeFileAbsolutePathPrefix )
|
||||
{
|
||||
if ( !m_hasParsedDeckForEquilData )
|
||||
{
|
||||
RifReaderEclipseOutput::importEquilData( dataDeckFile, includeFileAbsolutePathPrefix, this );
|
||||
|
||||
m_hasParsedDeckForEquilData = true;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RigEquil> RigEclipseCaseData::equilData() const
|
||||
{
|
||||
return m_equil;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseCaseData::setEquilData( const std::vector<RigEquil>& equilObjects )
|
||||
{
|
||||
m_equil = equilObjects;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigActiveCellInfo* RigEclipseCaseData::activeCellInfo( RiaDefines::PorosityModelType porosityModel )
|
||||
{
|
||||
if ( porosityModel == RiaDefines::PorosityModelType::MATRIX_MODEL )
|
||||
{
|
||||
return m_activeCellInfo.p();
|
||||
}
|
||||
|
||||
return m_fractureActiveCellInfo.p();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const RigActiveCellInfo* RigEclipseCaseData::activeCellInfo( RiaDefines::PorosityModelType porosityModel ) const
|
||||
{
|
||||
if ( porosityModel == RiaDefines::PorosityModelType::MATRIX_MODEL )
|
||||
{
|
||||
return m_activeCellInfo.p();
|
||||
}
|
||||
|
||||
return m_fractureActiveCellInfo.p();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseCaseData::setActiveCellInfo( RiaDefines::PorosityModelType porosityModel, RigActiveCellInfo* activeCellInfo )
|
||||
{
|
||||
if ( porosityModel == RiaDefines::PorosityModelType::MATRIX_MODEL )
|
||||
{
|
||||
m_activeCellInfo = activeCellInfo;
|
||||
m_matrixModelResults->setActiveCellInfo( m_activeCellInfo.p() );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fractureActiveCellInfo = activeCellInfo;
|
||||
m_fractureModelResults->setActiveCellInfo( m_fractureActiveCellInfo.p() );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigEclipseCaseData::hasFractureResults() const
|
||||
{
|
||||
if ( activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL ) &&
|
||||
activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL )->reservoirActiveCellCount() > 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseCaseData::computeActiveCellsGeometryBoundingBox()
|
||||
{
|
||||
if ( m_activeCellInfo.isNull() || m_fractureActiveCellInfo.isNull() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( m_mainGrid.isNull() )
|
||||
{
|
||||
cvf::BoundingBox bb;
|
||||
m_activeCellInfo->setGeometryBoundingBox( bb );
|
||||
m_fractureActiveCellInfo->setGeometryBoundingBox( bb );
|
||||
return;
|
||||
}
|
||||
|
||||
RigActiveCellInfo* activeInfos[2];
|
||||
activeInfos[0] = m_fractureActiveCellInfo.p();
|
||||
activeInfos[1] = m_activeCellInfo.p(); // Last, to make this bb.min become display offset
|
||||
|
||||
cvf::BoundingBox bb;
|
||||
for ( int acIdx = 0; acIdx < 2; ++acIdx )
|
||||
{
|
||||
bb.reset();
|
||||
if ( m_mainGrid->nodes().size() == 0 )
|
||||
{
|
||||
bb.add( cvf::Vec3d::ZERO );
|
||||
}
|
||||
else
|
||||
{
|
||||
std::array<cvf::Vec3d, 8> hexCorners;
|
||||
for ( size_t i = 0; i < m_mainGrid->cellCount(); i++ )
|
||||
{
|
||||
if ( activeInfos[acIdx]->isActive( i ) )
|
||||
{
|
||||
m_mainGrid->cellCornerVertices( i, hexCorners.data() );
|
||||
for ( const auto& corner : hexCorners )
|
||||
{
|
||||
bb.add( corner );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
activeInfos[acIdx]->setGeometryBoundingBox( bb );
|
||||
}
|
||||
|
||||
m_mainGrid->setDisplayModelOffset( bb.min() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseCaseData::setActiveFormationNames( RigFormationNames* activeFormationNames )
|
||||
{
|
||||
m_matrixModelResults->setActiveFormationNames( activeFormationNames );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const RigFormationNames* RigEclipseCaseData::activeFormationNames() const
|
||||
{
|
||||
return m_matrixModelResults->activeFormationNames();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<QString> RigEclipseCaseData::formationNames() const
|
||||
{
|
||||
if ( activeFormationNames() )
|
||||
{
|
||||
return activeFormationNames()->formationNames();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigAllanDiagramData* RigEclipseCaseData::allanDiagramData()
|
||||
{
|
||||
return m_matrixModelResults->allanDiagramData();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigCaseCellResultsData* RigEclipseCaseData::results( RiaDefines::PorosityModelType porosityModel )
|
||||
{
|
||||
if ( porosityModel == RiaDefines::PorosityModelType::MATRIX_MODEL )
|
||||
{
|
||||
return m_matrixModelResults.p();
|
||||
}
|
||||
|
||||
return m_fractureModelResults.p();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const RigCaseCellResultsData* RigEclipseCaseData::results( RiaDefines::PorosityModelType porosityModel ) const
|
||||
{
|
||||
if ( porosityModel == RiaDefines::PorosityModelType::MATRIX_MODEL )
|
||||
{
|
||||
return m_matrixModelResults.p();
|
||||
}
|
||||
|
||||
return m_fractureModelResults.p();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<double>* RigEclipseCaseData::resultValues( RiaDefines::PorosityModelType porosityModel,
|
||||
RiaDefines::ResultCatType type,
|
||||
const QString& resultName,
|
||||
size_t timeStepIndex )
|
||||
{
|
||||
RigCaseCellResultsData* gridCellResults = this->results( porosityModel );
|
||||
|
||||
const std::vector<double>* swatResults = nullptr;
|
||||
if ( gridCellResults->ensureKnownResultLoaded( RigEclipseResultAddress( type, resultName ) ) )
|
||||
{
|
||||
swatResults = &( gridCellResults->cellScalarResults( RigEclipseResultAddress( type, resultName ), timeStepIndex ) );
|
||||
}
|
||||
|
||||
return swatResults;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2011- Statoil ASA
|
||||
// Copyright (C) 2013- Ceetron Solutions AS
|
||||
// Copyright (C) 2011-2012 Ceetron AS
|
||||
//
|
||||
// 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 "RifReaderInterface.h"
|
||||
|
||||
#include "RiaEclipseUnitTools.h"
|
||||
|
||||
#include "cvfArray.h"
|
||||
#include "cvfAssert.h"
|
||||
#include "cvfCollection.h"
|
||||
#include "cvfObject.h"
|
||||
#include "cvfStructGrid.h"
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
class RigCaseCellResultsData;
|
||||
class RigFormationNames;
|
||||
class RigMainGrid;
|
||||
class RigGridBase;
|
||||
class RigCaseCellResultsData;
|
||||
class RigActiveCellInfo;
|
||||
class RigSimWellData;
|
||||
class RigCell;
|
||||
class RigWellPath;
|
||||
class RimEclipseCase;
|
||||
class RigVirtualPerforationTransmissibilities;
|
||||
class RigEquil;
|
||||
class RigAllanDiagramData;
|
||||
|
||||
struct RigWellResultPoint;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class RigEclipseCaseData : public cvf::Object
|
||||
{
|
||||
public:
|
||||
explicit RigEclipseCaseData( RimEclipseCase* ownerCase );
|
||||
~RigEclipseCaseData() override;
|
||||
|
||||
RimEclipseCase* ownerCase() const { return m_ownerCase; }
|
||||
|
||||
RigMainGrid* mainGrid();
|
||||
const RigMainGrid* mainGrid() const;
|
||||
void setMainGrid( RigMainGrid* mainGrid );
|
||||
|
||||
void allGrids( std::vector<RigGridBase*>* grids ); // To be removed
|
||||
void allGrids( std::vector<const RigGridBase*>* grids ) const; // To be removed
|
||||
const RigGridBase* grid( size_t index ) const;
|
||||
RigGridBase* grid( size_t index );
|
||||
size_t gridCount() const;
|
||||
const RigGridBase* grid( const QString& gridName ) const;
|
||||
|
||||
RigCaseCellResultsData* results( RiaDefines::PorosityModelType porosityModel );
|
||||
const RigCaseCellResultsData* results( RiaDefines::PorosityModelType porosityModel ) const;
|
||||
const std::vector<double>* resultValues( RiaDefines::PorosityModelType porosityModel,
|
||||
RiaDefines::ResultCatType type,
|
||||
const QString& resultName,
|
||||
size_t timeStepIndex );
|
||||
|
||||
RigActiveCellInfo* activeCellInfo( RiaDefines::PorosityModelType porosityModel );
|
||||
const RigActiveCellInfo* activeCellInfo( RiaDefines::PorosityModelType porosityModel ) const;
|
||||
void setActiveCellInfo( RiaDefines::PorosityModelType porosityModel, RigActiveCellInfo* activeCellInfo );
|
||||
|
||||
bool hasFractureResults() const;
|
||||
|
||||
void setActiveFormationNames( RigFormationNames* activeFormationNames );
|
||||
const std::vector<QString> formationNames() const;
|
||||
RigAllanDiagramData* allanDiagramData();
|
||||
|
||||
void setSimWellData( const cvf::Collection<RigSimWellData>& data );
|
||||
const cvf::Collection<RigSimWellData>& wellResults() const { return m_simWellData; }
|
||||
std::set<QString> findSortedWellNames() const;
|
||||
const RigSimWellData* findSimWellData( QString wellName ) const;
|
||||
|
||||
const cvf::UByteArray* wellCellsInGrid( size_t gridIndex );
|
||||
const cvf::UIntArray* gridCellToResultWellIndex( size_t gridIndex );
|
||||
|
||||
const RigCell& cellFromWellResultCell( const RigWellResultPoint& wellResultCell ) const;
|
||||
bool findSharedSourceFace( cvf::StructGridInterface::FaceType& sharedSourceFace,
|
||||
const RigWellResultPoint& sourceWellCellResult,
|
||||
const RigWellResultPoint& otherWellCellResult ) const;
|
||||
|
||||
void computeActiveCellBoundingBoxes();
|
||||
|
||||
RiaEclipseUnitTools::UnitSystem unitsType() const { return m_unitsType; }
|
||||
void setUnitsType( RiaEclipseUnitTools::UnitSystem unitsType ) { m_unitsType = unitsType; }
|
||||
|
||||
std::vector<QString> simulationWellNames() const;
|
||||
bool hasSimulationWell( const QString& simWellName ) const;
|
||||
|
||||
std::vector<const RigWellPath*> simulationWellBranches( const QString& simWellName,
|
||||
bool includeAllCellCenters,
|
||||
bool useAutoDetectionOfBranches ) const;
|
||||
|
||||
void setVirtualPerforationTransmissibilities( RigVirtualPerforationTransmissibilities* virtualPerforationTransmissibilities );
|
||||
const RigVirtualPerforationTransmissibilities* virtualPerforationTransmissibilities() const;
|
||||
|
||||
void clearWellCellsInGridCache() { m_wellCellsInGrid.clear(); }
|
||||
|
||||
void ensureDeckIsParsedForEquilData( const QString& dataDeckFile, const QString& includeFileAbsolutePathPrefix );
|
||||
std::vector<RigEquil> equilData() const;
|
||||
void setEquilData( const std::vector<RigEquil>& equilObjects );
|
||||
|
||||
private:
|
||||
void computeActiveCellIJKBBox();
|
||||
void computeWellCellsPrGrid();
|
||||
void computeActiveCellsGeometryBoundingBox();
|
||||
|
||||
const RigFormationNames* activeFormationNames() const;
|
||||
|
||||
private:
|
||||
cvf::ref<RigMainGrid> m_mainGrid;
|
||||
RimEclipseCase* m_ownerCase;
|
||||
|
||||
cvf::ref<RigActiveCellInfo> m_activeCellInfo;
|
||||
cvf::ref<RigActiveCellInfo> m_fractureActiveCellInfo;
|
||||
|
||||
cvf::ref<RigCaseCellResultsData> m_matrixModelResults;
|
||||
cvf::ref<RigCaseCellResultsData> m_fractureModelResults;
|
||||
|
||||
cvf::ref<RigVirtualPerforationTransmissibilities> m_virtualPerforationTransmissibilities;
|
||||
|
||||
cvf::Collection<RigSimWellData> m_simWellData; //< A WellResults object for each well in the reservoir
|
||||
cvf::Collection<cvf::UByteArray> m_wellCellsInGrid; //< A bool array pr grid with one bool pr cell telling whether
|
||||
// the cell is a well cell or not
|
||||
cvf::Collection<cvf::UIntArray> m_gridCellToResultWellIndex; //< Array pr grid with index to well pr cell telling
|
||||
// which well a cell is in
|
||||
|
||||
RiaEclipseUnitTools::UnitSystem m_unitsType;
|
||||
|
||||
bool m_hasParsedDeckForEquilData;
|
||||
std::vector<RigEquil> m_equil;
|
||||
|
||||
mutable std::map<std::tuple<QString, bool, bool>, cvf::Collection<RigWellPath>> m_simWellBranchCache;
|
||||
};
|
||||
@@ -0,0 +1,163 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2019- 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 "RigEclipseCrossPlotDataExtractor.h"
|
||||
|
||||
#include "RiaQDateTimeTools.h"
|
||||
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigActiveCellsResultAccessor.h"
|
||||
#include "RigCaseCellResultsData.h"
|
||||
#include "RigEclipseCaseData.h"
|
||||
#include "RigEclipseResultAddress.h"
|
||||
#include "RigFormationNames.h"
|
||||
#include "RigMainGrid.h"
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigEclipseCrossPlotResult RigEclipseCrossPlotDataExtractor::extract( RigEclipseCaseData* caseData,
|
||||
int resultTimeStep,
|
||||
const RigEclipseResultAddress& xAddress,
|
||||
const RigEclipseResultAddress& yAddress,
|
||||
RigGridCrossPlotCurveGrouping groupingType,
|
||||
const RigEclipseResultAddress& groupAddress,
|
||||
std::map<int, cvf::UByteArray> timeStepCellVisibilityMap )
|
||||
{
|
||||
RigEclipseCrossPlotResult result;
|
||||
|
||||
RigCaseCellResultsData* resultData = caseData->results( RiaDefines::PorosityModelType::MATRIX_MODEL );
|
||||
if ( !resultData ) return result;
|
||||
|
||||
const std::vector<std::vector<double>>* catValuesForAllSteps = nullptr;
|
||||
|
||||
if ( xAddress.isValid() && yAddress.isValid() )
|
||||
{
|
||||
RigActiveCellInfo* activeCellInfo = resultData->activeCellInfo();
|
||||
const RigMainGrid* mainGrid = caseData->mainGrid();
|
||||
|
||||
if ( !resultData->ensureKnownResultLoaded( xAddress ) )
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if ( !resultData->ensureKnownResultLoaded( yAddress ) )
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
const std::vector<std::vector<double>>& xValuesForAllSteps = resultData->cellScalarResults( xAddress );
|
||||
const std::vector<std::vector<double>>& yValuesForAllSteps = resultData->cellScalarResults( yAddress );
|
||||
|
||||
if ( groupingType == GROUP_BY_RESULT && groupAddress.isValid() )
|
||||
{
|
||||
if ( resultData->ensureKnownResultLoaded( groupAddress ) )
|
||||
{
|
||||
catValuesForAllSteps = &resultData->cellScalarResults( groupAddress );
|
||||
}
|
||||
}
|
||||
|
||||
std::set<int> timeStepsToInclude;
|
||||
if ( resultTimeStep == -1 )
|
||||
{
|
||||
size_t nStepsInData = std::max( xValuesForAllSteps.size(), yValuesForAllSteps.size() );
|
||||
bool xValid = xValuesForAllSteps.size() == 1u || xValuesForAllSteps.size() == nStepsInData;
|
||||
bool yValid = yValuesForAllSteps.size() == 1u || yValuesForAllSteps.size() == nStepsInData;
|
||||
|
||||
if ( !( xValid && yValid ) ) return result;
|
||||
|
||||
for ( size_t i = 0; i < nStepsInData; ++i )
|
||||
{
|
||||
timeStepsToInclude.insert( (int)i );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
timeStepsToInclude.insert( static_cast<size_t>( resultTimeStep ) );
|
||||
}
|
||||
|
||||
for ( int timeStep : timeStepsToInclude )
|
||||
{
|
||||
const cvf::UByteArray* cellVisibility = nullptr;
|
||||
if ( timeStepCellVisibilityMap.count( timeStep ) )
|
||||
{
|
||||
cellVisibility = &timeStepCellVisibilityMap[timeStep];
|
||||
}
|
||||
|
||||
int xIndex = timeStep >= (int)xValuesForAllSteps.size() ? 0 : timeStep;
|
||||
int yIndex = timeStep >= (int)yValuesForAllSteps.size() ? 0 : timeStep;
|
||||
|
||||
RigActiveCellsResultAccessor xAccessor( mainGrid, &xValuesForAllSteps[xIndex], activeCellInfo );
|
||||
RigActiveCellsResultAccessor yAccessor( mainGrid, &yValuesForAllSteps[yIndex], activeCellInfo );
|
||||
std::unique_ptr<RigActiveCellsResultAccessor> catAccessor;
|
||||
if ( catValuesForAllSteps )
|
||||
{
|
||||
int catIndex = timeStep >= (int)catValuesForAllSteps->size() ? 0 : timeStep;
|
||||
catAccessor.reset( new RigActiveCellsResultAccessor( mainGrid,
|
||||
&( catValuesForAllSteps->at( catIndex ) ),
|
||||
activeCellInfo ) );
|
||||
}
|
||||
|
||||
for ( size_t globalCellIdx = 0; globalCellIdx < activeCellInfo->reservoirCellCount(); ++globalCellIdx )
|
||||
{
|
||||
if ( cellVisibility && !( *cellVisibility )[globalCellIdx] ) continue;
|
||||
|
||||
double xValue = xAccessor.cellScalarGlobIdx( globalCellIdx );
|
||||
double yValue = yAccessor.cellScalarGlobIdx( globalCellIdx );
|
||||
|
||||
if ( xValue == HUGE_VAL || yValue == HUGE_VAL ) continue;
|
||||
|
||||
result.xValues.push_back( xValue );
|
||||
result.yValues.push_back( yValue );
|
||||
|
||||
if ( groupingType == GROUP_BY_TIME )
|
||||
{
|
||||
result.groupValuesDiscrete.push_back( timeStep );
|
||||
}
|
||||
else if ( groupingType == GROUP_BY_FORMATION )
|
||||
{
|
||||
const RigFormationNames* activeFormationNames = resultData->activeFormationNames();
|
||||
|
||||
if ( activeFormationNames )
|
||||
{
|
||||
int category = 0;
|
||||
size_t i( cvf::UNDEFINED_SIZE_T ), j( cvf::UNDEFINED_SIZE_T ), k( cvf::UNDEFINED_SIZE_T );
|
||||
if ( mainGrid->ijkFromCellIndex( globalCellIdx, &i, &j, &k ) )
|
||||
{
|
||||
category = activeFormationNames->formationIndexFromKLayerIdx( k );
|
||||
}
|
||||
result.groupValuesDiscrete.push_back( category );
|
||||
}
|
||||
}
|
||||
else if ( groupingType == GROUP_BY_RESULT )
|
||||
{
|
||||
double catValue = HUGE_VAL;
|
||||
if ( catAccessor )
|
||||
{
|
||||
catValue = catAccessor->cellScalarGlobIdx( globalCellIdx );
|
||||
}
|
||||
result.groupValuesContinuous.push_back( catValue );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2019- 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 "RigGridCrossPlotCurveGrouping.h"
|
||||
|
||||
#include "cvfArray.h"
|
||||
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class RigEclipseCaseData;
|
||||
class RigEclipseResultAddress;
|
||||
|
||||
class QString;
|
||||
|
||||
struct RigEclipseCrossPlotResult
|
||||
{
|
||||
std::vector<double> xValues;
|
||||
std::vector<double> yValues;
|
||||
std::vector<double> groupValuesContinuous;
|
||||
std::vector<int> groupValuesDiscrete;
|
||||
};
|
||||
|
||||
class RigEclipseCrossPlotDataExtractor
|
||||
{
|
||||
public:
|
||||
static RigEclipseCrossPlotResult extract( RigEclipseCaseData* eclipseCase,
|
||||
int resultTimeStep,
|
||||
const RigEclipseResultAddress& xAddress,
|
||||
const RigEclipseResultAddress& yAddress,
|
||||
RigGridCrossPlotCurveGrouping groupingType,
|
||||
const RigEclipseResultAddress& groupAddress,
|
||||
std::map<int, cvf::UByteArray> timeStepCellVisibilityMap );
|
||||
};
|
||||
@@ -0,0 +1,135 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigEclipseMultiPropertyStatCalc.h"
|
||||
#include "RigEclipseNativeStatCalc.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigEclipseMultiPropertyStatCalc::RigEclipseMultiPropertyStatCalc()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseMultiPropertyStatCalc::addStatisticsCalculator( RigStatisticsCalculator* statisticsCalculator )
|
||||
{
|
||||
if ( statisticsCalculator )
|
||||
{
|
||||
m_nativeStatisticsCalculators.push_back( statisticsCalculator );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseMultiPropertyStatCalc::minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max )
|
||||
{
|
||||
for ( size_t i = 0; i < m_nativeStatisticsCalculators.size(); i++ )
|
||||
{
|
||||
if ( m_nativeStatisticsCalculators.at( i ) )
|
||||
{
|
||||
m_nativeStatisticsCalculators.at( i )->minMaxCellScalarValues( timeStepIndex, min, max );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseMultiPropertyStatCalc::posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg )
|
||||
{
|
||||
for ( size_t i = 0; i < m_nativeStatisticsCalculators.size(); i++ )
|
||||
{
|
||||
if ( m_nativeStatisticsCalculators.at( i ) )
|
||||
{
|
||||
m_nativeStatisticsCalculators.at( i )->posNegClosestToZero( timeStepIndex, pos, neg );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseMultiPropertyStatCalc::valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount )
|
||||
{
|
||||
for ( size_t i = 0; i < m_nativeStatisticsCalculators.size(); i++ )
|
||||
{
|
||||
if ( m_nativeStatisticsCalculators.at( i ) )
|
||||
{
|
||||
m_nativeStatisticsCalculators.at( i )->valueSumAndSampleCount( timeStepIndex, valueSum, sampleCount );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseMultiPropertyStatCalc::addDataToHistogramCalculator( size_t timeStepIndex,
|
||||
RigHistogramCalculator& histogramCalculator )
|
||||
{
|
||||
for ( size_t i = 0; i < m_nativeStatisticsCalculators.size(); i++ )
|
||||
{
|
||||
if ( m_nativeStatisticsCalculators.at( i ) )
|
||||
{
|
||||
m_nativeStatisticsCalculators.at( i )->addDataToHistogramCalculator( timeStepIndex, histogramCalculator );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseMultiPropertyStatCalc::uniqueValues( size_t timeStepIndex, std::set<int>& values )
|
||||
{
|
||||
for ( size_t i = 0; i < m_nativeStatisticsCalculators.size(); i++ )
|
||||
{
|
||||
if ( m_nativeStatisticsCalculators.at( i ) )
|
||||
{
|
||||
m_nativeStatisticsCalculators.at( i )->uniqueValues( timeStepIndex, values );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigEclipseMultiPropertyStatCalc::timeStepCount()
|
||||
{
|
||||
if ( m_nativeStatisticsCalculators.size() > 0 )
|
||||
{
|
||||
return m_nativeStatisticsCalculators[0]->timeStepCount();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseMultiPropertyStatCalc::addNativeStatisticsCalculator( RigCaseCellResultsData* cellResultsData,
|
||||
const RigEclipseResultAddress& eclipseResultAddress )
|
||||
{
|
||||
if ( eclipseResultAddress.isValid() )
|
||||
{
|
||||
this->addStatisticsCalculator( new RigEclipseNativeStatCalc( cellResultsData, eclipseResultAddress ) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigStatisticsCalculator.h"
|
||||
|
||||
#include "cvfCollection.h"
|
||||
#include "cvfObject.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class RigHistogramCalculator;
|
||||
class RigCaseCellResultsData;
|
||||
class RigEclipseResultAddress;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigEclipseMultiPropertyStatCalc : public RigStatisticsCalculator
|
||||
{
|
||||
public:
|
||||
RigEclipseMultiPropertyStatCalc();
|
||||
void addStatisticsCalculator( RigStatisticsCalculator* statisticsCalculator );
|
||||
void addNativeStatisticsCalculator( RigCaseCellResultsData* cellResultsData,
|
||||
const RigEclipseResultAddress& scalarResultIndices );
|
||||
|
||||
void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override;
|
||||
void posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg ) override;
|
||||
|
||||
void valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount ) override;
|
||||
|
||||
void addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator ) override;
|
||||
|
||||
void uniqueValues( size_t timeStepIndex, std::set<int>& values ) override;
|
||||
|
||||
size_t timeStepCount() override;
|
||||
|
||||
private:
|
||||
cvf::Collection<RigStatisticsCalculator> m_nativeStatisticsCalculators;
|
||||
};
|
||||
@@ -0,0 +1,124 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigEclipseNativeStatCalc.h"
|
||||
|
||||
#include "RigCaseCellResultsData.h"
|
||||
#include "RigStatisticsMath.h"
|
||||
#include "RigWeightedMeanCalc.h"
|
||||
|
||||
#include <cmath> // Needed for HUGE_VAL on Linux
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigEclipseNativeStatCalc::RigEclipseNativeStatCalc( RigCaseCellResultsData* cellResultsData,
|
||||
const RigEclipseResultAddress& eclipseResultAddress )
|
||||
: m_resultsData( cellResultsData )
|
||||
, m_eclipseResultAddress( eclipseResultAddress )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseNativeStatCalc::minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max )
|
||||
{
|
||||
MinMaxAccumulator acc( min, max );
|
||||
traverseCells( acc, timeStepIndex );
|
||||
min = acc.min;
|
||||
max = acc.max;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseNativeStatCalc::posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg )
|
||||
{
|
||||
PosNegAccumulator acc( pos, neg );
|
||||
traverseCells( acc, timeStepIndex );
|
||||
pos = acc.pos;
|
||||
neg = acc.neg;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseNativeStatCalc::addDataToHistogramCalculator( size_t timeStepIndex,
|
||||
RigHistogramCalculator& histogramCalculator )
|
||||
{
|
||||
traverseCells( histogramCalculator, timeStepIndex );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseNativeStatCalc::uniqueValues( size_t timeStepIndex, std::set<int>& values )
|
||||
{
|
||||
UniqueValueAccumulator acc;
|
||||
traverseCells( acc, timeStepIndex );
|
||||
values = acc.uniqueValues;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseNativeStatCalc::valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount )
|
||||
{
|
||||
SumCountAccumulator acc( valueSum, sampleCount );
|
||||
traverseCells( acc, timeStepIndex );
|
||||
valueSum = acc.valueSum;
|
||||
sampleCount = acc.sampleCount;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigEclipseNativeStatCalc::timeStepCount()
|
||||
{
|
||||
return m_resultsData->timeStepCount( m_eclipseResultAddress );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseNativeStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex, double& mean )
|
||||
{
|
||||
RigEclipseResultAddress mobPorvAddress( RiaDefines::ResultCatType::STATIC_NATIVE, RiaDefines::mobilePoreVolumeName() );
|
||||
|
||||
// For statistics result cases, the pore volume is not available, as
|
||||
// RigCaseCellResultsData::createPlaceholderResultEntries has not been executed
|
||||
if ( !m_resultsData->ensureKnownResultLoaded( mobPorvAddress ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const std::vector<double>& weights = m_resultsData->cellScalarResults( mobPorvAddress, 0 );
|
||||
const std::vector<double>& values = m_resultsData->cellScalarResults( m_eclipseResultAddress, timeStepIndex );
|
||||
|
||||
const RigActiveCellInfo* actCellInfo = m_resultsData->activeCellInfo();
|
||||
|
||||
RigWeightedMeanCalc::weightedMeanOverCells( &weights,
|
||||
&values,
|
||||
nullptr,
|
||||
false,
|
||||
actCellInfo,
|
||||
m_resultsData->isUsingGlobalActiveIndex( m_eclipseResultAddress ),
|
||||
&mean );
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigStatisticsCalculator.h"
|
||||
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigCaseCellResultsData.h"
|
||||
|
||||
class RigHistogramCalculator;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
|
||||
class RigEclipseNativeStatCalc : public RigStatisticsCalculator
|
||||
{
|
||||
public:
|
||||
RigEclipseNativeStatCalc( RigCaseCellResultsData* cellResultsData, const RigEclipseResultAddress& eclipseResultAddress );
|
||||
|
||||
void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override;
|
||||
void posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg ) override;
|
||||
void valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount ) override;
|
||||
void addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator ) override;
|
||||
void uniqueValues( size_t timeStepIndex, std::set<int>& values ) override;
|
||||
size_t timeStepCount() override;
|
||||
void mobileVolumeWeightedMean( size_t timeStepIndex, double& mean ) override;
|
||||
|
||||
private:
|
||||
RigCaseCellResultsData* m_resultsData;
|
||||
RigEclipseResultAddress m_eclipseResultAddress;
|
||||
|
||||
template <typename StatisticsAccumulator>
|
||||
void traverseCells( StatisticsAccumulator& accumulator, size_t timeStepIndex )
|
||||
{
|
||||
if ( timeStepIndex >= m_resultsData->cellScalarResults( m_eclipseResultAddress ).size() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const std::vector<double>& values = m_resultsData->cellScalarResults( m_eclipseResultAddress, timeStepIndex );
|
||||
|
||||
if ( values.empty() )
|
||||
{
|
||||
// Can happen if values do not exist for the current time step index.
|
||||
return;
|
||||
}
|
||||
|
||||
const RigActiveCellInfo* actCellInfo = m_resultsData->activeCellInfo();
|
||||
size_t cellCount = actCellInfo->reservoirCellCount();
|
||||
|
||||
bool isUsingGlobalActiveIndex = m_resultsData->isUsingGlobalActiveIndex( m_eclipseResultAddress );
|
||||
for ( size_t cIdx = 0; cIdx < cellCount; ++cIdx )
|
||||
{
|
||||
// Filter out inactive cells
|
||||
if ( !actCellInfo->isActive( cIdx ) ) continue;
|
||||
|
||||
size_t cellResultIndex = cIdx;
|
||||
if ( isUsingGlobalActiveIndex )
|
||||
{
|
||||
cellResultIndex = actCellInfo->cellResultIndex( cIdx );
|
||||
}
|
||||
|
||||
if ( cellResultIndex != cvf::UNDEFINED_SIZE_T && cellResultIndex < values.size() )
|
||||
{
|
||||
accumulator.addValue( values[cellResultIndex] );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,129 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2015- Statoil ASA
|
||||
// Copyright (C) 2015- Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigEclipseNativeVisibleCellsStatCalc.h"
|
||||
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigCaseCellResultsData.h"
|
||||
#include "RigStatisticsMath.h"
|
||||
#include "RigWeightedMeanCalc.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigEclipseNativeVisibleCellsStatCalc::RigEclipseNativeVisibleCellsStatCalc( RigCaseCellResultsData* cellResultsData,
|
||||
const RigEclipseResultAddress& scalarResultIndex,
|
||||
const cvf::UByteArray* cellVisibilities )
|
||||
: m_caseData( cellResultsData )
|
||||
, m_resultAddress( scalarResultIndex )
|
||||
, m_cellVisibilities( cellVisibilities )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseNativeVisibleCellsStatCalc::minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max )
|
||||
{
|
||||
MinMaxAccumulator acc( min, max );
|
||||
traverseCells( acc, timeStepIndex );
|
||||
min = acc.min;
|
||||
max = acc.max;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseNativeVisibleCellsStatCalc::posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg )
|
||||
{
|
||||
PosNegAccumulator acc( pos, neg );
|
||||
traverseCells( acc, timeStepIndex );
|
||||
pos = acc.pos;
|
||||
neg = acc.neg;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseNativeVisibleCellsStatCalc::valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount )
|
||||
{
|
||||
SumCountAccumulator acc( valueSum, sampleCount );
|
||||
traverseCells( acc, timeStepIndex );
|
||||
valueSum = acc.valueSum;
|
||||
sampleCount = acc.sampleCount;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseNativeVisibleCellsStatCalc::addDataToHistogramCalculator( size_t timeStepIndex,
|
||||
RigHistogramCalculator& histogramCalculator )
|
||||
{
|
||||
traverseCells( histogramCalculator, timeStepIndex );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseNativeVisibleCellsStatCalc::uniqueValues( size_t timeStepIndex, std::set<int>& values )
|
||||
{
|
||||
UniqueValueAccumulator acc;
|
||||
traverseCells( acc, timeStepIndex );
|
||||
values = acc.uniqueValues;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigEclipseNativeVisibleCellsStatCalc::timeStepCount()
|
||||
{
|
||||
return m_caseData->timeStepCount( m_resultAddress );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseNativeVisibleCellsStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex, double& result )
|
||||
{
|
||||
RigEclipseResultAddress mobPorvAddress( RiaDefines::ResultCatType::STATIC_NATIVE, RiaDefines::mobilePoreVolumeName() );
|
||||
|
||||
// For statistics result cases, the pore volume is not available, as
|
||||
// RigCaseCellResultsData::createPlaceholderResultEntries has not been executed
|
||||
if ( !m_caseData->ensureKnownResultLoaded( mobPorvAddress ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_caseData->ensureKnownResultLoaded( mobPorvAddress );
|
||||
|
||||
const std::vector<double>& weights = m_caseData->cellScalarResults( mobPorvAddress, 0 );
|
||||
const std::vector<double>& values = m_caseData->cellScalarResults( m_resultAddress, timeStepIndex );
|
||||
|
||||
const RigActiveCellInfo* actCellInfo = m_caseData->activeCellInfo();
|
||||
|
||||
RigWeightedMeanCalc::weightedMeanOverCells( &weights,
|
||||
&values,
|
||||
m_cellVisibilities.p(),
|
||||
true,
|
||||
actCellInfo,
|
||||
m_caseData->isUsingGlobalActiveIndex( m_resultAddress ),
|
||||
&result );
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2015- Statoil ASA
|
||||
// Copyright (C) 2015- Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigStatisticsCalculator.h"
|
||||
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigCaseCellResultsData.h"
|
||||
|
||||
#include "cvfArray.h"
|
||||
|
||||
class RigEclipseNativeVisibleCellsStatCalc : public RigStatisticsCalculator
|
||||
{
|
||||
public:
|
||||
RigEclipseNativeVisibleCellsStatCalc( RigCaseCellResultsData* cellResultsData,
|
||||
const RigEclipseResultAddress& scalarResultIndex,
|
||||
const cvf::UByteArray* cellVisibilities );
|
||||
|
||||
void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override;
|
||||
void posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg ) override;
|
||||
void valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount ) override;
|
||||
void addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator ) override;
|
||||
void uniqueValues( size_t timeStepIndex, std::set<int>& values ) override;
|
||||
size_t timeStepCount() override;
|
||||
void mobileVolumeWeightedMean( size_t timeStepIndex, double& result ) override;
|
||||
|
||||
private:
|
||||
RigCaseCellResultsData* m_caseData;
|
||||
RigEclipseResultAddress m_resultAddress;
|
||||
cvf::cref<cvf::UByteArray> m_cellVisibilities;
|
||||
|
||||
template <typename StatisticsAccumulator>
|
||||
void traverseCells( StatisticsAccumulator& accumulator, size_t timeStepIndex )
|
||||
{
|
||||
const std::vector<double>& values = m_caseData->cellScalarResults( m_resultAddress, timeStepIndex );
|
||||
|
||||
if ( values.empty() )
|
||||
{
|
||||
// Can happen if values do not exist for the current time step index.
|
||||
return;
|
||||
}
|
||||
|
||||
const RigActiveCellInfo* actCellInfo = m_caseData->activeCellInfo();
|
||||
size_t cellCount = actCellInfo->reservoirCellCount();
|
||||
|
||||
CVF_TIGHT_ASSERT( cellCount == m_cellVisibilities->size() );
|
||||
|
||||
for ( size_t cIdx = 0; cIdx < cellCount; ++cIdx )
|
||||
{
|
||||
if ( !( *m_cellVisibilities )[cIdx] ) continue;
|
||||
|
||||
size_t cellResultIndex = cIdx;
|
||||
if ( m_caseData->isUsingGlobalActiveIndex( m_resultAddress ) )
|
||||
{
|
||||
cellResultIndex = actCellInfo->cellResultIndex( cIdx );
|
||||
}
|
||||
|
||||
if ( cellResultIndex != cvf::UNDEFINED_SIZE_T && cellResultIndex < values.size() )
|
||||
{
|
||||
accumulator.addValue( values[cellResultIndex] );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,115 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2019- 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 "RiaDefines.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
class RigEclipseResultAddress
|
||||
{
|
||||
public:
|
||||
RigEclipseResultAddress()
|
||||
: m_resultCatType( RiaDefines::ResultCatType::UNDEFINED )
|
||||
, m_timeLapseBaseFrameIdx( NO_TIME_LAPSE )
|
||||
, m_differenceCaseId( NO_CASE_DIFF )
|
||||
{
|
||||
}
|
||||
|
||||
explicit RigEclipseResultAddress( const QString& resultName )
|
||||
: m_resultCatType( RiaDefines::ResultCatType::UNDEFINED )
|
||||
, m_resultName( resultName )
|
||||
, m_timeLapseBaseFrameIdx( NO_TIME_LAPSE )
|
||||
, m_differenceCaseId( NO_CASE_DIFF )
|
||||
{
|
||||
}
|
||||
|
||||
explicit RigEclipseResultAddress( RiaDefines::ResultCatType type,
|
||||
const QString& resultName,
|
||||
int timeLapseBaseTimeStep = NO_TIME_LAPSE,
|
||||
int differenceCaseId = NO_CASE_DIFF )
|
||||
: m_resultCatType( type )
|
||||
, m_resultName( resultName )
|
||||
, m_timeLapseBaseFrameIdx( timeLapseBaseTimeStep )
|
||||
, m_differenceCaseId( differenceCaseId )
|
||||
{
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
if ( m_resultName.isEmpty() || m_resultName == RiaDefines::undefinedResultName() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr int allTimeLapsesValue() { return ALL_TIME_LAPSES; }
|
||||
static constexpr int noTimeLapseValue() { return NO_TIME_LAPSE; }
|
||||
static constexpr int noCaseDiffValue() { return NO_CASE_DIFF; }
|
||||
|
||||
bool isTimeLapse() const { return m_timeLapseBaseFrameIdx > NO_TIME_LAPSE; }
|
||||
bool representsAllTimeLapses() const { return m_timeLapseBaseFrameIdx == ALL_TIME_LAPSES; }
|
||||
|
||||
bool hasDifferenceCase() const { return m_differenceCaseId > NO_CASE_DIFF; }
|
||||
|
||||
bool operator<( const RigEclipseResultAddress& other ) const
|
||||
{
|
||||
if ( m_differenceCaseId != other.m_differenceCaseId )
|
||||
{
|
||||
return ( m_differenceCaseId < other.m_differenceCaseId );
|
||||
}
|
||||
|
||||
if ( m_timeLapseBaseFrameIdx != other.m_timeLapseBaseFrameIdx )
|
||||
{
|
||||
return ( m_timeLapseBaseFrameIdx < other.m_timeLapseBaseFrameIdx );
|
||||
}
|
||||
|
||||
if ( m_resultCatType != other.m_resultCatType )
|
||||
{
|
||||
return ( m_resultCatType < other.m_resultCatType );
|
||||
}
|
||||
|
||||
return ( m_resultName < other.m_resultName );
|
||||
}
|
||||
|
||||
bool operator==( const RigEclipseResultAddress& other ) const
|
||||
{
|
||||
if ( m_resultCatType != other.m_resultCatType || m_resultName != other.m_resultName ||
|
||||
m_timeLapseBaseFrameIdx != other.m_timeLapseBaseFrameIdx || m_differenceCaseId != other.m_differenceCaseId )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RiaDefines::ResultCatType m_resultCatType;
|
||||
QString m_resultName;
|
||||
|
||||
int m_timeLapseBaseFrameIdx;
|
||||
int m_differenceCaseId;
|
||||
|
||||
private:
|
||||
static const int ALL_TIME_LAPSES = -2;
|
||||
static const int NO_TIME_LAPSE = -1;
|
||||
static const int NO_CASE_DIFF = -1;
|
||||
};
|
||||
@@ -0,0 +1,199 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigEclipseResultInfo.h"
|
||||
|
||||
#include "cvfAssert.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigEclipseTimeStepInfo::RigEclipseTimeStepInfo( const QDateTime& date, int reportNumber, double daysSinceSimulationStart )
|
||||
: m_date( date )
|
||||
, m_reportNumber( reportNumber )
|
||||
, m_daysSinceSimulationStart( daysSinceSimulationStart )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RigEclipseTimeStepInfo>
|
||||
RigEclipseTimeStepInfo::createTimeStepInfos( std::vector<QDateTime> dates,
|
||||
std::vector<int> reportNumbers,
|
||||
std::vector<double> daysSinceSimulationStarts )
|
||||
{
|
||||
CVF_ASSERT( dates.size() == reportNumbers.size() );
|
||||
CVF_ASSERT( dates.size() == daysSinceSimulationStarts.size() );
|
||||
|
||||
std::vector<RigEclipseTimeStepInfo> timeStepInfos;
|
||||
|
||||
for ( size_t i = 0; i < dates.size(); i++ )
|
||||
{
|
||||
timeStepInfos.push_back( RigEclipseTimeStepInfo( dates[i], reportNumbers[i], daysSinceSimulationStarts[i] ) );
|
||||
}
|
||||
|
||||
return timeStepInfos;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigEclipseResultInfo::RigEclipseResultInfo( const RigEclipseResultAddress& resultAddress,
|
||||
bool needsToBeStored,
|
||||
bool mustBeCalculated,
|
||||
size_t gridScalarResultIndex )
|
||||
: m_resultAddress( resultAddress )
|
||||
, m_needsToBeStored( needsToBeStored )
|
||||
, m_mustBeCalculated( mustBeCalculated )
|
||||
, m_gridScalarResultIndex( gridScalarResultIndex )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RiaDefines::ResultCatType RigEclipseResultInfo::resultType() const
|
||||
{
|
||||
return m_resultAddress.m_resultCatType;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseResultInfo::setResultType( RiaDefines::ResultCatType newType )
|
||||
{
|
||||
m_resultAddress.m_resultCatType = newType;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const QString& RigEclipseResultInfo::resultName() const
|
||||
{
|
||||
return m_resultAddress.m_resultName;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseResultInfo::setResultName( const QString& name )
|
||||
{
|
||||
m_resultAddress.m_resultName = name;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigEclipseResultInfo::needsToBeStored() const
|
||||
{
|
||||
return m_needsToBeStored;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigEclipseResultInfo::mustBeCalculated() const
|
||||
{
|
||||
return m_mustBeCalculated;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseResultInfo::setMustBeCalculated( bool mustCalculate )
|
||||
{
|
||||
m_mustBeCalculated = mustCalculate;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigEclipseResultInfo::gridScalarResultIndex() const
|
||||
{
|
||||
return m_gridScalarResultIndex;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<RigEclipseTimeStepInfo>& RigEclipseResultInfo::timeStepInfos() const
|
||||
{
|
||||
return m_timeStepInfos;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseResultInfo::setTimeStepInfos( const std::vector<RigEclipseTimeStepInfo>& timeSteps )
|
||||
{
|
||||
m_timeStepInfos = timeSteps;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<QDateTime> RigEclipseResultInfo::dates() const
|
||||
{
|
||||
std::vector<QDateTime> values;
|
||||
|
||||
for ( auto v : m_timeStepInfos )
|
||||
{
|
||||
values.push_back( v.m_date );
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<double> RigEclipseResultInfo::daysSinceSimulationStarts() const
|
||||
{
|
||||
std::vector<double> values;
|
||||
|
||||
for ( auto v : m_timeStepInfos )
|
||||
{
|
||||
values.push_back( v.m_daysSinceSimulationStart );
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<int> RigEclipseResultInfo::reportNumbers() const
|
||||
{
|
||||
std::vector<int> values;
|
||||
|
||||
for ( auto v : m_timeStepInfos )
|
||||
{
|
||||
values.push_back( v.m_reportNumber );
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Ordering operator for set storage. Just the type and name are used to find unique addresses.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigEclipseResultInfo::operator<( const RigEclipseResultInfo& rhs ) const
|
||||
{
|
||||
return m_resultAddress < rhs.m_resultAddress;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RigEclipseResultAddress.h"
|
||||
|
||||
#include "RiaDefines.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QString>
|
||||
|
||||
#include <vector>
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigEclipseTimeStepInfo
|
||||
{
|
||||
public:
|
||||
RigEclipseTimeStepInfo( const QDateTime& date, int reportNumber, double daysSinceSimulationStart );
|
||||
|
||||
static std::vector<RigEclipseTimeStepInfo> createTimeStepInfos( std::vector<QDateTime> dates,
|
||||
std::vector<int> reportNumbers,
|
||||
std::vector<double> daysSinceSimulationStarts );
|
||||
|
||||
public:
|
||||
QDateTime m_date;
|
||||
int m_reportNumber;
|
||||
double m_daysSinceSimulationStart;
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigEclipseResultInfo
|
||||
{
|
||||
public:
|
||||
RigEclipseResultInfo( const RigEclipseResultAddress& resultAddress,
|
||||
bool needsToBeStored = false,
|
||||
bool mustBeCalculated = false,
|
||||
size_t gridScalarResultIndex = 0u );
|
||||
|
||||
RiaDefines::ResultCatType resultType() const;
|
||||
const QString& resultName() const;
|
||||
bool needsToBeStored() const;
|
||||
|
||||
std::vector<QDateTime> dates() const;
|
||||
std::vector<double> daysSinceSimulationStarts() const;
|
||||
std::vector<int> reportNumbers() const;
|
||||
|
||||
bool operator<( const RigEclipseResultInfo& rhs ) const;
|
||||
|
||||
const RigEclipseResultAddress& eclipseResultAddress() const { return m_resultAddress; }
|
||||
|
||||
private:
|
||||
friend class RigCaseCellResultsData;
|
||||
void setResultType( RiaDefines::ResultCatType newType );
|
||||
void setResultName( const QString& name );
|
||||
bool mustBeCalculated() const;
|
||||
void setMustBeCalculated( bool mustCalculate );
|
||||
size_t gridScalarResultIndex() const;
|
||||
|
||||
const std::vector<RigEclipseTimeStepInfo>& timeStepInfos() const;
|
||||
void setTimeStepInfos( const std::vector<RigEclipseTimeStepInfo>& timeSteps );
|
||||
|
||||
RigEclipseResultAddress m_resultAddress;
|
||||
size_t m_gridScalarResultIndex;
|
||||
std::vector<RigEclipseTimeStepInfo> m_timeStepInfos;
|
||||
|
||||
bool m_needsToBeStored;
|
||||
bool m_mustBeCalculated;
|
||||
};
|
||||
@@ -0,0 +1,210 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigEclipseWellLogExtractor.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "RigEclipseCaseData.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigResultAccessor.h"
|
||||
#include "RigWellLogExtractionTools.h"
|
||||
#include "RigWellPath.h"
|
||||
#include "RigWellPathIntersectionTools.h"
|
||||
|
||||
#include "cvfBoundingBox.h"
|
||||
#include "cvfGeometryTools.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
|
||||
RigEclipseWellLogExtractor::RigEclipseWellLogExtractor( gsl::not_null<const RigEclipseCaseData*> aCase,
|
||||
gsl::not_null<const RigWellPath*> wellpath,
|
||||
const std::string& wellCaseErrorMsgName )
|
||||
: RigWellLogExtractor( wellpath, wellCaseErrorMsgName )
|
||||
, m_caseData( aCase )
|
||||
{
|
||||
calculateIntersection();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseWellLogExtractor::calculateIntersection()
|
||||
{
|
||||
std::map<RigMDCellIdxEnterLeaveKey, HexIntersectionInfo> uniqueIntersections;
|
||||
|
||||
bool isCellFaceNormalsOut = m_caseData->mainGrid()->isFaceNormalsOutwards();
|
||||
|
||||
if ( m_wellPathGeometry->wellPathPoints().empty() ) return;
|
||||
|
||||
for ( size_t wpp = 0; wpp < m_wellPathGeometry->wellPathPoints().size() - 1; ++wpp )
|
||||
{
|
||||
std::vector<HexIntersectionInfo> intersections;
|
||||
cvf::Vec3d p1 = m_wellPathGeometry->wellPathPoints()[wpp];
|
||||
cvf::Vec3d p2 = m_wellPathGeometry->wellPathPoints()[wpp + 1];
|
||||
|
||||
cvf::BoundingBox bb;
|
||||
|
||||
bb.add( p1 );
|
||||
bb.add( p2 );
|
||||
|
||||
std::vector<size_t> closeCellIndices = findCloseCellIndices( bb );
|
||||
|
||||
cvf::Vec3d hexCorners[8];
|
||||
for ( const auto& globalCellIndex : closeCellIndices )
|
||||
{
|
||||
const RigCell& cell = m_caseData->mainGrid()->globalCellArray()[globalCellIndex];
|
||||
|
||||
if ( cell.isInvalid() || cell.subGrid() != nullptr ) continue;
|
||||
|
||||
m_caseData->mainGrid()->cellCornerVertices( globalCellIndex, hexCorners );
|
||||
|
||||
RigHexIntersectionTools::lineHexCellIntersection( p1, p2, hexCorners, globalCellIndex, &intersections );
|
||||
}
|
||||
|
||||
if ( !isCellFaceNormalsOut )
|
||||
{
|
||||
for ( auto& intersection : intersections )
|
||||
{
|
||||
intersection.m_isIntersectionEntering = !intersection.m_isIntersectionEntering;
|
||||
}
|
||||
}
|
||||
|
||||
// Now, with all the intersections of this piece of line, we need to
|
||||
// sort them in order, and set the measured depth and corresponding cell index
|
||||
|
||||
// Inserting the intersections in this map will remove identical intersections
|
||||
// and sort them according to MD, CellIdx, Leave/enter
|
||||
|
||||
double md1 = m_wellPathGeometry->measuredDepths()[wpp];
|
||||
double md2 = m_wellPathGeometry->measuredDepths()[wpp + 1];
|
||||
|
||||
insertIntersectionsInMap( intersections, p1, md1, p2, md2, &uniqueIntersections );
|
||||
}
|
||||
|
||||
if ( uniqueIntersections.empty() && m_wellPathGeometry->wellPathPoints().size() > 1 )
|
||||
{
|
||||
// When entering this function, all well path points are either completely outside the grid
|
||||
// or all well path points are inside one cell
|
||||
|
||||
cvf::Vec3d firstPoint = m_wellPathGeometry->wellPathPoints().front();
|
||||
cvf::Vec3d lastPoint = m_wellPathGeometry->wellPathPoints().back();
|
||||
|
||||
{
|
||||
cvf::BoundingBox bb;
|
||||
bb.add( firstPoint );
|
||||
|
||||
std::vector<size_t> closeCellIndices = findCloseCellIndices( bb );
|
||||
|
||||
cvf::Vec3d hexCorners[8];
|
||||
for ( const auto& globalCellIndex : closeCellIndices )
|
||||
{
|
||||
const RigCell& cell = m_caseData->mainGrid()->globalCellArray()[globalCellIndex];
|
||||
|
||||
if ( cell.isInvalid() ) continue;
|
||||
|
||||
m_caseData->mainGrid()->cellCornerVertices( globalCellIndex, hexCorners );
|
||||
|
||||
if ( RigHexIntersectionTools::isPointInCell( firstPoint, hexCorners ) )
|
||||
{
|
||||
if ( RigHexIntersectionTools::isPointInCell( lastPoint, hexCorners ) )
|
||||
{
|
||||
{
|
||||
// Mark the first well path point as entering the cell
|
||||
|
||||
bool isEntering = true;
|
||||
HexIntersectionInfo info( firstPoint,
|
||||
isEntering,
|
||||
cvf::StructGridInterface::NO_FACE,
|
||||
globalCellIndex );
|
||||
RigMDCellIdxEnterLeaveKey enterLeaveKey( m_wellPathGeometry->measuredDepths().front(),
|
||||
globalCellIndex,
|
||||
isEntering );
|
||||
|
||||
uniqueIntersections.insert( std::make_pair( enterLeaveKey, info ) );
|
||||
}
|
||||
|
||||
{
|
||||
// Mark the last well path point as leaving cell
|
||||
|
||||
bool isEntering = false;
|
||||
HexIntersectionInfo info( lastPoint, isEntering, cvf::StructGridInterface::NO_FACE, globalCellIndex );
|
||||
RigMDCellIdxEnterLeaveKey enterLeaveKey( m_wellPathGeometry->measuredDepths().back(),
|
||||
globalCellIndex,
|
||||
isEntering );
|
||||
|
||||
uniqueIntersections.insert( std::make_pair( enterLeaveKey, info ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QString txt =
|
||||
"Detected two points assumed to be in the same cell, but they are in two different cells";
|
||||
RiaLogging::debug( txt );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->populateReturnArrays( uniqueIntersections );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigEclipseWellLogExtractor::curveData( const RigResultAccessor* resultAccessor, std::vector<double>* values )
|
||||
{
|
||||
CVF_TIGHT_ASSERT( values );
|
||||
values->resize( m_intersections.size() );
|
||||
|
||||
for ( size_t cpIdx = 0; cpIdx < m_intersections.size(); ++cpIdx )
|
||||
{
|
||||
size_t cellIdx = m_intersectedCellsGlobIdx[cpIdx];
|
||||
cvf::StructGridInterface::FaceType cellFace = m_intersectedCellFaces[cpIdx];
|
||||
( *values )[cpIdx] = resultAccessor->cellFaceScalarGlobIdx( cellIdx, cellFace );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<size_t> RigEclipseWellLogExtractor::findCloseCellIndices( const cvf::BoundingBox& bb )
|
||||
{
|
||||
std::vector<size_t> closeCells;
|
||||
m_caseData->mainGrid()->findIntersectingCells( bb, &closeCells );
|
||||
return closeCells;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Vec3d RigEclipseWellLogExtractor::calculateLengthInCell( size_t cellIndex,
|
||||
const cvf::Vec3d& startPoint,
|
||||
const cvf::Vec3d& endPoint ) const
|
||||
{
|
||||
std::array<cvf::Vec3d, 8> hexCorners;
|
||||
m_caseData->mainGrid()->cellCornerVertices( cellIndex, hexCorners.data() );
|
||||
|
||||
return RigWellPathIntersectionTools::calculateLengthInCell( hexCorners, startPoint, endPoint );
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigWellLogExtractor.h"
|
||||
|
||||
class RigEclipseCaseData;
|
||||
class RigWellPath;
|
||||
class RigResultAccessor;
|
||||
|
||||
namespace cvf
|
||||
{
|
||||
class BoundingBox;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigEclipseWellLogExtractor : public RigWellLogExtractor
|
||||
{
|
||||
public:
|
||||
RigEclipseWellLogExtractor( gsl::not_null<const RigEclipseCaseData*> aCase,
|
||||
gsl::not_null<const RigWellPath*> wellpath,
|
||||
const std::string& wellCaseErrorMsgName );
|
||||
|
||||
void curveData( const RigResultAccessor* resultAccessor, std::vector<double>* values );
|
||||
const RigEclipseCaseData* caseData() { return m_caseData.p(); }
|
||||
|
||||
private:
|
||||
void calculateIntersection();
|
||||
std::vector<size_t> findCloseCellIndices( const cvf::BoundingBox& bb );
|
||||
cvf::Vec3d
|
||||
calculateLengthInCell( size_t cellIndex, const cvf::Vec3d& startPoint, const cvf::Vec3d& endPoint ) const override;
|
||||
|
||||
cvf::cref<RigEclipseCaseData> m_caseData;
|
||||
};
|
||||
@@ -0,0 +1,161 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2020 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 "RigElasticProperties.h"
|
||||
|
||||
#include "RiaInterpolationTools.h"
|
||||
|
||||
#include "cafAssert.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigElasticProperties::RigElasticProperties( const QString& fieldName, const QString& formationName, const QString& faciesName )
|
||||
: m_fieldName( fieldName )
|
||||
, m_formationName( formationName )
|
||||
, m_faciesName( faciesName )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const QString& RigElasticProperties::fieldName() const
|
||||
{
|
||||
return m_fieldName;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const QString& RigElasticProperties::formationName() const
|
||||
{
|
||||
return m_formationName;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const QString& RigElasticProperties::faciesName() const
|
||||
{
|
||||
return m_faciesName;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<double>& RigElasticProperties::porosity() const
|
||||
{
|
||||
return m_porosity;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigElasticProperties::porosityMin() const
|
||||
{
|
||||
if ( m_porosity.empty() ) return 0.0;
|
||||
return m_porosity[0];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigElasticProperties::porosityMax() const
|
||||
{
|
||||
if ( m_porosity.empty() ) return 0.0;
|
||||
return m_porosity[m_porosity.size() - 1];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigElasticProperties::appendValues( double porosity,
|
||||
double youngsModulus,
|
||||
double poissonsRatio,
|
||||
double K_Ic,
|
||||
double proppantEmbedment,
|
||||
double biotCoefficient,
|
||||
double k0,
|
||||
double fluidLossCoefficient,
|
||||
double spurtLoss,
|
||||
double immobileFluidSaturation )
|
||||
{
|
||||
m_porosity.push_back( porosity );
|
||||
m_youngsModulus.push_back( youngsModulus );
|
||||
m_poissonsRatio.push_back( poissonsRatio );
|
||||
m_K_Ic.push_back( K_Ic );
|
||||
m_proppantEmbedment.push_back( proppantEmbedment );
|
||||
m_biotCoefficient.push_back( biotCoefficient );
|
||||
m_k0.push_back( k0 );
|
||||
m_fluidLossCoefficient.push_back( fluidLossCoefficient );
|
||||
m_spurtLoss.push_back( spurtLoss );
|
||||
m_immobileFluidSaturation.push_back( immobileFluidSaturation );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigElasticProperties::numValues() const
|
||||
{
|
||||
return m_porosity.size();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigElasticProperties::getValue( RiaDefines::CurveProperty property, size_t index, double scale ) const
|
||||
{
|
||||
CAF_ASSERT( index < numValues() );
|
||||
const std::vector<double>& unscaledValues = getVector( property );
|
||||
return unscaledValues[index] * scale;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<double>& RigElasticProperties::getVector( RiaDefines::CurveProperty property ) const
|
||||
{
|
||||
if ( property == RiaDefines::CurveProperty::YOUNGS_MODULUS ) return m_youngsModulus;
|
||||
if ( property == RiaDefines::CurveProperty::POISSONS_RATIO ) return m_poissonsRatio;
|
||||
if ( property == RiaDefines::CurveProperty::K_IC ) return m_K_Ic;
|
||||
if ( property == RiaDefines::CurveProperty::PROPPANT_EMBEDMENT ) return m_proppantEmbedment;
|
||||
if ( property == RiaDefines::CurveProperty::BIOT_COEFFICIENT ) return m_biotCoefficient;
|
||||
if ( property == RiaDefines::CurveProperty::K0 ) return m_k0;
|
||||
if ( property == RiaDefines::CurveProperty::FLUID_LOSS_COEFFICIENT ) return m_fluidLossCoefficient;
|
||||
if ( property == RiaDefines::CurveProperty::SPURT_LOSS ) return m_spurtLoss;
|
||||
|
||||
// Default: if we get this far only one option left
|
||||
CAF_ASSERT( property == RiaDefines::CurveProperty::IMMOBILE_FLUID_SATURATION );
|
||||
return m_immobileFluidSaturation;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigElasticProperties::getValueForPorosity( RiaDefines::CurveProperty property, double porosity, double scale ) const
|
||||
{
|
||||
const std::vector<double>& unscaledValues = getVector( property );
|
||||
std::vector<double> scaledValues;
|
||||
for ( double unscaled : unscaledValues )
|
||||
{
|
||||
scaledValues.push_back( unscaled * scale );
|
||||
}
|
||||
|
||||
return RiaInterpolationTools::linear( m_porosity, scaledValues, porosity );
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2020 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 "RiaStimPlanModelDefines.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <vector>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class RigElasticProperties
|
||||
{
|
||||
public:
|
||||
RigElasticProperties( const QString& fieldName, const QString& formationName, const QString& faciesName );
|
||||
const QString& fieldName() const;
|
||||
const QString& formationName() const;
|
||||
const QString& faciesName() const;
|
||||
|
||||
void appendValues( double porosity,
|
||||
double youngsModulus,
|
||||
double poissonsRatio,
|
||||
double m_K_Ic,
|
||||
double proppantEmbedment,
|
||||
double biotCoefficient,
|
||||
double k0,
|
||||
double fluidLossCoefficient,
|
||||
double spurtLoss,
|
||||
double immobileFluidSaturation );
|
||||
|
||||
size_t numValues() const;
|
||||
double getValue( RiaDefines::CurveProperty property, size_t index, double scale = 1.0 ) const;
|
||||
double getValueForPorosity( RiaDefines::CurveProperty property, double porosity, double scale = 1.0 ) const;
|
||||
|
||||
const std::vector<double>& porosity() const;
|
||||
double porosityMin() const;
|
||||
double porosityMax() const;
|
||||
|
||||
private:
|
||||
const std::vector<double>& getVector( RiaDefines::CurveProperty property ) const;
|
||||
|
||||
QString m_fieldName;
|
||||
QString m_formationName;
|
||||
QString m_faciesName;
|
||||
|
||||
std::vector<double> m_porosity;
|
||||
std::vector<double> m_youngsModulus;
|
||||
std::vector<double> m_poissonsRatio;
|
||||
std::vector<double> m_K_Ic;
|
||||
std::vector<double> m_proppantEmbedment;
|
||||
std::vector<double> m_biotCoefficient;
|
||||
std::vector<double> m_k0;
|
||||
std::vector<double> m_fluidLossCoefficient;
|
||||
std::vector<double> m_spurtLoss;
|
||||
std::vector<double> m_immobileFluidSaturation;
|
||||
};
|
||||
@@ -0,0 +1,184 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2019- 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 "RigEquil.h"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigEquil::RigEquil( double datumDepth,
|
||||
double datumDepthPressure,
|
||||
double waterOilContactDepth,
|
||||
double waterOilContactCapillaryPressure,
|
||||
double gasOilContactDepth,
|
||||
double gasOilContactCapillaryPressure,
|
||||
bool liveOilInitConstantRs,
|
||||
bool wetGasInitConstantRv,
|
||||
int initializationTargetAccuracy )
|
||||
: datum_depth( datumDepth )
|
||||
, datum_depth_ps( datumDepthPressure )
|
||||
, water_oil_contact_depth( waterOilContactDepth )
|
||||
, water_oil_contact_capillary_pressure( waterOilContactCapillaryPressure )
|
||||
, gas_oil_contact_depth( gasOilContactDepth )
|
||||
, gas_oil_contact_capillary_pressure( gasOilContactCapillaryPressure )
|
||||
, live_oil_init_proc( liveOilInitConstantRs )
|
||||
, wet_gas_init_proc( wetGasInitConstantRv )
|
||||
, init_target_accuracy( initializationTargetAccuracy )
|
||||
{
|
||||
}
|
||||
|
||||
double RigEquil::datumDepth() const
|
||||
{
|
||||
return this->datum_depth;
|
||||
}
|
||||
|
||||
double RigEquil::datumDepthPressure() const
|
||||
{
|
||||
return this->datum_depth_ps;
|
||||
}
|
||||
|
||||
double RigEquil::waterOilContactDepth() const
|
||||
{
|
||||
return this->water_oil_contact_depth;
|
||||
}
|
||||
|
||||
double RigEquil::waterOilContactCapillaryPressure() const
|
||||
{
|
||||
return this->water_oil_contact_capillary_pressure;
|
||||
}
|
||||
|
||||
double RigEquil::gasOilContactDepth() const
|
||||
{
|
||||
return this->gas_oil_contact_depth;
|
||||
}
|
||||
|
||||
double RigEquil::gasOilContactCapillaryPressure() const
|
||||
{
|
||||
return this->gas_oil_contact_capillary_pressure;
|
||||
}
|
||||
|
||||
bool RigEquil::liveOilInitConstantRs() const
|
||||
{
|
||||
return this->live_oil_init_proc;
|
||||
}
|
||||
|
||||
bool RigEquil::wetGasInitConstantRv() const
|
||||
{
|
||||
return this->wet_gas_init_proc;
|
||||
}
|
||||
|
||||
int RigEquil::initializationTargetAccuracy() const
|
||||
{
|
||||
return this->init_target_accuracy;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigEquil RigEquil::defaultObject()
|
||||
{
|
||||
double datumDepth = 0.0;
|
||||
double datuDepthPressure = 0.0;
|
||||
double waterOilContactDepth = 0.0;
|
||||
double waterOilContactCapillaryPressure = 0.0;
|
||||
double gasOilContactDepth = 0.0;
|
||||
double gasOilContactCapillaryPressure = 0.0;
|
||||
int liveOilInitConstantRs = -1;
|
||||
int wetGasInitConstantRv = -1;
|
||||
int initializationTargetAccuracy = -5;
|
||||
|
||||
return RigEquil( datumDepth,
|
||||
datuDepthPressure,
|
||||
waterOilContactDepth,
|
||||
waterOilContactCapillaryPressure,
|
||||
gasOilContactDepth,
|
||||
gasOilContactCapillaryPressure,
|
||||
liveOilInitConstantRs,
|
||||
wetGasInitConstantRv,
|
||||
initializationTargetAccuracy );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigEquil RigEquil::parseString( const QString& keywordData )
|
||||
{
|
||||
double datumDepth = 0.0;
|
||||
double datuDepthPressure = 0.0;
|
||||
double waterOilContactDepth = 0.0;
|
||||
double waterOilContactCapillaryPressure = 0.0;
|
||||
double gasOilContactDepth = 0.0;
|
||||
double gasOilContactCapillaryPressure = 0.0;
|
||||
bool liveOilInitConstantRs = false;
|
||||
bool wetGasInitConstantRv = false;
|
||||
int initializationTargetAccuracy = -5;
|
||||
|
||||
QString line( keywordData );
|
||||
line.replace( "\t", " " );
|
||||
|
||||
QStringList items = line.split( " ", QString::SkipEmptyParts );
|
||||
if ( items.size() > 0 )
|
||||
{
|
||||
datumDepth = items.at( 0 ).toDouble();
|
||||
}
|
||||
|
||||
if ( items.size() > 1 )
|
||||
{
|
||||
datuDepthPressure = items.at( 1 ).toDouble();
|
||||
}
|
||||
if ( items.size() > 2 )
|
||||
{
|
||||
waterOilContactDepth = items.at( 2 ).toDouble();
|
||||
}
|
||||
if ( items.size() > 3 )
|
||||
{
|
||||
waterOilContactCapillaryPressure = items.at( 3 ).toDouble();
|
||||
}
|
||||
if ( items.size() > 4 )
|
||||
{
|
||||
gasOilContactDepth = items.at( 4 ).toDouble();
|
||||
}
|
||||
if ( items.size() > 5 )
|
||||
{
|
||||
gasOilContactCapillaryPressure = items.at( 5 ).toDouble();
|
||||
}
|
||||
if ( items.size() > 6 )
|
||||
{
|
||||
liveOilInitConstantRs = items.at( 6 ).toInt() > 0 ? true : false;
|
||||
}
|
||||
if ( items.size() > 7 )
|
||||
{
|
||||
wetGasInitConstantRv = items.at( 7 ).toInt() > 0 ? true : false;
|
||||
}
|
||||
if ( items.size() > 8 )
|
||||
{
|
||||
initializationTargetAccuracy = items.at( 8 ).toInt();
|
||||
}
|
||||
|
||||
return RigEquil( datumDepth,
|
||||
datuDepthPressure,
|
||||
waterOilContactDepth,
|
||||
waterOilContactCapillaryPressure,
|
||||
gasOilContactDepth,
|
||||
gasOilContactCapillaryPressure,
|
||||
liveOilInitConstantRs,
|
||||
wetGasInitConstantRv,
|
||||
initializationTargetAccuracy );
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2019- 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 <QString>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// Inspired by /opm-common/src/opm/parser/eclipse/EclipseState/InitConfig/Equil.cpp
|
||||
//
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class RigEquil
|
||||
{
|
||||
public:
|
||||
explicit RigEquil( double datumDepth,
|
||||
double datuDepthPressure,
|
||||
double waterOilContactDepth,
|
||||
double waterOilContactCapillaryPressure,
|
||||
double gasOilContactDepth,
|
||||
double gasOilContactCapillaryPressure,
|
||||
bool liveOilInitConstantRs,
|
||||
bool wetGasInitConstantRv,
|
||||
int initializationTargetAccuracy );
|
||||
|
||||
double datumDepth() const;
|
||||
double datumDepthPressure() const;
|
||||
double waterOilContactDepth() const;
|
||||
double waterOilContactCapillaryPressure() const;
|
||||
double gasOilContactDepth() const;
|
||||
double gasOilContactCapillaryPressure() const;
|
||||
|
||||
bool liveOilInitConstantRs() const;
|
||||
bool wetGasInitConstantRv() const;
|
||||
int initializationTargetAccuracy() const;
|
||||
|
||||
static RigEquil defaultObject();
|
||||
static RigEquil parseString( const QString& keywordData );
|
||||
|
||||
private:
|
||||
double datum_depth;
|
||||
double datum_depth_ps;
|
||||
double water_oil_contact_depth;
|
||||
double water_oil_contact_capillary_pressure;
|
||||
double gas_oil_contact_depth;
|
||||
double gas_oil_contact_capillary_pressure;
|
||||
|
||||
bool live_oil_init_proc;
|
||||
bool wet_gas_init_proc;
|
||||
int init_target_accuracy;
|
||||
};
|
||||
@@ -0,0 +1,205 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigFault.h"
|
||||
|
||||
#include "RigMainGrid.h"
|
||||
|
||||
cvf::ref<RigFaultsPrCellAccumulator> RigFault::m_faultsPrCellAcc;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigFault::RigFault()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFault::addCellRangeForFace( cvf::StructGridInterface::FaceType face, const cvf::CellRange& cellRange )
|
||||
{
|
||||
size_t faceIndex = static_cast<size_t>( face );
|
||||
CVF_ASSERT( faceIndex < 6 );
|
||||
|
||||
m_cellRangesForFaces[faceIndex].push_back( cellRange );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFault::setName( const QString& name )
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RigFault::name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RigFault::FaultFace>& RigFault::faultFaces()
|
||||
{
|
||||
return m_faultFaces;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<RigFault::FaultFace>& RigFault::faultFaces() const
|
||||
{
|
||||
return m_faultFaces;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<size_t>& RigFault::connectionIndices()
|
||||
{
|
||||
return m_connectionIndices;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<size_t>& RigFault::connectionIndices() const
|
||||
{
|
||||
return m_connectionIndices;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Order FaultCellAndFace by i, j, face then k.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigFault::ordering( CellAndFace first, CellAndFace second )
|
||||
{
|
||||
size_t i1, i2, j1, j2, k1, k2;
|
||||
cvf::StructGridInterface::FaceType f1, f2;
|
||||
std::tie( i1, j1, k1, f1 ) = first;
|
||||
std::tie( i2, j2, k2, f2 ) = second;
|
||||
if ( i1 == i2 )
|
||||
{
|
||||
if ( j1 == j2 )
|
||||
{
|
||||
if ( f1 == f2 )
|
||||
{
|
||||
return k1 < k2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return f1 < f2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return j1 < j2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return i1 < i2;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFault::computeFaultFacesFromCellRanges( const RigMainGrid* mainGrid )
|
||||
{
|
||||
if ( !mainGrid ) return;
|
||||
|
||||
m_faultFaces.clear();
|
||||
|
||||
for ( size_t faceType = 0; faceType < 6; faceType++ )
|
||||
{
|
||||
cvf::StructGridInterface::FaceType faceEnum = cvf::StructGridInterface::FaceType( faceType );
|
||||
|
||||
const std::vector<cvf::CellRange>& cellRanges = m_cellRangesForFaces[faceType];
|
||||
|
||||
for ( const cvf::CellRange& cellRange : cellRanges )
|
||||
{
|
||||
cvf::Vec3st min, max;
|
||||
cellRange.range( min, max );
|
||||
|
||||
for ( size_t i = min.x(); i <= max.x(); i++ )
|
||||
{
|
||||
if ( i >= mainGrid->cellCountI() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for ( size_t j = min.y(); j <= max.y(); j++ )
|
||||
{
|
||||
if ( j >= mainGrid->cellCountJ() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for ( size_t k = min.z(); k <= max.z(); k++ )
|
||||
{
|
||||
if ( k >= mainGrid->cellCountK() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do not need to compute global grid cell index as for a maingrid localIndex == globalIndex
|
||||
// size_t reservoirCellIndex = grid->reservoirCellIndex(gridLocalCellIndex);
|
||||
|
||||
size_t ni, nj, nk;
|
||||
mainGrid->neighborIJKAtCellFace( i, j, k, faceEnum, &ni, &nj, &nk );
|
||||
if ( ni < mainGrid->cellCountI() && nj < mainGrid->cellCountJ() && nk < mainGrid->cellCountK() )
|
||||
{
|
||||
size_t gridLocalCellIndex = mainGrid->cellIndexFromIJK( i, j, k );
|
||||
size_t oppositeCellIndex = mainGrid->cellIndexFromIJK( ni, nj, nk );
|
||||
|
||||
m_faultFaces.push_back( FaultFace( gridLocalCellIndex, faceEnum, oppositeCellIndex ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// cvf::Trace::show("Warning: Undefined Fault neighbor detected.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFault::accumulateFaultsPrCell( RigFaultsPrCellAccumulator* faultsPrCellAcc, int faultIdx )
|
||||
{
|
||||
for ( const FaultFace& ff : m_faultFaces )
|
||||
{
|
||||
// Could detect overlapping faults here .... if (faultsPrCellAcc->faultIdx(ff.m_nativeReservoirCellIndex,
|
||||
// ff.m_nativeFace)
|
||||
// >= 0)
|
||||
|
||||
faultsPrCellAcc->setFaultIdx( ff.m_nativeReservoirCellIndex, ff.m_nativeFace, faultIdx );
|
||||
faultsPrCellAcc->setFaultIdx( ff.m_oppositeReservoirCellIndex,
|
||||
cvf::StructGridInterface::oppositeFace( ff.m_nativeFace ),
|
||||
faultIdx );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "cvfBoundingBox.h"
|
||||
#include "cvfCellRange.h"
|
||||
#include "cvfObject.h"
|
||||
#include "cvfStructGrid.h"
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class RigMainGrid;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class RigFaultsPrCellAccumulator : public cvf::Object
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
NO_FAULT = -1,
|
||||
UNKNOWN_FAULT = -2
|
||||
};
|
||||
|
||||
public:
|
||||
explicit RigFaultsPrCellAccumulator( size_t reservoirCellCount )
|
||||
{
|
||||
std::array<int, 6> initVals = {NO_FAULT, NO_FAULT, NO_FAULT, NO_FAULT, NO_FAULT, NO_FAULT};
|
||||
m_faultIdxForCellFace.resize( reservoirCellCount, initVals );
|
||||
}
|
||||
|
||||
inline int faultIdx( size_t reservoirCellIndex, cvf::StructGridInterface::FaceType face ) const
|
||||
{
|
||||
// Ensure no crash after creating temporary LGRs
|
||||
if ( reservoirCellIndex < m_faultIdxForCellFace.size() )
|
||||
{
|
||||
return m_faultIdxForCellFace[reservoirCellIndex][face];
|
||||
}
|
||||
return NO_FAULT;
|
||||
}
|
||||
|
||||
inline void setFaultIdx( size_t reservoirCellIndex, cvf::StructGridInterface::FaceType face, int faultIdx )
|
||||
{
|
||||
m_faultIdxForCellFace[reservoirCellIndex][face] = faultIdx;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::array<int, 6>> m_faultIdxForCellFace;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class RigFault : public cvf::Object
|
||||
{
|
||||
public:
|
||||
typedef std::tuple<size_t, size_t, size_t, cvf::StructGridInterface::FaceType> CellAndFace;
|
||||
|
||||
struct FaultFace
|
||||
{
|
||||
FaultFace( size_t nativeReservoirCellIndex,
|
||||
cvf::StructGridInterface::FaceType nativeFace,
|
||||
size_t oppositeReservoirCellIndex )
|
||||
: m_nativeReservoirCellIndex( nativeReservoirCellIndex )
|
||||
, m_nativeFace( nativeFace )
|
||||
, m_oppositeReservoirCellIndex( oppositeReservoirCellIndex )
|
||||
{
|
||||
}
|
||||
|
||||
size_t m_nativeReservoirCellIndex;
|
||||
cvf::StructGridInterface::FaceType m_nativeFace;
|
||||
size_t m_oppositeReservoirCellIndex;
|
||||
};
|
||||
|
||||
public:
|
||||
RigFault();
|
||||
|
||||
void setName( const QString& name );
|
||||
QString name() const;
|
||||
|
||||
void addCellRangeForFace( cvf::StructGridInterface::FaceType face, const cvf::CellRange& cellRange );
|
||||
void computeFaultFacesFromCellRanges( const RigMainGrid* grid );
|
||||
|
||||
void accumulateFaultsPrCell( RigFaultsPrCellAccumulator* faultsPrCellAcc, int faultIdx );
|
||||
|
||||
std::vector<FaultFace>& faultFaces();
|
||||
const std::vector<FaultFace>& faultFaces() const;
|
||||
|
||||
std::vector<size_t>& connectionIndices();
|
||||
const std::vector<size_t>& connectionIndices() const;
|
||||
|
||||
static bool ordering( CellAndFace first, CellAndFace second );
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
|
||||
std::array<std::vector<cvf::CellRange>, 6> m_cellRangesForFaces;
|
||||
|
||||
std::vector<FaultFace> m_faultFaces;
|
||||
std::vector<size_t> m_connectionIndices;
|
||||
|
||||
static cvf::ref<RigFaultsPrCellAccumulator> m_faultsPrCellAcc;
|
||||
};
|
||||
@@ -0,0 +1,213 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigFishbonesGeometry.h"
|
||||
|
||||
#include "RimFishbonesMultipleSubs.h"
|
||||
|
||||
#include "RigWellPath.h"
|
||||
#include "RimWellPath.h"
|
||||
#include "cvfAssert.h"
|
||||
#include "cvfMatrix4.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigFisbonesGeometry::RigFisbonesGeometry( RimFishbonesMultipleSubs* fishbonesSub )
|
||||
: m_fishbonesSub( fishbonesSub )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<std::pair<cvf::Vec3d, double>> RigFisbonesGeometry::coordsForLateral( size_t subIndex, size_t lateralIndex ) const
|
||||
{
|
||||
CVF_ASSERT( lateralIndex < m_fishbonesSub->lateralLengths().size() );
|
||||
|
||||
bool found = false;
|
||||
for ( auto& sub : m_fishbonesSub->installedLateralIndices() )
|
||||
{
|
||||
if ( sub.subIndex == subIndex )
|
||||
{
|
||||
auto it = std::find( sub.lateralIndices.begin(), sub.lateralIndices.end(), lateralIndex );
|
||||
if ( it != sub.lateralIndices.end() )
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
CVF_ASSERT( found );
|
||||
|
||||
cvf::Vec3d position;
|
||||
cvf::Vec3d lateralInitialDirection;
|
||||
cvf::Mat4d buildAngleRotationMatrix;
|
||||
|
||||
computeLateralPositionAndOrientation( subIndex, lateralIndex, &position, &lateralInitialDirection, &buildAngleRotationMatrix );
|
||||
|
||||
return computeCoordsAlongLateral( m_fishbonesSub->measuredDepth( subIndex ),
|
||||
m_fishbonesSub->lateralLengths()[lateralIndex],
|
||||
position,
|
||||
lateralInitialDirection,
|
||||
buildAngleRotationMatrix );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFisbonesGeometry::computeLateralPositionAndOrientation( size_t subIndex,
|
||||
size_t lateralIndex,
|
||||
cvf::Vec3d* startCoord,
|
||||
cvf::Vec3d* startDirection,
|
||||
cvf::Mat4d* buildAngleMatrix ) const
|
||||
{
|
||||
RimWellPath* wellPath = nullptr;
|
||||
m_fishbonesSub->firstAncestorOrThisOfTypeAsserted( wellPath );
|
||||
|
||||
auto wellPathGeometry = wellPath->wellPathGeometry();
|
||||
if ( !wellPathGeometry ) return;
|
||||
|
||||
double measuredDepth = m_fishbonesSub->measuredDepth( subIndex );
|
||||
|
||||
cvf::Vec3d position = wellPathGeometry->interpolatedPointAlongWellPath( measuredDepth );
|
||||
|
||||
cvf::Mat4d buildAngleMat;
|
||||
cvf::Vec3d lateralDirection;
|
||||
|
||||
{
|
||||
cvf::Vec3d lateralInitialDirection = cvf::Vec3d::Z_AXIS;
|
||||
cvf::Vec3d p1 = cvf::Vec3d::UNDEFINED;
|
||||
cvf::Vec3d p2 = cvf::Vec3d::UNDEFINED;
|
||||
wellPathGeometry->twoClosestPoints( position, &p1, &p2 );
|
||||
|
||||
CVF_ASSERT( !p1.isUndefined() && !p2.isUndefined() );
|
||||
|
||||
cvf::Vec3d alongWellPath = ( p2 - p1 ).getNormalized();
|
||||
|
||||
if ( RigFisbonesGeometry::closestMainAxis( alongWellPath ) == cvf::Vec3d::Z_AXIS )
|
||||
{
|
||||
// Use Y-AXIS if well path is heading close to Z-AXIS
|
||||
lateralInitialDirection = cvf::Vec3d::Y_AXIS;
|
||||
}
|
||||
|
||||
{
|
||||
double initialRotationAngle = m_fishbonesSub->rotationAngle( subIndex );
|
||||
double lateralOffsetDegrees = 360.0 / m_fishbonesSub->lateralLengths().size();
|
||||
|
||||
double lateralOffsetRadians =
|
||||
cvf::Math::toRadians( initialRotationAngle + lateralOffsetDegrees * lateralIndex );
|
||||
|
||||
cvf::Mat4d lateralOffsetMatrix = cvf::Mat4d::fromRotation( alongWellPath, lateralOffsetRadians );
|
||||
|
||||
lateralInitialDirection = lateralInitialDirection.getTransformedVector( lateralOffsetMatrix );
|
||||
}
|
||||
|
||||
cvf::Vec3d rotationAxis;
|
||||
rotationAxis.cross( alongWellPath, lateralInitialDirection );
|
||||
|
||||
double exitAngleRadians = cvf::Math::toRadians( m_fishbonesSub->exitAngle() );
|
||||
cvf::Mat4d lateralRotationMatrix = cvf::Mat4d::fromRotation( rotationAxis, exitAngleRadians );
|
||||
|
||||
lateralDirection = alongWellPath.getTransformedVector( lateralRotationMatrix );
|
||||
|
||||
double buildAngleRadians = cvf::Math::toRadians( m_fishbonesSub->buildAngle() );
|
||||
buildAngleMat = cvf::Mat4d::fromRotation( rotationAxis, buildAngleRadians );
|
||||
}
|
||||
|
||||
*startCoord = position;
|
||||
*startDirection = lateralDirection;
|
||||
*buildAngleMatrix = buildAngleMat;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<std::pair<cvf::Vec3d, double>>
|
||||
RigFisbonesGeometry::computeCoordsAlongLateral( double startMeasuredDepth,
|
||||
double lateralLength,
|
||||
const cvf::Vec3d& startCoord,
|
||||
const cvf::Vec3d& startDirection,
|
||||
const cvf::Mat4d& buildAngleMatrix )
|
||||
{
|
||||
std::vector<std::pair<cvf::Vec3d, double>> coords;
|
||||
|
||||
cvf::Vec3d lateralDirection( startDirection );
|
||||
|
||||
// Compute coordinates along the lateral by modifying the lateral direction by the build angle for
|
||||
// every unit vector along the lateral
|
||||
cvf::Vec3d accumulatedPosition = startCoord;
|
||||
double measuredDepth = startMeasuredDepth;
|
||||
|
||||
double accumulatedLength = 0.0;
|
||||
while ( accumulatedLength < lateralLength )
|
||||
{
|
||||
coords.push_back( std::make_pair( accumulatedPosition, measuredDepth ) );
|
||||
|
||||
double delta = 1.0;
|
||||
|
||||
if ( lateralLength - accumulatedLength < 1.0 )
|
||||
{
|
||||
delta = lateralLength - accumulatedLength;
|
||||
}
|
||||
|
||||
accumulatedPosition += delta * lateralDirection;
|
||||
|
||||
// Modify the lateral direction by the build angle for each unit vector
|
||||
lateralDirection = lateralDirection.getTransformedVector( buildAngleMatrix );
|
||||
|
||||
accumulatedLength += delta;
|
||||
measuredDepth += delta;
|
||||
}
|
||||
|
||||
coords.push_back( std::make_pair( accumulatedPosition, measuredDepth ) );
|
||||
|
||||
return coords;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Vec3d RigFisbonesGeometry::closestMainAxis( const cvf::Vec3d& vec )
|
||||
{
|
||||
size_t maxComponent = 0;
|
||||
double maxValue = cvf::Math::abs( vec.x() );
|
||||
if ( cvf::Math::abs( vec.y() ) > maxValue )
|
||||
{
|
||||
maxComponent = 1;
|
||||
maxValue = cvf::Math::abs( vec.y() );
|
||||
}
|
||||
|
||||
if ( cvf::Math::abs( vec.z() ) > maxValue )
|
||||
{
|
||||
maxComponent = 2;
|
||||
}
|
||||
|
||||
if ( maxComponent == 0 )
|
||||
{
|
||||
return cvf::Vec3d::X_AXIS;
|
||||
}
|
||||
else if ( maxComponent == 1 )
|
||||
{
|
||||
return cvf::Vec3d::Y_AXIS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return cvf::Vec3d::Z_AXIS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cafPdmPointer.h"
|
||||
|
||||
#include "cvfMatrix4.h"
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class RimFishbonesMultipleSubs;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigFisbonesGeometry
|
||||
{
|
||||
public:
|
||||
explicit RigFisbonesGeometry( RimFishbonesMultipleSubs* fishbonesSub );
|
||||
|
||||
std::vector<std::pair<cvf::Vec3d, double>> coordsForLateral( size_t subIndex, size_t lateralIndex ) const;
|
||||
|
||||
private:
|
||||
void computeLateralPositionAndOrientation( size_t subIndex,
|
||||
size_t lateralIndex,
|
||||
cvf::Vec3d* startCoord,
|
||||
cvf::Vec3d* startDirection,
|
||||
cvf::Mat4d* buildAngleMatrix ) const;
|
||||
|
||||
static std::vector<std::pair<cvf::Vec3d, double>> computeCoordsAlongLateral( double startMeasuredDepth,
|
||||
double lateralLength,
|
||||
const cvf::Vec3d& startCoord,
|
||||
const cvf::Vec3d& startDirection,
|
||||
const cvf::Mat4d& buildAngleMatrix );
|
||||
|
||||
static cvf::Vec3d closestMainAxis( const cvf::Vec3d& vec );
|
||||
|
||||
private:
|
||||
caf::PdmPointer<RimFishbonesMultipleSubs> m_fishbonesSub;
|
||||
};
|
||||
@@ -0,0 +1,143 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RigFlowDiagResultAddress.h"
|
||||
|
||||
#include <opm/flowdiagnostics/ConnectionValues.hpp>
|
||||
#include <opm/flowdiagnostics/ConnectivityGraph.hpp>
|
||||
#include <opm/flowdiagnostics/Toolbox.hpp>
|
||||
|
||||
#include <opm/utility/ECLFluxCalc.hpp>
|
||||
#include <opm/utility/ECLGraph.hpp>
|
||||
#include <opm/utility/ECLWellSolution.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace RigFlowDiagInterfaceTools
|
||||
{
|
||||
std::vector<Opm::ECLPhaseIndex> getPhases( RigFlowDiagResultAddress::PhaseSelection phaseSelection )
|
||||
{
|
||||
std::vector<Opm::ECLPhaseIndex> phases;
|
||||
|
||||
if ( phaseSelection & RigFlowDiagResultAddress::PHASE_GAS )
|
||||
{
|
||||
phases.push_back( Opm::ECLPhaseIndex::Vapour );
|
||||
}
|
||||
if ( phaseSelection & RigFlowDiagResultAddress::PHASE_OIL )
|
||||
{
|
||||
phases.push_back( Opm::ECLPhaseIndex::Liquid );
|
||||
}
|
||||
if ( phaseSelection & RigFlowDiagResultAddress::PHASE_WAT )
|
||||
{
|
||||
phases.push_back( Opm::ECLPhaseIndex::Aqua );
|
||||
}
|
||||
|
||||
return phases;
|
||||
}
|
||||
|
||||
template <class FluxCalc>
|
||||
inline Opm::FlowDiagnostics::ConnectionValues
|
||||
extractFluxField( const Opm::ECLGraph& G, FluxCalc&& getFlux, std::vector<Opm::ECLPhaseIndex> actPh )
|
||||
{
|
||||
using ConnVals = Opm::FlowDiagnostics::ConnectionValues;
|
||||
|
||||
auto flux = ConnVals( ConnVals::NumConnections{G.numConnections()}, ConnVals::NumPhases{actPh.size()} );
|
||||
|
||||
auto phas = ConnVals::PhaseID{0};
|
||||
|
||||
for ( const auto& p : actPh )
|
||||
{
|
||||
const auto pflux = getFlux( p );
|
||||
|
||||
if ( !pflux.empty() )
|
||||
{
|
||||
assert( pflux.size() == flux.numConnections() );
|
||||
|
||||
auto conn = ConnVals::ConnID{0};
|
||||
for ( const auto& v : pflux )
|
||||
{
|
||||
flux( conn, phas ) = v;
|
||||
conn.id += 1;
|
||||
}
|
||||
}
|
||||
|
||||
phas.id += 1;
|
||||
}
|
||||
|
||||
return flux;
|
||||
}
|
||||
|
||||
inline Opm::FlowDiagnostics::ConnectionValues
|
||||
extractFluxFieldFromRestartFile( const Opm::ECLGraph& G,
|
||||
const Opm::ECLRestartData& rstrt,
|
||||
RigFlowDiagResultAddress::PhaseSelection phaseSelection )
|
||||
{
|
||||
auto getFlux = [&G, &rstrt]( const Opm::ECLPhaseIndex p ) { return G.flux( rstrt, p ); };
|
||||
|
||||
return extractFluxField( G, getFlux, getPhases( phaseSelection ) );
|
||||
}
|
||||
|
||||
inline Opm::FlowDiagnostics::ConnectionValues calculateFluxField( const Opm::ECLGraph& G,
|
||||
const Opm::ECLInitFileData& init,
|
||||
const Opm::ECLRestartData& rstrt,
|
||||
RigFlowDiagResultAddress::PhaseSelection phaseSelection )
|
||||
{
|
||||
auto satfunc = Opm::ECLSaturationFunc( G, init );
|
||||
|
||||
Opm::ECLFluxCalc calc( G, init, 9.80665, false );
|
||||
|
||||
auto getFlux = [&calc, &rstrt]( const Opm::ECLPhaseIndex p ) { return calc.flux( rstrt, p ); };
|
||||
|
||||
return extractFluxField( G, getFlux, getPhases( phaseSelection ) );
|
||||
}
|
||||
|
||||
template <class WellFluxes>
|
||||
std::map<Opm::FlowDiagnostics::CellSetID, Opm::FlowDiagnostics::CellSetValues>
|
||||
extractWellFlows( const Opm::ECLGraph& G, const WellFluxes& well_fluxes )
|
||||
{
|
||||
std::map<Opm::FlowDiagnostics::CellSetID, Opm::FlowDiagnostics::CellSetValues> well_flows;
|
||||
for ( const auto& well : well_fluxes )
|
||||
{
|
||||
Opm::FlowDiagnostics::CellSetValues& inflow = well_flows[Opm::FlowDiagnostics::CellSetID( well.name )];
|
||||
for ( const auto& completion : well.completions )
|
||||
{
|
||||
const auto& gridName = completion.gridName;
|
||||
const auto& ijk = completion.ijk;
|
||||
const int cell_index = G.activeCell( ijk, gridName );
|
||||
if ( cell_index >= 0 )
|
||||
{
|
||||
// Since inflow is a std::map, if the key was not
|
||||
// already present operator[] will insert a
|
||||
// value-initialized value (as in T() for a type
|
||||
// T), which is zero for built-in numerical types,
|
||||
// including double.
|
||||
inflow[cell_index] += completion.reservoir_inflow_rate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return well_flows;
|
||||
}
|
||||
|
||||
} // namespace RigFlowDiagInterfaceTools
|
||||
@@ -0,0 +1,76 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017- Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigFlowDiagResultAddress.h"
|
||||
|
||||
namespace caf
|
||||
{
|
||||
template <>
|
||||
void RigFlowDiagResultAddress::PhaseSelectionEnum::setUp()
|
||||
{
|
||||
addItem( RigFlowDiagResultAddress::PHASE_ALL, "PHASE_ALL", "All" );
|
||||
addItem( RigFlowDiagResultAddress::PHASE_OIL, "PHASE_OIL", "Oil" );
|
||||
addItem( RigFlowDiagResultAddress::PHASE_GAS, "PHASE_GAS", "Gas" );
|
||||
addItem( RigFlowDiagResultAddress::PHASE_WAT, "PHASE_WAT", "Water" );
|
||||
|
||||
setDefault( RigFlowDiagResultAddress::PHASE_ALL );
|
||||
}
|
||||
} // namespace caf
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigFlowDiagResultAddress::isNativeResult() const
|
||||
{
|
||||
return ( ( ( variableName == RIG_FLD_TOF_RESNAME ) || ( variableName == RIG_FLD_CELL_FRACTION_RESNAME ) ) &&
|
||||
selectedTracerNames.size() <= 1 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::string RigFlowDiagResultAddress::uiText() const
|
||||
{
|
||||
std::string uiVarname = variableName;
|
||||
|
||||
std::string uitext = uiVarname;
|
||||
if ( phaseSelection != PHASE_ALL )
|
||||
{
|
||||
uitext += " (" + RigFlowDiagResultAddress::PhaseSelectionEnum( phaseSelection ).uiText().toStdString() + ")";
|
||||
}
|
||||
uitext += " (";
|
||||
for ( const std::string& tracerName : selectedTracerNames )
|
||||
{
|
||||
uitext += " " + tracerName;
|
||||
}
|
||||
uitext += " )";
|
||||
return uitext;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::string RigFlowDiagResultAddress::uiShortText() const
|
||||
{
|
||||
std::string uitext = variableName;
|
||||
if ( phaseSelection != PHASE_ALL )
|
||||
{
|
||||
uitext += " (" + RigFlowDiagResultAddress::PhaseSelectionEnum( phaseSelection ).uiText().toStdString() + ")";
|
||||
}
|
||||
return uitext;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2016- Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
|
||||
#include "cafAppEnum.h"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#define RIG_FLD_TOF_RESNAME "TOF"
|
||||
#define RIG_FLD_CELL_FRACTION_RESNAME "Fraction"
|
||||
#define RIG_FLD_MAX_FRACTION_TRACER_RESNAME "MaxFractionTracer"
|
||||
#define RIG_FLD_COMMUNICATION_RESNAME "Communication"
|
||||
#define RIG_NUM_FLOODED_PV "Water Flooded PV"
|
||||
|
||||
#define RIG_FLOW_TOTAL_NAME "Total"
|
||||
#define RIG_FLOW_OIL_NAME "Oil"
|
||||
#define RIG_FLOW_GAS_NAME "Gas"
|
||||
#define RIG_FLOW_WATER_NAME "Water"
|
||||
|
||||
#define RIG_RESERVOIR_TRACER_NAME "Reservoir"
|
||||
#define RIG_TINY_TRACER_GROUP_NAME "Other"
|
||||
|
||||
class RigFlowDiagResultAddress
|
||||
{
|
||||
public:
|
||||
enum PhaseSelection
|
||||
{
|
||||
PHASE_ALL = 0b111,
|
||||
PHASE_OIL = 0b001,
|
||||
PHASE_GAS = 0b010,
|
||||
PHASE_WAT = 0b100,
|
||||
};
|
||||
|
||||
typedef caf::AppEnum<PhaseSelection> PhaseSelectionEnum;
|
||||
|
||||
RigFlowDiagResultAddress( const std::string& aVariableName,
|
||||
PhaseSelection phaseSelection,
|
||||
const std::set<std::string>& someSelectedTracerNames )
|
||||
: variableName( aVariableName )
|
||||
, selectedTracerNames( someSelectedTracerNames )
|
||||
, phaseSelection( phaseSelection )
|
||||
{
|
||||
}
|
||||
|
||||
RigFlowDiagResultAddress( const std::string& aVariableName, PhaseSelection phaseSelection, const std::string& tracerName )
|
||||
: variableName( aVariableName )
|
||||
, phaseSelection( phaseSelection )
|
||||
{
|
||||
selectedTracerNames.insert( tracerName );
|
||||
}
|
||||
|
||||
bool isNativeResult() const;
|
||||
|
||||
std::string uiText() const;
|
||||
std::string uiShortText() const;
|
||||
|
||||
std::string variableName;
|
||||
std::set<std::string> selectedTracerNames;
|
||||
PhaseSelection phaseSelection;
|
||||
|
||||
bool operator<( const RigFlowDiagResultAddress& other ) const
|
||||
{
|
||||
if ( selectedTracerNames != other.selectedTracerNames )
|
||||
{
|
||||
return selectedTracerNames < other.selectedTracerNames;
|
||||
}
|
||||
if ( phaseSelection != other.phaseSelection )
|
||||
{
|
||||
return phaseSelection < other.phaseSelection;
|
||||
}
|
||||
|
||||
return variableName < other.variableName;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2015- Statoil ASA
|
||||
// Copyright (C) 2015- Ceetron Solutions AS
|
||||
//
|
||||
// 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 <cstdlib>
|
||||
|
||||
#include "RigFlowDiagResultFrames.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigFlowDiagResultFrames::RigFlowDiagResultFrames( size_t frameCount )
|
||||
{
|
||||
m_dataForEachFrame.resize( frameCount );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigFlowDiagResultFrames::~RigFlowDiagResultFrames()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigFlowDiagResultFrames::frameCount() const
|
||||
{
|
||||
return m_dataForEachFrame.size();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<double>& RigFlowDiagResultFrames::frameData( size_t frameIndex )
|
||||
{
|
||||
return m_dataForEachFrame[frameIndex];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<double>& RigFlowDiagResultFrames::frameData( size_t frameIndex ) const
|
||||
{
|
||||
return m_dataForEachFrame[frameIndex];
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2016- Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
|
||||
#include "cvfObject.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class RigFlowDiagResultFrames : public cvf::Object
|
||||
{
|
||||
public:
|
||||
explicit RigFlowDiagResultFrames( size_t frameCount );
|
||||
~RigFlowDiagResultFrames() override;
|
||||
|
||||
const std::vector<double>& frameData( size_t frameIndex ) const;
|
||||
std::vector<double>& frameData( size_t frameIndex );
|
||||
size_t frameCount() const;
|
||||
|
||||
private:
|
||||
std::vector<std::vector<double>> m_dataForEachFrame;
|
||||
};
|
||||
@@ -0,0 +1,943 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2016- Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigFlowDiagResults.h"
|
||||
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigEclipseCaseData.h"
|
||||
#include "RigFlowDiagSolverInterface.h"
|
||||
#include "RigFlowDiagStatCalc.h"
|
||||
#include "RigMainGrid.h"
|
||||
|
||||
#include "RigFlowDiagResultFrames.h"
|
||||
#include "RigNumberOfFloodedPoreVolumesCalculator.h"
|
||||
#include "RigStatisticsDataCache.h"
|
||||
#include "RimEclipseCase.h"
|
||||
#include "RimEclipseResultCase.h"
|
||||
#include "RimFlowDiagSolution.h"
|
||||
|
||||
#include <cmath> // Needed for HUGE_VAL on Linux
|
||||
|
||||
namespace caf
|
||||
{
|
||||
template <>
|
||||
void RigFlowDiagResults::CellFilterEnum::setUp()
|
||||
{
|
||||
addItem( RigFlowDiagResults::CELLS_ACTIVE, "CELLS_ACTIVE", "All Active Cells" );
|
||||
addItem( RigFlowDiagResults::CELLS_VISIBLE, "CELLS_VISIBLE", "Visible Cells" );
|
||||
addItem( RigFlowDiagResults::CELLS_COMMUNICATION, "CELLS_COMMUNICATION", "Injector Producer Communication" );
|
||||
addItem( RigFlowDiagResults::CELLS_FLOODED, "CELLS_FLOODED", "Flooded by Injector" );
|
||||
addItem( RigFlowDiagResults::CELLS_DRAINED, "CELLS_DRAINED", "Drained by Producer" );
|
||||
setDefault( RigFlowDiagResults::CELLS_ACTIVE );
|
||||
}
|
||||
} // namespace caf
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigFlowDiagResults::RigFlowDiagResults( RimFlowDiagSolution* flowSolution, size_t timeStepCount )
|
||||
: m_flowDiagSolution( flowSolution )
|
||||
{
|
||||
m_timeStepCount = timeStepCount;
|
||||
m_hasAtemptedNativeResults.resize( timeStepCount );
|
||||
m_injProdPairFluxCommunicationTimesteps.resize( timeStepCount );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigFlowDiagResults::~RigFlowDiagResults()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<double>* RigFlowDiagResults::resultValues( const RigFlowDiagResultAddress& resVarAddr,
|
||||
size_t timeStepIndex )
|
||||
{
|
||||
CVF_ASSERT( m_timeStepCount != cvf::UNDEFINED_SIZE_T ); // Forgotten to call init
|
||||
|
||||
return findOrCalculateResult( resVarAddr, timeStepIndex );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const RigActiveCellInfo* RigFlowDiagResults::activeCellInfo( const RigFlowDiagResultAddress& resVarAddr )
|
||||
{
|
||||
RimEclipseResultCase* eclCase;
|
||||
m_flowDiagSolution->firstAncestorOrThisOfType( eclCase );
|
||||
|
||||
return eclCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ); // Todo: base on
|
||||
// resVarAddr member
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<double>* RigFlowDiagResults::findOrCalculateResult( const RigFlowDiagResultAddress& resVarAddr,
|
||||
size_t timeStepIndex )
|
||||
{
|
||||
std::vector<double>* frameData = findScalarResultFrame( resVarAddr, timeStepIndex );
|
||||
|
||||
if ( frameData ) return frameData;
|
||||
|
||||
frameData = calculateDerivedResult( resVarAddr, timeStepIndex );
|
||||
|
||||
if ( frameData ) return frameData;
|
||||
|
||||
// We need to access the native data from the opm solver
|
||||
|
||||
if ( !solverInterface() ) return nullptr;
|
||||
|
||||
calculateNativeResultsIfNotPreviouslyAttempted( timeStepIndex, resVarAddr.phaseSelection );
|
||||
|
||||
return findScalarResultFrame( resVarAddr, timeStepIndex );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagResults::calculateNativeResultsIfNotPreviouslyAttempted( size_t timeStepIndex,
|
||||
RigFlowDiagResultAddress::PhaseSelection phaseSelection )
|
||||
{
|
||||
if ( timeStepIndex >= m_hasAtemptedNativeResults.size() ) return;
|
||||
|
||||
auto it = m_hasAtemptedNativeResults[timeStepIndex].find( phaseSelection );
|
||||
if ( it == m_hasAtemptedNativeResults[timeStepIndex].end() || !it->second )
|
||||
{
|
||||
RigFlowDiagTimeStepResult nativeTimestepResults =
|
||||
solverInterface()->calculate( timeStepIndex,
|
||||
phaseSelection,
|
||||
m_flowDiagSolution->allInjectorTracerActiveCellIndices( timeStepIndex ),
|
||||
m_flowDiagSolution->allProducerTracerActiveCellIndices( timeStepIndex ) );
|
||||
|
||||
std::map<RigFlowDiagResultAddress, std::vector<double>>& nativeResults = nativeTimestepResults.nativeResults();
|
||||
|
||||
for ( auto& resIt : nativeResults )
|
||||
{
|
||||
RigFlowDiagResultFrames* nativeResFrames = findScalarResult( resIt.first );
|
||||
if ( !nativeResFrames ) nativeResFrames = createScalarResult( resIt.first );
|
||||
|
||||
nativeResFrames->frameData( timeStepIndex ).swap( resIt.second );
|
||||
}
|
||||
|
||||
m_injProdPairFluxCommunicationTimesteps[timeStepIndex][phaseSelection].swap(
|
||||
nativeTimestepResults.injProdWellPairFluxes() );
|
||||
|
||||
m_hasAtemptedNativeResults[timeStepIndex][phaseSelection] = true;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<double>* RigFlowDiagResults::findScalarResultFrame( const RigFlowDiagResultAddress& resVarAddr,
|
||||
size_t timeStepIndex )
|
||||
{
|
||||
RigFlowDiagResultFrames* resFrames = findScalarResult( resVarAddr );
|
||||
|
||||
if ( resFrames )
|
||||
{
|
||||
std::vector<double>& frame = resFrames->frameData( timeStepIndex );
|
||||
if ( frame.size() ) return ( &frame );
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigFlowDiagSolverInterface* RigFlowDiagResults::solverInterface()
|
||||
{
|
||||
RimEclipseResultCase* eclCase;
|
||||
m_flowDiagSolution->firstAncestorOrThisOfType( eclCase );
|
||||
|
||||
return eclCase->flowDiagSolverInterface();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigFlowDiagResultFrames* RigFlowDiagResults::createScalarResult( const RigFlowDiagResultAddress& resVarAddr )
|
||||
{
|
||||
cvf::ref<RigFlowDiagResultFrames> newFrameSet = new RigFlowDiagResultFrames( m_timeStepCount );
|
||||
m_resultSets[resVarAddr] = newFrameSet;
|
||||
|
||||
return newFrameSet.p();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigFlowDiagResultFrames* RigFlowDiagResults::findScalarResult( const RigFlowDiagResultAddress& resVarAddr )
|
||||
{
|
||||
decltype( m_resultSets )::iterator it = m_resultSets.find( resVarAddr );
|
||||
|
||||
if ( it == m_resultSets.end() ) return nullptr;
|
||||
|
||||
return it->second.p();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<double>* RigFlowDiagResults::calculateDerivedResult( const RigFlowDiagResultAddress& resVarAddr,
|
||||
size_t timeStepIndex )
|
||||
{
|
||||
if ( resVarAddr.isNativeResult() ) return nullptr;
|
||||
|
||||
if ( resVarAddr.variableName == RIG_FLD_TOF_RESNAME )
|
||||
{
|
||||
return calculateAverageTOFResult( resVarAddr, timeStepIndex );
|
||||
}
|
||||
else if ( resVarAddr.variableName == RIG_FLD_CELL_FRACTION_RESNAME )
|
||||
{
|
||||
return calculateSumOfFractionsResult( resVarAddr, timeStepIndex );
|
||||
}
|
||||
else if ( resVarAddr.variableName == RIG_FLD_COMMUNICATION_RESNAME )
|
||||
{
|
||||
return calculateCommunicationResult( resVarAddr, timeStepIndex );
|
||||
}
|
||||
else if ( resVarAddr.variableName == RIG_FLD_MAX_FRACTION_TRACER_RESNAME )
|
||||
{
|
||||
return calculateTracerWithMaxFractionResult( resVarAddr, timeStepIndex );
|
||||
}
|
||||
else if ( resVarAddr.variableName == RIG_NUM_FLOODED_PV )
|
||||
{
|
||||
calculateNumFloodedPV( resVarAddr );
|
||||
return findScalarResultFrame( resVarAddr, timeStepIndex );
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<double>* RigFlowDiagResults::calculateAverageTOFResult( const RigFlowDiagResultAddress& resVarAddr,
|
||||
size_t timeStepIndex )
|
||||
{
|
||||
std::vector<const std::vector<double>*> injectorTOFs =
|
||||
findResultsForSelectedTracers( resVarAddr, timeStepIndex, RIG_FLD_TOF_RESNAME, RimFlowDiagSolution::INJECTOR );
|
||||
std::vector<const std::vector<double>*> injectorFractions =
|
||||
findResultsForSelectedTracers( resVarAddr, timeStepIndex, RIG_FLD_CELL_FRACTION_RESNAME, RimFlowDiagSolution::INJECTOR );
|
||||
|
||||
std::vector<const std::vector<double>*> producerTOFs =
|
||||
findResultsForSelectedTracers( resVarAddr, timeStepIndex, RIG_FLD_TOF_RESNAME, RimFlowDiagSolution::PRODUCER );
|
||||
std::vector<const std::vector<double>*> producerFractions =
|
||||
findResultsForSelectedTracers( resVarAddr, timeStepIndex, RIG_FLD_CELL_FRACTION_RESNAME, RimFlowDiagSolution::PRODUCER );
|
||||
size_t activeCellCount = this->activeCellInfo( resVarAddr )->reservoirActiveCellCount();
|
||||
|
||||
std::vector<double> injectorTotalFractions;
|
||||
std::vector<double> injectorFractMultTof;
|
||||
calculateSumOfFractionAndFractionMultTOF( activeCellCount,
|
||||
injectorFractions,
|
||||
injectorTOFs,
|
||||
&injectorTotalFractions,
|
||||
&injectorFractMultTof );
|
||||
|
||||
std::vector<double> producerTotalFractions;
|
||||
std::vector<double> producerFractMultTof;
|
||||
calculateSumOfFractionAndFractionMultTOF( activeCellCount,
|
||||
producerFractions,
|
||||
producerTOFs,
|
||||
&producerTotalFractions,
|
||||
&producerFractMultTof );
|
||||
|
||||
RigFlowDiagResultFrames* averageTofFrames = this->createScalarResult( resVarAddr );
|
||||
std::vector<double>& averageTof = averageTofFrames->frameData( timeStepIndex );
|
||||
averageTof.resize( activeCellCount, HUGE_VAL );
|
||||
|
||||
for ( size_t acIdx = 0; acIdx < activeCellCount; ++acIdx )
|
||||
{
|
||||
if ( injectorTotalFractions[acIdx] == 0.0 && producerTotalFractions[acIdx] == 0.0 )
|
||||
{
|
||||
averageTof[acIdx] = HUGE_VAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
double retVal = 0.0;
|
||||
if ( injectorTotalFractions[acIdx] != 0.0 )
|
||||
retVal += ( 1.0 / injectorTotalFractions[acIdx] ) * injectorFractMultTof[acIdx];
|
||||
if ( producerTotalFractions[acIdx] != 0.0 )
|
||||
retVal += ( 1.0 / producerTotalFractions[acIdx] ) * producerFractMultTof[acIdx];
|
||||
averageTof[acIdx] = retVal;
|
||||
}
|
||||
}
|
||||
|
||||
/// Test to remove all averaging
|
||||
// if (injectorTOFs.size()) averageTof = (*injectorTOFs[0]);
|
||||
|
||||
return &averageTof;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagResults::calculateSumOfFractionAndFractionMultTOF( size_t activeCellCount,
|
||||
const std::vector<const std::vector<double>*>& fractions,
|
||||
const std::vector<const std::vector<double>*>& TOFs,
|
||||
std::vector<double>* sumOfFractions,
|
||||
std::vector<double>* fractionMultTOF )
|
||||
{
|
||||
sumOfFractions->resize( activeCellCount, 0.0 );
|
||||
fractionMultTOF->resize( activeCellCount, 0.0 );
|
||||
|
||||
for ( size_t iIdx = 0; iIdx < fractions.size(); ++iIdx )
|
||||
{
|
||||
const std::vector<double>* frInj = fractions[iIdx];
|
||||
const std::vector<double>* tofInj = TOFs[iIdx];
|
||||
|
||||
if ( !( frInj && tofInj ) ) continue;
|
||||
|
||||
for ( size_t acIdx = 0; acIdx < activeCellCount; ++acIdx )
|
||||
{
|
||||
if ( ( *frInj )[acIdx] == HUGE_VAL ) continue;
|
||||
|
||||
( *sumOfFractions )[acIdx] += ( *frInj )[acIdx];
|
||||
( *fractionMultTOF )[acIdx] += ( *frInj )[acIdx] * ( *tofInj )[acIdx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<double>* RigFlowDiagResults::calculateSumOfFractionsResult( const RigFlowDiagResultAddress& resVarAddr,
|
||||
size_t timeStepIndex )
|
||||
{
|
||||
std::vector<const std::vector<double>*> fractions = findResultsForSelectedTracers( resVarAddr,
|
||||
timeStepIndex,
|
||||
RIG_FLD_CELL_FRACTION_RESNAME,
|
||||
RimFlowDiagSolution::UNDEFINED );
|
||||
|
||||
RigFlowDiagResultFrames* sumOfFractionsFrames = this->createScalarResult( resVarAddr );
|
||||
std::vector<double>& sumOfFractions = sumOfFractionsFrames->frameData( timeStepIndex );
|
||||
|
||||
size_t activeCellCount = this->activeCellInfo( resVarAddr )->reservoirActiveCellCount();
|
||||
|
||||
calculateSumOfFractions( fractions, activeCellCount, &sumOfFractions );
|
||||
|
||||
return &sumOfFractions;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<double>* RigFlowDiagResults::calculateTracerWithMaxFractionResult( const RigFlowDiagResultAddress& resVarAddr,
|
||||
size_t timeStepIndex )
|
||||
{
|
||||
std::vector<std::pair<std::string, const std::vector<double>*>> fractions =
|
||||
findNamedResultsForSelectedTracers( resVarAddr,
|
||||
timeStepIndex,
|
||||
RIG_FLD_CELL_FRACTION_RESNAME,
|
||||
RimFlowDiagSolution::UNDEFINED );
|
||||
|
||||
std::vector<int> resultTracerIdxToGlobalTracerIdx;
|
||||
{
|
||||
resultTracerIdxToGlobalTracerIdx.resize( fractions.size(), -1 );
|
||||
|
||||
std::vector<QString> allTracerNames = m_flowDiagSolution->tracerNames();
|
||||
int selTracerIdx = 0;
|
||||
for ( const auto& trNameFractionPair : fractions )
|
||||
{
|
||||
for ( size_t globIdx = 0; globIdx < allTracerNames.size(); ++globIdx )
|
||||
{
|
||||
if ( allTracerNames[globIdx].toStdString() == trNameFractionPair.first )
|
||||
{
|
||||
resultTracerIdxToGlobalTracerIdx[selTracerIdx] = static_cast<int>( globIdx );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++selTracerIdx;
|
||||
}
|
||||
}
|
||||
|
||||
size_t activeCellCount = this->activeCellInfo( resVarAddr )->reservoirActiveCellCount();
|
||||
|
||||
RigFlowDiagResultFrames* maxFractionTracerIdxFrames = this->createScalarResult( resVarAddr );
|
||||
std::vector<double>& maxFractionTracerIdx = maxFractionTracerIdxFrames->frameData( timeStepIndex );
|
||||
{
|
||||
maxFractionTracerIdx.resize( activeCellCount, HUGE_VAL );
|
||||
|
||||
std::vector<double> maxFraction;
|
||||
maxFraction.resize( activeCellCount, -HUGE_VAL );
|
||||
|
||||
for ( size_t frIdx = 0; frIdx < fractions.size(); ++frIdx )
|
||||
{
|
||||
const std::vector<double>* fr = fractions[frIdx].second;
|
||||
|
||||
if ( !fr ) continue;
|
||||
|
||||
for ( size_t acIdx = 0; acIdx < activeCellCount; ++acIdx )
|
||||
{
|
||||
if ( ( *fr )[acIdx] == HUGE_VAL ) continue;
|
||||
|
||||
if ( maxFraction[acIdx] < ( *fr )[acIdx] )
|
||||
{
|
||||
maxFraction[acIdx] = ( *fr )[acIdx];
|
||||
maxFractionTracerIdx[acIdx] = resultTracerIdxToGlobalTracerIdx[frIdx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &maxFractionTracerIdx;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<double>* RigFlowDiagResults::calculateCommunicationResult( const RigFlowDiagResultAddress& resVarAddr,
|
||||
size_t timeStepIndex )
|
||||
{
|
||||
std::vector<const std::vector<double>*> injectorFractions =
|
||||
findResultsForSelectedTracers( resVarAddr, timeStepIndex, RIG_FLD_CELL_FRACTION_RESNAME, RimFlowDiagSolution::INJECTOR );
|
||||
std::vector<const std::vector<double>*> producerFractions =
|
||||
findResultsForSelectedTracers( resVarAddr, timeStepIndex, RIG_FLD_CELL_FRACTION_RESNAME, RimFlowDiagSolution::PRODUCER );
|
||||
size_t activeCellCount = this->activeCellInfo( resVarAddr )->reservoirActiveCellCount();
|
||||
|
||||
std::vector<double> sumOfInjectorFractions;
|
||||
calculateSumOfFractions( injectorFractions, activeCellCount, &sumOfInjectorFractions );
|
||||
|
||||
std::vector<double> sumOfProducerFractions;
|
||||
calculateSumOfFractions( producerFractions, activeCellCount, &sumOfProducerFractions );
|
||||
|
||||
RigFlowDiagResultFrames* commFrames = this->createScalarResult( resVarAddr );
|
||||
std::vector<double>& commPI = commFrames->frameData( timeStepIndex );
|
||||
commPI.resize( activeCellCount, HUGE_VAL );
|
||||
|
||||
for ( size_t acIdx = 0; acIdx < activeCellCount; ++acIdx )
|
||||
{
|
||||
if ( ( sumOfInjectorFractions )[acIdx] == HUGE_VAL ) continue;
|
||||
if ( ( sumOfProducerFractions )[acIdx] == HUGE_VAL ) continue;
|
||||
|
||||
( commPI )[acIdx] = ( sumOfInjectorFractions )[acIdx] * ( sumOfProducerFractions )[acIdx];
|
||||
}
|
||||
|
||||
return &commPI;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagResults::calculateNumFloodedPV( const RigFlowDiagResultAddress& resVarAddr )
|
||||
{
|
||||
RimEclipseCase* eclipseCase;
|
||||
m_flowDiagSolution->firstAncestorOrThisOfTypeAsserted( eclipseCase );
|
||||
std::vector<QString> tracerNames;
|
||||
for ( const std::string& tracerName : resVarAddr.selectedTracerNames )
|
||||
{
|
||||
tracerNames.push_back( QString::fromUtf8( tracerName.c_str() ) );
|
||||
}
|
||||
RigNumberOfFloodedPoreVolumesCalculator calc( eclipseCase, tracerNames );
|
||||
|
||||
RigFlowDiagResultFrames* frames = this->createScalarResult( resVarAddr );
|
||||
for ( size_t frameIdx = 0; frameIdx < m_timeStepCount; ++frameIdx )
|
||||
{
|
||||
std::vector<double>& frame = frames->frameData( frameIdx );
|
||||
|
||||
frame.swap( calc.numberOfFloodedPorevolumes()[frameIdx] );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<const std::vector<double>*>
|
||||
RigFlowDiagResults::findResultsForSelectedTracers( const RigFlowDiagResultAddress& resVarAddr,
|
||||
size_t timeStepIndex,
|
||||
const std::string& nativeResultName,
|
||||
RimFlowDiagSolution::TracerStatusType wantedTracerType )
|
||||
{
|
||||
std::vector<const std::vector<double>*> selectedTracersResults;
|
||||
|
||||
for ( const std::string& tracerName : resVarAddr.selectedTracerNames )
|
||||
{
|
||||
RimFlowDiagSolution::TracerStatusType tracerType =
|
||||
m_flowDiagSolution->tracerStatusInTimeStep( QString::fromStdString( tracerName ), timeStepIndex );
|
||||
|
||||
if ( tracerType != RimFlowDiagSolution::CLOSED &&
|
||||
( tracerType == wantedTracerType || wantedTracerType == RimFlowDiagSolution::UNDEFINED ) )
|
||||
{
|
||||
selectedTracersResults.push_back(
|
||||
findOrCalculateResult( RigFlowDiagResultAddress( nativeResultName, resVarAddr.phaseSelection, tracerName ),
|
||||
timeStepIndex ) );
|
||||
}
|
||||
}
|
||||
|
||||
return selectedTracersResults;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<std::pair<std::string, const std::vector<double>*>>
|
||||
RigFlowDiagResults::findNamedResultsForSelectedTracers( const RigFlowDiagResultAddress& resVarAddr,
|
||||
size_t timeStepIndex,
|
||||
const std::string& nativeResultName,
|
||||
RimFlowDiagSolution::TracerStatusType wantedTracerType )
|
||||
{
|
||||
std::vector<std::pair<std::string, const std::vector<double>*>> selectedTracersResults;
|
||||
|
||||
for ( const std::string& tracerName : resVarAddr.selectedTracerNames )
|
||||
{
|
||||
RimFlowDiagSolution::TracerStatusType tracerType =
|
||||
m_flowDiagSolution->tracerStatusInTimeStep( QString::fromStdString( tracerName ), timeStepIndex );
|
||||
|
||||
if ( tracerType != RimFlowDiagSolution::CLOSED &&
|
||||
( tracerType == wantedTracerType || wantedTracerType == RimFlowDiagSolution::UNDEFINED ) )
|
||||
{
|
||||
selectedTracersResults.push_back(
|
||||
std::make_pair( tracerName,
|
||||
findOrCalculateResult( RigFlowDiagResultAddress( nativeResultName,
|
||||
resVarAddr.phaseSelection,
|
||||
tracerName ),
|
||||
timeStepIndex ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
return selectedTracersResults;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigStatisticsDataCache* RigFlowDiagResults::statistics( const RigFlowDiagResultAddress& resVarAddr )
|
||||
{
|
||||
RigStatisticsDataCache* statCache = m_resultStatistics[resVarAddr].p();
|
||||
if ( !statCache )
|
||||
{
|
||||
RigFlowDiagStatCalc* calculator = new RigFlowDiagStatCalc( this, resVarAddr );
|
||||
statCache = new RigStatisticsDataCache( calculator );
|
||||
m_resultStatistics[resVarAddr] = statCache;
|
||||
}
|
||||
|
||||
return statCache;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagResults::calculateSumOfFractions( const std::vector<const std::vector<double>*>& fractions,
|
||||
size_t activeCellCount,
|
||||
std::vector<double>* sumOfFractions )
|
||||
{
|
||||
sumOfFractions->resize( activeCellCount, HUGE_VAL );
|
||||
|
||||
for ( size_t iIdx = 0; iIdx < fractions.size(); ++iIdx )
|
||||
{
|
||||
const std::vector<double>* fraction = fractions[iIdx];
|
||||
|
||||
if ( !( fraction ) ) continue;
|
||||
|
||||
for ( size_t acIdx = 0; acIdx < activeCellCount; ++acIdx )
|
||||
{
|
||||
if ( ( *fraction )[acIdx] == HUGE_VAL ) continue;
|
||||
|
||||
if ( ( *sumOfFractions )[acIdx] == HUGE_VAL ) ( *sumOfFractions )[acIdx] = 0.0;
|
||||
|
||||
( *sumOfFractions )[acIdx] += ( *fraction )[acIdx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagResults::minMaxScalarValues( const RigFlowDiagResultAddress& resVarAddr,
|
||||
int timeStepIndex,
|
||||
double* localMin,
|
||||
double* localMax )
|
||||
{
|
||||
this->statistics( resVarAddr )->minMaxCellScalarValues( timeStepIndex, *localMin, *localMax );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagResults::minMaxScalarValues( const RigFlowDiagResultAddress& resVarAddr, double* globalMin, double* globalMax )
|
||||
{
|
||||
this->statistics( resVarAddr )->minMaxCellScalarValues( *globalMin, *globalMax );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagResults::posNegClosestToZero( const RigFlowDiagResultAddress& resVarAddr,
|
||||
int timeStepIndex,
|
||||
double* localPosClosestToZero,
|
||||
double* localNegClosestToZero )
|
||||
{
|
||||
this->statistics( resVarAddr )->posNegClosestToZero( timeStepIndex, *localPosClosestToZero, *localNegClosestToZero );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagResults::posNegClosestToZero( const RigFlowDiagResultAddress& resVarAddr,
|
||||
double* globalPosClosestToZero,
|
||||
double* globalNegClosestToZero )
|
||||
{
|
||||
this->statistics( resVarAddr )->posNegClosestToZero( *globalPosClosestToZero, *globalNegClosestToZero );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagResults::meanScalarValue( const RigFlowDiagResultAddress& resVarAddr, double* meanValue )
|
||||
{
|
||||
CVF_ASSERT( meanValue );
|
||||
|
||||
this->statistics( resVarAddr )->meanCellScalarValues( *meanValue );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagResults::meanScalarValue( const RigFlowDiagResultAddress& resVarAddr, int timeStepIndex, double* meanValue )
|
||||
{
|
||||
this->statistics( resVarAddr )->meanCellScalarValues( timeStepIndex, *meanValue );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagResults::p10p90ScalarValues( const RigFlowDiagResultAddress& resVarAddr, double* p10, double* p90 )
|
||||
{
|
||||
this->statistics( resVarAddr )->p10p90CellScalarValues( *p10, *p90 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagResults::p10p90ScalarValues( const RigFlowDiagResultAddress& resVarAddr,
|
||||
int timeStepIndex,
|
||||
double* p10,
|
||||
double* p90 )
|
||||
{
|
||||
this->statistics( resVarAddr )->p10p90CellScalarValues( timeStepIndex, *p10, *p90 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagResults::sumScalarValue( const RigFlowDiagResultAddress& resVarAddr, double* sum )
|
||||
{
|
||||
CVF_ASSERT( sum );
|
||||
|
||||
this->statistics( resVarAddr )->sumCellScalarValues( *sum );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagResults::sumScalarValue( const RigFlowDiagResultAddress& resVarAddr, int timeStepIndex, double* sum )
|
||||
{
|
||||
CVF_ASSERT( sum );
|
||||
|
||||
this->statistics( resVarAddr )->sumCellScalarValues( timeStepIndex, *sum );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<size_t>& RigFlowDiagResults::scalarValuesHistogram( const RigFlowDiagResultAddress& resVarAddr )
|
||||
{
|
||||
return this->statistics( resVarAddr )->cellScalarValuesHistogram();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<size_t>& RigFlowDiagResults::scalarValuesHistogram( const RigFlowDiagResultAddress& resVarAddr,
|
||||
int timeStepIndex )
|
||||
{
|
||||
return this->statistics( resVarAddr )->cellScalarValuesHistogram( timeStepIndex );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<int>& RigFlowDiagResults::uniqueCellScalarValues( const RigFlowDiagResultAddress& resVarAddr )
|
||||
{
|
||||
return this->statistics( resVarAddr )->uniqueCellScalarValues();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<int>& RigFlowDiagResults::uniqueCellScalarValues( const RigFlowDiagResultAddress& resVarAddr,
|
||||
int timeStepIndex )
|
||||
{
|
||||
return this->statistics( resVarAddr )->uniqueCellScalarValues( timeStepIndex );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagResults::mobileVolumeWeightedMean( const RigFlowDiagResultAddress& resVarAddr, int timeStepIndex, double* mean )
|
||||
{
|
||||
this->statistics( resVarAddr )->mobileVolumeWeightedMean( timeStepIndex, *mean );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::pair<double, double> RigFlowDiagResults::injectorProducerPairFluxes( const std::string& injTracername,
|
||||
const std::string& prodTracerName,
|
||||
int timeStepIndex )
|
||||
{
|
||||
calculateNativeResultsIfNotPreviouslyAttempted( timeStepIndex, RigFlowDiagResultAddress::PHASE_ALL );
|
||||
|
||||
auto commPair = m_injProdPairFluxCommunicationTimesteps[timeStepIndex][RigFlowDiagResultAddress::PHASE_ALL].find(
|
||||
std::make_pair( injTracername, prodTracerName ) );
|
||||
if ( commPair != m_injProdPairFluxCommunicationTimesteps[timeStepIndex][RigFlowDiagResultAddress::PHASE_ALL].end() )
|
||||
{
|
||||
return commPair->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::make_pair( 0.0, 0.0 );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigFlowDiagResults::maxAbsPairFlux( int timeStepIndex )
|
||||
{
|
||||
calculateNativeResultsIfNotPreviouslyAttempted( timeStepIndex, RigFlowDiagResultAddress::PHASE_ALL );
|
||||
double maxFlux = 0.0;
|
||||
|
||||
if ( (size_t)timeStepIndex < m_injProdPairFluxCommunicationTimesteps.size() )
|
||||
{
|
||||
for ( const auto& commPair :
|
||||
m_injProdPairFluxCommunicationTimesteps[timeStepIndex][RigFlowDiagResultAddress::PHASE_ALL] )
|
||||
{
|
||||
if ( fabs( commPair.second.first ) > maxFlux ) maxFlux = fabs( commPair.second.first );
|
||||
if ( fabs( commPair.second.second ) > maxFlux ) maxFlux = fabs( commPair.second.second );
|
||||
}
|
||||
}
|
||||
|
||||
return maxFlux;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<int> RigFlowDiagResults::calculatedTimeSteps( RigFlowDiagResultAddress::PhaseSelection phaseSelection )
|
||||
{
|
||||
std::vector<int> timestepIndices;
|
||||
for ( size_t tsIdx = 0; tsIdx < m_timeStepCount; ++tsIdx )
|
||||
{
|
||||
auto it = m_hasAtemptedNativeResults[tsIdx].find( phaseSelection );
|
||||
if ( it != m_hasAtemptedNativeResults[tsIdx].end() && it->second )
|
||||
{
|
||||
timestepIndices.push_back( static_cast<int>( tsIdx ) );
|
||||
}
|
||||
}
|
||||
|
||||
return timestepIndices;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigFlowDiagSolverInterface::FlowCharacteristicsResultFrame
|
||||
RigFlowDiagResults::flowCharacteristicsResults( int timeStepIndex,
|
||||
CellFilter cellSelection,
|
||||
const std::vector<QString>& tracerNames,
|
||||
double max_pv_fraction,
|
||||
double minCommunication,
|
||||
int maxTof )
|
||||
{
|
||||
std::set<std::string> injectorNames;
|
||||
std::set<std::string> producerNames;
|
||||
|
||||
for ( const QString& tracerName : tracerNames )
|
||||
{
|
||||
RimFlowDiagSolution::TracerStatusType status =
|
||||
m_flowDiagSolution->tracerStatusInTimeStep( tracerName, timeStepIndex );
|
||||
if ( status == RimFlowDiagSolution::INJECTOR )
|
||||
{
|
||||
injectorNames.insert( tracerName.toStdString() );
|
||||
}
|
||||
else if ( status == RimFlowDiagSolution::PRODUCER )
|
||||
{
|
||||
producerNames.insert( tracerName.toStdString() );
|
||||
}
|
||||
}
|
||||
|
||||
RigFlowDiagResultAddress injectorAddress( RIG_FLD_TOF_RESNAME, RigFlowDiagResultAddress::PHASE_ALL, injectorNames );
|
||||
RigFlowDiagResultAddress producerAddress( RIG_FLD_TOF_RESNAME, RigFlowDiagResultAddress::PHASE_ALL, producerNames );
|
||||
|
||||
const std::vector<double>* allInjectorResults = resultValues( injectorAddress, timeStepIndex );
|
||||
const std::vector<double>* allProducerResults = resultValues( producerAddress, timeStepIndex );
|
||||
|
||||
std::vector<double> injectorResults;
|
||||
std::vector<double> producerResults;
|
||||
std::vector<size_t> selectedCellIndices;
|
||||
|
||||
if ( cellSelection == CELLS_COMMUNICATION )
|
||||
{
|
||||
std::set<std::string> allTracers;
|
||||
allTracers.insert( injectorNames.begin(), injectorNames.end() );
|
||||
allTracers.insert( producerNames.begin(), producerNames.end() );
|
||||
|
||||
RigFlowDiagResultAddress communicationAddress( RIG_FLD_COMMUNICATION_RESNAME,
|
||||
RigFlowDiagResultAddress::PHASE_ALL,
|
||||
allTracers );
|
||||
const std::vector<double>* communicationResult = resultValues( communicationAddress, timeStepIndex );
|
||||
|
||||
for ( size_t i = 0; i < communicationResult->size(); ++i )
|
||||
{
|
||||
if ( communicationResult->at( i ) != HUGE_VAL && communicationResult->at( i ) >= minCommunication )
|
||||
{
|
||||
selectedCellIndices.push_back( i );
|
||||
if ( allInjectorResults != nullptr ) injectorResults.push_back( allInjectorResults->at( i ) );
|
||||
if ( allProducerResults != nullptr ) producerResults.push_back( allProducerResults->at( i ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( cellSelection == CELLS_FLOODED )
|
||||
{
|
||||
if ( allInjectorResults != nullptr )
|
||||
{
|
||||
for ( size_t i = 0; i < allInjectorResults->size(); ++i )
|
||||
{
|
||||
if ( allInjectorResults->at( i ) != HUGE_VAL && allInjectorResults->at( i ) <= maxTof )
|
||||
{
|
||||
selectedCellIndices.push_back( i );
|
||||
injectorResults.push_back( allInjectorResults->at( i ) );
|
||||
if ( allProducerResults != nullptr )
|
||||
{
|
||||
producerResults.push_back( allProducerResults->at( i ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
producerResults.push_back( 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( cellSelection == CELLS_DRAINED )
|
||||
{
|
||||
if ( allProducerResults != nullptr )
|
||||
{
|
||||
for ( size_t i = 0; i < allProducerResults->size(); ++i )
|
||||
{
|
||||
if ( allProducerResults->at( i ) != HUGE_VAL && allProducerResults->at( i ) <= maxTof )
|
||||
{
|
||||
selectedCellIndices.push_back( i );
|
||||
producerResults.push_back( allProducerResults->at( i ) );
|
||||
if ( allInjectorResults != nullptr )
|
||||
{
|
||||
injectorResults.push_back( allInjectorResults->at( i ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
injectorResults.push_back( 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( allInjectorResults != nullptr ) injectorResults = *allInjectorResults;
|
||||
if ( allProducerResults != nullptr ) producerResults = *allProducerResults;
|
||||
|
||||
for ( size_t i = 0; i < injectorResults.size(); ++i )
|
||||
{
|
||||
selectedCellIndices.push_back( i );
|
||||
}
|
||||
}
|
||||
|
||||
return solverInterface()->calculateFlowCharacteristics( &injectorResults,
|
||||
&producerResults,
|
||||
selectedCellIndices,
|
||||
max_pv_fraction );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigFlowDiagSolverInterface::FlowCharacteristicsResultFrame
|
||||
RigFlowDiagResults::flowCharacteristicsResults( int timeStepIndex,
|
||||
const std::vector<char>& visibleActiveCells,
|
||||
double max_pv_fraction )
|
||||
{
|
||||
std::vector<QString> tracerNames = m_flowDiagSolution->tracerNames();
|
||||
|
||||
std::set<std::string> injectorNames;
|
||||
std::set<std::string> producerNames;
|
||||
|
||||
for ( const QString& tracerName : tracerNames )
|
||||
{
|
||||
RimFlowDiagSolution::TracerStatusType status =
|
||||
m_flowDiagSolution->tracerStatusInTimeStep( tracerName, timeStepIndex );
|
||||
if ( status == RimFlowDiagSolution::INJECTOR )
|
||||
{
|
||||
injectorNames.insert( tracerName.toStdString() );
|
||||
}
|
||||
else if ( status == RimFlowDiagSolution::PRODUCER )
|
||||
{
|
||||
producerNames.insert( tracerName.toStdString() );
|
||||
}
|
||||
}
|
||||
|
||||
RigFlowDiagResultAddress injectorAddress( RIG_FLD_TOF_RESNAME, RigFlowDiagResultAddress::PHASE_ALL, injectorNames );
|
||||
RigFlowDiagResultAddress producerAddress( RIG_FLD_TOF_RESNAME, RigFlowDiagResultAddress::PHASE_ALL, producerNames );
|
||||
|
||||
const std::vector<double>* allInjectorResults = resultValues( injectorAddress, timeStepIndex );
|
||||
const std::vector<double>* allProducerResults = resultValues( producerAddress, timeStepIndex );
|
||||
|
||||
std::vector<size_t> selectedCellIndices;
|
||||
std::vector<double> injectorResults;
|
||||
std::vector<double> producerResults;
|
||||
|
||||
for ( size_t i = 0; i < visibleActiveCells.size(); ++i )
|
||||
{
|
||||
if ( visibleActiveCells[i] )
|
||||
{
|
||||
selectedCellIndices.push_back( i );
|
||||
injectorResults.push_back( allInjectorResults->at( i ) );
|
||||
producerResults.push_back( allProducerResults->at( i ) );
|
||||
}
|
||||
}
|
||||
|
||||
return solverInterface()->calculateFlowCharacteristics( &injectorResults,
|
||||
&producerResults,
|
||||
selectedCellIndices,
|
||||
max_pv_fraction );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimFlowDiagSolution* RigFlowDiagResults::flowDiagSolution()
|
||||
{
|
||||
{
|
||||
return m_flowDiagSolution;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2016- Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
|
||||
#include "RigFlowDiagResultAddress.h"
|
||||
|
||||
#include "RigFlowDiagSolverInterface.h"
|
||||
|
||||
#include "RimFlowDiagSolution.h"
|
||||
|
||||
#include "cafAppEnum.h"
|
||||
#include "cafPdmPointer.h"
|
||||
|
||||
#include "cvfArray.h"
|
||||
#include "cvfObject.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class RigFlowDiagResultFrames;
|
||||
class RigStatisticsDataCache;
|
||||
class RigActiveCellInfo;
|
||||
|
||||
class RigFlowDiagResults : public cvf::Object
|
||||
{
|
||||
public:
|
||||
enum CellFilter
|
||||
{
|
||||
CELLS_ACTIVE,
|
||||
CELLS_VISIBLE,
|
||||
CELLS_COMMUNICATION,
|
||||
CELLS_FLOODED,
|
||||
CELLS_DRAINED,
|
||||
};
|
||||
|
||||
typedef caf::AppEnum<CellFilter> CellFilterEnum;
|
||||
|
||||
public:
|
||||
RigFlowDiagResults( RimFlowDiagSolution* flowSolution, size_t timeStepCount );
|
||||
~RigFlowDiagResults() override;
|
||||
|
||||
const std::vector<double>* resultValues( const RigFlowDiagResultAddress& resVarAddr, size_t timeStepIndex );
|
||||
size_t timeStepCount() { return m_timeStepCount; }
|
||||
const RigActiveCellInfo* activeCellInfo( const RigFlowDiagResultAddress& resVarAddr );
|
||||
|
||||
void minMaxScalarValues( const RigFlowDiagResultAddress& resVarAddr, int timeStepIndex, double* localMin, double* localMax );
|
||||
void minMaxScalarValues( const RigFlowDiagResultAddress& resVarAddr, double* globalMin, double* globalMax );
|
||||
void posNegClosestToZero( const RigFlowDiagResultAddress& resVarAddr,
|
||||
int timeStepIndex,
|
||||
double* localPosClosestToZero,
|
||||
double* localNegClosestToZero );
|
||||
void posNegClosestToZero( const RigFlowDiagResultAddress& resVarAddr,
|
||||
double* globalPosClosestToZero,
|
||||
double* globalNegClosestToZero );
|
||||
void meanScalarValue( const RigFlowDiagResultAddress& resVarAddr, double* meanValue );
|
||||
void meanScalarValue( const RigFlowDiagResultAddress& resVarAddr, int timeStepIndex, double* meanValue );
|
||||
void p10p90ScalarValues( const RigFlowDiagResultAddress& resVarAddr, double* p10, double* p90 );
|
||||
void p10p90ScalarValues( const RigFlowDiagResultAddress& resVarAddr, int timeStepIndex, double* p10, double* p90 );
|
||||
void sumScalarValue( const RigFlowDiagResultAddress& resVarAddr, double* sum );
|
||||
void sumScalarValue( const RigFlowDiagResultAddress& resVarAddr, int timeStepIndex, double* sum );
|
||||
const std::vector<size_t>& scalarValuesHistogram( const RigFlowDiagResultAddress& resVarAddr );
|
||||
const std::vector<size_t>& scalarValuesHistogram( const RigFlowDiagResultAddress& resVarAddr, int timeStepIndex );
|
||||
const std::vector<int>& uniqueCellScalarValues( const RigFlowDiagResultAddress& resVarAddr );
|
||||
const std::vector<int>& uniqueCellScalarValues( const RigFlowDiagResultAddress& resVarAddr, int timeStepIndex );
|
||||
void mobileVolumeWeightedMean( const RigFlowDiagResultAddress& resVarAddr, int timeStepIndex, double* mean );
|
||||
|
||||
std::pair<double, double> injectorProducerPairFluxes( const std::string& injTracername,
|
||||
const std::string& prodTracerName,
|
||||
int timeStepIndex );
|
||||
double maxAbsPairFlux( int timeStepIndex );
|
||||
|
||||
std::vector<int> calculatedTimeSteps( RigFlowDiagResultAddress::PhaseSelection phaseSelection );
|
||||
|
||||
RigFlowDiagSolverInterface::FlowCharacteristicsResultFrame
|
||||
flowCharacteristicsResults( int timeStepIndex,
|
||||
CellFilter cellSelection,
|
||||
const std::vector<QString>& tracerNames,
|
||||
double max_pv_fraction,
|
||||
double minCommunication,
|
||||
int maxTof );
|
||||
|
||||
RigFlowDiagSolverInterface::FlowCharacteristicsResultFrame
|
||||
flowCharacteristicsResults( int timeStepIndex, const std::vector<char>& visibleActiveCells, double max_pv_fraction );
|
||||
|
||||
RimFlowDiagSolution* flowDiagSolution();
|
||||
|
||||
private:
|
||||
const std::vector<double>* findOrCalculateResult( const RigFlowDiagResultAddress& resVarAddr, size_t timeStepIndex );
|
||||
void calculateNativeResultsIfNotPreviouslyAttempted( size_t timeStepIndex,
|
||||
RigFlowDiagResultAddress::PhaseSelection phaseSelection );
|
||||
|
||||
std::vector<double>* calculateDerivedResult( const RigFlowDiagResultAddress& resVarAddr, size_t timeStepIndex );
|
||||
|
||||
std::vector<double>* calculateAverageTOFResult( const RigFlowDiagResultAddress& resVarAddr, size_t timeStepIndex );
|
||||
std::vector<double>* calculateSumOfFractionsResult( const RigFlowDiagResultAddress& resVarAddr, size_t timeStepIndex );
|
||||
std::vector<double>* calculateTracerWithMaxFractionResult( const RigFlowDiagResultAddress& resVarAddr,
|
||||
size_t timeStepIndex );
|
||||
std::vector<double>* calculateCommunicationResult( const RigFlowDiagResultAddress& resVarAddr, size_t timeStepIndex );
|
||||
void calculateNumFloodedPV( const RigFlowDiagResultAddress& resVarAddr );
|
||||
|
||||
std::vector<const std::vector<double>*>
|
||||
findResultsForSelectedTracers( const RigFlowDiagResultAddress& resVarAddr,
|
||||
size_t timeStepIndex,
|
||||
const std::string& nativeResultName,
|
||||
RimFlowDiagSolution::TracerStatusType wantedTracerType );
|
||||
std::vector<std::pair<std::string, const std::vector<double>*>>
|
||||
findNamedResultsForSelectedTracers( const RigFlowDiagResultAddress& resVarAddr,
|
||||
size_t timeStepIndex,
|
||||
const std::string& nativeResultName,
|
||||
RimFlowDiagSolution::TracerStatusType wantedTracerType );
|
||||
|
||||
void calculateSumOfFractionAndFractionMultTOF( size_t activeCellCount,
|
||||
const std::vector<const std::vector<double>*>& injectorFractions,
|
||||
const std::vector<const std::vector<double>*>& injectorTOFs,
|
||||
std::vector<double>* injectorTotalFractions,
|
||||
std::vector<double>* injectorFractMultTof );
|
||||
|
||||
void calculateSumOfFractions( const std::vector<const std::vector<double>*>& fractions,
|
||||
size_t activeCellCount,
|
||||
std::vector<double>* sumOfFractions );
|
||||
|
||||
RigStatisticsDataCache* statistics( const RigFlowDiagResultAddress& resVarAddr );
|
||||
|
||||
RigFlowDiagResultFrames* createScalarResult( const RigFlowDiagResultAddress& resVarAddr );
|
||||
RigFlowDiagResultFrames* findScalarResult( const RigFlowDiagResultAddress& resVarAddr );
|
||||
std::vector<double>* findScalarResultFrame( const RigFlowDiagResultAddress& resVarAddr, size_t timeStepIndex );
|
||||
|
||||
// void deleteScalarResult(const RigFlowDiagResultAddress& resVarAddr);
|
||||
|
||||
RigFlowDiagSolverInterface* solverInterface();
|
||||
|
||||
size_t m_timeStepCount;
|
||||
caf::PdmPointer<RimFlowDiagSolution> m_flowDiagSolution;
|
||||
|
||||
std::vector<std::map<RigFlowDiagResultAddress::PhaseSelection, bool>> m_hasAtemptedNativeResults;
|
||||
|
||||
std::map<RigFlowDiagResultAddress, cvf::ref<RigFlowDiagResultFrames>> m_resultSets;
|
||||
std::map<RigFlowDiagResultAddress, cvf::ref<RigStatisticsDataCache>> m_resultStatistics;
|
||||
|
||||
using InjectorProducerCommunicationMap = std::map<std::pair<std::string, std::string>, std::pair<double, double>>;
|
||||
std::vector<std::map<RigFlowDiagResultAddress::PhaseSelection, InjectorProducerCommunicationMap>> m_injProdPairFluxCommunicationTimesteps;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,171 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2016- Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
|
||||
#include "RigFlowDiagResultAddress.h"
|
||||
|
||||
#include "cafPdmPointer.h"
|
||||
#include "cvfObject.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class RimEclipseResultCase;
|
||||
class RimFlowDiagSolution;
|
||||
|
||||
class RigFlowDiagTimeStepResult
|
||||
{
|
||||
public:
|
||||
explicit RigFlowDiagTimeStepResult( size_t activeCellCount );
|
||||
|
||||
void setTracerTOF( const std::string& tracerName,
|
||||
RigFlowDiagResultAddress::PhaseSelection phaseSelection,
|
||||
const std::map<int, double>& cellValues );
|
||||
void setTracerFraction( const std::string& tracerName,
|
||||
RigFlowDiagResultAddress::PhaseSelection phaseSelection,
|
||||
const std::map<int, double>& cellValues );
|
||||
void setInjProdWellPairFlux( const std::string& injectorTracerName,
|
||||
const std::string& producerTracerName,
|
||||
const std::pair<double, double>& injProdFluxes );
|
||||
|
||||
using Curve = std::pair<std::vector<double>, std::vector<double>>;
|
||||
|
||||
// Used to "steal" the data from this one using swap
|
||||
std::map<RigFlowDiagResultAddress, std::vector<double>>& nativeResults() { return m_nativeResults; }
|
||||
std::map<std::pair<std::string, std::string>, std::pair<double, double>>& injProdWellPairFluxes()
|
||||
{
|
||||
return m_injProdWellPairFluxes;
|
||||
}
|
||||
|
||||
private:
|
||||
void addResult( const RigFlowDiagResultAddress& resAddr, const std::map<int, double>& cellValues );
|
||||
|
||||
std::map<RigFlowDiagResultAddress, std::vector<double>> m_nativeResults;
|
||||
std::map<std::pair<std::string, std::string>, std::pair<double, double>> m_injProdWellPairFluxes;
|
||||
|
||||
size_t m_activeCellCount;
|
||||
};
|
||||
|
||||
class RigEclipseCaseData;
|
||||
class RigOpmFlowDiagStaticData;
|
||||
|
||||
class RigFlowDiagSolverInterface : public cvf::Object
|
||||
{
|
||||
public:
|
||||
struct FlowCharacteristicsResultFrame
|
||||
{
|
||||
FlowCharacteristicsResultFrame();
|
||||
|
||||
using Curve = std::pair<std::vector<double>, std::vector<double>>;
|
||||
|
||||
Curve m_storageCapFlowCapCurve;
|
||||
Curve m_dimensionlessTimeSweepEfficiencyCurve;
|
||||
double m_lorenzCoefficient;
|
||||
};
|
||||
|
||||
struct RelPermCurve
|
||||
{
|
||||
enum Ident
|
||||
{
|
||||
KRW,
|
||||
KRG,
|
||||
KROW,
|
||||
KROG,
|
||||
PCOW,
|
||||
PCOG
|
||||
};
|
||||
enum EpsMode
|
||||
{
|
||||
EPS_ON,
|
||||
EPS_OFF
|
||||
};
|
||||
|
||||
Ident ident;
|
||||
std::string name;
|
||||
EpsMode epsMode;
|
||||
std::vector<double> saturationVals;
|
||||
std::vector<double> yVals;
|
||||
};
|
||||
|
||||
enum PvtCurveType
|
||||
{
|
||||
PVT_CT_FVF,
|
||||
PVT_CT_VISCOSITY
|
||||
};
|
||||
|
||||
struct PvtCurve
|
||||
{
|
||||
enum Phase
|
||||
{
|
||||
OIL,
|
||||
GAS
|
||||
};
|
||||
enum Ident
|
||||
{
|
||||
Unknown,
|
||||
Bo,
|
||||
Bg,
|
||||
Visc_o,
|
||||
Visc_g
|
||||
};
|
||||
|
||||
Ident ident;
|
||||
Phase phase;
|
||||
std::vector<double> pressureVals;
|
||||
std::vector<double> yVals;
|
||||
std::vector<double> mixRatVals;
|
||||
};
|
||||
|
||||
public:
|
||||
explicit RigFlowDiagSolverInterface( RimEclipseResultCase* eclipseCase );
|
||||
~RigFlowDiagSolverInterface() override;
|
||||
|
||||
RigFlowDiagTimeStepResult calculate( size_t timeStepIdx,
|
||||
RigFlowDiagResultAddress::PhaseSelection phaseSelection,
|
||||
std::map<std::string, std::vector<int>> injectorTracers,
|
||||
std::map<std::string, std::vector<int>> producerTracers );
|
||||
|
||||
FlowCharacteristicsResultFrame calculateFlowCharacteristics( const std::vector<double>* injector_tof,
|
||||
const std::vector<double>* producer_tof,
|
||||
const std::vector<size_t>& selected_cell_indices,
|
||||
double max_pv_fraction );
|
||||
|
||||
std::vector<RelPermCurve> calculateRelPermCurves( size_t activeCellIndex );
|
||||
std::vector<PvtCurve> calculatePvtCurves( PvtCurveType pvtCurveType, size_t activeCellIndex );
|
||||
bool calculatePvtDynamicPropertiesFvf( size_t activeCellIndex, double pressure, double rs, double rv, double* bo, double* bg );
|
||||
bool calculatePvtDynamicPropertiesViscosity( size_t activeCellIndex,
|
||||
double pressure,
|
||||
double rs,
|
||||
double rv,
|
||||
double* mu_o,
|
||||
double* mu_g );
|
||||
|
||||
private:
|
||||
std::wstring getInitFileName() const;
|
||||
bool ensureStaticDataObjectInstanceCreated();
|
||||
void assignPhaseCorrecedPORV( RigFlowDiagResultAddress::PhaseSelection phaseSelection, size_t timeStepIdx );
|
||||
void reportRelPermCurveError( const QString& message );
|
||||
void reportPvtCurveError( const QString& message );
|
||||
|
||||
RimEclipseResultCase* m_eclipseCase;
|
||||
cvf::ref<RigOpmFlowDiagStaticData> m_opmFlowDiagStaticData;
|
||||
|
||||
int m_pvtCurveErrorCount;
|
||||
int m_relpermCurveErrorCount;
|
||||
};
|
||||
@@ -0,0 +1,133 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigFlowDiagStatCalc.h"
|
||||
|
||||
#include "RigCaseCellResultsData.h"
|
||||
#include "RigFlowDiagResults.h"
|
||||
#include "RigStatisticsMath.h"
|
||||
#include "RigWeightedMeanCalc.h"
|
||||
|
||||
#include "RimEclipseResultCase.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigFlowDiagStatCalc::RigFlowDiagStatCalc( RigFlowDiagResults* flowDiagResults, const RigFlowDiagResultAddress& resVarAddr )
|
||||
: m_resVarAddr( resVarAddr )
|
||||
{
|
||||
m_resultsData = flowDiagResults;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagStatCalc::minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max )
|
||||
{
|
||||
MinMaxAccumulator minMaxCalc( min, max );
|
||||
const std::vector<double>* vals = m_resultsData->resultValues( m_resVarAddr, timeStepIndex );
|
||||
|
||||
if ( vals ) minMaxCalc.addData( *vals );
|
||||
|
||||
min = minMaxCalc.min;
|
||||
max = minMaxCalc.max;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagStatCalc::posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg )
|
||||
{
|
||||
PosNegAccumulator posNegCalc( pos, neg );
|
||||
const std::vector<double>* vals = m_resultsData->resultValues( m_resVarAddr, timeStepIndex );
|
||||
|
||||
if ( vals ) posNegCalc.addData( *vals );
|
||||
|
||||
pos = posNegCalc.pos;
|
||||
neg = posNegCalc.neg;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagStatCalc::valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount )
|
||||
{
|
||||
SumCountAccumulator sumCountCalc( valueSum, sampleCount );
|
||||
const std::vector<double>* vals = m_resultsData->resultValues( m_resVarAddr, timeStepIndex );
|
||||
|
||||
if ( vals ) sumCountCalc.addData( *vals );
|
||||
|
||||
valueSum = sumCountCalc.valueSum;
|
||||
sampleCount = sumCountCalc.sampleCount;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagStatCalc::addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator )
|
||||
{
|
||||
const std::vector<double>* vals = m_resultsData->resultValues( m_resVarAddr, timeStepIndex );
|
||||
|
||||
if ( vals ) histogramCalculator.addData( *vals );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagStatCalc::uniqueValues( size_t timeStepIndex, std::set<int>& uniqueValues )
|
||||
{
|
||||
const std::vector<double>* vals = m_resultsData->resultValues( m_resVarAddr, timeStepIndex );
|
||||
|
||||
if ( vals )
|
||||
for ( double val : ( *vals ) )
|
||||
uniqueValues.insert( static_cast<int>( val ) );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigFlowDiagStatCalc::timeStepCount()
|
||||
{
|
||||
return m_resultsData->timeStepCount();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex, double& mean )
|
||||
{
|
||||
RimEclipseResultCase* eclCase = nullptr;
|
||||
m_resultsData->flowDiagSolution()->firstAncestorOrThisOfType( eclCase );
|
||||
if ( !eclCase ) return;
|
||||
|
||||
RigCaseCellResultsData* caseCellResultsData = eclCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL );
|
||||
RigEclipseResultAddress mobPoreVolResAddr( RiaDefines::ResultCatType::STATIC_NATIVE,
|
||||
RiaDefines::mobilePoreVolumeName() );
|
||||
|
||||
caseCellResultsData->ensureKnownResultLoaded( mobPoreVolResAddr );
|
||||
|
||||
const std::vector<double>& weights = caseCellResultsData->cellScalarResults( mobPoreVolResAddr, 0 );
|
||||
const std::vector<double>* values = m_resultsData->resultValues( m_resVarAddr, timeStepIndex );
|
||||
|
||||
const RigActiveCellInfo* actCellInfo = m_resultsData->activeCellInfo( m_resVarAddr );
|
||||
|
||||
RigWeightedMeanCalc::weightedMeanOverCells( &weights, values, nullptr, false, actCellInfo, true, &mean );
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
// Copyright (C) Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigFlowDiagResultAddress.h"
|
||||
#include "RigStatisticsCalculator.h"
|
||||
|
||||
class RigHistogramCalculator;
|
||||
class RigFlowDiagResults;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
|
||||
class RigFlowDiagStatCalc : public RigStatisticsCalculator
|
||||
{
|
||||
public:
|
||||
RigFlowDiagStatCalc( RigFlowDiagResults* flowDiagResults, const RigFlowDiagResultAddress& resVarAddr );
|
||||
|
||||
void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override;
|
||||
void posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg ) override;
|
||||
void valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount ) override;
|
||||
void addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator ) override;
|
||||
void uniqueValues( size_t timeStepIndex, std::set<int>& values ) override;
|
||||
size_t timeStepCount() override;
|
||||
void mobileVolumeWeightedMean( size_t timeStepIndex, double& mean ) override;
|
||||
|
||||
private:
|
||||
RigFlowDiagResults* m_resultsData;
|
||||
RigFlowDiagResultAddress m_resVarAddr;
|
||||
};
|
||||
@@ -0,0 +1,123 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2015- Statoil ASA
|
||||
// Copyright (C) 2015- Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigFlowDiagVisibleCellsStatCalc.h"
|
||||
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigCaseCellResultsData.h"
|
||||
#include "RigStatisticsMath.h"
|
||||
#include "RigWeightedMeanCalc.h"
|
||||
|
||||
#include "RimEclipseResultCase.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigFlowDiagVisibleCellsStatCalc::RigFlowDiagVisibleCellsStatCalc( RigFlowDiagResults* resultsData,
|
||||
const RigFlowDiagResultAddress& resVarAddr,
|
||||
const cvf::UByteArray* cellVisibilities )
|
||||
: m_resultsData( resultsData )
|
||||
, m_resVarAddr( resVarAddr )
|
||||
, m_cellVisibilities( cellVisibilities )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagVisibleCellsStatCalc::minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max )
|
||||
{
|
||||
MinMaxAccumulator acc( min, max );
|
||||
traverseElementNodes( acc, timeStepIndex );
|
||||
min = acc.min;
|
||||
max = acc.max;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagVisibleCellsStatCalc::posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg )
|
||||
{
|
||||
PosNegAccumulator acc( pos, neg );
|
||||
traverseElementNodes( acc, timeStepIndex );
|
||||
pos = acc.pos;
|
||||
neg = acc.neg;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagVisibleCellsStatCalc::valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount )
|
||||
{
|
||||
SumCountAccumulator acc( valueSum, sampleCount );
|
||||
traverseElementNodes( acc, timeStepIndex );
|
||||
valueSum = acc.valueSum;
|
||||
sampleCount = acc.sampleCount;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagVisibleCellsStatCalc::addDataToHistogramCalculator( size_t timeStepIndex,
|
||||
RigHistogramCalculator& histogramCalculator )
|
||||
{
|
||||
traverseElementNodes( histogramCalculator, timeStepIndex );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagVisibleCellsStatCalc::uniqueValues( size_t timeStepIndex, std::set<int>& values )
|
||||
{
|
||||
UniqueValueAccumulator acc;
|
||||
traverseElementNodes( acc, timeStepIndex );
|
||||
values = acc.uniqueValues;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigFlowDiagVisibleCellsStatCalc::timeStepCount()
|
||||
{
|
||||
return m_resultsData->timeStepCount();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFlowDiagVisibleCellsStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex, double& result )
|
||||
{
|
||||
RimEclipseResultCase* eclCase = nullptr;
|
||||
m_resultsData->flowDiagSolution()->firstAncestorOrThisOfType( eclCase );
|
||||
if ( !eclCase ) return;
|
||||
|
||||
RigCaseCellResultsData* caseCellResultsData = eclCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL );
|
||||
|
||||
RigEclipseResultAddress mobPorvAddr( RiaDefines::ResultCatType::STATIC_NATIVE, RiaDefines::mobilePoreVolumeName() );
|
||||
|
||||
caseCellResultsData->ensureKnownResultLoaded( mobPorvAddr );
|
||||
const std::vector<double>& weights = caseCellResultsData->cellScalarResults( mobPorvAddr, 0 );
|
||||
const std::vector<double>* values = m_resultsData->resultValues( m_resVarAddr, timeStepIndex );
|
||||
|
||||
const RigActiveCellInfo* actCellInfo = m_resultsData->activeCellInfo( m_resVarAddr );
|
||||
|
||||
RigWeightedMeanCalc::weightedMeanOverCells( &weights, values, m_cellVisibilities.p(), true, actCellInfo, true, &result );
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2015- Statoil ASA
|
||||
// Copyright (C) 2015- Ceetron Solutions AS
|
||||
//
|
||||
// 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 "RigActiveCellInfo.h"
|
||||
#include "RigFlowDiagResultAddress.h"
|
||||
#include "RigFlowDiagResults.h"
|
||||
#include "RigStatisticsCalculator.h"
|
||||
|
||||
#include "cvfArray.h"
|
||||
|
||||
class RigFlowDiagResults;
|
||||
class RigActiveCellInfo;
|
||||
|
||||
class RigFlowDiagVisibleCellsStatCalc : public RigStatisticsCalculator
|
||||
{
|
||||
public:
|
||||
RigFlowDiagVisibleCellsStatCalc( RigFlowDiagResults* resultsData,
|
||||
const RigFlowDiagResultAddress& resVarAddr,
|
||||
const cvf::UByteArray* cellVisibilities );
|
||||
|
||||
void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override;
|
||||
void posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg ) override;
|
||||
void valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount ) override;
|
||||
void addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator ) override;
|
||||
void uniqueValues( size_t timeStepIndex, std::set<int>& values ) override;
|
||||
size_t timeStepCount() override;
|
||||
void mobileVolumeWeightedMean( size_t timeStepIndex, double& result ) override;
|
||||
|
||||
private:
|
||||
RigFlowDiagResults* m_resultsData;
|
||||
RigFlowDiagResultAddress m_resVarAddr;
|
||||
cvf::cref<cvf::UByteArray> m_cellVisibilities;
|
||||
|
||||
template <typename StatisticsAccumulator>
|
||||
void traverseElementNodes( StatisticsAccumulator& accumulator, size_t timeStepIndex )
|
||||
{
|
||||
const std::vector<double>* values = m_resultsData->resultValues( m_resVarAddr, timeStepIndex );
|
||||
if ( !values ) return;
|
||||
|
||||
const RigActiveCellInfo* actCellInfo = m_resultsData->activeCellInfo( m_resVarAddr );
|
||||
|
||||
size_t cellCount = actCellInfo->reservoirCellCount();
|
||||
|
||||
CVF_TIGHT_ASSERT( cellCount == m_cellVisibilities->size() );
|
||||
|
||||
for ( size_t cIdx = 0; cIdx < cellCount; ++cIdx )
|
||||
{
|
||||
if ( !( *m_cellVisibilities )[cIdx] ) continue;
|
||||
|
||||
size_t cellResultIndex = actCellInfo->cellResultIndex( cIdx );
|
||||
|
||||
if ( cellResultIndex != cvf::UNDEFINED_SIZE_T ) accumulator.addValue( ( *values )[cellResultIndex] );
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,139 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigFormationNames.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigFormationNames::RigFormationNames()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigFormationNames::~RigFormationNames()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RigFormationNames::formationNameFromKLayerIdx( size_t Kidx )
|
||||
{
|
||||
int idx = formationIndexFromKLayerIdx( Kidx );
|
||||
if ( idx >= static_cast<int>( m_formationNames.size() ) ) return "";
|
||||
if ( idx == -1 ) return "";
|
||||
|
||||
return m_formationNames[idx];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigFormationNames::formationColorFromKLayerIdx( size_t Kidx, cvf::Color3f* formationColor )
|
||||
{
|
||||
int idx = formationIndexFromKLayerIdx( Kidx );
|
||||
|
||||
if ( idx == -1 || idx >= static_cast<int>( m_formationColors.size() ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_formationColors[idx] == undefinedColor() ) return false;
|
||||
|
||||
*formationColor = m_formationColors[idx];
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFormationNames::appendFormationRange( const QString& name, int kStartIdx, int kEndIdx )
|
||||
{
|
||||
appendFormationRangeWithColor( name, undefinedColor(), kStartIdx, kEndIdx );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFormationNames::appendFormationRange( const QString& name, cvf::Color3f color, int kStartIdx, int kEndIdx )
|
||||
{
|
||||
appendFormationRangeWithColor( name, color, kStartIdx, kEndIdx );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFormationNames::appendFormationRangeHeight( const QString& name, int kLayerCount )
|
||||
{
|
||||
if ( kLayerCount < 1 ) return;
|
||||
|
||||
int kStartIdx = static_cast<int>( m_nameIndexPrKLayer.size() );
|
||||
int kEndIdx = kStartIdx + kLayerCount;
|
||||
|
||||
appendFormationRangeWithColor( name, undefinedColor(), kStartIdx, kEndIdx );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFormationNames::appendFormationRangeHeight( const QString& name, cvf::Color3f color, int kLayerCount )
|
||||
{
|
||||
if ( kLayerCount < 1 ) return;
|
||||
|
||||
int kStartIdx = static_cast<int>( m_nameIndexPrKLayer.size() );
|
||||
int kEndIdx = kStartIdx + kLayerCount;
|
||||
|
||||
appendFormationRangeWithColor( name, color, kStartIdx, kEndIdx );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Color3f RigFormationNames::undefinedColor()
|
||||
{
|
||||
static cvf::Color3f noColor( -1.0f, -1.0f, -1.0f );
|
||||
|
||||
return noColor;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFormationNames::appendFormationRangeWithColor( const QString& name, cvf::Color3f color, int kStartIdx, int kEndIdx )
|
||||
{
|
||||
CVF_ASSERT( kStartIdx <= kEndIdx );
|
||||
|
||||
int nameIdx = static_cast<int>( m_formationNames.size() );
|
||||
|
||||
m_formationNames.push_back( name );
|
||||
|
||||
if ( kEndIdx >= static_cast<int>( m_nameIndexPrKLayer.size() ) )
|
||||
{
|
||||
m_nameIndexPrKLayer.resize( kEndIdx + 1, -1 );
|
||||
}
|
||||
|
||||
for ( int kIdx = kStartIdx; kIdx <= kEndIdx; ++kIdx )
|
||||
{
|
||||
m_nameIndexPrKLayer[kIdx] = nameIdx;
|
||||
}
|
||||
|
||||
m_formationColors.push_back( color );
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cvfObject.h"
|
||||
#include <QString>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "cvfColor3.h"
|
||||
|
||||
class RigFormationNames : public cvf::Object
|
||||
{
|
||||
public:
|
||||
RigFormationNames();
|
||||
~RigFormationNames() override;
|
||||
|
||||
int formationIndexFromKLayerIdx( size_t Kidx ) const
|
||||
{
|
||||
if ( Kidx >= m_nameIndexPrKLayer.size() ) return -1;
|
||||
return m_nameIndexPrKLayer[Kidx];
|
||||
}
|
||||
|
||||
QString formationNameFromKLayerIdx( size_t Kidx );
|
||||
|
||||
bool formationColorFromKLayerIdx( size_t Kidx, cvf::Color3f* formationColor );
|
||||
|
||||
const std::vector<QString>& formationNames() const { return m_formationNames; }
|
||||
const std::vector<cvf::Color3f>& formationColors() const { return m_formationColors; }
|
||||
|
||||
void appendFormationRange( const QString& name, int kStartIdx, int kEndIdx );
|
||||
void appendFormationRangeHeight( const QString& name, int kLayerCount );
|
||||
|
||||
void appendFormationRange( const QString& name, cvf::Color3f color, int kStartIdx, int kEndIdx );
|
||||
void appendFormationRangeHeight( const QString& name, cvf::Color3f color, int kLayerCount );
|
||||
|
||||
private:
|
||||
static cvf::Color3f undefinedColor();
|
||||
void appendFormationRangeWithColor( const QString& name, cvf::Color3f color, int kStartIdx, int kEndIdx );
|
||||
|
||||
private:
|
||||
std::vector<int> m_nameIndexPrKLayer;
|
||||
std::vector<QString> m_formationNames;
|
||||
std::vector<cvf::Color3f> m_formationColors; // optional color per formation
|
||||
};
|
||||
@@ -0,0 +1,101 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 - Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigFractureCell.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigFractureCell::RigFractureCell( std::vector<cvf::Vec3d> polygon, size_t i, size_t j )
|
||||
: m_polygon( polygon )
|
||||
, m_i( i )
|
||||
, m_j( j )
|
||||
, m_conductivityValue( 0.0 )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<cvf::Vec3d>& RigFractureCell::getPolygon() const
|
||||
{
|
||||
return m_polygon;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigFractureCell::getConductivityValue() const
|
||||
{
|
||||
return m_conductivityValue;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigFractureCell::getI() const
|
||||
{
|
||||
return m_i;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigFractureCell::getJ() const
|
||||
{
|
||||
return m_j;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigFractureCell::hasNonZeroConductivity() const
|
||||
{
|
||||
return m_conductivityValue > 1e-7;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFractureCell::setConductivityValue( double cond )
|
||||
{
|
||||
m_conductivityValue = cond;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigFractureCell::cellSizeX() const
|
||||
{
|
||||
// The polygon corners are always stored in the same order
|
||||
if ( m_polygon.size() > 1 ) return ( m_polygon[1] - m_polygon[0] ).length();
|
||||
return cvf::UNDEFINED_DOUBLE;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigFractureCell::cellSizeZ() const
|
||||
{
|
||||
if ( m_polygon.size() > 2 ) return ( m_polygon[2] - m_polygon[1] ).length();
|
||||
return cvf::UNDEFINED_DOUBLE;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 - Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigFractureCell
|
||||
{
|
||||
public:
|
||||
RigFractureCell( std::vector<cvf::Vec3d> polygon, size_t i, size_t j );
|
||||
|
||||
const std::vector<cvf::Vec3d>& getPolygon() const;
|
||||
double getConductivityValue() const;
|
||||
size_t getI() const;
|
||||
size_t getJ() const;
|
||||
|
||||
bool hasNonZeroConductivity() const;
|
||||
void setConductivityValue( double cond );
|
||||
|
||||
double cellSizeX() const;
|
||||
double cellSizeZ() const;
|
||||
|
||||
private:
|
||||
std::vector<cvf::Vec3d> m_polygon;
|
||||
double m_conductivityValue;
|
||||
size_t m_i;
|
||||
size_t m_j;
|
||||
};
|
||||
@@ -0,0 +1,124 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 - Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigFractureGrid.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigFractureGrid::RigFractureGrid()
|
||||
: m_iCellCount( 0 )
|
||||
, m_jCellCount( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFractureGrid::setFractureCells( std::vector<RigFractureCell> fractureCells )
|
||||
{
|
||||
m_fractureCells = fractureCells;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFractureGrid::setWellCenterFractureCellIJ( std::pair<size_t, size_t> wellCenterFractureCellIJ )
|
||||
{
|
||||
m_wellCenterFractureCellIJ = wellCenterFractureCellIJ;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFractureGrid::setICellCount( size_t iCellCount )
|
||||
{
|
||||
m_iCellCount = iCellCount;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigFractureGrid::setJCellCount( size_t jCellCount )
|
||||
{
|
||||
m_jCellCount = jCellCount;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<RigFractureCell>& RigFractureGrid::fractureCells() const
|
||||
{
|
||||
return m_fractureCells;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigFractureGrid::getGlobalIndexFromIJ( size_t i, size_t j ) const
|
||||
{
|
||||
return i * m_jCellCount + j;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const RigFractureCell& RigFractureGrid::cellFromIndex( size_t index ) const
|
||||
{
|
||||
if ( index < m_fractureCells.size() )
|
||||
{
|
||||
const RigFractureCell& cell = m_fractureCells[index];
|
||||
return cell;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Better error handling?
|
||||
RiaLogging::error( QString( "Requesting non-existent StimPlanCell" ) );
|
||||
RiaLogging::error( QString( "Returning cell 0, results will be invalid" ) );
|
||||
const RigFractureCell& cell = m_fractureCells[0];
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigFractureGrid::jCellCount() const
|
||||
{
|
||||
return m_jCellCount;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigFractureGrid::iCellCount() const
|
||||
{
|
||||
return m_iCellCount;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::pair<size_t, size_t> RigFractureGrid::fractureCellAtWellCenter() const
|
||||
{
|
||||
return m_wellCenterFractureCellIJ;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017 - Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RigFractureCell.h"
|
||||
#include "cvfObject.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class RigFractureCell;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigFractureGrid : public cvf::Object
|
||||
{
|
||||
public:
|
||||
RigFractureGrid();
|
||||
|
||||
void setFractureCells( std::vector<RigFractureCell> fractureCells );
|
||||
void setWellCenterFractureCellIJ( std::pair<size_t, size_t> wellCenterFractureCellIJ );
|
||||
void setICellCount( size_t iCellCount );
|
||||
void setJCellCount( size_t jCellCount );
|
||||
|
||||
const std::vector<RigFractureCell>& fractureCells() const;
|
||||
size_t getGlobalIndexFromIJ( size_t i, size_t j ) const;
|
||||
const RigFractureCell& cellFromIndex( size_t index ) const;
|
||||
size_t jCellCount() const;
|
||||
size_t iCellCount() const;
|
||||
|
||||
std::pair<size_t, size_t> fractureCellAtWellCenter() const;
|
||||
|
||||
private:
|
||||
std::vector<RigFractureCell> m_fractureCells;
|
||||
std::pair<size_t, size_t> m_wellCenterFractureCellIJ;
|
||||
size_t m_iCellCount;
|
||||
size_t m_jCellCount;
|
||||
};
|
||||
@@ -0,0 +1,251 @@
|
||||
#include "RigGeoMechBoreHoleStressCalculator.h"
|
||||
|
||||
//==================================================================================================
|
||||
/// Internal root finding class to find a Well Pressure that gives:
|
||||
/// a) a zero SigmaT for estimating the fracture gradient.
|
||||
/// b) a solution to the Stassi-d'Alia failure criterion for estimating the shear failure gradient.
|
||||
//==================================================================================================
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigGeoMechBoreHoleStressCalculator::RigGeoMechBoreHoleStressCalculator( const caf::Ten3d& tensor,
|
||||
double porePressure,
|
||||
double poissonRatio,
|
||||
double uniaxialCompressiveStrength,
|
||||
int nThetaSubSamples )
|
||||
: m_tensor( tensor )
|
||||
, m_porePressure( porePressure )
|
||||
, m_poissonRatio( poissonRatio )
|
||||
, m_uniaxialCompressiveStrength( uniaxialCompressiveStrength )
|
||||
, m_nThetaSubSamples( nThetaSubSamples )
|
||||
{
|
||||
calculateStressComponents();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Simple bisection method for now
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigGeoMechBoreHoleStressCalculator::solveFractureGradient( double* thetaOut )
|
||||
{
|
||||
MemberFunc fn = &RigGeoMechBoreHoleStressCalculator::sigmaTMinOfMin;
|
||||
return solveSecant( fn, thetaOut );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigGeoMechBoreHoleStressCalculator::solveStassiDalia( double* thetaOut )
|
||||
{
|
||||
MemberFunc fn = &RigGeoMechBoreHoleStressCalculator::stassiDalia;
|
||||
return solveSecant( fn, thetaOut );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Bi-section root finding method: https://en.wikipedia.org/wiki/Bisection_method
|
||||
/// Used as fall-back in case the secant method doesn't converge.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigGeoMechBoreHoleStressCalculator::solveBisection( double minPw, double maxPw, MemberFunc fn, double* thetaOut )
|
||||
{
|
||||
const int N = 50;
|
||||
const double epsilon = 1.0e-10;
|
||||
|
||||
double theta = 0.0;
|
||||
|
||||
std::pair<double, double> largestNegativeValue( 0.0, -std::numeric_limits<double>::infinity() );
|
||||
std::pair<double, double> smallestPositiveValue( 0.0, std::numeric_limits<double>::infinity() );
|
||||
|
||||
for ( int i = 0; i <= N; ++i )
|
||||
{
|
||||
double pw = minPw + ( maxPw - minPw ) * i / static_cast<double>( N );
|
||||
double f_pw = ( this->*fn )( pw, &theta );
|
||||
if ( f_pw >= 0.0 && f_pw < smallestPositiveValue.second )
|
||||
{
|
||||
smallestPositiveValue = std::make_pair( pw, f_pw );
|
||||
}
|
||||
if ( f_pw < 0.0 && f_pw > largestNegativeValue.second )
|
||||
{
|
||||
largestNegativeValue = std::make_pair( pw, f_pw );
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Provide a warning if there was no solution to the equation
|
||||
if ( largestNegativeValue.second == -std::numeric_limits<double>::infinity() )
|
||||
{
|
||||
// No solution. Function is always positive. Pick smallest value.
|
||||
return smallestPositiveValue.first;
|
||||
}
|
||||
if ( smallestPositiveValue.second == std::numeric_limits<double>::infinity() )
|
||||
{
|
||||
// No solution. Function is always negative. Pick largest value.
|
||||
return largestNegativeValue.first;
|
||||
}
|
||||
minPw = largestNegativeValue.first;
|
||||
double minPwFuncVal = largestNegativeValue.second;
|
||||
maxPw = smallestPositiveValue.first;
|
||||
|
||||
double range = std::abs( maxPw - minPw );
|
||||
|
||||
int i = 0;
|
||||
for ( ; i <= N && range > m_porePressure * epsilon; ++i )
|
||||
{
|
||||
double midPw = ( minPw + maxPw ) * 0.5;
|
||||
double midPwFuncVal = ( this->*fn )( midPw, &theta );
|
||||
if ( midPwFuncVal * minPwFuncVal < 0.0 )
|
||||
{
|
||||
maxPw = midPw;
|
||||
}
|
||||
else
|
||||
{
|
||||
minPw = midPw;
|
||||
minPwFuncVal = midPwFuncVal;
|
||||
}
|
||||
range = std::abs( maxPw - minPw );
|
||||
}
|
||||
CVF_ASSERT( i < N ); // Otherwise it hasn't converged
|
||||
|
||||
if ( thetaOut )
|
||||
{
|
||||
*thetaOut = theta;
|
||||
}
|
||||
|
||||
// Return average of minPw and maxPw.
|
||||
return 0.5 * ( maxPw + minPw );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Secant root finding method: https://en.wikipedia.org/wiki/Secant_method
|
||||
/// Basically a Newton's method using finite differences for the derivative.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigGeoMechBoreHoleStressCalculator::solveSecant( MemberFunc fn, double* thetaOut )
|
||||
{
|
||||
const double epsilon = 1.0e-10;
|
||||
const int N = 50;
|
||||
double theta = 0.0;
|
||||
|
||||
double x_0 = 0.0;
|
||||
double f_x0 = ( this->*fn )( x_0, &theta );
|
||||
double x_1 = m_porePressure;
|
||||
double f_x1 = ( this->*fn )( x_1, &theta );
|
||||
double x = m_porePressure;
|
||||
double f_x = 0.0;
|
||||
int i = 0;
|
||||
for ( ; i <= N && std::abs( f_x1 - f_x0 ) > epsilon; ++i )
|
||||
{
|
||||
x = x_1 - f_x1 * ( x_1 - x_0 ) / ( f_x1 - f_x0 );
|
||||
f_x = ( this->*fn )( x, &theta );
|
||||
if ( std::abs( f_x ) < epsilon * m_porePressure ) break;
|
||||
|
||||
// Update iteration variables
|
||||
x_0 = x_1;
|
||||
f_x0 = f_x1;
|
||||
x_1 = x;
|
||||
f_x1 = f_x;
|
||||
}
|
||||
|
||||
if ( i == 0 || i == N || std::abs( f_x ) > epsilon * m_porePressure )
|
||||
{
|
||||
// Fallback to bisection if secant doesn't converge or converged to a wrong solution.
|
||||
return solveBisection( 0.0, m_porePressure * 2.0, fn, thetaOut );
|
||||
}
|
||||
|
||||
if ( thetaOut )
|
||||
{
|
||||
*thetaOut = theta;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigGeoMechBoreHoleStressCalculator::sigmaTMinOfMin( double wellPressure, double* thetaAtMin ) const
|
||||
{
|
||||
CVF_ASSERT( thetaAtMin );
|
||||
double sigma_t_min_min = std::numeric_limits<double>::max();
|
||||
for ( const cvf::Vec4d& stressComponentsForAngle : m_stressComponents )
|
||||
{
|
||||
// Perform all these internal calculations in double to reduce significance errors
|
||||
double sigma_theta = stressComponentsForAngle[1] - wellPressure;
|
||||
const double& sigma_z = stressComponentsForAngle[2];
|
||||
double tauSqrx4 = std::pow( stressComponentsForAngle[3], 2 ) * 4.0;
|
||||
double sigma_t_min =
|
||||
0.5 * ( ( sigma_z + sigma_theta ) - std::sqrt( std::pow( sigma_z - sigma_theta, 2 ) + tauSqrx4 ) ) -
|
||||
m_porePressure;
|
||||
if ( sigma_t_min < sigma_t_min_min )
|
||||
{
|
||||
sigma_t_min_min = sigma_t_min;
|
||||
*thetaAtMin = stressComponentsForAngle[0];
|
||||
}
|
||||
}
|
||||
return sigma_t_min_min;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RigGeoMechBoreHoleStressCalculator::stassiDalia( double wellPressure, double* thetaAtMin ) const
|
||||
{
|
||||
CVF_ASSERT( thetaAtMin );
|
||||
double minStassiDalia = std::numeric_limits<double>::max();
|
||||
for ( const cvf::Vec4d& stressComponentsForAngle : m_stressComponents )
|
||||
{
|
||||
double sigma_theta = stressComponentsForAngle[1] - wellPressure;
|
||||
const double& sigma_z = stressComponentsForAngle[2];
|
||||
double tauSqrx4 = std::pow( stressComponentsForAngle[3], 2 ) * 4.0;
|
||||
|
||||
double sigma_1 = wellPressure - m_porePressure;
|
||||
double sigma_2 = 0.5 * ( ( sigma_z + sigma_theta ) + std::sqrt( std::pow( sigma_z - sigma_theta, 2 ) + tauSqrx4 ) ) -
|
||||
m_porePressure;
|
||||
double sigma_3 = 0.5 * ( ( sigma_z + sigma_theta ) - std::sqrt( std::pow( sigma_z - sigma_theta, 2 ) + tauSqrx4 ) ) -
|
||||
m_porePressure;
|
||||
|
||||
double stassiDalia = std::pow( sigma_1 - sigma_2, 2 ) + std::pow( sigma_2 - sigma_3, 2 ) +
|
||||
std::pow( sigma_1 - sigma_3, 2 ) -
|
||||
2 * m_uniaxialCompressiveStrength * ( sigma_1 + sigma_2 + sigma_3 );
|
||||
|
||||
if ( stassiDalia < minStassiDalia )
|
||||
{
|
||||
minStassiDalia = stassiDalia;
|
||||
*thetaAtMin = stressComponentsForAngle[0];
|
||||
}
|
||||
}
|
||||
return minStassiDalia;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigGeoMechBoreHoleStressCalculator::calculateStressComponents()
|
||||
{
|
||||
m_stressComponents.reserve( m_nThetaSubSamples );
|
||||
|
||||
for ( int i = 0; i < m_nThetaSubSamples; ++i )
|
||||
{
|
||||
double theta = ( i * cvf::PI_F ) / ( m_nThetaSubSamples - 1.0 );
|
||||
cvf::Vec4d stressComponentsForAngle = calculateStressComponentsForSegmentAngle( theta );
|
||||
m_stressComponents.push_back( stressComponentsForAngle );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Vec4d RigGeoMechBoreHoleStressCalculator::calculateStressComponentsForSegmentAngle( double theta ) const
|
||||
{
|
||||
cvf::Vec4d stressComponents;
|
||||
|
||||
const double& sx = m_tensor[caf::Ten3d::SXX];
|
||||
const double& sy = m_tensor[caf::Ten3d::SYY];
|
||||
const double& sz = m_tensor[caf::Ten3d::SZZ];
|
||||
const double& txy = m_tensor[caf::Ten3d::SXY];
|
||||
const double& txz = m_tensor[caf::Ten3d::SZX];
|
||||
const double& tyz = m_tensor[caf::Ten3d::SYZ];
|
||||
|
||||
stressComponents[0] = theta;
|
||||
stressComponents[1] = sx + sy - 2 * ( sx - sy ) * cos( 2 * theta ) - 4 * txy * sin( 2 * theta );
|
||||
stressComponents[2] = sz - m_poissonRatio * ( 2 * ( sx - sy ) * cos( 2 * theta ) + 4 * txy * sin( 2 * theta ) );
|
||||
stressComponents[3] = 2 * ( tyz * cos( theta ) - txz * sin( theta ) );
|
||||
|
||||
return stressComponents;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user