ResInsight/ApplicationLibCode/ReservoirDataModel/RigEnsembleFractureStatisticsCalculator.cpp
Magne Sjaastad 952e766c2f
Update clang-format.yml (#10068)
* Update to clang-format-15
Removed two custom .clang-format files in subfolders of AppFwk

* Fixes by clang-format
2023-04-13 07:05:53 +02:00

444 lines
20 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 "RigEnsembleFractureStatisticsCalculator.h"
#include "RiaDefines.h"
#include "RiaEclipseUnitTools.h"
#include "RiaNumberFormat.h"
#include "RiaWeightedMeanCalculator.h"
#include "RigFractureCell.h"
#include "RigFractureGrid.h"
#include "RigHistogramData.h"
#include "RigStatisticsMath.h"
#include "RigStimPlanFractureDefinition.h"
#include "RigTransmissibilityEquations.h"
#include "RimEnsembleFractureStatistics.h"
#include "RimStimPlanFractureTemplate.h"
#include "cafAppEnum.h"
#include "cvfObject.h"
#include <limits>
namespace caf
{
template <>
void caf::AppEnum<RigEnsembleFractureStatisticsCalculator::PropertyType>::setUp()
{
addItem( RigEnsembleFractureStatisticsCalculator::PropertyType::HEIGHT, "HEIGHT", "Height" );
addItem( RigEnsembleFractureStatisticsCalculator::PropertyType::AREA, "AREA", "Area" );
addItem( RigEnsembleFractureStatisticsCalculator::PropertyType::WIDTH, "WIDTH", "Width" );
addItem( RigEnsembleFractureStatisticsCalculator::PropertyType::XF, "XF", "Halflength (Xf)" );
addItem( RigEnsembleFractureStatisticsCalculator::PropertyType::KFWF, "KFWF", "Conductivity (KfWf)" );
addItem( RigEnsembleFractureStatisticsCalculator::PropertyType::PERMEABILITY, "PERMEABILITY", "Permeability" );
addItem( RigEnsembleFractureStatisticsCalculator::PropertyType::FORMATION_DIP, "FORMATION_DIP", "Formation Dip" );
setDefault( RigEnsembleFractureStatisticsCalculator::PropertyType::HEIGHT );
}
}; // namespace caf
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigHistogramData RigEnsembleFractureStatisticsCalculator::createStatisticsData( const RimEnsembleFractureStatistics* esf,
PropertyType propertyType,
int numBins )
{
std::vector<cvf::ref<RigStimPlanFractureDefinition>> fractureDefinitions = esf->readFractureDefinitions();
if ( esf->excludeZeroWidthFractures() )
{
fractureDefinitions = RigEnsembleFractureStatisticsCalculator::removeZeroWidthDefinitions( fractureDefinitions );
}
return createStatisticsData( fractureDefinitions, propertyType, numBins );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigHistogramData
RigEnsembleFractureStatisticsCalculator::createStatisticsData( const std::vector<cvf::ref<RigStimPlanFractureDefinition>>& fractureDefinitions,
PropertyType propertyType,
int numBins )
{
std::vector<double> samples = calculateProperty( fractureDefinitions, propertyType );
RigHistogramData histogramData;
double sum;
double range;
double dev;
RigStatisticsMath::calculateBasicStatistics( samples, &histogramData.min, &histogramData.max, &sum, &range, &histogramData.mean, &dev );
double p50;
double mean;
RigStatisticsMath::calculateStatisticsCurves( samples,
&histogramData.p10,
&p50,
&histogramData.p90,
&mean,
RigStatisticsMath::PercentileStyle::SWITCHED );
std::vector<size_t> histogram;
RigHistogramCalculator histogramCalculator( histogramData.min, histogramData.max, numBins, &histogram );
for ( auto s : samples )
histogramCalculator.addValue( s );
histogramData.histogram = histogram;
return histogramData;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double>
RigEnsembleFractureStatisticsCalculator::calculateProperty( const std::vector<cvf::ref<RigStimPlanFractureDefinition>>& fractureDefinitions,
PropertyType propertyType )
{
std::vector<double> samples;
if ( propertyType == PropertyType::HEIGHT )
{
samples = calculateGridStatistics( fractureDefinitions, &RigEnsembleFractureStatisticsCalculator::calculateHeight );
}
else if ( propertyType == PropertyType::AREA )
{
samples = calculateGridStatistics( fractureDefinitions, &RigEnsembleFractureStatisticsCalculator::calculateArea );
}
else if ( propertyType == PropertyType::WIDTH )
{
samples = calculateAreaWeightedStatistics( fractureDefinitions, &RigEnsembleFractureStatisticsCalculator::calculateAreaWeightedWidth );
}
else if ( propertyType == PropertyType::PERMEABILITY )
{
samples = calculateAreaWeightedStatistics( fractureDefinitions,
&RigEnsembleFractureStatisticsCalculator::calculateAreaWeightedPermeability );
}
else if ( propertyType == PropertyType::XF )
{
samples = calculateGridStatistics( fractureDefinitions, &RigEnsembleFractureStatisticsCalculator::calculateXf );
}
else if ( propertyType == PropertyType::KFWF )
{
samples = calculateGridStatistics( fractureDefinitions, &RigEnsembleFractureStatisticsCalculator::calculateKfWf );
}
else if ( propertyType == PropertyType::FORMATION_DIP )
{
samples = calculateFormationDip( fractureDefinitions );
}
return samples;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double> RigEnsembleFractureStatisticsCalculator::calculateGridStatistics(
const std::vector<cvf::ref<RigStimPlanFractureDefinition>>& fractureDefinitions,
double( func )( cvf::cref<RigFractureGrid> ) )
{
std::vector<double> samples;
if ( fractureDefinitions.empty() ) return samples;
// TODO: heuristic to find conductivity name?
QString conductivityResultName = fractureDefinitions[0]->conductivityResultNames()[0];
std::vector<cvf::cref<RigFractureGrid>> grids =
RimEnsembleFractureStatistics::createFractureGrids( fractureDefinitions,
RiaDefines::EclipseUnitSystem::UNITS_METRIC,
conductivityResultName,
RimEnsembleFractureStatistics::MeshAlignmentType::PERFORATION_DEPTH );
for ( auto grid : grids )
{
double result = func( grid );
samples.push_back( result );
}
return samples;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RigEnsembleFractureStatisticsCalculator::calculateHeight( cvf::cref<RigFractureGrid> fractureGrid )
{
double longestRange = 0.0;
for ( size_t i = 0; i < fractureGrid->iCellCount(); i++ )
{
double currentAggregatedDistanceY = 0.0;
for ( size_t j = 0; j < fractureGrid->jCellCount(); j++ )
{
size_t idx = fractureGrid->getGlobalIndexFromIJ( i, j );
auto fractureCell = fractureGrid->cellFromIndex( idx );
double conductivityValue = fractureCell.getConductivityValue();
if ( conductivityValue > 0.0 )
{
currentAggregatedDistanceY += fractureCell.cellSizeZ();
}
else
{
longestRange = std::max( longestRange, currentAggregatedDistanceY );
currentAggregatedDistanceY = 0.0;
}
}
longestRange = std::max( longestRange, currentAggregatedDistanceY );
}
return longestRange;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RigEnsembleFractureStatisticsCalculator::calculateArea( cvf::cref<RigFractureGrid> fractureGrid )
{
double sum = 0.0;
for ( auto fractureCell : fractureGrid->fractureCells() )
{
double value = fractureCell.getConductivityValue();
if ( !std::isinf( value ) && value > 0.0 )
{
sum += fractureCell.area();
}
}
return sum;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RigEnsembleFractureStatisticsCalculator::calculateKfWf( cvf::cref<RigFractureGrid> fractureGrid )
{
RiaWeightedMeanCalculator<double> calc;
for ( auto fractureCell : fractureGrid->fractureCells() )
{
double value = fractureCell.getConductivityValue();
if ( !std::isinf( value ) && value > 0.0 )
{
double area = fractureCell.area();
calc.addValueAndWeight( value, area );
}
}
return calc.weightedMean();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double> RigEnsembleFractureStatisticsCalculator::calculateAreaWeightedStatistics(
const std::vector<cvf::ref<RigStimPlanFractureDefinition>>& fractureDefinitions,
double( func )( cvf::cref<RigFractureGrid>, cvf::cref<RigFractureGrid>, RiaDefines::EclipseUnitSystem, const QString& ) )
{
std::vector<double> samples;
if ( fractureDefinitions.empty() ) return samples;
// TODO: heuristic to find conductivity name?
QString conductivityResultName = fractureDefinitions[0]->conductivityResultNames()[0];
std::vector<cvf::cref<RigFractureGrid>> grids =
RimEnsembleFractureStatistics::createFractureGrids( fractureDefinitions,
RiaDefines::EclipseUnitSystem::UNITS_METRIC,
conductivityResultName,
RimEnsembleFractureStatistics::MeshAlignmentType::PERFORATION_DEPTH );
auto [widthResultName, widthResultUnit] = RimStimPlanFractureTemplate::widthParameterNameAndUnit( fractureDefinitions[0] );
std::vector<cvf::cref<RigFractureGrid>> widthGrids =
RimEnsembleFractureStatistics::createFractureGrids( fractureDefinitions,
RiaDefines::EclipseUnitSystem::UNITS_METRIC,
widthResultName,
RimEnsembleFractureStatistics::MeshAlignmentType::PERFORATION_DEPTH );
CAF_ASSERT( grids.size() == widthGrids.size() );
for ( size_t i = 0; i < grids.size(); i++ )
{
double result = func( grids[i], widthGrids[i], RiaDefines::EclipseUnitSystem::UNITS_METRIC, widthResultUnit );
samples.push_back( result );
}
return samples;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RigEnsembleFractureStatisticsCalculator::calculateAreaWeightedWidth( cvf::cref<RigFractureGrid> conductivityGrid,
cvf::cref<RigFractureGrid> widthGrid,
RiaDefines::EclipseUnitSystem widthUnitSystem,
const QString& widthUnit )
{
RiaWeightedMeanCalculator<double> calc;
const std::vector<RigFractureCell>& conductivityCells = conductivityGrid->fractureCells();
const std::vector<RigFractureCell>& widthCells = widthGrid->fractureCells();
CAF_ASSERT( conductivityCells.size() == widthCells.size() );
for ( size_t i = 0; i < conductivityCells.size(); i++ )
{
double value = conductivityCells[i].getConductivityValue();
if ( !std::isinf( value ) && value > 0.0 )
{
double cellArea = conductivityCells[i].area();
// TODO: conductivity is misleading here
double width = convertUnit( widthCells[i].getConductivityValue(), widthUnitSystem, widthUnit );
calc.addValueAndWeight( width, cellArea );
}
}
return calc.weightedMean();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RigEnsembleFractureStatisticsCalculator::calculateAreaWeightedPermeability( cvf::cref<RigFractureGrid> conductivityGrid,
cvf::cref<RigFractureGrid> widthGrid,
RiaDefines::EclipseUnitSystem widthUnitSystem,
const QString& widthUnit )
{
RiaWeightedMeanCalculator<double> calc;
const std::vector<RigFractureCell>& conductivityCells = conductivityGrid->fractureCells();
const std::vector<RigFractureCell>& widthCells = widthGrid->fractureCells();
CAF_ASSERT( conductivityCells.size() == widthCells.size() );
for ( size_t i = 0; i < conductivityCells.size(); i++ )
{
double conductivity = conductivityCells[i].getConductivityValue();
if ( !std::isinf( conductivity ) && conductivity > 0.0 )
{
double cellArea = conductivityCells[i].area();
// TODO: conductivity is misleading here
double width = convertUnit( widthCells[i].getConductivityValue(), widthUnitSystem, widthUnit );
double permeability = RigTransmissibilityEquations::permeability( conductivity, width );
calc.addValueAndWeight( permeability, cellArea );
}
}
return calc.weightedMean();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RigEnsembleFractureStatisticsCalculator::calculateXf( cvf::cref<RigFractureGrid> fractureGrid )
{
double height = calculateHeight( fractureGrid );
double area = calculateArea( fractureGrid );
if ( height > 0.0 )
{
double length = area / height;
double halfLength = length / 2.0;
return halfLength;
}
return 0.0;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double>
RigEnsembleFractureStatisticsCalculator::calculateFormationDip( const std::vector<cvf::ref<RigStimPlanFractureDefinition>>& fractureDefinitions )
{
std::vector<double> formationDips;
for ( auto fractureDefinition : fractureDefinitions )
{
formationDips.push_back( fractureDefinition->formationDip() );
}
return formationDips;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RigEnsembleFractureStatisticsCalculator::convertUnit( double value, RiaDefines::EclipseUnitSystem unitSystem, const QString& unitName )
{
if ( unitSystem == RiaDefines::EclipseUnitSystem::UNITS_METRIC )
{
return RiaEclipseUnitTools::convertToMeter( value, unitName );
}
else if ( unitSystem == RiaDefines::EclipseUnitSystem::UNITS_FIELD )
{
return RiaEclipseUnitTools::convertToFeet( value, unitName );
}
return value;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<cvf::ref<RigStimPlanFractureDefinition>> RigEnsembleFractureStatisticsCalculator::removeZeroWidthDefinitions(
const std::vector<cvf::ref<RigStimPlanFractureDefinition>>& fractureDefinitions )
{
std::vector<double> samples =
calculateAreaWeightedStatistics( fractureDefinitions, &RigEnsembleFractureStatisticsCalculator::calculateAreaWeightedWidth );
std::vector<cvf::ref<RigStimPlanFractureDefinition>> filteredFractureDefinitions;
int index = 0;
for ( double sample : samples )
{
if ( sample > 0.0 )
{
filteredFractureDefinitions.push_back( fractureDefinitions[index] );
}
index++;
}
return filteredFractureDefinitions;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RigEnsembleFractureStatisticsCalculator::PropertyType> RigEnsembleFractureStatisticsCalculator::propertyTypes()
{
std::vector<RigEnsembleFractureStatisticsCalculator::PropertyType> types = {
RigEnsembleFractureStatisticsCalculator::PropertyType::HEIGHT,
RigEnsembleFractureStatisticsCalculator::PropertyType::XF,
RigEnsembleFractureStatisticsCalculator::PropertyType::AREA,
RigEnsembleFractureStatisticsCalculator::PropertyType::PERMEABILITY,
RigEnsembleFractureStatisticsCalculator::PropertyType::WIDTH,
RigEnsembleFractureStatisticsCalculator::PropertyType::KFWF,
RigEnsembleFractureStatisticsCalculator::PropertyType::FORMATION_DIP,
};
return types;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<RiaNumberFormat::NumberFormatType, int> RigEnsembleFractureStatisticsCalculator::numberFormatForProperty( PropertyType propertyType )
{
if ( propertyType == PropertyType::WIDTH )
return std::make_pair( RiaNumberFormat::NumberFormatType::FIXED, 4 );
else
return std::make_pair( RiaNumberFormat::NumberFormatType::FIXED, 1 );
}