///////////////////////////////////////////////////////////////////////////////// // // 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 // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RimStimPlanModelWellLogCalculator.h" #include "RiaDefines.h" #include "RiaInterpolationTools.h" #include "RiaLogging.h" #include "RiaStimPlanModelDefines.h" #include "RigActiveCellInfo.h" #include "RigEclipseCaseData.h" #include "RigEclipseResultAddress.h" #include "RigEclipseWellLogExtractor.h" #include "RigMainGrid.h" #include "RigResultAccessor.h" #include "RigResultAccessorFactory.h" #include "RigWellLogCurveData.h" #include "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" //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimStimPlanModelWellLogCalculator::RimStimPlanModelWellLogCalculator( RimStimPlanModelCalculator* stimPlanModelCalculator ) : m_stimPlanModelCalculator( stimPlanModelCalculator ) { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimStimPlanModelWellLogCalculator::isMatching( RiaDefines::CurveProperty curveProperty ) const { std::vector 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& values, std::vector& measuredDepthValues, std::vector& tvDepthValues, double& rkbDiff ) const { RiaLogging::debug( QString( "Calculating well log for '%1'." ).arg( caf::AppEnum( curveProperty ).uiText() ) ); std::deque extractionConfigurations = stimPlanModel->extractionConfigurations( curveProperty ); std::deque 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( QString( "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 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 targetMds; std::vector targetTvds; std::vector 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( curveProperty ).uiText() ) .arg( values.size() ) ); return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimStimPlanModelWellLogCalculator::hasMissingValues( const std::vector& values ) { for ( double v : values ) { if ( v == std::numeric_limits::infinity() ) { return true; } } return false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimStimPlanModelWellLogCalculator::replaceMissingValues( std::vector& values, double defaultValue ) { for ( double& v : values ) { if ( v == std::numeric_limits::infinity() ) { v = defaultValue; } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimStimPlanModelWellLogCalculator::replaceMissingValues( std::vector& values, const std::vector& replacementValues ) { CVF_ASSERT( values.size() == replacementValues.size() ); for ( size_t i = 0; i < values.size(); i++ ) { if ( values[i] == std::numeric_limits::infinity() ) { values[i] = replacementValues[i]; } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- cvf::ref 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->inputProperties() ) { // Look for input properties starting with the same name as result definition if ( inputProperty && inputProperty->resultName().startsWith( resultName ) ) { RiaLogging::info( QString( "Found missing values result for %1: %2" ).arg( resultName ).arg( inputProperty->resultName() ) ); RigEclipseResultAddress resultAddress( RiaDefines::ResultCatType::INPUT_PROPERTY, inputProperty->resultName() ); caseData->results( porosityModelType )->ensureKnownResultLoaded( resultAddress ); cvf::ref resAcc = RigResultAccessorFactory::createFromResultAddress( caseData, gridIndex, porosityModelType, timeStepIndex, resultAddress ); return resAcc; } } return nullptr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimStimPlanModelWellLogCalculator::addOverburden( RiaDefines::CurveProperty curveProperty, const RimStimPlanModel* stimPlanModel, std::vector& tvDepthValues, std::vector& measuredDepthValues, std::vector& values ) const { if ( !values.empty() ) { double overburdenHeight = stimPlanModel->overburdenHeight(); double tvdOverburdenBottom = tvDepthValues[0]; double tvdOverburdenTop = tvdOverburdenBottom - overburdenHeight; double overburdenTopValue = std::numeric_limits::infinity(); double overburdenBottomValue = std::numeric_limits::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& tvDepthValues, std::vector& measuredDepthValues, std::vector& 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::infinity(); double underburdenBottomValue = std::numeric_limits::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& netToGross, std::vector& 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() ) ); 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& values, std::vector& measuredDepthValues, std::vector& tvDepthValues, double& rkbDiff ) const { std::deque extractionConfigurations = stimPlanModel->extractionConfigurations( curveProperty ); QString curvePropertyName = caf::AppEnum( 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( resultType ).uiText() ) .arg( caf::AppEnum( eclipseCaseType ).uiText() ) ); 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( QString( "Extraction failed. Tried %1 configurations." ).arg( extractionConfigurations.size() ) ); return false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimStimPlanModelWellLogCalculator::extractValuesForProperty( RiaDefines::CurveProperty curveProperty, const RimStimPlanModel* stimPlanModel, int timeStep, std::vector& values, std::vector& measuredDepthValues, std::vector& 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& values, std::vector& measuredDepthValues, std::vector& 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( curveProperty ).uiText() ) .arg( eclipseCase->caseUserDescription() ) ); RigEclipseWellLogExtractor eclExtractor( eclipseCase->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 resAcc = RigResultAccessorFactory::createFromResultDefinition( eclipseCase->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( curveProperty ).uiText() ) ); } else { RiaLogging::error( QString( "No result found for %1" ).arg( eclipseResultDefinition.resultVariable() ) ); return false; } return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimStimPlanModelWellLogCalculator::replaceMissingValuesWithDefault( RiaDefines::CurveProperty curveProperty, const RimStimPlanModel* stimPlanModel, std::vector& values, std::vector& measuredDepthValues, std::vector& 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 ); // Try to locate a backup accessor (e.g. PORO_1 for PORO) cvf::ref backupResAcc = findMissingValuesAccessor( eclipseCase->eclipseCaseData(), eclipseCase->inputPropertyCollection(), 0, replacementTimeStep, resultVariable ); if ( backupResAcc.notNull() ) { RiaLogging::info( QString( "Reading missing values from input properties for %1." ).arg( resultVariable ) ); std::vector replacementValues; RigEclipseWellLogExtractor eclExtractor( eclipseCase->eclipseCaseData(), wellPathGeometry, "fracture model" ); eclExtractor.curveData( backupResAcc.p(), &replacementValues ); RiaLogging::debug( QString( "Read %1 values for '%2'" ).arg( replacementValues.size() ).arg( 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::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::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& values ) const { // Get the missing data from other curve RiaDefines::CurveProperty replacementProperty = stimPlanModel->getDefaultPropertyForMissingValues( curveProperty ); std::vector initialValues; std::vector initialMds; std::vector initialTvds; double initialRkbDiff = -1.0; calculate( replacementProperty, stimPlanModel, timeStep, initialValues, initialMds, initialTvds, initialRkbDiff ); if ( initialValues.empty() ) { RiaLogging::error( QString( "Empty replacement data found for fracture model curve." ) ); return false; } CVF_ASSERT( values.size() == initialValues.size() ); replaceMissingValues( values, initialValues ); return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::vector& 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& measuredDepths, const std::vector& tvDepthValues, std::vector& 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(); RiaDefines::PorosityModelType porosityModel = RiaDefines::PorosityModelType::MATRIX_MODEL; RiaDefines::ResultCatType resultType = stimPlanModel->eclipseResultCategory( curveProperty ); QString resultName = stimPlanModel->eclipseResultVariable( curveProperty ); if ( resultName.isEmpty() ) { RiaLogging::error( QString( "Invalid result for k layer replacement: %1" ).arg( stimPlanModel->name() ) ); return false; } const std::vector& 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 ) ); int neighborK = static_cast( k ) + moveDirection; const int minK = static_cast( 1 ); const int maxK = static_cast( mainGrid->cellCountK() ); bool isFound = false; while ( !isFound && neighborK >= minK && neighborK <= maxK ) { size_t neighborCellIdx = mainGrid->cellIndexFromIJK( i, j, neighborK ); size_t resultIdx = activeCellInfo->cellResultIndex( neighborCellIdx ); 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 ) ); 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() ) ); } } } return true; }