Fracture Surfaces: Add surface import from VTU/PVD files

Parse VTU files. If element results are detected, duplicate nodes and assign result values to nodes for display.
This commit is contained in:
Magne Sjaastad authored and Kristian Bendiksen committed 2025-03-07 10:34:48 +01:00
1 parent d0a9c0a820
commit c66f4f8158
23 files changed
+721 -25

No files matched your search

@@ -195,7 +195,7 @@ void RimGeoMechView::onLoadDataAndUpdate()
geoMechPropertyFilterCollection()->loadAndInitializePropertyFilters();
m_wellMeasurementCollection->syncWithChangesInWellMeasurementCollection();
if ( m_surfaceCollection ) m_surfaceCollection->loadData();
if ( m_surfaceCollection ) m_surfaceCollection->loadData( m_currentTimeStep );
if ( m_partsCollection ) m_partsCollection->syncWithCase( m_geomechCase );
@@ -897,6 +897,14 @@ void RimEclipseView::onUpdateDisplayModelForCurrentTimeStep()
appendIntersectionsForCurrentTimeStep();
}
if ( surfaceInViewCollection() )
{
m_surfaceVizModel->removeAllParts();
surfaceInViewCollection()->loadData( currentTimeStep() );
surfaceInViewCollection()->appendPartsToModel( m_surfaceVizModel.p(), m_reservoirGridPartManager->scaleTransform() );
}
updateVisibleCellColors();
wellCollection()->scaleWellDisks();
@@ -908,7 +916,7 @@ void RimEclipseView::onUpdateDisplayModelForCurrentTimeStep()
m_overlayInfoConfig()->update3DInfo();
// Invisible Wells are marked as read only when "show wells intersecting visible cells" is enabled
// Visibility of wells differ betweeen time steps, so trigger a rebuild of tree state items
// Visibility of wells differ between time steps, so trigger a rebuild of tree state items
wellCollection()->updateConnectedEditors();
RicExportToSharingServerScheduler::instance()->scheduleUpdateSession();
@@ -1217,7 +1225,7 @@ void RimEclipseView::onLoadDataAndUpdate()
m_wellCollection->scaleWellDisks();
if ( m_surfaceCollection ) m_surfaceCollection->loadData();
if ( m_surfaceCollection ) m_surfaceCollection->loadData( m_currentTimeStep );
scheduleReservoirGridGeometryRegen();
m_simWellsPartManager->clearGeometryCache();
@@ -413,7 +413,7 @@ void RimSeismicView::onLoadDataAndUpdate()
updateMdiWindowVisibility();
if ( m_surfaceCollection ) m_surfaceCollection->loadData();
if ( m_surfaceCollection ) m_surfaceCollection->loadData( m_currentTimeStep );
scheduleCreateDisplayModelAndRedraw();
}
@@ -9,6 +9,7 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RimEnsembleSurface.h
${CMAKE_CURRENT_LIST_DIR}/RimEnsembleStatisticsSurface.h
${CMAKE_CURRENT_LIST_DIR}/RimDepthSurface.h
${CMAKE_CURRENT_LIST_DIR}/RimFractureSurface.h
)
set(SOURCE_GROUP_SOURCE_FILES
@@ -22,6 +23,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RimEnsembleSurface.cpp
${CMAKE_CURRENT_LIST_DIR}/RimEnsembleStatisticsSurface.cpp
${CMAKE_CURRENT_LIST_DIR}/RimDepthSurface.cpp
${CMAKE_CURRENT_LIST_DIR}/RimFractureSurface.cpp
)
list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})
@@ -20,11 +20,13 @@
#include "RiaPreferences.h"
#include "RimSurfaceCollection.h"
#include "RifSurfaceImporter.h"
#include "RifVtkSurfaceImporter.h"
#include "RigGocadData.h"
#include "RigSurface.h"
#include "RimSurfaceCollection.h"
#include "cafPdmFieldScriptingCapability.h"
#include "cafPdmObjectScriptingCapability.h"
@@ -177,6 +179,13 @@ bool RimFileSurface::loadDataFromFile()
surface = m_gocadData->gocadGeometry();
}
else if ( filePath.endsWith( "vtu", Qt::CaseInsensitive ) )
{
m_gocadData = std::make_unique<RigGocadData>();
RifVtkSurfaceImporter::importFromFile( filePath.toStdString(), m_gocadData.get() );
surface = m_gocadData->gocadGeometry();
}
else if ( filePath.endsWith( "dat", Qt::CaseInsensitive ) || filePath.endsWith( "xyz", Qt::CaseInsensitive ) )
{
double resamplingDistance = RiaPreferences::current()->surfaceImportResamplingDistance();
@@ -0,0 +1,206 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RimFractureSurface.h"
#include "RiaPreferences.h"
#include "RimSurfaceCollection.h"
#include "RifSurfaceImporter.h"
#include "RifVtkSurfaceImporter.h"
#include "RigGocadData.h"
#include "RigSurface.h"
#include "cafPdmFieldScriptingCapability.h"
#include "cafPdmObjectScriptingCapability.h"
#include <QFileInfo>
#include <memory>
CAF_PDM_SOURCE_INIT( RimFractureSurface, "RimFractureSurface" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimFractureSurface::RimFractureSurface()
{
CAF_PDM_InitScriptableObject( "Surface", ":/ReservoirSurface16x16.png" );
CAF_PDM_InitFieldNoDefault( &m_surfaceDefinitionFilePath, "SurfaceFilePath", "File" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimFractureSurface::~RimFractureSurface()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFractureSurface::setSurfaceFilePath( const QString& filePath )
{
m_surfaceDefinitionFilePath = filePath;
clearCachedNativeData();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimFractureSurface::surfaceFilePath()
{
return m_surfaceDefinitionFilePath().path();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimFractureSurface::onLoadData()
{
return updateSurfaceData();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimSurface* RimFractureSurface::createCopy()
{
auto newSurface = copyObject<RimFractureSurface>();
if ( !newSurface->onLoadData() )
{
delete newSurface;
return nullptr;
}
return newSurface;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFractureSurface::loadSurfaceDataForTimeStep( int timeStep )
{
if ( m_surfacePerTimeStep.empty() )
{
loadDataFromFile();
}
if ( timeStep >= static_cast<int>( m_surfacePerTimeStep.size() ) ) return;
auto surface = new RigSurface;
auto gocadData = m_surfacePerTimeStep[timeStep];
const auto& [coordinates, indices] = gocadData.gocadGeometry();
surface->setTriangleData( indices, coordinates );
auto propertyNames = gocadData.propertyNames();
for ( const auto& name : propertyNames )
{
auto values = gocadData.propertyValues( name );
surface->addVerticeResult( name, values );
}
setSurfaceData( surface );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<std::vector<double>> RimFractureSurface::valuesForProperty( const QString& propertyName ) const
{
std::vector<std::vector<double>> values;
for ( auto allTimeStepValues : m_surfacePerTimeStep )
{
const auto& [coordinates, indices] = allTimeStepValues.gocadGeometry();
auto valuesOneTimeStep = allTimeStepValues.propertyValues( propertyName );
// convert to double vector
std::vector<double> valuesOneTimeStepDouble( valuesOneTimeStep.begin(), valuesOneTimeStep.end() );
values.push_back( valuesOneTimeStepDouble );
}
return values;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFractureSurface::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
{
RimSurface::fieldChangedByUi( changedField, oldValue, newValue );
if ( changedField == &m_surfaceDefinitionFilePath )
{
clearCachedNativeData();
updateSurfaceData();
auto surfColl = firstAncestorOrThisOfTypeAsserted<RimSurfaceCollection>();
surfColl->updateViews( { this } );
}
}
//--------------------------------------------------------------------------------------------------
/// Regenerate the surface geometry, using the offset specified.
/// If the surface data hasn't been loaded from file yet, load it.
/// Returns false for fatal failure
//--------------------------------------------------------------------------------------------------
bool RimFractureSurface::updateSurfaceData()
{
loadSurfaceDataForTimeStep( 0 );
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFractureSurface::clearCachedNativeData()
{
m_secondsSinceSimulationStart.clear();
m_surfacePerTimeStep.clear();
/*
m_vertices.clear();
m_tringleIndices.clear();
*/
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimFractureSurface::loadDataFromFile()
{
auto surfaceInfo = RifVtkSurfaceImporter::parsePvdDatasets( m_surfaceDefinitionFilePath().path().toStdString() );
for ( const auto& s : surfaceInfo )
{
RigGocadData gocadData;
if ( RifVtkSurfaceImporter::importFromFile( s.filename, &gocadData ) )
{
m_secondsSinceSimulationStart.push_back( s.timestep );
m_surfacePerTimeStep.push_back( gocadData );
}
}
return false;
}
@@ -0,0 +1,60 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "RimSurface.h"
#include "cafPdmChildArrayField.h"
class RimFileSurface;
class RigGocadData;
class RimFractureSurface : public RimSurface
{
CAF_PDM_HEADER_INIT;
public:
RimFractureSurface();
~RimFractureSurface() override;
void setSurfaceFilePath( const QString& filePath );
QString surfaceFilePath();
bool onLoadData() override;
RimSurface* createCopy() override;
void loadSurfaceDataForTimeStep( int timeStep ) override;
std::vector<std::vector<double>> valuesForProperty( const QString& propertyName ) const;
protected:
bool updateSurfaceData() override;
void clearCachedNativeData() override;
private:
bool loadDataFromFile();
private:
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
caf::PdmField<caf::FilePath> m_surfaceDefinitionFilePath;
std::vector<size_t> m_secondsSinceSimulationStart;
std::vector<RigGocadData> m_surfacePerTimeStep;
};
@@ -221,6 +221,13 @@ RigSurface* RimSurface::surfaceData()
return m_surfaceData.p();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSurface::loadSurfaceDataForTimeStep( int timeStep )
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -60,6 +60,7 @@ public:
QString userDescription();
void setUserDescription( const QString& description );
virtual void loadSurfaceDataForTimeStep( int timeStep );
virtual QString fullName() const;
virtual bool onLoadData() = 0;
virtual RimSurface* createCopy() = 0;
@@ -41,6 +41,7 @@
#include "cafPdmFieldScriptingCapability.h"
#include "cafPdmObjectScriptingCapability.h"
#include "RimFractureSurface.h"
#include <QFile>
#include <QFileInfo>
@@ -142,14 +143,26 @@ RimSurface* RimSurfaceCollection::importSurfacesFromFiles( const QStringList& fi
for ( const QString& newFileName : fileNames )
{
RimFileSurface* newSurface = new RimFileSurface;
RimSurface* newSurface = nullptr;
if ( newFileName.endsWith( ".pvd" ) )
{
RimFractureSurface* fractureSurface = new RimFractureSurface;
fractureSurface->setSurfaceFilePath( newFileName );
newSurface = fractureSurface;
}
else
{
RimFileSurface* fileSurface = new RimFileSurface;
fileSurface->setSurfaceFilePath( newFileName );
newSurface = fileSurface;
}
auto newColor = RiaColorTables::categoryPaletteColors().cycledColor3f( existingSurfCount + newSurfCount );
newSurface->setSurfaceFilePath( newFileName );
newSurface->setUserDescription( QFileInfo( newFileName ).fileName() );
newSurface->setColor( newColor );
newSurface->setUserDescription( QFileInfo( newFileName ).fileName() );
if ( !newSurface->onLoadData() )
{
@@ -32,6 +32,7 @@
#include "RivSurfacePartMgr.h"
#include "RiaApplication.h"
#include "cafPdmUiDoubleSliderEditor.h"
CAF_PDM_SOURCE_INIT( RimSurfaceInView, "SurfaceInView" );
@@ -102,7 +103,7 @@ void RimSurfaceInView::setSurface( RimSurface* surf )
m_resultDefinition->assignDefaultProperty();
}
m_resultDefinition->updateMinMaxValues();
m_resultDefinition->updateMinMaxValues( -1 );
}
//--------------------------------------------------------------------------------------------------
@@ -166,11 +167,12 @@ const RivIntersectionGeometryGeneratorInterface* RimSurfaceInView::intersectionG
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSurfaceInView::loadDataAndUpdate()
void RimSurfaceInView::loadDataAndUpdate( int timeStep )
{
if ( surface() )
{
surface()->loadDataIfRequired();
surface()->loadSurfaceDataForTimeStep( timeStep );
if ( surface()->surfaceData() && surface()->surfaceData()->propertyNames().empty() )
{
@@ -183,7 +185,7 @@ void RimSurfaceInView::loadDataAndUpdate()
m_resultDefinition->setCheckState( true );
}
m_resultDefinition->updateMinMaxValues();
m_resultDefinition->updateMinMaxValues( timeStep );
}
}
@@ -56,7 +56,7 @@ public:
RivSurfacePartMgr* nativeSurfacePartMgr();
const RivIntersectionGeometryGeneratorInterface* intersectionGeometryGenerator() const override;
void loadDataAndUpdate();
void loadDataAndUpdate( int timeStep );
void updateLegendRangesTextAndVisibility( RiuViewer* nativeOrOverrideViewer, bool isUsingOverrideViewer );
@@ -243,18 +243,18 @@ void RimSurfaceInViewCollection::updateFromSurfaceCollection()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSurfaceInViewCollection::loadData()
void RimSurfaceInViewCollection::loadData( int timeStep )
{
for ( RimSurfaceInViewCollection* coll : m_collectionsInView )
{
coll->loadData();
coll->loadData( timeStep );
}
for ( RimSurfaceInView* surf : m_surfacesInView )
{
if ( surf->isActive() )
{
surf->loadDataAndUpdate();
surf->loadDataAndUpdate( timeStep );
}
}
}
@@ -54,7 +54,7 @@ public:
void setSurfaceCollection( RimSurfaceCollection* surfcoll );
void updateFromSurfaceCollection();
void loadData();
void loadData( int timeStep );
void clearGeometry();
void appendPartsToModel( cvf::ModelBasicList* surfaceVizModel, cvf::Transform* scaleTransform, bool onlyNativeParts = false );
@@ -25,6 +25,7 @@
#include "RigSurface.h"
#include "Rim3dView.h"
#include "RimFractureSurface.h"
#include "RimRegularLegendConfig.h"
#include "RimSurface.h"
#include "RimSurfaceInView.h"
@@ -87,8 +88,58 @@ RimRegularLegendConfig* RimSurfaceResultDefinition::legendConfig()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSurfaceResultDefinition::updateMinMaxValues()
void RimSurfaceResultDefinition::updateMinMaxValues( int currentTimeStep )
{
if ( currentTimeStep < 0 ) currentTimeStep = 0;
// TODO: This is a hack, we need to find a better way to handle time dependent data for a surface
if ( auto fractureSurface = dynamic_cast<RimFractureSurface*>( m_surfaceInView->surface() ) )
{
double localMin = 0.0;
double localMax = 0.0;
double localPosClosestToZero = 0.0;
double localNegClosestToZero = 0.0;
double globalMin = 0.0;
double globalMax = 0.0;
double globalPosClosestToZero = 0.0;
double globalNegClosestToZero = 0.0;
auto valuesForTimeSteps = fractureSurface->valuesForProperty( m_propertyName );
MinMaxAccumulator minMaxAccumulator;
PosNegAccumulator posNegAccumulator;
for ( size_t timeIndex = 0; timeIndex < valuesForTimeSteps.size(); timeIndex++ )
{
auto values = valuesForTimeSteps[timeIndex];
minMaxAccumulator.addData( values );
posNegAccumulator.addData( values );
if ( static_cast<int>( timeIndex ) == currentTimeStep )
{
MinMaxAccumulator localMinMaxAccumulator;
PosNegAccumulator localPosNegAccumulator;
localMinMaxAccumulator.addData( values );
localPosNegAccumulator.addData( values );
localPosClosestToZero = localPosNegAccumulator.pos;
localNegClosestToZero = localPosNegAccumulator.neg;
localMin = localMinMaxAccumulator.min;
localMax = localMinMaxAccumulator.max;
}
}
globalPosClosestToZero = posNegAccumulator.pos;
globalNegClosestToZero = posNegAccumulator.neg;
globalMin = minMaxAccumulator.min;
globalMax = minMaxAccumulator.max;
m_legendConfig->setClosestToZeroValues( globalPosClosestToZero, globalNegClosestToZero, localPosClosestToZero, localNegClosestToZero );
m_legendConfig->setAutomaticRanges( globalMin, globalMax, localMin, localMax );
return;
}
RigSurface* surfData = surfaceData();
if ( surfData )
{
@@ -139,7 +190,7 @@ void RimSurfaceResultDefinition::fieldChangedByUi( const caf::PdmFieldHandle* ch
{
if ( changedField == &m_propertyName )
{
updateMinMaxValues();
updateMinMaxValues( -1 );
}
auto view = firstAncestorOrThisOfType<Rim3dView>();
@@ -46,7 +46,7 @@ public:
RimRegularLegendConfig* legendConfig();
void updateMinMaxValues();
void updateMinMaxValues( int currentTimeStep );
void assignDefaultProperty();
private: