Rename ApplicationCode to ApplicationLibCode

This commit is contained in:
Gaute Lindkvist
2021-01-11 15:27:45 +01:00
parent 751df1a421
commit 81699db187
3242 changed files with 0 additions and 0 deletions
@@ -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;
};
@@ -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 ) );
}
@@ -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;
};
@@ -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;
}
@@ -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;
};
@@ -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;
}
@@ -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;
};
@@ -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;
}
@@ -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