mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-26 07:00:17 -05:00
Migrate all assert macros in ApplicationLibCode to CAF_ASSERT and remove every use of cvfAssert.h. CVF_ASSERT is replaced one to one. CVF_TIGHT_ASSERT is also replaced by CAF_ASSERT, which is semantically exact: CVF_ENABLE_TIGHT_ASSERTS is 1 only under _DEBUG, and that is what CAF_ASSERT now does. The two CVF_FAIL_MSG sites become CAF_ASSERT( false && "message" ), preserving the message with the idiom already used elsewhere in the code base. Counts before and after: CVF_ASSERT 1044 to 0, CVF_TIGHT_ASSERT 66 to 0, CVF_FAIL_MSG 2 to 0, cvfAssert.h references 154 to 0. Include handling: files that included cvfAssert.h directly now include cafAssert.h instead, includes left dead by the migration are removed, and files that were relying on cvfAssert.h transitively get an explicit cafAssert.h. Files that reach cafAssert.h through another caf header are left unchanged; a missing include here is a compile error, not a silently disabled assert. ResultStatisticsCache links only LibCore and therefore had no path to cafAssert.h. Add the cafPdmCore directory as a private include path rather than linking the library, since cafAssert.h is header only. Note that this stops these asserts from firing in Release and RelWithDebInfo, where CVF_ASSERT was previously active.
850 lines
40 KiB
C++
850 lines
40 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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 "RimStimPlanModelWellLogCalculator.h"
|
|
|
|
#include "RiaDefines.h"
|
|
#include "RiaInterpolationTools.h"
|
|
#include "RiaLogging.h"
|
|
#include "RiaQStringFormatter.h"
|
|
#include "RiaStimPlanModelDefines.h"
|
|
|
|
#include "RigActiveCellInfo.h"
|
|
#include "RigCaseCellResultsData.h"
|
|
#include "RigEclipseCaseData.h"
|
|
#include "RigEclipseResultAddress.h"
|
|
#include "RigMainGrid.h"
|
|
#include "RigResultAccessor.h"
|
|
#include "RigResultAccessorFactory.h"
|
|
#include "Well/RigEclipseWellLogExtractor.h"
|
|
#include "Well/RigWellLogCurveData.h"
|
|
#include "Well/RigWellPath.h"
|
|
|
|
#include "RimCase.h"
|
|
#include "RimEclipseCase.h"
|
|
#include "RimEclipseInputProperty.h"
|
|
#include "RimEclipseInputPropertyCollection.h"
|
|
#include "RimEclipseResultDefinition.h"
|
|
#include "RimExtractionConfiguration.h"
|
|
#include "RimModeledWellPath.h"
|
|
#include "RimNonNetLayers.h"
|
|
#include "RimStimPlanModel.h"
|
|
#include "RimStimPlanModelCalculator.h"
|
|
#include "RimStimPlanModelPressureCalculator.h"
|
|
#include "RimStimPlanModelTemplate.h"
|
|
#include "RimWellPath.h"
|
|
|
|
#include "cafAssert.h"
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimStimPlanModelWellLogCalculator::RimStimPlanModelWellLogCalculator( RimStimPlanModelCalculator* stimPlanModelCalculator )
|
|
: m_stimPlanModelCalculator( stimPlanModelCalculator )
|
|
{
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimStimPlanModelWellLogCalculator::isMatching( RiaDefines::CurveProperty curveProperty ) const
|
|
{
|
|
std::vector<RiaDefines::CurveProperty> matching = {
|
|
RiaDefines::CurveProperty::FACIES,
|
|
RiaDefines::CurveProperty::POROSITY,
|
|
RiaDefines::CurveProperty::POROSITY_UNSCALED,
|
|
RiaDefines::CurveProperty::PERMEABILITY_X,
|
|
RiaDefines::CurveProperty::PERMEABILITY_Z,
|
|
RiaDefines::CurveProperty::NET_TO_GROSS,
|
|
RiaDefines::CurveProperty::EQLNUM,
|
|
};
|
|
|
|
return std::find( matching.begin(), matching.end(), curveProperty ) != matching.end();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimStimPlanModelWellLogCalculator::calculate( RiaDefines::CurveProperty curveProperty,
|
|
const RimStimPlanModel* stimPlanModel,
|
|
int timeStep,
|
|
std::vector<double>& values,
|
|
std::vector<double>& measuredDepthValues,
|
|
std::vector<double>& tvDepthValues,
|
|
double& rkbDiff ) const
|
|
{
|
|
RiaLogging::debug( std::format( "Calculating well log for '{}'.", caf::AppEnum<RiaDefines::CurveProperty>( curveProperty ).uiText() ) );
|
|
|
|
std::deque<RimExtractionConfiguration> extractionConfigurations = stimPlanModel->extractionConfigurations( curveProperty );
|
|
|
|
std::deque<RimStimPlanModel::MissingValueStrategy> missingValueStratgies = stimPlanModel->missingValueStrategies( curveProperty );
|
|
|
|
if ( extractionConfigurations.empty() )
|
|
{
|
|
if ( !extractValuesForProperty( curveProperty, stimPlanModel, timeStep, values, measuredDepthValues, tvDepthValues, rkbDiff ) )
|
|
{
|
|
if ( std::find( missingValueStratgies.begin(), missingValueStratgies.end(), RimStimPlanModel::MissingValueStrategy::DEFAULT_VALUE ) !=
|
|
missingValueStratgies.end() )
|
|
{
|
|
RiaLogging::warning( "Extraction failed. Trying fallback" );
|
|
if ( !replaceMissingValuesWithDefault( curveProperty, stimPlanModel, values, measuredDepthValues, tvDepthValues, rkbDiff ) )
|
|
{
|
|
RiaLogging::error( "Fallback failed too." );
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !extractValuesForPropertyWithConfigurations( curveProperty, stimPlanModel, timeStep, values, measuredDepthValues, tvDepthValues, rkbDiff ) )
|
|
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
double overburdenHeight = stimPlanModel->overburdenHeight();
|
|
if ( overburdenHeight > 0.0 )
|
|
{
|
|
addOverburden( curveProperty, stimPlanModel, tvDepthValues, measuredDepthValues, values );
|
|
}
|
|
|
|
double underburdenHeight = stimPlanModel->underburdenHeight();
|
|
if ( underburdenHeight > 0.0 )
|
|
{
|
|
addUnderburden( curveProperty, stimPlanModel, tvDepthValues, measuredDepthValues, values );
|
|
}
|
|
|
|
while ( hasMissingValues( values ) && !missingValueStratgies.empty() )
|
|
{
|
|
RimStimPlanModel::MissingValueStrategy strategy = missingValueStratgies.front();
|
|
missingValueStratgies.pop_front();
|
|
|
|
if ( strategy == RimStimPlanModel::MissingValueStrategy::DEFAULT_VALUE )
|
|
{
|
|
if ( !replaceMissingValuesWithDefault( curveProperty, stimPlanModel, values, measuredDepthValues, tvDepthValues, rkbDiff ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if ( strategy == RimStimPlanModel::MissingValueStrategy::LINEAR_INTERPOLATION )
|
|
{
|
|
RiaInterpolationTools::interpolateMissingValues( measuredDepthValues, values );
|
|
}
|
|
else if ( strategy == RimStimPlanModel::MissingValueStrategy::OTHER_CURVE_PROPERTY )
|
|
{
|
|
if ( !replaceMissingValuesWithOtherProperty( curveProperty, stimPlanModel, timeStep, values ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if ( strategy == RimStimPlanModel::MissingValueStrategy::CELLS_ABOVE )
|
|
{
|
|
// K-1 is up
|
|
int kDirection = -1;
|
|
if ( !replaceMissingValuesWithOtherKLayer( curveProperty, stimPlanModel, timeStep, measuredDepthValues, tvDepthValues, values, kDirection ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if ( strategy == RimStimPlanModel::MissingValueStrategy::CELLS_BELOW )
|
|
{
|
|
// K+1 is down
|
|
int kDirection = 1;
|
|
if ( !replaceMissingValuesWithOtherKLayer( curveProperty, stimPlanModel, timeStep, measuredDepthValues, tvDepthValues, values, kDirection ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( stimPlanModel->isScaledByNetToGross( curveProperty ) )
|
|
{
|
|
std::vector<double> netToGross = m_stimPlanModelCalculator->extractValues( RiaDefines::CurveProperty::NET_TO_GROSS, timeStep );
|
|
|
|
scaleByNetToGross( stimPlanModel, netToGross, values );
|
|
}
|
|
|
|
// Extracted well log needs to be sampled at same depths as well logs from static grid.
|
|
// If the well log is extracted from a different model it needs to be resampled.
|
|
if ( curveProperty != RiaDefines::CurveProperty::FACIES )
|
|
{
|
|
std::vector<double> targetMds;
|
|
std::vector<double> targetTvds;
|
|
std::vector<double> faciesValues;
|
|
if ( !stimPlanModel->calculator()->extractCurveData( RiaDefines::CurveProperty::FACIES, timeStep, faciesValues, targetMds, targetTvds, rkbDiff ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( targetMds.size() != measuredDepthValues.size() )
|
|
{
|
|
RiaLogging::info( "Resampling data to fit static case." );
|
|
auto [tvds, mds, results] =
|
|
RimStimPlanModelPressureCalculator::interpolateMissingValues( targetTvds, targetMds, measuredDepthValues, values );
|
|
tvDepthValues = tvds;
|
|
measuredDepthValues = mds;
|
|
values = results;
|
|
}
|
|
}
|
|
|
|
RiaLogging::debug( QString( "Well log for '%1' done. Size: %2." )
|
|
.arg( caf::AppEnum<RiaDefines::CurveProperty>( curveProperty ).uiText() )
|
|
.arg( values.size() )
|
|
.toStdString() );
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimStimPlanModelWellLogCalculator::hasMissingValues( const std::vector<double>& values )
|
|
{
|
|
for ( double v : values )
|
|
{
|
|
if ( v == std::numeric_limits<double>::infinity() )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimStimPlanModelWellLogCalculator::replaceMissingValues( std::vector<double>& values, double defaultValue )
|
|
{
|
|
for ( double& v : values )
|
|
{
|
|
if ( v == std::numeric_limits<double>::infinity() )
|
|
{
|
|
v = defaultValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimStimPlanModelWellLogCalculator::replaceMissingValues( std::vector<double>& values, const std::vector<double>& replacementValues )
|
|
{
|
|
CAF_ASSERT( values.size() == replacementValues.size() );
|
|
for ( size_t i = 0; i < values.size(); i++ )
|
|
{
|
|
if ( values[i] == std::numeric_limits<double>::infinity() )
|
|
{
|
|
values[i] = replacementValues[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
cvf::ref<RigResultAccessor>
|
|
RimStimPlanModelWellLogCalculator::findMissingValuesAccessor( RigEclipseCaseData* caseData,
|
|
RimEclipseInputPropertyCollection* inputPropertyCollection,
|
|
int gridIndex,
|
|
int timeStepIndex,
|
|
const QString& resultName ) const
|
|
{
|
|
if ( resultName.isEmpty() ) return nullptr;
|
|
|
|
RiaDefines::PorosityModelType porosityModelType = RiaDefines::PorosityModelType::MATRIX_MODEL;
|
|
|
|
for ( RimEclipseInputProperty* inputProperty : inputPropertyCollection->items() )
|
|
{
|
|
// Look for input properties starting with the same name as result definition
|
|
if ( inputProperty && inputProperty->resultName().startsWith( resultName ) )
|
|
{
|
|
RiaLogging::info( std::format( "Found missing values result for {}: {}", resultName, inputProperty->resultName() ) );
|
|
|
|
RigEclipseResultAddress resultAddress( RiaDefines::ResultCatType::INPUT_PROPERTY, inputProperty->resultName() );
|
|
caseData->results( porosityModelType )->ensureKnownResultLoaded( resultAddress );
|
|
cvf::ref<RigResultAccessor> resAcc =
|
|
RigResultAccessorFactory::createFromResultAddress( caseData, gridIndex, porosityModelType, timeStepIndex, resultAddress );
|
|
|
|
return resAcc;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimStimPlanModelWellLogCalculator::addOverburden( RiaDefines::CurveProperty curveProperty,
|
|
const RimStimPlanModel* stimPlanModel,
|
|
std::vector<double>& tvDepthValues,
|
|
std::vector<double>& measuredDepthValues,
|
|
std::vector<double>& values ) const
|
|
{
|
|
if ( !values.empty() )
|
|
{
|
|
double overburdenHeight = stimPlanModel->overburdenHeight();
|
|
double tvdOverburdenBottom = tvDepthValues[0];
|
|
double tvdOverburdenTop = tvdOverburdenBottom - overburdenHeight;
|
|
|
|
double overburdenTopValue = std::numeric_limits<double>::infinity();
|
|
double overburdenBottomValue = std::numeric_limits<double>::infinity();
|
|
if ( stimPlanModel->burdenStrategy( curveProperty ) == RimStimPlanModel::BurdenStrategy::DEFAULT_VALUE )
|
|
{
|
|
overburdenTopValue = stimPlanModel->getDefaultForMissingOverburdenValue( curveProperty );
|
|
overburdenBottomValue = overburdenTopValue;
|
|
}
|
|
else
|
|
{
|
|
double gradient = stimPlanModel->getOverburdenGradient( curveProperty );
|
|
overburdenBottomValue = values[0];
|
|
overburdenTopValue = overburdenBottomValue + gradient * -overburdenHeight;
|
|
}
|
|
|
|
// Prepend the new "fake" depth for start of overburden
|
|
tvDepthValues.insert( tvDepthValues.begin(), tvdOverburdenBottom );
|
|
tvDepthValues.insert( tvDepthValues.begin(), tvdOverburdenTop );
|
|
|
|
// TODO: this is not always correct
|
|
double mdTop = measuredDepthValues[0];
|
|
measuredDepthValues.insert( measuredDepthValues.begin(), mdTop );
|
|
measuredDepthValues.insert( measuredDepthValues.begin(), mdTop - overburdenHeight );
|
|
|
|
values.insert( values.begin(), overburdenBottomValue );
|
|
values.insert( values.begin(), overburdenTopValue );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimStimPlanModelWellLogCalculator::addUnderburden( RiaDefines::CurveProperty curveProperty,
|
|
const RimStimPlanModel* stimPlanModel,
|
|
std::vector<double>& tvDepthValues,
|
|
std::vector<double>& measuredDepthValues,
|
|
std::vector<double>& values ) const
|
|
{
|
|
if ( !values.empty() )
|
|
{
|
|
size_t lastIndex = tvDepthValues.size() - 1;
|
|
|
|
double underburdenHeight = stimPlanModel->underburdenHeight();
|
|
double tvdUnderburdenTop = tvDepthValues[lastIndex];
|
|
double tvdUnderburdenBottom = tvdUnderburdenTop + underburdenHeight;
|
|
|
|
double underburdenTopValue = std::numeric_limits<double>::infinity();
|
|
double underburdenBottomValue = std::numeric_limits<double>::infinity();
|
|
if ( stimPlanModel->burdenStrategy( curveProperty ) == RimStimPlanModel::BurdenStrategy::DEFAULT_VALUE )
|
|
{
|
|
underburdenTopValue = stimPlanModel->getDefaultForMissingUnderburdenValue( curveProperty );
|
|
underburdenBottomValue = underburdenTopValue;
|
|
}
|
|
else
|
|
{
|
|
double gradient = stimPlanModel->getUnderburdenGradient( curveProperty );
|
|
underburdenTopValue = values[lastIndex];
|
|
underburdenBottomValue = underburdenTopValue + gradient * underburdenHeight;
|
|
}
|
|
|
|
// Append the new "fake" depth for start of underburden
|
|
tvDepthValues.push_back( tvdUnderburdenTop );
|
|
tvDepthValues.push_back( tvdUnderburdenBottom );
|
|
|
|
// Append the new "fake" md
|
|
// TODO: check if this is correct???
|
|
double mdBottom = measuredDepthValues[lastIndex];
|
|
measuredDepthValues.push_back( mdBottom );
|
|
measuredDepthValues.push_back( mdBottom + underburdenHeight );
|
|
|
|
values.push_back( underburdenTopValue );
|
|
values.push_back( underburdenBottomValue );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimStimPlanModelWellLogCalculator::scaleByNetToGross( const RimStimPlanModel* stimPlanModel,
|
|
const std::vector<double>& netToGross,
|
|
std::vector<double>& values )
|
|
{
|
|
if ( netToGross.size() != values.size() )
|
|
{
|
|
RiaLogging::error( QString( "Different sizes for net to gross calculation. NTG length: %1. Values length: %2" )
|
|
.arg( netToGross.size() )
|
|
.arg( values.size() )
|
|
.toStdString() );
|
|
return;
|
|
}
|
|
|
|
double netToGrossCutoff = 1.0;
|
|
bool useNetToGross = false;
|
|
|
|
if ( stimPlanModel->stimPlanModelTemplate() && stimPlanModel->stimPlanModelTemplate()->nonNetLayers() )
|
|
{
|
|
netToGrossCutoff = stimPlanModel->stimPlanModelTemplate()->nonNetLayers()->cutOff();
|
|
useNetToGross = !netToGross.empty() && stimPlanModel->stimPlanModelTemplate()->nonNetLayers()->isChecked();
|
|
}
|
|
|
|
for ( size_t i = 0; i < values.size(); i++ )
|
|
{
|
|
double ntg = netToGross[i];
|
|
if ( useNetToGross && ntg <= netToGrossCutoff )
|
|
{
|
|
values[i] = ntg * values[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimStimPlanModelWellLogCalculator::extractValuesForPropertyWithConfigurations( RiaDefines::CurveProperty curveProperty,
|
|
const RimStimPlanModel* stimPlanModel,
|
|
int timeStep,
|
|
std::vector<double>& values,
|
|
std::vector<double>& measuredDepthValues,
|
|
std::vector<double>& tvDepthValues,
|
|
double& rkbDiff ) const
|
|
{
|
|
std::deque<RimExtractionConfiguration> extractionConfigurations = stimPlanModel->extractionConfigurations( curveProperty );
|
|
|
|
QString curvePropertyName = caf::AppEnum<RiaDefines::CurveProperty>( curveProperty ).uiText();
|
|
|
|
for ( auto extractionConfig : extractionConfigurations )
|
|
{
|
|
RiaDefines::ResultCatType resultType = extractionConfig.resultCategory;
|
|
QString resultVariable = extractionConfig.resultVariable;
|
|
RimExtractionConfiguration::EclipseCaseType eclipseCaseType = extractionConfig.eclipseCaseType;
|
|
|
|
RiaLogging::info( QString( "Trying extraction option for '%1': result property: '%2' result type: '%3' case type: '%4'" )
|
|
.arg( curvePropertyName )
|
|
.arg( resultVariable )
|
|
.arg( caf::AppEnum<RiaDefines::ResultCatType>( resultType ).uiText() )
|
|
.arg( caf::AppEnum<RimExtractionConfiguration::EclipseCaseType>( eclipseCaseType ).uiText() )
|
|
.toStdString() );
|
|
|
|
RimEclipseCase* eclipseCase = stimPlanModel->eclipseCaseForType( eclipseCaseType );
|
|
|
|
if ( !eclipseCase )
|
|
{
|
|
RiaLogging::info( "Skipping extraction config due to missing model." );
|
|
}
|
|
else
|
|
{
|
|
bool isOk = extractValuesForProperty( curveProperty,
|
|
stimPlanModel,
|
|
eclipseCase,
|
|
resultType,
|
|
resultVariable,
|
|
timeStep,
|
|
values,
|
|
measuredDepthValues,
|
|
tvDepthValues,
|
|
rkbDiff );
|
|
if ( isOk )
|
|
{
|
|
RiaLogging::info( "Extraction succeeded" );
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
RiaLogging::info( std::format( "Extraction failed. Tried {} configurations.", extractionConfigurations.size() ) );
|
|
return false;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimStimPlanModelWellLogCalculator::extractValuesForProperty( RiaDefines::CurveProperty curveProperty,
|
|
const RimStimPlanModel* stimPlanModel,
|
|
int timeStep,
|
|
std::vector<double>& values,
|
|
std::vector<double>& measuredDepthValues,
|
|
std::vector<double>& tvDepthValues,
|
|
double& rkbDiff ) const
|
|
{
|
|
RimEclipseCase* eclipseCase = stimPlanModel->eclipseCaseForProperty( curveProperty );
|
|
if ( !eclipseCase ) return false;
|
|
|
|
RiaDefines::ResultCatType resultType = stimPlanModel->eclipseResultCategory( curveProperty );
|
|
QString resultVariable = stimPlanModel->eclipseResultVariable( curveProperty );
|
|
|
|
return extractValuesForProperty( curveProperty,
|
|
stimPlanModel,
|
|
eclipseCase,
|
|
resultType,
|
|
resultVariable,
|
|
timeStep,
|
|
values,
|
|
measuredDepthValues,
|
|
tvDepthValues,
|
|
rkbDiff );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimStimPlanModelWellLogCalculator::extractValuesForProperty( RiaDefines::CurveProperty curveProperty,
|
|
const RimStimPlanModel* stimPlanModel,
|
|
RimEclipseCase* eclipseCase,
|
|
RiaDefines::ResultCatType resultCategory,
|
|
const QString resultVariable,
|
|
int timeStep,
|
|
std::vector<double>& values,
|
|
std::vector<double>& measuredDepthValues,
|
|
std::vector<double>& tvDepthValues,
|
|
double& rkbDiff ) const
|
|
{
|
|
if ( !stimPlanModel->thicknessDirectionWellPath() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
RigWellPath* wellPathGeometry = stimPlanModel->thicknessDirectionWellPath()->wellPathGeometry();
|
|
if ( !wellPathGeometry )
|
|
{
|
|
RiaLogging::error( "No well path geometry found for well log exctration" );
|
|
return false;
|
|
}
|
|
|
|
RiaLogging::info( QString( "Extracting values for '%1' from grid '%2'." )
|
|
.arg( caf::AppEnum<RiaDefines::CurveProperty>( curveProperty ).uiText() )
|
|
.arg( eclipseCase->caseUserDescription() )
|
|
.toStdString() );
|
|
|
|
auto eclipseCaseData = eclipseCase->eclipseCaseData();
|
|
if ( !eclipseCaseData )
|
|
{
|
|
RiaLogging::error( "Missing Eclipse case data." );
|
|
return false;
|
|
}
|
|
|
|
RigEclipseWellLogExtractor eclExtractor( eclipseCaseData, wellPathGeometry, "fracture model" );
|
|
|
|
measuredDepthValues = eclExtractor.cellIntersectionMDs();
|
|
tvDepthValues = eclExtractor.cellIntersectionTVDs();
|
|
rkbDiff = eclExtractor.wellPathGeometry()->rkbDiff();
|
|
|
|
RimEclipseResultDefinition eclipseResultDefinition;
|
|
eclipseResultDefinition.setEclipseCase( eclipseCase );
|
|
eclipseResultDefinition.setResultType( resultCategory );
|
|
eclipseResultDefinition.setPorosityModel( RiaDefines::PorosityModelType::MATRIX_MODEL );
|
|
eclipseResultDefinition.setResultVariable( resultVariable );
|
|
|
|
eclipseResultDefinition.loadResult();
|
|
|
|
if ( resultCategory != RiaDefines::ResultCatType::DYNAMIC_NATIVE || curveProperty == RiaDefines::CurveProperty::INITIAL_PRESSURE )
|
|
{
|
|
timeStep = 0;
|
|
}
|
|
|
|
cvf::ref<RigResultAccessor> resAcc =
|
|
RigResultAccessorFactory::createFromResultDefinition( eclipseCaseData, 0, timeStep, &eclipseResultDefinition );
|
|
|
|
if ( resAcc.notNull() )
|
|
{
|
|
eclExtractor.curveData( resAcc.p(), &values );
|
|
RiaLogging::info( QString( "Extracted values %1 from grid '%2' for '%3'." )
|
|
.arg( values.size() )
|
|
.arg( eclipseCase->caseUserDescription() )
|
|
.arg( caf::AppEnum<RiaDefines::CurveProperty>( curveProperty ).uiText() )
|
|
.toStdString() );
|
|
}
|
|
else
|
|
{
|
|
RiaLogging::error( std::format( "No result found for {}", eclipseResultDefinition.resultVariable() ) );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimStimPlanModelWellLogCalculator::replaceMissingValuesWithDefault( RiaDefines::CurveProperty curveProperty,
|
|
const RimStimPlanModel* stimPlanModel,
|
|
std::vector<double>& values,
|
|
std::vector<double>& measuredDepthValues,
|
|
std::vector<double>& tvDepthValues,
|
|
double& rkbDiff ) const
|
|
{
|
|
RimEclipseCase* eclipseCase = stimPlanModel->eclipseCaseForProperty( curveProperty );
|
|
|
|
if ( !stimPlanModel->thicknessDirectionWellPath() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
RigWellPath* wellPathGeometry = stimPlanModel->thicknessDirectionWellPath()->wellPathGeometry();
|
|
if ( !wellPathGeometry )
|
|
{
|
|
RiaLogging::error( "No well path geometry found for well log exctration" );
|
|
return false;
|
|
}
|
|
|
|
// Input properties must use first time step
|
|
int replacementTimeStep = 0;
|
|
|
|
QString resultVariable = stimPlanModel->eclipseResultVariable( curveProperty );
|
|
|
|
auto eclipseCaseData = eclipseCase->eclipseCaseData();
|
|
if ( !eclipseCaseData )
|
|
{
|
|
RiaLogging::error( "Missing Eclipse case data." );
|
|
return false;
|
|
}
|
|
|
|
// Try to locate a backup accessor (e.g. PORO_1 for PORO)
|
|
cvf::ref<RigResultAccessor> backupResAcc =
|
|
findMissingValuesAccessor( eclipseCaseData, eclipseCase->inputPropertyCollection(), 0, replacementTimeStep, resultVariable );
|
|
|
|
if ( backupResAcc.notNull() )
|
|
{
|
|
RiaLogging::info( std::format( "Reading missing values from input properties for {}.", resultVariable ) );
|
|
std::vector<double> replacementValues;
|
|
|
|
RigEclipseWellLogExtractor eclExtractor( eclipseCase->eclipseCaseData(), wellPathGeometry, "fracture model" );
|
|
eclExtractor.curveData( backupResAcc.p(), &replacementValues );
|
|
|
|
RiaLogging::debug( std::format( "Read {} values for '{}'", replacementValues.size(), resultVariable ) );
|
|
|
|
if ( values.empty() )
|
|
{
|
|
values = replacementValues;
|
|
measuredDepthValues = eclExtractor.cellIntersectionMDs();
|
|
tvDepthValues = eclExtractor.cellIntersectionTVDs();
|
|
rkbDiff = eclExtractor.wellPathGeometry()->rkbDiff();
|
|
}
|
|
else
|
|
{
|
|
double overburdenHeight = stimPlanModel->overburdenHeight();
|
|
if ( overburdenHeight > 0.0 )
|
|
{
|
|
double defaultOverburdenValue = std::numeric_limits<double>::infinity();
|
|
if ( stimPlanModel->burdenStrategy( curveProperty ) == RimStimPlanModel::BurdenStrategy::DEFAULT_VALUE )
|
|
{
|
|
defaultOverburdenValue = stimPlanModel->getDefaultForMissingOverburdenValue( curveProperty );
|
|
}
|
|
|
|
replacementValues.insert( replacementValues.begin(), defaultOverburdenValue );
|
|
replacementValues.insert( replacementValues.begin(), defaultOverburdenValue );
|
|
}
|
|
|
|
double underburdenHeight = stimPlanModel->underburdenHeight();
|
|
if ( underburdenHeight > 0.0 )
|
|
{
|
|
double defaultUnderburdenValue = std::numeric_limits<double>::infinity();
|
|
if ( stimPlanModel->burdenStrategy( curveProperty ) == RimStimPlanModel::BurdenStrategy::DEFAULT_VALUE )
|
|
{
|
|
defaultUnderburdenValue = stimPlanModel->getDefaultForMissingUnderburdenValue( curveProperty );
|
|
}
|
|
|
|
replacementValues.push_back( defaultUnderburdenValue );
|
|
replacementValues.push_back( defaultUnderburdenValue );
|
|
}
|
|
|
|
replaceMissingValues( values, replacementValues );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RiaLogging::debug( "No backup result accessor found." );
|
|
}
|
|
|
|
// If the backup accessor is not found, or does not provide all the missing values:
|
|
// use default value from the fracture model
|
|
if ( !backupResAcc.notNull() || hasMissingValues( values ) )
|
|
{
|
|
double defaultValue = stimPlanModel->getDefaultForMissingValue( curveProperty );
|
|
replaceMissingValues( values, defaultValue );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimStimPlanModelWellLogCalculator::replaceMissingValuesWithOtherProperty( RiaDefines::CurveProperty curveProperty,
|
|
const RimStimPlanModel* stimPlanModel,
|
|
int timeStep,
|
|
std::vector<double>& values ) const
|
|
{
|
|
// Get the missing data from other curve
|
|
RiaDefines::CurveProperty replacementProperty = stimPlanModel->getDefaultPropertyForMissingValues( curveProperty );
|
|
|
|
std::vector<double> initialValues;
|
|
std::vector<double> initialMds;
|
|
std::vector<double> initialTvds;
|
|
double initialRkbDiff = -1.0;
|
|
calculate( replacementProperty, stimPlanModel, timeStep, initialValues, initialMds, initialTvds, initialRkbDiff );
|
|
|
|
if ( initialValues.empty() )
|
|
{
|
|
RiaLogging::error( "Empty replacement data found for fracture model curve." );
|
|
return false;
|
|
}
|
|
|
|
CAF_ASSERT( values.size() == initialValues.size() );
|
|
replaceMissingValues( values, initialValues );
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
const std::vector<double>& RimStimPlanModelWellLogCalculator::loadResults( RigEclipseCaseData* caseData,
|
|
RiaDefines::PorosityModelType porosityModel,
|
|
RiaDefines::ResultCatType resultType,
|
|
const QString& propertyName )
|
|
{
|
|
// TODO: is this always enough?
|
|
auto resultData = caseData->results( porosityModel );
|
|
|
|
int timeStepIndex = 0;
|
|
RigEclipseResultAddress resultAddress( resultType, propertyName );
|
|
|
|
if ( !resultData->hasResultEntry( resultAddress ) && resultType != RiaDefines::ResultCatType::INPUT_PROPERTY )
|
|
{
|
|
return loadResults( caseData, porosityModel, RiaDefines::ResultCatType::INPUT_PROPERTY, propertyName );
|
|
}
|
|
|
|
CAF_ASSERT( resultData->hasResultEntry( resultAddress ) );
|
|
resultData->ensureKnownResultLoaded( resultAddress );
|
|
return caseData->results( porosityModel )->cellScalarResults( resultAddress, timeStepIndex );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimStimPlanModelWellLogCalculator::replaceMissingValuesWithOtherKLayer( RiaDefines::CurveProperty curveProperty,
|
|
const RimStimPlanModel* stimPlanModel,
|
|
int timeStep,
|
|
const std::vector<double>& measuredDepths,
|
|
const std::vector<double>& tvDepthValues,
|
|
std::vector<double>& values,
|
|
int moveDirection ) const
|
|
{
|
|
RimEclipseCase* eclipseCase = stimPlanModel->eclipseCaseForProperty( curveProperty );
|
|
if ( !eclipseCase ) return false;
|
|
|
|
if ( !stimPlanModel->thicknessDirectionWellPath() ) return false;
|
|
|
|
RigWellPath* wellPathGeometry = stimPlanModel->thicknessDirectionWellPath()->wellPathGeometry();
|
|
if ( !wellPathGeometry ) return false;
|
|
|
|
const RigMainGrid* mainGrid = eclipseCase->mainGrid();
|
|
|
|
RigEclipseCaseData* caseData = eclipseCase->eclipseCaseData();
|
|
if ( !caseData )
|
|
{
|
|
RiaLogging::error( "Missing Eclipse case data." );
|
|
return false;
|
|
}
|
|
|
|
RiaDefines::PorosityModelType porosityModel = RiaDefines::PorosityModelType::MATRIX_MODEL;
|
|
|
|
RiaDefines::ResultCatType resultType = stimPlanModel->eclipseResultCategory( curveProperty );
|
|
QString resultName = stimPlanModel->eclipseResultVariable( curveProperty );
|
|
if ( resultName.isEmpty() )
|
|
{
|
|
RiaLogging::error( std::format( "Invalid result for k layer replacement: {}", stimPlanModel->name() ) );
|
|
return false;
|
|
}
|
|
|
|
const std::vector<double>& cellValues = loadResults( caseData, porosityModel, resultType, resultName );
|
|
auto activeCellInfo = caseData->activeCellInfo( porosityModel );
|
|
|
|
for ( size_t idx = 0; idx < values.size(); idx++ )
|
|
{
|
|
if ( std::isinf( values[idx] ) )
|
|
{
|
|
double measuredDepth = measuredDepths[idx];
|
|
double tvd = tvDepthValues[idx];
|
|
cvf::Vec3d position = wellPathGeometry->interpolatedPointAlongWellPath( measuredDepth );
|
|
|
|
size_t cellIdx = mainGrid->findReservoirCellIndexFromPoint( position );
|
|
|
|
if ( cellIdx != cvf::UNDEFINED_SIZE_T )
|
|
{
|
|
size_t i;
|
|
size_t j;
|
|
size_t k;
|
|
bool isValid = mainGrid->ijkFromCellIndex( cellIdx, &i, &j, &k );
|
|
if ( isValid )
|
|
{
|
|
RiaLogging::debug( QString( "K-Layer replacement: Replace missing value at MD: %1 TVD: %2. Cell [%3, %4, %5]" )
|
|
.arg( measuredDepth )
|
|
.arg( tvd )
|
|
.arg( i + 1 )
|
|
.arg( j + 1 )
|
|
.arg( k + 1 )
|
|
.toStdString() );
|
|
|
|
int neighborK = static_cast<int>( k ) + moveDirection;
|
|
const int minK = static_cast<int>( 1 );
|
|
const int maxK = static_cast<int>( mainGrid->cellCountK() );
|
|
|
|
bool isFound = false;
|
|
while ( !isFound && neighborK >= minK && neighborK <= maxK )
|
|
{
|
|
size_t neighborCellIdx = mainGrid->cellIndexFromIJK( i, j, neighborK );
|
|
size_t resultIdx = activeCellInfo->cellResultIndex( ReservoirCellIndex( neighborCellIdx ) ).value();
|
|
|
|
if ( neighborCellIdx != cvf::UNDEFINED_SIZE_T && resultIdx < cellValues.size() )
|
|
{
|
|
double neighborValue = cellValues[resultIdx];
|
|
|
|
if ( !std::isinf( neighborValue ) )
|
|
{
|
|
RiaLogging::info( QString( "Found value. [%1, %2, %3]. Idx=%4 Value=%5." )
|
|
.arg( i + 1 )
|
|
.arg( j + 1 )
|
|
.arg( neighborK + 1 )
|
|
.arg( neighborCellIdx )
|
|
.arg( neighborValue )
|
|
.toStdString() );
|
|
isFound = true;
|
|
values[idx] = neighborValue;
|
|
}
|
|
}
|
|
|
|
neighborK += moveDirection;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RiaLogging::debug( QString( "K-Layer Replacement: Invalid cell idx: MD=%1 TVD=%2 Pos: [%3 %4 %5]" )
|
|
.arg( measuredDepth )
|
|
.arg( tvd )
|
|
.arg( position.x() )
|
|
.arg( position.y() )
|
|
.arg( position.z() )
|
|
.toStdString() );
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|