mirror of
https://github.com/OPM/ResInsight.git
synced 2026-09-03 20:53:13 -05:00
#14606 Contour Map: Use the cached ensemble statistics when the project is reloaded
The map size of the sample grid was recomputed from the expanded bounding box and the sample spacing stored in the project file. The expanded bounding box is an exact multiple of the sample spacing, but after the limited precision of the project file the division can land just above a whole number of cells, and the map size gets one extra cell. The cache files were then rejected, and the ensemble statistics recomputed. Store the map size in the project file, and use it directly when the sample grid is restored from the cache. Identify the input grids of the cache validity key by file path, size and modification time, so that the cache is also invalidated when a grid file is modified in place. Label and count the primary case and the ensemble cases, so that a primary case can not be mistaken for the first ensemble case.
This commit is contained in:
@@ -62,7 +62,9 @@
|
||||
#include "cafProgressInfo.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QRegularExpression>
|
||||
#include <QUuid>
|
||||
|
||||
@@ -217,6 +219,9 @@ RimStatisticsContourMap::RimStatisticsContourMap()
|
||||
CAF_PDM_InitFieldNoDefault( &m_cacheExpandedBoundingBox, "CacheExpandedBoundingBox", "Cache Expanded Bounding Box" );
|
||||
m_cacheExpandedBoundingBox.uiCapability()->setUiHidden( true );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_cacheMapSize, "CacheMapSize", "Cache Map Size" );
|
||||
m_cacheMapSize.uiCapability()->setUiHidden( true );
|
||||
|
||||
setDeletable( true );
|
||||
}
|
||||
|
||||
@@ -979,7 +984,7 @@ void RimStatisticsContourMap::ensureResultsComputed()
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Hash of all settings that affect the computed statistics. Cached results are only reused when
|
||||
/// Hash of all settings and input grid files that affect the computed statistics. Cached results are only reused when
|
||||
/// the stored key matches the key computed from the current settings.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RimStatisticsContourMap::computeCacheValidityKey() const
|
||||
@@ -1014,10 +1019,24 @@ QString RimStatisticsContourMap::computeCacheValidityKey() const
|
||||
parts << QString( "%1,%2,%3" ).arg( point.x(), 0, 'f', 3 ).arg( point.y(), 0, 'f', 3 ).arg( point.z(), 0, 'f', 3 );
|
||||
}
|
||||
|
||||
if ( auto primaryCase = eclipseCase() ) parts << primaryCase->gridFileName();
|
||||
// Identify a grid file by path, size and modification time, so that the cache is invalidated both when the
|
||||
// ensemble is replaced or its cases are added/removed, and when a grid file is overwritten in place
|
||||
auto gridFileFingerprint = []( const RimEclipseCase* gridCase ) -> QString
|
||||
{
|
||||
const QString gridFileName = gridCase->gridFileName();
|
||||
const QFileInfo fileInfo( gridFileName );
|
||||
if ( !fileInfo.exists() ) return gridFileName;
|
||||
|
||||
for ( RimEclipseCase* eCase : ensembleCases() )
|
||||
parts << eCase->gridFileName();
|
||||
return QString( "%1,%2,%3" ).arg( gridFileName ).arg( fileInfo.size() ).arg( fileInfo.lastModified().toMSecsSinceEpoch() );
|
||||
};
|
||||
|
||||
// Label and count the two sets of cases, so that a primary case can not be mistaken for the first ensemble case
|
||||
parts << "primaryCase" << ( eclipseCase() != nullptr ? gridFileFingerprint( eclipseCase() ) : QString() );
|
||||
|
||||
const std::vector<RimEclipseCase*> cases = ensembleCases();
|
||||
parts << "ensembleCases" << QString::number( cases.size() );
|
||||
for ( const RimEclipseCase* eCase : cases )
|
||||
parts << gridFileFingerprint( eCase );
|
||||
|
||||
const QByteArray hash = QCryptographicHash::hash( parts.join( ";" ).toUtf8(), QCryptographicHash::Md5 );
|
||||
return QString::fromLatin1( hash.toHex() );
|
||||
@@ -1033,15 +1052,23 @@ bool RimStatisticsContourMap::loadCachedResults()
|
||||
if ( m_cacheSampleSpacing() <= 0.0 || m_cacheOriginalBoundingBox().size() != 6 || m_cacheExpandedBoundingBox().size() != 6 )
|
||||
return false;
|
||||
|
||||
// The map size is stored explicitly, as recomputing it from the extent of the expanded bounding box is sensitive to
|
||||
// the limited precision of the doubles in the project file
|
||||
if ( m_cacheMapSize().size() != 2 || m_cacheMapSize()[0] <= 0 || m_cacheMapSize()[1] <= 0 ) return false;
|
||||
|
||||
const QString expectedKey = computeCacheValidityKey();
|
||||
if ( m_cacheValidityKey().isEmpty() || m_cacheValidityKey() != expectedKey ) return false;
|
||||
|
||||
auto toBoundingBox = []( const std::vector<double>& c )
|
||||
{ return cvf::BoundingBox( cvf::Vec3d( c[0], c[1], c[2] ), cvf::Vec3d( c[3], c[4], c[5] ) ); };
|
||||
|
||||
auto contourMapGrid = std::make_unique<RigContourMapGrid>( toBoundingBox( m_cacheOriginalBoundingBox() ),
|
||||
toBoundingBox( m_cacheExpandedBoundingBox() ),
|
||||
m_cacheSampleSpacing() );
|
||||
const cvf::BoundingBox originalBoundingBox = toBoundingBox( m_cacheOriginalBoundingBox() );
|
||||
const cvf::BoundingBox expandedBoundingBox = toBoundingBox( m_cacheExpandedBoundingBox() );
|
||||
|
||||
const cvf::Vec2ui storedMapSize( static_cast<cvf::uint>( m_cacheMapSize()[0] ), static_cast<cvf::uint>( m_cacheMapSize()[1] ) );
|
||||
|
||||
auto contourMapGrid =
|
||||
std::make_unique<RigContourMapGrid>( originalBoundingBox, expandedBoundingBox, m_cacheSampleSpacing(), storedMapSize );
|
||||
|
||||
const cvf::Vec2ui& mapSize = contourMapGrid->mapSize();
|
||||
|
||||
@@ -1184,6 +1211,8 @@ void RimStatisticsContourMap::setupBeforeSave()
|
||||
m_cacheSampleSpacing = m_contourMapGrid->sampleSpacing();
|
||||
m_cacheOriginalBoundingBox = boundingBoxCoords( m_contourMapGrid->originalBoundingBox() );
|
||||
m_cacheExpandedBoundingBox = boundingBoxCoords( m_contourMapGrid->expandedBoundingBox() );
|
||||
m_cacheMapSize =
|
||||
std::vector<int>{ static_cast<int>( m_contourMapGrid->mapSize().x() ), static_cast<int>( m_contourMapGrid->mapSize().y() ) };
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1242,6 +1271,7 @@ void RimStatisticsContourMap::clearCacheFields()
|
||||
m_cacheSampleSpacing = 0.0;
|
||||
m_cacheOriginalBoundingBox = std::vector<double>();
|
||||
m_cacheExpandedBoundingBox = std::vector<double>();
|
||||
m_cacheMapSize = std::vector<int>();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -165,6 +165,7 @@ private:
|
||||
caf::PdmField<double> m_cacheSampleSpacing;
|
||||
caf::PdmField<std::vector<double>> m_cacheOriginalBoundingBox;
|
||||
caf::PdmField<std::vector<double>> m_cacheExpandedBoundingBox;
|
||||
caf::PdmField<std::vector<int>> m_cacheMapSize;
|
||||
|
||||
std::unique_ptr<RigContourMapGrid> m_contourMapGrid;
|
||||
std::map<size_t, std::map<StatisticsType, std::vector<double>>> m_timeResults;
|
||||
|
||||
@@ -58,6 +58,22 @@ RigContourMapGrid::RigContourMapGrid( const cvf::BoundingBox& originalBoundingBo
|
||||
m_expandedBoundingBox = makeMaxPointMultipleOfCellSize( m_expandedBoundingBox, m_mapSize, sampleSpacing );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigContourMapGrid::RigContourMapGrid( const cvf::BoundingBox& originalBoundingBox,
|
||||
const cvf::BoundingBox& expandedBoundingBox,
|
||||
double sampleSpacing,
|
||||
const cvf::Vec2ui& mapSize )
|
||||
: m_sampleSpacing( sampleSpacing )
|
||||
, m_mapSize( mapSize )
|
||||
, m_originalBoundingBox( originalBoundingBox )
|
||||
, m_expandedBoundingBox( expandedBoundingBox )
|
||||
{
|
||||
// Re-jig max point to be an exact multiple of cell size
|
||||
m_expandedBoundingBox = makeMaxPointMultipleOfCellSize( m_expandedBoundingBox, m_mapSize, sampleSpacing );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -33,6 +33,13 @@ public:
|
||||
RigContourMapGrid( const cvf::BoundingBox& originalBoundingBox, double sampleSpacing );
|
||||
RigContourMapGrid( const cvf::BoundingBox& originalBoundingBox, const cvf::BoundingBox& expandedBoundingBox, double sampleSpacing );
|
||||
|
||||
// Use an explicit map size instead of computing it from the extent of the expanded bounding box. Intended for
|
||||
// restoring a sample grid from a project file, where the bounding box is stored with limited precision.
|
||||
RigContourMapGrid( const cvf::BoundingBox& originalBoundingBox,
|
||||
const cvf::BoundingBox& expandedBoundingBox,
|
||||
double sampleSpacing,
|
||||
const cvf::Vec2ui& mapSize );
|
||||
|
||||
// Copy constructor
|
||||
RigContourMapGrid( const RigContourMapGrid& other )
|
||||
: m_sampleSpacing( other.m_sampleSpacing )
|
||||
|
||||
@@ -12,6 +12,7 @@ set(SOURCE_UNITTEST_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigPadModel-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSimulationInputTool-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigBoundingBoxIjk-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigContourMapGrid-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifcCommandCore-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifEclipseInputFileTools-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifEclipseOutputFileTools-Test.cpp
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2026- 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 "gtest/gtest.h"
|
||||
|
||||
#include "ContourMap/RigContourMapGrid.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// The map size of a cached ensemble contour map is stored in the project file, and is used directly instead of being
|
||||
/// recomputed from the extent of the expanded bounding box, which is stored with limited precision. The expanded
|
||||
/// bounding box must still end up as an exact multiple of the sample spacing.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigContourMapGrid, ExplicitMapSizeIsUsedWhenRestoringGrid )
|
||||
{
|
||||
const double sampleSpacing = 134.078604562017;
|
||||
|
||||
const cvf::BoundingBox originalBoundingBox( cvf::Vec3d( 455778.046191406, 5926228.75742188, -2013.02879333496 ),
|
||||
cvf::Vec3d( 467774.981152344, 5939762.94570312, -1549.74769592285 ) );
|
||||
|
||||
const RigContourMapGrid computedGrid( originalBoundingBox, sampleSpacing );
|
||||
EXPECT_EQ( 94u, computedGrid.mapSize().x() );
|
||||
EXPECT_EQ( 105u, computedGrid.mapSize().y() );
|
||||
|
||||
// The expanded bounding box as read back from a project file, stored with 15 significant digits
|
||||
const cvf::BoundingBox restoredExpandedBoundingBox( cvf::Vec3d( 455509.888982282, 5925960.60021275, -2013.02879333496 ),
|
||||
cvf::Vec3d( 468113.277811112, 5940038.85369176, -1549.74769592285 ) );
|
||||
|
||||
const RigContourMapGrid restoredGrid( originalBoundingBox, restoredExpandedBoundingBox, sampleSpacing, computedGrid.mapSize() );
|
||||
EXPECT_EQ( computedGrid.mapSize().x(), restoredGrid.mapSize().x() );
|
||||
EXPECT_EQ( computedGrid.mapSize().y(), restoredGrid.mapSize().y() );
|
||||
|
||||
const cvf::Vec3d extent = restoredGrid.expandedBoundingBox().extent();
|
||||
EXPECT_NEAR( restoredGrid.mapSize().x() * sampleSpacing, extent.x(), 1.0e-9 );
|
||||
EXPECT_NEAR( restoredGrid.mapSize().y() * sampleSpacing, extent.y(), 1.0e-9 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// A bounding box extent that is clearly larger than a whole number of cells must add a cell
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigContourMapGrid, MapSizeIsRoundedUpForPartialCell )
|
||||
{
|
||||
const double sampleSpacing = 100.0;
|
||||
|
||||
// The bounding box is expanded by two cells in each direction, giving an extent of 4.5 cells in x and 4 cells in y
|
||||
const cvf::BoundingBox originalBoundingBox( cvf::Vec3d( 0.0, 0.0, 0.0 ), cvf::Vec3d( 50.0, 0.0, 10.0 ) );
|
||||
|
||||
const RigContourMapGrid grid( originalBoundingBox, sampleSpacing );
|
||||
EXPECT_EQ( 5u, grid.mapSize().x() );
|
||||
EXPECT_EQ( 4u, grid.mapSize().y() );
|
||||
}
|
||||
Reference in New Issue
Block a user