#3630 Implement volume calculation on grid

This commit is contained in:
Gaute Lindkvist 2018-11-06 12:47:54 +01:00
parent 8ebfe074f1
commit ef4b70d6e5
12 changed files with 261 additions and 22 deletions

View File

@ -293,6 +293,14 @@ QString RiaDefines::combinedRiAreaNormTranResultName()
return "riTRANXYZbyArea";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RiaDefines::riCellVolumeResultName()
{
return "riCELLVOLUME";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -84,6 +84,7 @@ namespace RiaDefines
QString riAreaNormTranZResultName();
QString combinedRiAreaNormTranResultName();
QString riCellVolumeResultName();
QString mobilePoreVolumeName();
QString completionTypeResultName();

View File

@ -291,6 +291,33 @@ double Rim2dGridProjection::minValue() const
return minV;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double Rim2dGridProjection::meanValue() const
{
return sumAllValues() / validVertexCount();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double Rim2dGridProjection::sumAllValues() const
{
double sum = 0.0;
int nVertices = vertexCount();
for (int index = 0; index < nVertices; ++index)
{
if (m_aggregatedResults[index] != std::numeric_limits<double>::infinity())
{
sum += m_aggregatedResults[index];
}
}
return sum;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -443,7 +470,7 @@ double Rim2dGridProjection::calculateValue(uint i, uint j) const
for (auto cellIdxAndWeight : matchingCells)
{
size_t cellIdx = cellIdxAndWeight.first;
double cellValue = m_resultAccessor->cellScalarGlobIdx(cellIdx);
double cellValue = m_resultAccessor->cellScalarGlobIdx(cellIdx);
sum += cellValue * cellIdxAndWeight.second;
}
return sum;
@ -507,6 +534,23 @@ uint Rim2dGridProjection::vertexCount() const
return gridSize2d.x() * gridSize2d.y();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
uint Rim2dGridProjection::validVertexCount() const
{
uint validCount = 0u;
for (uint i = 0; i < vertexCount(); ++i)
{
cvf::Vec2ui ij = ijFromGridIndex(i);
if (hasResultAt(ij.x(), ij.y()))
{
validCount++;
}
}
return validCount;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -699,7 +743,9 @@ std::vector<std::pair<size_t, float>> Rim2dGridProjection::visibleCellsAndWeight
return std::get<2>(lhs) > std::get<2>(rhs);
});
float adjustmentFactor = static_cast<float>(chopped2dBBoxVolume / totalOverlapVolume);
float adjustmentFactor = 1.0;
if (totalOverlapVolume > chopped2dBBoxVolume) // Don't scale up if overlap volume is smaller 2d extrusion!
adjustmentFactor = static_cast<float>(chopped2dBBoxVolume / totalOverlapVolume);
CVF_ASSERT(adjustmentFactor > 0.0f);
for (const auto& visWeightHeight : matchingVisibleCellsWeightAndHeight)
{
@ -836,6 +882,22 @@ void Rim2dGridProjection::updateLegend()
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
Rim2dGridProjection::ResultAggregation Rim2dGridProjection::resultAggregation() const
{
return m_resultAggregation();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString Rim2dGridProjection::resultAggregationText() const
{
return m_resultAggregation().uiText();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -68,6 +68,8 @@ public:
void generateResults();
double maxValue() const;
double minValue() const;
double meanValue() const;
double sumAllValues() const;
double sampleSpacing() const;
bool showContourLines() const;
@ -81,12 +83,16 @@ public:
cvf::Vec2ui surfaceGridSize() const;
uint vertexCount() const;
uint validVertexCount() const;
RimRegularLegendConfig* legendConfig() const;
size_t gridIndex(uint i, uint j) const;
cvf::Vec2ui ijFromGridIndex(size_t gridIndex) const;
void updateLegend();
ResultAggregation resultAggregation() const;
QString resultAggregationText() const;
protected:
double calculateValue(uint i, uint j) const;

View File

@ -37,6 +37,8 @@
#include "RigMainGrid.h"
#include "RigStatisticsDataCache.h"
#include "Rim2dEclipseView.h"
#include "Rim2dGridProjection.h"
#include "Rim2dIntersectionView.h"
#include "Rim2dIntersectionViewCollection.h"
#include "Rim3dView.h"
@ -174,9 +176,11 @@ Rim3dOverlayInfoConfig::HistogramData Rim3dOverlayInfoConfig::histogramData()
{
auto eclipseView = dynamic_cast<RimEclipseView*>(m_viewDef.p());
auto geoMechView = dynamic_cast<RimGeoMechView*>(m_viewDef.p());
auto contourMap = dynamic_cast<Rim2dEclipseView*>(eclipseView);
if (eclipseView) return histogramData(eclipseView);
if (geoMechView) return histogramData(geoMechView);
if (contourMap) return histogramData(contourMap);
else if (eclipseView) return histogramData(eclipseView);
else if (geoMechView) return histogramData(geoMechView);
return HistogramData();
}
@ -271,6 +275,29 @@ void Rim3dOverlayInfoConfig::setIsActive(bool active)
m_active = active;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
Rim3dOverlayInfoConfig::HistogramData Rim3dOverlayInfoConfig::histogramData(Rim2dEclipseView* contourMap)
{
HistogramData histData;
if (contourMap)
{
bool isResultsInfoRelevant = contourMap->grid2dProjection()->validVertexCount() > 0u;
if (isResultsInfoRelevant)
{
histData.min = contourMap->grid2dProjection()->minValue();
histData.max = contourMap->grid2dProjection()->maxValue();
histData.mean = contourMap->grid2dProjection()->meanValue();
histData.sum = contourMap->grid2dProjection()->sumAllValues();
}
}
return histData;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -467,35 +494,46 @@ QString Rim3dOverlayInfoConfig::caseInfoText(RimEclipseView* eclipseView)
if (eclipseView)
{
QString caseName;
QString totCellCount;
QString activeCellCountText;
QString fractureActiveCellCount;
QString iSize, jSize, kSize;
QString zScale;
QString caseName = eclipseView->eclipseCase()->caseUserDescription();
if (eclipseView->mainGrid())
Rim2dEclipseView* contourMap = dynamic_cast<Rim2dEclipseView*>(eclipseView);
if (contourMap && contourMap->grid2dProjection())
{
caseName = eclipseView->eclipseCase()->caseUserDescription();
totCellCount = QString::number(eclipseView->mainGrid()->globalCellArray().size());
QString totCellCount = QString::number(contourMap->grid2dProjection()->vertexCount());
cvf::uint validCellCount = contourMap->grid2dProjection()->validVertexCount();
QString activeCellCountText = QString::number(validCellCount);
QString iSize = QString::number(contourMap->grid2dProjection()->surfaceGridSize().x());
QString jSize = QString::number(contourMap->grid2dProjection()->surfaceGridSize().y());
QString aggregationType = contourMap->grid2dProjection()->resultAggregationText();
infoText += QString(
"<p><b>-- %1 --</b><p> "
"<b>Cell count. Total:</b> %2 <b>Valid Result:</b> %3 <br>"
"<b>2d Projection [%4] I,J, Aggregation Type:</b> %5, %6 <br>").arg(caseName, totCellCount, activeCellCountText, aggregationType, iSize, jSize);
}
else if (eclipseView->mainGrid())
{
QString totCellCount = QString::number(eclipseView->mainGrid()->globalCellArray().size());
size_t mxActCellCount = eclipseView->eclipseCase()->eclipseCaseData()->activeCellInfo(RiaDefines::MATRIX_MODEL)->reservoirActiveCellCount();
size_t frActCellCount = eclipseView->eclipseCase()->eclipseCaseData()->activeCellInfo(RiaDefines::FRACTURE_MODEL)->reservoirActiveCellCount();
QString activeCellCountText;
if (frActCellCount > 0) activeCellCountText += "Matrix : ";
activeCellCountText += QString::number(mxActCellCount);
if (frActCellCount > 0) activeCellCountText += " Fracture : " + QString::number(frActCellCount);
iSize = QString::number(eclipseView->mainGrid()->cellCountI());
jSize = QString::number(eclipseView->mainGrid()->cellCountJ());
kSize = QString::number(eclipseView->mainGrid()->cellCountK());
QString iSize = QString::number(eclipseView->mainGrid()->cellCountI());
QString jSize = QString::number(eclipseView->mainGrid()->cellCountJ());
QString kSize = QString::number(eclipseView->mainGrid()->cellCountK());
zScale = QString::number(eclipseView->scaleZ());
QString zScale = QString::number(eclipseView->scaleZ());
infoText += QString(
"<p><b>-- %1 --</b><p> "
"<b>Cell count. Total:</b> %2 <b>Active:</b> %3 <br>"
"<b>Main Grid I,J,K:</b> %4, %5, %6 <b>Z-Scale:</b> %7<br>").arg(caseName, totCellCount, activeCellCountText, iSize, jSize, kSize, zScale);
}
infoText += QString(
"<p><b>-- %1 --</b><p> "
"<b>Cell count. Total:</b> %2 <b>Active:</b> %3 <br>"
"<b>Main Grid I,J,K:</b> %4, %5, %6 <b>Z-Scale:</b> %7<br>").arg(caseName, totCellCount, activeCellCountText, iSize, jSize, kSize, zScale);
}
return infoText;
@ -535,7 +573,23 @@ QString Rim3dOverlayInfoConfig::resultInfoText(const HistogramData& histData, Ri
{
QString infoText;
if (eclipseView)
Rim2dEclipseView* contourMap = dynamic_cast<Rim2dEclipseView*>(eclipseView);
if (contourMap)
{
bool isResultsInfoRelevant = contourMap->grid2dProjection()->validVertexCount() > 0u;
if (isResultsInfoRelevant)
{
QString propName = eclipseView->cellResult()->resultVariableUiShortName();
infoText += QString("<b>Cell Property:</b> %1 ").arg(propName);
infoText += QString("<br><b>Statistics:</b> ");
infoText += QString("<table border=0 cellspacing=5 >"
"<tr> <td>Min</td> <td>Mean</td> <td>Max</td> <td>Sum</td> </tr>"
"<tr> <td>%1</td> <td> %2</td> <td> %3</td> <td> %4</td> </tr>"
"</table>").arg(histData.min).arg(histData.mean).arg(histData.max).arg(histData.sum);
}
}
else if (eclipseView)
{
bool isResultsInfoRelevant = eclipseView->hasUserRequestedAnimation() && eclipseView->cellResult()->hasResult();

View File

@ -30,6 +30,7 @@
#include <cmath>
#include <memory>
class Rim2dEclipseView;
class RimEclipseView;
class RimGeoMechView;
class RimGridView;
@ -111,6 +112,7 @@ private:
QString timeStepText(RimEclipseView* eclipseView);
QString timeStepText(RimGeoMechView* geoMechView);
HistogramData histogramData(Rim2dEclipseView* contourMap);
HistogramData histogramData(RimEclipseView* eclipseView);
HistogramData histogramData(RimGeoMechView* geoMechView);
QString caseInfoText(RimEclipseView* eclipseView);

View File

@ -961,6 +961,12 @@ void RigCaseCellResultsData::createPlaceholderResultEntries()
}
}
// Cell Volume
{
addStaticScalarResult(RiaDefines::STATIC_NATIVE, RiaDefines::riCellVolumeResultName(), false, 0);
}
// Mobile Pore Volume
{
if (findScalarResultIndex(RiaDefines::STATIC_NATIVE, "PORV") != cvf::UNDEFINED_SIZE_T)
@ -1140,6 +1146,10 @@ size_t RigCaseCellResultsData::findOrLoadScalarResult(RiaDefines::ResultCatType
progressInfo.incrementProgress();
}
}
else if (resultName == RiaDefines::riCellVolumeResultName())
{
computeCellVolumes();
}
else if (resultName == RiaDefines::mobilePoreVolumeName())
{
computeMobilePV();
@ -2404,6 +2414,30 @@ double RigCaseCellResultsData::darchysValue()
return RiaEclipseUnitTools::darcysConstant(m_ownerCaseData->unitsType());
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigCaseCellResultsData::computeCellVolumes()
{
size_t cellVolIdx = this->findOrCreateScalarResultIndex(RiaDefines::STATIC_NATIVE, RiaDefines::riCellVolumeResultName(), false);
std::vector<double>& cellVolumeResults = this->cellScalarResults(cellVolIdx)[0];
size_t cellResultCount = m_activeCellInfo->reservoirCellResultCount();
cellVolumeResults.resize(cellResultCount, 0u);
#pragma omp parallel for
for (int nativeResvCellIndex = 0; nativeResvCellIndex < static_cast<int>(m_ownerMainGrid->globalCellArray().size()); nativeResvCellIndex++)
{
size_t resultIndex = activeCellInfo()->cellResultIndex(nativeResvCellIndex);
if (resultIndex != cvf::UNDEFINED_SIZE_T)
{
const RigCell& cell = m_ownerMainGrid->globalCellArray()[nativeResvCellIndex];
cellVolumeResults[resultIndex] = cell.volume();
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -154,6 +154,7 @@ private: // from RimReservoirCellResultsStorage
void computeCompletionTypeForTimeStep(size_t timeStep);
double darchysValue();
void computeCellVolumes();
void computeMobilePV();
bool isDataPresent(size_t scalarResultIndex) const;

View File

@ -20,6 +20,7 @@
#include "RigCell.h"
#include "RigCellGeometryTools.h"
#include "RigMainGrid.h"
#include "cvfPlane.h"
#include "cvfRay.h"
@ -301,6 +302,21 @@ cvf::Vec3d RigCell::faceNormalWithAreaLenght(cvf::StructGridInterface::FaceType
( 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

View File

@ -70,6 +70,8 @@ public:
cvf::Vec3d center() const;
cvf::Vec3d faceCenter(cvf::StructGridInterface::FaceType face) const;
cvf::Vec3d faceNormalWithAreaLenght(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;

View File

@ -23,12 +23,64 @@
#include "cafHexGridIntersectionTools/cafHexGridIntersectionTools.h"
#include "cvfBoundingBox.h"
#include "cvfMatrix3.h"
#include "clipper/clipper.hpp"
#include <vector>
#include <array>
//--------------------------------------------------------------------------------------------------
/// 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 2<->3, 6<->7 from the paper.
/// Note the negative k!
/// 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;
CVF_ASSERT(volume > 0.0);
return volume;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -33,6 +33,7 @@
class RigCellGeometryTools
{
public:
static double calculateCellVolume(const std::array<cvf::Vec3d, 8>& hexCorners);
static void createPolygonFromLineSegments(std::list<std::pair<cvf::Vec3d, cvf::Vec3d>> &intersectionLineSegments, std::vector<std::vector<cvf::Vec3d>> &polygons);