mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
Ensemble surface import and statistics
This commit is contained in:
committed by
GitHub
parent
d1e81f3c1e
commit
966bcd1e77
@@ -62,6 +62,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RicRecursiveFileSearchDialog.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicSummaryCaseRestartDialog.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicImportEnsembleFeature.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicImportSummaryGroupFeature.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicImportEnsembleSurfaceFeature.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicConvertGroupToEnsembleFeature.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicImportEnsembleWellLogsFeature.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicResampleDialog.h
|
||||
@@ -147,6 +148,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RicRecursiveFileSearchDialog.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicSummaryCaseRestartDialog.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicImportEnsembleFeature.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicImportSummaryGroupFeature.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicImportEnsembleSurfaceFeature.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicConvertGroupToEnsembleFeature.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicImportEnsembleWellLogsFeature.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicResampleDialog.cpp
|
||||
|
127
ApplicationLibCode/Commands/RicImportEnsembleSurfaceFeature.cpp
Normal file
127
ApplicationLibCode/Commands/RicImportEnsembleSurfaceFeature.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2021- 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 "RicImportEnsembleSurfaceFeature.h"
|
||||
|
||||
#include "RiaApplication.h"
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "RimEnsembleSurface.h"
|
||||
#include "RimFileSurface.h"
|
||||
#include "RimOilField.h"
|
||||
#include "RimProject.h"
|
||||
#include "RimSurfaceCollection.h"
|
||||
|
||||
#include "RicRecursiveFileSearchDialog.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QFileInfo>
|
||||
|
||||
CAF_CMD_SOURCE_INIT( RicImportEnsembleSurfaceFeature, "RicImportEnsembleSurfaceFeature" );
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RicImportEnsembleSurfaceFeature::RicImportEnsembleSurfaceFeature()
|
||||
: m_pathFilter( "*" )
|
||||
, m_fileNameFilter( "*" )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RicImportEnsembleSurfaceFeature::isCommandEnabled()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicImportEnsembleSurfaceFeature::onActionTriggered( bool isChecked )
|
||||
{
|
||||
RiaApplication* app = RiaApplication::instance();
|
||||
QString pathCacheName = "ENSEMBLE_SURFACE_FILES";
|
||||
QStringList fileNames = runRecursiveFileSearchDialog( "Import Ensemble Surface", pathCacheName );
|
||||
if ( fileNames.isEmpty() ) return;
|
||||
|
||||
QString ensembleName = "Ensemble Surface";
|
||||
if ( ensembleName.isEmpty() ) return;
|
||||
|
||||
std::vector<RimFileSurface*> surfaces;
|
||||
for ( QString fileName : fileNames )
|
||||
{
|
||||
RimFileSurface* fileSurface = new RimFileSurface;
|
||||
fileSurface->setSurfaceFilePath( fileName );
|
||||
|
||||
if ( fileSurface->onLoadData() )
|
||||
{
|
||||
surfaces.push_back( fileSurface );
|
||||
}
|
||||
}
|
||||
|
||||
if ( surfaces.empty() ) return;
|
||||
|
||||
RimEnsembleSurface* ensemble = new RimEnsembleSurface;
|
||||
ensemble->setName( ensembleName );
|
||||
for ( auto surface : surfaces )
|
||||
ensemble->addFileSurface( surface );
|
||||
|
||||
RimProject::current()->activeOilField()->surfaceCollection->addEnsembleSurface( ensemble );
|
||||
RimProject::current()->activeOilField()->surfaceCollection->updateConnectedEditors();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicImportEnsembleSurfaceFeature::setupActionLook( QAction* actionToSetup )
|
||||
{
|
||||
actionToSetup->setIcon( QIcon( ":/ReservoirSurfaces16x16.png" ) );
|
||||
actionToSetup->setText( "Import Ensemble Surface" );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QStringList RicImportEnsembleSurfaceFeature::runRecursiveFileSearchDialog( const QString& dialogTitle,
|
||||
const QString& pathCacheName )
|
||||
{
|
||||
RiaApplication* app = RiaApplication::instance();
|
||||
QString defaultDir = app->lastUsedDialogDirectory( pathCacheName );
|
||||
|
||||
RicRecursiveFileSearchDialogResult result = RicRecursiveFileSearchDialog::runRecursiveSearchDialog( nullptr,
|
||||
dialogTitle,
|
||||
defaultDir,
|
||||
m_pathFilter,
|
||||
m_fileNameFilter,
|
||||
QStringList()
|
||||
<< ".TS"
|
||||
<< ".ts" );
|
||||
|
||||
// Remember filters
|
||||
m_pathFilter = result.pathFilter;
|
||||
m_fileNameFilter = result.fileNameFilter;
|
||||
|
||||
if ( !result.ok ) return QStringList();
|
||||
|
||||
// Remember the path to next time
|
||||
app->setLastUsedDialogDirectory( pathCacheName, QFileInfo( result.rootDir ).absoluteFilePath() );
|
||||
|
||||
return result.files;
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2021- 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 "cafCmdFeature.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RicImportEnsembleSurfaceFeature : public caf::CmdFeature
|
||||
{
|
||||
CAF_CMD_HEADER_INIT;
|
||||
|
||||
RicImportEnsembleSurfaceFeature();
|
||||
|
||||
protected:
|
||||
// Overrides
|
||||
bool isCommandEnabled() override;
|
||||
void onActionTriggered( bool isChecked ) override;
|
||||
void setupActionLook( QAction* actionToSetup ) override;
|
||||
|
||||
QStringList runRecursiveFileSearchDialog( const QString& dialogTitle, const QString& pathCacheName );
|
||||
|
||||
private:
|
||||
QString m_pathFilter;
|
||||
QString m_fileNameFilter;
|
||||
};
|
@@ -61,8 +61,7 @@ if(MSVC)
|
||||
${RESINSIGHT_ODB_API_DIR}/lib/ABQSMAOdbCoreGeom.lib
|
||||
${RESINSIGHT_ODB_API_DIR}/lib/ABQSMAOdbDdbOdb.lib
|
||||
${RESINSIGHT_ODB_API_DIR}/lib/ABQSMARomDiagEx.lib
|
||||
${RESINSIGHT_ODB_API_DIR}/lib/ABQSMASimInterface.lib
|
||||
)
|
||||
${RESINSIGHT_ODB_API_DIR}/lib/ABQSMASimInterface.lib)
|
||||
else()
|
||||
list(
|
||||
APPEND
|
||||
@@ -78,8 +77,7 @@ else()
|
||||
${RESINSIGHT_ODB_API_DIR}/lib/libABQSMABasAlloc.so
|
||||
${RESINSIGHT_ODB_API_DIR}/lib/libABQSMAAbuGeom.so
|
||||
${RESINSIGHT_ODB_API_DIR}/lib/libABQSMARomDiagEx.so
|
||||
${RESINSIGHT_ODB_API_DIR}/lib/libABQSMASimInterface.so
|
||||
)
|
||||
${RESINSIGHT_ODB_API_DIR}/lib/libABQSMASimInterface.so)
|
||||
endif(MSVC)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
@@ -969,6 +969,7 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection()
|
||||
{
|
||||
menuBuilder << "RicImportSurfacesFeature";
|
||||
menuBuilder << "RicNewGridSurfaceFeature";
|
||||
menuBuilder << "RicImportEnsembleSurfaceFeature";
|
||||
menuBuilder.addSeparator();
|
||||
menuBuilder << "RicNewSurfaceCollectionFeature";
|
||||
}
|
||||
|
@@ -7,6 +7,9 @@ ${CMAKE_CURRENT_LIST_DIR}/RimSurfaceCollection.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSurfaceInView.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSurfaceInViewCollection.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSurfaceResultDefinition.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimEnsembleSurface.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimEnsembleSurfaceInView.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimEnsembleStatisticsSurface.h
|
||||
)
|
||||
|
||||
set (SOURCE_GROUP_SOURCE_FILES
|
||||
@@ -17,6 +20,9 @@ ${CMAKE_CURRENT_LIST_DIR}/RimSurfaceCollection.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSurfaceInView.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSurfaceInViewCollection.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSurfaceResultDefinition.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimEnsembleSurface.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimEnsembleSurfaceInView.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimEnsembleStatisticsSurface.cpp
|
||||
)
|
||||
|
||||
list(APPEND CODE_HEADER_FILES
|
||||
|
@@ -0,0 +1,139 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimEnsembleStatisticsSurface.h"
|
||||
|
||||
#include "RigSurface.h"
|
||||
#include "RigSurfaceStatisticsCalculator.h"
|
||||
#include "RimEnsembleSurface.h"
|
||||
#include "RimSurfaceCollection.h"
|
||||
|
||||
#include "cafPdmFieldScriptingCapability.h"
|
||||
#include "cafPdmObjectScriptingCapability.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
// TODO: Use the alias concept prototyped below when the alias concept for class is ready
|
||||
// CAF_PDM_SOURCE_INIT( RimEnsembleStatisticsSurface, "EnsembleStatisticsSurface", "Surface" );
|
||||
// CAF_PDM_SOURCE_INIT( <class> , <keyword> , <alias>);
|
||||
CAF_PDM_SOURCE_INIT( RimEnsembleStatisticsSurface, "EnsembleStatisticsSurface" );
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimEnsembleStatisticsSurface::RimEnsembleStatisticsSurface()
|
||||
{
|
||||
CAF_PDM_InitScriptableObject( "Surface", ":/ReservoirSurface16x16.png", "", "" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_statisticsType, "StatisticsType", "StatisticsType", "", "", "" );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimEnsembleStatisticsSurface::~RimEnsembleStatisticsSurface()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleStatisticsSurface::setStatisticsType( RigSurfaceStatisticsCalculator::StatisticsType statisticsType )
|
||||
{
|
||||
m_statisticsType = statisticsType;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RimEnsembleStatisticsSurface::fullName() const
|
||||
{
|
||||
return caf::AppEnum<RigSurfaceStatisticsCalculator::StatisticsType>::uiText( m_statisticsType.v() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimEnsembleStatisticsSurface::onLoadData()
|
||||
{
|
||||
return updateSurfaceData();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimSurface* RimEnsembleStatisticsSurface::createCopy()
|
||||
{
|
||||
RimEnsembleStatisticsSurface* newSurface = dynamic_cast<RimEnsembleStatisticsSurface*>(
|
||||
xmlCapability()->copyByXmlSerialization( caf::PdmDefaultObjectFactory::instance() ) );
|
||||
|
||||
if ( !newSurface->onLoadData() )
|
||||
{
|
||||
delete newSurface;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return newSurface;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimEnsembleStatisticsSurface ::updateSurfaceData()
|
||||
{
|
||||
RimEnsembleSurface* ensembleSurface;
|
||||
firstAncestorOrThisOfType( ensembleSurface );
|
||||
|
||||
if ( ensembleSurface )
|
||||
{
|
||||
const RigSurface* surface = ensembleSurface->statisticsSurface();
|
||||
|
||||
if ( surface )
|
||||
{
|
||||
const std::vector<unsigned int>& indices = surface->triangleIndices();
|
||||
const std::vector<cvf::Vec3d>& vertices = surface->vertices();
|
||||
|
||||
const std::vector<float>& meanValues = surface->propertyValues(
|
||||
caf::AppEnum<RigSurfaceStatisticsCalculator::StatisticsType>::text( m_statisticsType.v() ) );
|
||||
|
||||
std::vector<cvf::Vec3d> verts;
|
||||
for ( size_t i = 0; i < vertices.size(); i++ )
|
||||
{
|
||||
verts.push_back( cvf::Vec3d( vertices[i].x(), vertices[i].y(), meanValues[i] ) );
|
||||
}
|
||||
|
||||
m_tringleIndices = indices;
|
||||
m_vertices = verts;
|
||||
|
||||
m_surfaceData = new RigSurface;
|
||||
|
||||
m_surfaceData->setTriangleData( m_tringleIndices, m_vertices );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleStatisticsSurface ::clearCachedNativeData()
|
||||
{
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2021- 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 "RigSurface.h"
|
||||
|
||||
#include "RigSurfaceStatisticsCalculator.h"
|
||||
|
||||
#include "RimSurface.h"
|
||||
#include "cafAppEnum.h"
|
||||
|
||||
class RimEnsembleStatisticsSurface : public RimSurface
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimEnsembleStatisticsSurface();
|
||||
~RimEnsembleStatisticsSurface() override;
|
||||
|
||||
bool onLoadData() override;
|
||||
RimSurface* createCopy() override;
|
||||
|
||||
void setStatisticsType( RigSurfaceStatisticsCalculator::StatisticsType statisticsType );
|
||||
|
||||
QString fullName() const override;
|
||||
|
||||
protected:
|
||||
bool updateSurfaceData() override;
|
||||
void clearCachedNativeData() override;
|
||||
|
||||
private:
|
||||
std::vector<unsigned> m_tringleIndices;
|
||||
std::vector<cvf::Vec3d> m_vertices;
|
||||
|
||||
caf::PdmField<caf::AppEnum<RigSurfaceStatisticsCalculator::StatisticsType>> m_statisticsType;
|
||||
};
|
@@ -0,0 +1,279 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2021- 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 "RimEnsembleSurface.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "RigSurfaceResampler.h"
|
||||
#include "RigSurfaceStatisticsCalculator.h"
|
||||
|
||||
#include "RimEnsembleCurveSet.h"
|
||||
#include "RimEnsembleStatisticsSurface.h"
|
||||
#include "RimFileSurface.h"
|
||||
#include "RimMainPlotCollection.h"
|
||||
#include "RimProject.h"
|
||||
#include "RimSurfaceCollection.h"
|
||||
|
||||
#include "cafPdmFieldScriptingCapability.h"
|
||||
#include "cafPdmObjectScriptingCapability.h"
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimEnsembleSurface, "EnsembleSurface" );
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimEnsembleSurface::RimEnsembleSurface()
|
||||
{
|
||||
CAF_PDM_InitScriptableObject( "Ensemble Surface", ":/ReservoirSurfaces16x16.png", "", "" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_fileSurfaces, "FileSurfaces", "", "", "", "" );
|
||||
m_fileSurfaces.uiCapability()->setUiHidden( true );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_statisticsSurfaces, "StatisticsSurfaces", "", "", "", "" );
|
||||
m_statisticsSurfaces.uiCapability()->setUiHidden( true );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_ensembleCurveSet, "FilterEnsembleCurveSet", "Filter by Ensemble Curve Set", "", "", "" );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurface::removeFileSurface( RimFileSurface* fileSurface )
|
||||
{
|
||||
m_fileSurfaces.removeChildObject( fileSurface );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurface::addFileSurface( RimFileSurface* fileSurface )
|
||||
{
|
||||
m_fileSurfaces.push_back( fileSurface );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RimFileSurface*> RimEnsembleSurface::fileSurfaces() const
|
||||
{
|
||||
return m_fileSurfaces().childObjects();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RimSurface*> RimEnsembleSurface::surfaces() const
|
||||
{
|
||||
std::vector<RimSurface*> surfaces;
|
||||
for ( auto fs : m_fileSurfaces.childObjects() )
|
||||
surfaces.push_back( fs );
|
||||
|
||||
for ( auto s : m_statisticsSurfaces.childObjects() )
|
||||
surfaces.push_back( s );
|
||||
|
||||
return surfaces;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurface::loadDataAndUpdate()
|
||||
{
|
||||
for ( auto& w : m_fileSurfaces )
|
||||
{
|
||||
if ( !w->onLoadData() )
|
||||
{
|
||||
RiaLogging::warning( QString( "Failed to load surface: %1" ).arg( w->surfaceFilePath() ) );
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<RimFileSurface*> fileSurfaces = m_fileSurfaces.childObjects();
|
||||
if ( m_ensembleCurveSet != nullptr )
|
||||
{
|
||||
fileSurfaces = filterByEnsembleCurveSet( fileSurfaces );
|
||||
}
|
||||
|
||||
m_statisticsSurfaces.deleteAllChildObjects();
|
||||
m_statisticsSurfaces.clear();
|
||||
|
||||
if ( !fileSurfaces.empty() )
|
||||
{
|
||||
cvf::ref<RigSurface> firstSurface = fileSurfaces[0]->surfaceData();
|
||||
|
||||
std::vector<cvf::ref<RigSurface>> surfaces;
|
||||
for ( auto& w : fileSurfaces )
|
||||
surfaces.push_back( RigSurfaceResampler::resampleSurface( firstSurface, w->surfaceData() ) );
|
||||
|
||||
m_statisticsSurface = RigSurfaceStatisticsCalculator::computeStatistics( surfaces );
|
||||
if ( !m_statisticsSurface.isNull() )
|
||||
{
|
||||
std::vector<RigSurfaceStatisticsCalculator::StatisticsType> statisticsTypes =
|
||||
{ RigSurfaceStatisticsCalculator::StatisticsType::MIN,
|
||||
RigSurfaceStatisticsCalculator::StatisticsType::MAX,
|
||||
RigSurfaceStatisticsCalculator::StatisticsType::MEAN,
|
||||
RigSurfaceStatisticsCalculator::StatisticsType::P10,
|
||||
RigSurfaceStatisticsCalculator::StatisticsType::P50,
|
||||
RigSurfaceStatisticsCalculator::StatisticsType::P90 };
|
||||
for ( auto s : statisticsTypes )
|
||||
{
|
||||
auto statSurface = new RimEnsembleStatisticsSurface;
|
||||
statSurface->setStatisticsType( s );
|
||||
m_statisticsSurfaces.push_back( statSurface );
|
||||
statSurface->onLoadData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RimSurfaceCollection* surfColl;
|
||||
this->firstAncestorOrThisOfTypeAsserted( surfColl );
|
||||
|
||||
std::vector<RimSurface*> surfacesToUpdate;
|
||||
surfColl->updateViews( surfaces(), false );
|
||||
updateConnectedEditors();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RimFileSurface*>
|
||||
RimEnsembleSurface::filterByEnsembleCurveSet( const std::vector<RimFileSurface*>& fileSurfaces ) const
|
||||
{
|
||||
std::vector<RimFileSurface*> filteredCases;
|
||||
|
||||
if ( m_ensembleCurveSet != nullptr )
|
||||
{
|
||||
// Get the summary cases from the related ensemble summary curve set.
|
||||
RimSummaryCaseCollection* summaryCaseCollection = m_ensembleCurveSet->summaryCaseCollection();
|
||||
|
||||
//
|
||||
std::vector<RimSummaryCase*> sumCases =
|
||||
m_ensembleCurveSet->filterEnsembleCases( summaryCaseCollection->allSummaryCases() );
|
||||
for ( auto sumCase : sumCases )
|
||||
{
|
||||
for ( auto fileSurface : fileSurfaces )
|
||||
{
|
||||
if ( isSameRealization( sumCase, fileSurface ) )
|
||||
{
|
||||
filteredCases.push_back( fileSurface );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
filteredCases = fileSurfaces;
|
||||
}
|
||||
|
||||
return filteredCases;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimEnsembleSurface::isSameRealization( RimSummaryCase* summaryCase, RimFileSurface* fileSurface ) const
|
||||
{
|
||||
// TODO: duplication with RimEnsembleWellLogCurveSet::isSameRealization
|
||||
QString fileSurfaceName = fileSurface->surfaceFilePath();
|
||||
if ( summaryCase->hasCaseRealizationParameters() )
|
||||
{
|
||||
// TODO: make less naive..
|
||||
int realizationNumber = summaryCase->caseRealizationParameters()->realizationNumber();
|
||||
QString summaryCaseFileName = summaryCase->summaryHeaderFilename();
|
||||
|
||||
if ( fileSurfaceName.contains( QString( "realization-%1" ).arg( realizationNumber ) ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const RigSurface* RimEnsembleSurface::statisticsSurface() const
|
||||
{
|
||||
return m_statisticsSurface.p();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QList<caf::PdmOptionItemInfo> RimEnsembleSurface::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions,
|
||||
bool* useOptionsOnly )
|
||||
{
|
||||
QList<caf::PdmOptionItemInfo> options;
|
||||
|
||||
if ( fieldNeedingOptions == &m_ensembleCurveSet )
|
||||
{
|
||||
options.push_back( caf::PdmOptionItemInfo( "None", nullptr ) );
|
||||
|
||||
RimMainPlotCollection* mainPlotColl = RimProject::current()->mainPlotCollection();
|
||||
std::vector<RimEnsembleCurveSet*> ensembleCurveSets;
|
||||
mainPlotColl->descendantsOfType( ensembleCurveSets );
|
||||
for ( auto ensembleCurveSet : ensembleCurveSets )
|
||||
{
|
||||
options.push_back( caf::PdmOptionItemInfo( ensembleCurveSet->name(), ensembleCurveSet ) );
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurface::connectEnsembleCurveSetFilterSignals()
|
||||
{
|
||||
if ( m_ensembleCurveSet() )
|
||||
{
|
||||
m_ensembleCurveSet()->filterChanged.connect( this, &RimEnsembleSurface::onFilterSourceChanged );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurface::onFilterSourceChanged( const caf::SignalEmitter* emitter )
|
||||
{
|
||||
if ( m_ensembleCurveSet() ) loadDataAndUpdate();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurface::initAfterRead()
|
||||
{
|
||||
connectEnsembleCurveSetFilterSignals();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurface::fieldChangedByUi( const caf::PdmFieldHandle* changedField,
|
||||
const QVariant& oldValue,
|
||||
const QVariant& newValue )
|
||||
{
|
||||
if ( changedField == &m_ensembleCurveSet )
|
||||
{
|
||||
connectEnsembleCurveSetFilterSignals();
|
||||
loadDataAndUpdate();
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2021- 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 "RimNamedObject.h"
|
||||
|
||||
#include "cafPdmChildArrayField.h"
|
||||
#include "cafPdmPtrField.h"
|
||||
|
||||
#include "cvfObject.h"
|
||||
|
||||
class RimFileSurface;
|
||||
class RimSurface;
|
||||
class RigSurface;
|
||||
class RimEnsembleStatisticsSurface;
|
||||
class RimEnsembleCurveSet;
|
||||
class RimSummaryCase;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RimEnsembleSurface : public RimNamedObject
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimEnsembleSurface();
|
||||
void removeFileSurface( RimFileSurface* fileSurface );
|
||||
void addFileSurface( RimFileSurface* fileSurface );
|
||||
|
||||
std::vector<RimFileSurface*> fileSurfaces() const;
|
||||
|
||||
std::vector<RimSurface*> surfaces() const;
|
||||
|
||||
void loadDataAndUpdate();
|
||||
|
||||
const RigSurface* statisticsSurface() const;
|
||||
|
||||
protected:
|
||||
QList<caf::PdmOptionItemInfo> calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions,
|
||||
bool* useOptionsOnly ) override;
|
||||
|
||||
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
|
||||
void initAfterRead() override;
|
||||
|
||||
std::vector<RimFileSurface*> filterByEnsembleCurveSet( const std::vector<RimFileSurface*>& fileSurfaces ) const;
|
||||
|
||||
bool isSameRealization( RimSummaryCase* summaryCase, RimFileSurface* fileSurface ) const;
|
||||
|
||||
private:
|
||||
void connectEnsembleCurveSetFilterSignals();
|
||||
void onFilterSourceChanged( const caf::SignalEmitter* emitter );
|
||||
|
||||
caf::PdmChildArrayField<RimFileSurface*> m_fileSurfaces;
|
||||
caf::PdmChildArrayField<RimEnsembleStatisticsSurface*> m_statisticsSurfaces;
|
||||
caf::PdmPtrField<RimEnsembleCurveSet*> m_ensembleCurveSet;
|
||||
|
||||
cvf::ref<RigSurface> m_statisticsSurface;
|
||||
};
|
@@ -0,0 +1,342 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2021- 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 "RimEnsembleSurfaceInView.h"
|
||||
|
||||
#include "RimEnsembleSurface.h"
|
||||
#include "RimGridView.h"
|
||||
#include "RimIntersectionResultDefinition.h"
|
||||
#include "RimSurface.h"
|
||||
#include "RimSurfaceCollection.h"
|
||||
#include "RimSurfaceInView.h"
|
||||
#include "RimSurfaceResultDefinition.h"
|
||||
|
||||
#include "RivSurfacePartMgr.h"
|
||||
|
||||
#include "cvfModelBasicList.h"
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimEnsembleSurfaceInView, "EnsembleSurfaceInView" );
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimEnsembleSurfaceInView::RimEnsembleSurfaceInView()
|
||||
{
|
||||
CAF_PDM_InitObject( "Ensemble Surface", ":/ReservoirSurfaces16x16.png", "", "" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_ensembleSurfaceName, "EnsembleSurfaceName", "EnsembleSurfaceName", "", "", "" );
|
||||
m_ensembleSurfaceName.registerGetMethod( this, &RimEnsembleSurfaceInView::name );
|
||||
m_ensembleSurfaceName.uiCapability()->setUiReadOnly( true );
|
||||
m_ensembleSurfaceName.xmlCapability()->disableIO();
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_surfacesInView, "SurfacesInViewField", "SurfacesInViewField", "", "", "" );
|
||||
m_surfacesInView.uiCapability()->setUiTreeHidden( true );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_ensembleSurface, "EnsembleSurface", "EnsembleSurface", "", "", "" );
|
||||
m_ensembleSurface.uiCapability()->setUiTreeHidden( true );
|
||||
|
||||
nameField()->uiCapability()->setUiHidden( true );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimEnsembleSurfaceInView::~RimEnsembleSurfaceInView()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
caf::PdmFieldHandle* RimEnsembleSurfaceInView::userDescriptionField()
|
||||
{
|
||||
return &m_ensembleSurfaceName;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RimEnsembleSurfaceInView::name() const
|
||||
{
|
||||
if ( m_ensembleSurface ) return m_ensembleSurface->uiName();
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimEnsembleSurface* RimEnsembleSurfaceInView::ensembleSurface() const
|
||||
{
|
||||
return m_ensembleSurface;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurfaceInView::setEnsembleSurface( RimEnsembleSurface* surfcoll )
|
||||
{
|
||||
m_ensembleSurface = surfcoll;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurfaceInView::updateAllViewItems()
|
||||
{
|
||||
syncSurfacesWithView();
|
||||
updateConnectedEditors();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurfaceInView::syncSurfacesWithView()
|
||||
{
|
||||
// Delete surfaceInView without any real Surface connection
|
||||
|
||||
std::vector<RimSurfaceInView*> surfsInView = m_surfacesInView.childObjects();
|
||||
|
||||
for ( auto surf : surfsInView )
|
||||
{
|
||||
if ( !surf->surface() )
|
||||
{
|
||||
m_surfacesInView.removeChildObject( surf );
|
||||
delete surf;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new surfade entries and reorder
|
||||
std::vector<RimSurfaceInView*> orderedSurfs;
|
||||
|
||||
if ( m_ensembleSurface )
|
||||
{
|
||||
// pick up the surfaces and the order from the surface collection
|
||||
std::vector<RimSurface*> surfs = m_ensembleSurface->surfaces();
|
||||
for ( auto surf : surfs )
|
||||
{
|
||||
// check if this is a surface we need to create
|
||||
RimSurfaceInView* viewSurf = this->getSurfaceInViewForSurface( surf );
|
||||
if ( viewSurf == nullptr )
|
||||
{
|
||||
RimSurfaceInView* newSurfInView = new RimSurfaceInView();
|
||||
newSurfInView->setSurface( surf );
|
||||
orderedSurfs.push_back( newSurfInView );
|
||||
}
|
||||
else
|
||||
{
|
||||
orderedSurfs.push_back( viewSurf );
|
||||
}
|
||||
}
|
||||
|
||||
// make sure our view surfaces have the same order as the source surface collection
|
||||
m_surfacesInView.clear();
|
||||
for ( auto viewSurf : orderedSurfs )
|
||||
{
|
||||
m_surfacesInView.push_back( viewSurf );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurfaceInView::loadData()
|
||||
{
|
||||
for ( RimSurfaceInView* surf : m_surfacesInView )
|
||||
{
|
||||
if ( surf->isActive() )
|
||||
{
|
||||
surf->loadDataAndUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurfaceInView::clearGeometry()
|
||||
{
|
||||
for ( RimSurfaceInView* surf : m_surfacesInView )
|
||||
{
|
||||
surf->clearGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurfaceInView::appendPartsToModel( cvf::ModelBasicList* model, cvf::Transform* scaleTransform )
|
||||
{
|
||||
if ( !isChecked() ) return;
|
||||
|
||||
for ( RimSurfaceInView* surf : m_surfacesInView )
|
||||
{
|
||||
if ( surf->isActive() )
|
||||
{
|
||||
surf->surfacePartMgr()->appendIntersectionGeometryPartsToModel( model, scaleTransform );
|
||||
}
|
||||
}
|
||||
|
||||
model->updateBoundingBoxesRecursive();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurfaceInView::fieldChangedByUi( const caf::PdmFieldHandle* changedField,
|
||||
const QVariant& oldValue,
|
||||
const QVariant& newValue )
|
||||
{
|
||||
this->updateUiIconFromToggleField();
|
||||
|
||||
if ( changedField == &m_isChecked )
|
||||
{
|
||||
RimGridView* ownerView;
|
||||
this->firstAncestorOrThisOfTypeAsserted( ownerView );
|
||||
ownerView->scheduleCreateDisplayModelAndRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurfaceInView::initAfterRead()
|
||||
{
|
||||
this->updateUiIconFromToggleField();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimSurfaceInView* RimEnsembleSurfaceInView::getSurfaceInViewForSurface( const RimSurface* surf ) const
|
||||
{
|
||||
for ( auto surfInView : m_surfacesInView )
|
||||
{
|
||||
if ( surfInView->surface() == surf )
|
||||
{
|
||||
return surfInView;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurfaceInView::updateCellResultColor( bool hasGeneralCellResult, size_t timeStepIndex )
|
||||
{
|
||||
if ( !this->isChecked() ) return;
|
||||
|
||||
for ( RimSurfaceInView* surf : m_surfacesInView )
|
||||
{
|
||||
if ( surf->isActive() )
|
||||
{
|
||||
bool useNativeSurfaceColors = false;
|
||||
|
||||
if ( surf->isNativeSurfaceResultsActive() ) useNativeSurfaceColors = true;
|
||||
|
||||
if ( !useNativeSurfaceColors )
|
||||
{
|
||||
bool showResults = surf->activeSeparateResultDefinition()
|
||||
? surf->activeSeparateResultDefinition()->hasResult()
|
||||
: hasGeneralCellResult;
|
||||
|
||||
if ( showResults )
|
||||
{
|
||||
surf->surfacePartMgr()->updateCellResultColor( timeStepIndex );
|
||||
}
|
||||
else
|
||||
{
|
||||
useNativeSurfaceColors = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( useNativeSurfaceColors )
|
||||
{
|
||||
surf->surfacePartMgr()->updateNativeSurfaceColors();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurfaceInView::applySingleColorEffect()
|
||||
{
|
||||
if ( !this->isChecked() ) return;
|
||||
|
||||
for ( RimSurfaceInView* surf : m_surfacesInView )
|
||||
{
|
||||
if ( surf->isActive() )
|
||||
{
|
||||
surf->surfacePartMgr()->updateNativeSurfaceColors();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimEnsembleSurfaceInView::hasAnyActiveSeparateResults()
|
||||
{
|
||||
if ( !this->isChecked() ) return false;
|
||||
|
||||
for ( RimSurfaceInView* surf : m_surfacesInView )
|
||||
{
|
||||
if ( surf->isActive() && surf->activeSeparateResultDefinition() &&
|
||||
surf->activeSeparateResultDefinition()->hasResult() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleSurfaceInView::updateLegendRangesTextAndVisibility( RiuViewer* nativeOrOverrideViewer,
|
||||
bool isUsingOverrideViewer )
|
||||
{
|
||||
for ( RimSurfaceInView* surf : m_surfacesInView )
|
||||
{
|
||||
surf->updateLegendRangesTextAndVisibility( nativeOrOverrideViewer, isUsingOverrideViewer );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RimRegularLegendConfig*> RimEnsembleSurfaceInView::legendConfigs()
|
||||
{
|
||||
std::vector<RimRegularLegendConfig*> configs;
|
||||
|
||||
for ( RimSurfaceInView* surf : m_surfacesInView )
|
||||
{
|
||||
if ( surf->isActive() && surf->surfaceResultDefinition() )
|
||||
{
|
||||
configs.push_back( surf->surfaceResultDefinition()->legendConfig() );
|
||||
}
|
||||
}
|
||||
|
||||
return configs;
|
||||
}
|
@@ -0,0 +1,86 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2021- 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 "RimCheckableNamedObject.h"
|
||||
|
||||
#include "cafPdmChildArrayField.h"
|
||||
#include "cafPdmField.h"
|
||||
#include "cafPdmObject.h"
|
||||
#include "cafPdmProxyValueField.h"
|
||||
#include "cafPdmPtrField.h"
|
||||
|
||||
namespace cvf
|
||||
{
|
||||
class ModelBasicList;
|
||||
class Transform;
|
||||
class ScalarMapper;
|
||||
} // namespace cvf
|
||||
|
||||
class RimSurfaceInView;
|
||||
class RimSurface;
|
||||
class RimSurfaceCollection;
|
||||
class RimEnsembleSurfaceInView;
|
||||
class RimEnsembleSurface;
|
||||
class RimRegularLegendConfig;
|
||||
class RiuViewer;
|
||||
class RivIntersectionGeometryGeneratorIF;
|
||||
|
||||
class RimEnsembleSurfaceInView : public RimCheckableNamedObject
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimEnsembleSurfaceInView();
|
||||
~RimEnsembleSurfaceInView() override;
|
||||
|
||||
QString name() const override;
|
||||
|
||||
RimEnsembleSurface* ensembleSurface() const;
|
||||
void setEnsembleSurface( RimEnsembleSurface* surfcoll );
|
||||
|
||||
void updateFromSurfaceCollection();
|
||||
void loadData();
|
||||
void clearGeometry();
|
||||
|
||||
void appendPartsToModel( cvf::ModelBasicList* surfaceVizModel, cvf::Transform* scaleTransform );
|
||||
void updateCellResultColor( bool hasGeneralCellResult, size_t timeStepIndex );
|
||||
void applySingleColorEffect();
|
||||
|
||||
bool hasAnyActiveSeparateResults();
|
||||
void updateLegendRangesTextAndVisibility( RiuViewer* nativeOrOverrideViewer, bool isUsingOverrideViewer );
|
||||
|
||||
std::vector<RimRegularLegendConfig*> legendConfigs();
|
||||
|
||||
void updateAllViewItems();
|
||||
|
||||
protected:
|
||||
void initAfterRead() override;
|
||||
caf::PdmFieldHandle* userDescriptionField() override;
|
||||
|
||||
private:
|
||||
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
|
||||
|
||||
RimSurfaceInView* getSurfaceInViewForSurface( const RimSurface* surf ) const;
|
||||
|
||||
void syncSurfacesWithView();
|
||||
|
||||
caf::PdmProxyValueField<QString> m_ensembleSurfaceName;
|
||||
caf::PdmChildArrayField<RimSurfaceInView*> m_surfacesInView;
|
||||
caf::PdmPtrField<RimEnsembleSurface*> m_ensembleSurface;
|
||||
};
|
@@ -18,17 +18,19 @@
|
||||
|
||||
#include "RimGridCaseSurface.h"
|
||||
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigReservoirGridTools.h"
|
||||
#include "RigSurface.h"
|
||||
|
||||
#include "RigMainGrid.h"
|
||||
#include "RimCase.h"
|
||||
#include "RimEclipseCase.h"
|
||||
#include "RimSurfaceCollection.h"
|
||||
#include "RimTools.h"
|
||||
|
||||
#include "cafPdmFieldScriptingCapability.h"
|
||||
#include "cafPdmObjectScriptingCapability.h"
|
||||
#include "cafPdmUiSliderEditor.h"
|
||||
|
||||
#include "RigReservoirGridTools.h"
|
||||
#include "cvfVector3.h"
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimGridCaseSurface, "GridCaseSurface" );
|
||||
@@ -38,11 +40,11 @@ CAF_PDM_SOURCE_INIT( RimGridCaseSurface, "GridCaseSurface" );
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimGridCaseSurface::RimGridCaseSurface()
|
||||
{
|
||||
CAF_PDM_InitObject( "Surface", ":/ReservoirSurface16x16.png", "", "" );
|
||||
CAF_PDM_InitScriptableObject( "Surface", ":/ReservoirSurface16x16.png", "", "" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_case, "SourceCase", "Source Case", "", "", "" );
|
||||
CAF_PDM_InitScriptableFieldNoDefault( &m_case, "SourceCase", "Source Case", "", "", "" );
|
||||
|
||||
CAF_PDM_InitField( &m_oneBasedSliceIndex, "SliceIndex", 1, "Slice Index (K)", "", "", "" );
|
||||
CAF_PDM_InitScriptableField( &m_oneBasedSliceIndex, "SliceIndex", 1, "Slice Index (K)", "", "", "" );
|
||||
m_oneBasedSliceIndex.uiCapability()->setUiEditorTypeName( caf::PdmUiSliderEditor::uiEditorTypeName() );
|
||||
}
|
||||
|
||||
|
@@ -71,11 +71,12 @@ protected:
|
||||
virtual bool updateSurfaceData() = 0;
|
||||
virtual void clearCachedNativeData() = 0;
|
||||
|
||||
protected:
|
||||
cvf::ref<RigSurface> m_surfaceData;
|
||||
|
||||
private:
|
||||
caf::PdmField<QString> m_userDescription;
|
||||
caf::PdmField<cvf::Color3f> m_color;
|
||||
caf::PdmField<double> m_depthOffset;
|
||||
caf::PdmProxyValueField<QString> m_nameProxy;
|
||||
|
||||
cvf::ref<RigSurface> m_surfaceData;
|
||||
};
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#include "RiaColorTables.h"
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "RimEnsembleSurface.h"
|
||||
#include "RimFileSurface.h"
|
||||
#include "RimGridCaseSurface.h"
|
||||
#include "RimGridView.h"
|
||||
@@ -57,6 +58,9 @@ RimSurfaceCollection::RimSurfaceCollection()
|
||||
CAF_PDM_InitScriptableFieldNoDefault( &m_surfaces, "SurfacesField", "Surfaces", "", "", "" );
|
||||
m_surfaces.uiCapability()->setUiTreeHidden( true );
|
||||
|
||||
CAF_PDM_InitScriptableFieldNoDefault( &m_ensembleSurfaces, "EnsembleSurfaces", "Ensemble Surfaces", "", "", "" );
|
||||
m_ensembleSurfaces.uiCapability()->setUiTreeHidden( true );
|
||||
|
||||
setDeletable( true );
|
||||
}
|
||||
|
||||
@@ -108,6 +112,14 @@ void RimSurfaceCollection::addSurface( RimSurface* surface )
|
||||
m_surfaces.push_back( surface );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSurfaceCollection::addEnsembleSurface( RimEnsembleSurface* ensembleSurface )
|
||||
{
|
||||
m_ensembleSurfaces.push_back( ensembleSurface );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -212,13 +224,11 @@ RimSurface* RimSurfaceCollection::copySurfaces( std::vector<RimSurface*> surface
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimSurface* RimSurfaceCollection::addGridCaseSurface( RimCase* sourceCase )
|
||||
RimSurface* RimSurfaceCollection::addGridCaseSurface( RimCase* sourceCase, int oneBasedSliceIndex )
|
||||
{
|
||||
auto s = new RimGridCaseSurface;
|
||||
s->setCase( sourceCase );
|
||||
|
||||
int oneBasedSliceIndex = 1;
|
||||
|
||||
s->setOneBasedIndex( oneBasedSliceIndex );
|
||||
s->setUserDescription( "Surface" );
|
||||
|
||||
@@ -255,6 +265,14 @@ std::vector<RimSurfaceCollection*> RimSurfaceCollection::subCollections() const
|
||||
return m_subCollections.childObjects();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RimEnsembleSurface*> RimSurfaceCollection::ensembleSurfaces() const
|
||||
{
|
||||
return m_ensembleSurfaces.childObjects();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -264,6 +282,11 @@ void RimSurfaceCollection::loadData()
|
||||
{
|
||||
surf->loadDataIfRequired();
|
||||
}
|
||||
|
||||
for ( auto ensSurf : m_ensembleSurfaces )
|
||||
{
|
||||
ensSurf->loadDataAndUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -477,6 +500,12 @@ bool RimSurfaceCollection::containsSurface()
|
||||
{
|
||||
containsSurface |= coll->containsSurface();
|
||||
}
|
||||
|
||||
for ( auto ensSurf : m_ensembleSurfaces )
|
||||
{
|
||||
containsSurface |= ( ensSurf->surfaces().size() > 0 );
|
||||
}
|
||||
|
||||
return containsSurface;
|
||||
}
|
||||
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#include "cafPdmObject.h"
|
||||
|
||||
class RimSurface;
|
||||
class RimEnsembleSurface;
|
||||
class RimCase;
|
||||
|
||||
class RimSurfaceCollection : public caf::PdmObject
|
||||
@@ -36,8 +37,10 @@ public:
|
||||
void addSurface( RimSurface* surface );
|
||||
void setAsTopmostFolder();
|
||||
|
||||
void addEnsembleSurface( RimEnsembleSurface* ensembleSurface );
|
||||
|
||||
RimSurface* importSurfacesFromFiles( const QStringList& fileNames, bool showLegend = true );
|
||||
RimSurface* addGridCaseSurface( RimCase* sourceCase );
|
||||
RimSurface* addGridCaseSurface( RimCase* sourceCase, int oneBasedSliceIndex = 1 );
|
||||
RimSurface* copySurfaces( std::vector<RimSurface*> surfaces );
|
||||
RimSurface* addSurfacesAtIndex( int index, std::vector<RimSurface*> surfaces );
|
||||
|
||||
@@ -64,6 +67,7 @@ public:
|
||||
|
||||
std::vector<RimSurface*> surfaces() const;
|
||||
std::vector<RimSurfaceCollection*> subCollections() const;
|
||||
std::vector<RimEnsembleSurface*> ensembleSurfaces() const;
|
||||
|
||||
protected:
|
||||
caf::PdmFieldHandle* userDescriptionField() override;
|
||||
@@ -74,4 +78,5 @@ private:
|
||||
caf::PdmField<QString> m_collectionName;
|
||||
caf::PdmChildArrayField<RimSurface*> m_surfaces;
|
||||
caf::PdmChildArrayField<RimSurfaceCollection*> m_subCollections;
|
||||
caf::PdmChildArrayField<RimEnsembleSurface*> m_ensembleSurfaces;
|
||||
};
|
||||
|
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "RimSurfaceInViewCollection.h"
|
||||
|
||||
#include "RimEnsembleSurfaceInView.h"
|
||||
#include "RimGridView.h"
|
||||
#include "RimIntersectionResultDefinition.h"
|
||||
#include "RimOilField.h"
|
||||
@@ -56,6 +57,9 @@ RimSurfaceInViewCollection::RimSurfaceInViewCollection()
|
||||
CAF_PDM_InitFieldNoDefault( &m_surfacesInView, "SurfacesInViewField", "SurfacesInViewField", "", "", "" );
|
||||
m_surfacesInView.uiCapability()->setUiTreeHidden( true );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_ensembleSurfacesInView, "EnsemblesSurfacesInView", "EnsembleSurfacesInView", "", "", "" );
|
||||
m_ensembleSurfacesInView.uiCapability()->setUiTreeHidden( true );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_surfaceCollection, "SurfaceCollectionRef", "SurfaceCollection", "", "", "" );
|
||||
m_surfaceCollection.uiCapability()->setUiHidden( true );
|
||||
|
||||
@@ -109,6 +113,7 @@ void RimSurfaceInViewCollection::setSurfaceCollection( RimSurfaceCollection* sur
|
||||
void RimSurfaceInViewCollection::updateAllViewItems()
|
||||
{
|
||||
syncCollectionsWithView();
|
||||
syncEnsembleSurfacesWithView();
|
||||
syncSurfacesWithView();
|
||||
updateConnectedEditors();
|
||||
}
|
||||
@@ -163,6 +168,56 @@ void RimSurfaceInViewCollection::syncCollectionsWithView()
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSurfaceInViewCollection::syncEnsembleSurfacesWithView()
|
||||
{
|
||||
// check that we have ensemble in view collectinons
|
||||
std::vector<RimEnsembleSurfaceInView*> ensembleSurfaces = m_ensembleSurfacesInView.childObjects();
|
||||
|
||||
for ( auto ensembleSurface : ensembleSurfaces )
|
||||
{
|
||||
if ( !ensembleSurface->ensembleSurface() )
|
||||
{
|
||||
m_ensembleSurfacesInView.removeChildObject( ensembleSurface );
|
||||
delete ensembleSurface;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new collection entries and reorder
|
||||
std::vector<RimEnsembleSurfaceInView*> orderedEnsembleSurfaces;
|
||||
if ( m_surfaceCollection )
|
||||
{
|
||||
// pick up the collections and the order from the surface collection
|
||||
std::vector<RimEnsembleSurface*> ensSurfs = m_surfaceCollection->ensembleSurfaces();
|
||||
for ( auto ensSurf : ensSurfs )
|
||||
{
|
||||
// check if this is a collection we need to create
|
||||
|
||||
RimEnsembleSurfaceInView* ensembleSurfaceInView = this->getEnsembleSurfaceInViewForEnsembleSurface( ensSurf );
|
||||
if ( ensembleSurfaceInView == nullptr )
|
||||
{
|
||||
RimEnsembleSurfaceInView* newColl = new RimEnsembleSurfaceInView();
|
||||
newColl->setEnsembleSurface( ensSurf );
|
||||
orderedEnsembleSurfaces.push_back( newColl );
|
||||
}
|
||||
else
|
||||
{
|
||||
orderedEnsembleSurfaces.push_back( ensembleSurfaceInView );
|
||||
}
|
||||
}
|
||||
|
||||
// make sure our view surfaces have the same order as the source surface collection
|
||||
m_ensembleSurfacesInView.clear();
|
||||
for ( auto ensSurf : orderedEnsembleSurfaces )
|
||||
{
|
||||
m_ensembleSurfacesInView.push_back( ensSurf );
|
||||
ensSurf->updateAllViewItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -231,6 +286,11 @@ void RimSurfaceInViewCollection::loadData()
|
||||
coll->loadData();
|
||||
}
|
||||
|
||||
for ( RimEnsembleSurfaceInView* ensSurf : m_ensembleSurfacesInView )
|
||||
{
|
||||
ensSurf->loadData();
|
||||
}
|
||||
|
||||
for ( RimSurfaceInView* surf : m_surfacesInView )
|
||||
{
|
||||
if ( surf->isActive() )
|
||||
@@ -250,6 +310,11 @@ void RimSurfaceInViewCollection::clearGeometry()
|
||||
coll->clearGeometry();
|
||||
}
|
||||
|
||||
for ( RimEnsembleSurfaceInView* ensSurf : m_ensembleSurfacesInView )
|
||||
{
|
||||
ensSurf->clearGeometry();
|
||||
}
|
||||
|
||||
for ( RimSurfaceInView* surf : m_surfacesInView )
|
||||
{
|
||||
surf->clearGeometry();
|
||||
@@ -271,6 +336,14 @@ void RimSurfaceInViewCollection::appendPartsToModel( cvf::ModelBasicList* model,
|
||||
}
|
||||
}
|
||||
|
||||
for ( RimEnsembleSurfaceInView* ensSurf : m_ensembleSurfacesInView )
|
||||
{
|
||||
if ( ensSurf->isChecked() )
|
||||
{
|
||||
ensSurf->appendPartsToModel( model, scaleTransform );
|
||||
}
|
||||
}
|
||||
|
||||
for ( RimSurfaceInView* surf : m_surfacesInView )
|
||||
{
|
||||
if ( surf->isActive() )
|
||||
@@ -340,6 +413,23 @@ RimSurfaceInViewCollection*
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimEnsembleSurfaceInView*
|
||||
RimSurfaceInViewCollection::getEnsembleSurfaceInViewForEnsembleSurface( const RimEnsembleSurface* coll ) const
|
||||
{
|
||||
for ( auto collInView : m_ensembleSurfacesInView )
|
||||
{
|
||||
if ( collInView->ensembleSurface() == coll )
|
||||
{
|
||||
return collInView;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
@@ -35,6 +35,8 @@ class ScalarMapper;
|
||||
class RimSurfaceInView;
|
||||
class RimSurface;
|
||||
class RimSurfaceCollection;
|
||||
class RimEnsembleSurfaceInView;
|
||||
class RimEnsembleSurface;
|
||||
class RimRegularLegendConfig;
|
||||
class RiuViewer;
|
||||
class RivIntersectionGeometryGeneratorIF;
|
||||
@@ -76,13 +78,17 @@ private:
|
||||
|
||||
RimSurfaceInView* getSurfaceInViewForSurface( const RimSurface* surf ) const;
|
||||
RimSurfaceInViewCollection* getCollectionInViewForCollection( const RimSurfaceCollection* coll ) const;
|
||||
RimEnsembleSurfaceInView* getEnsembleSurfaceInViewForEnsembleSurface( const RimEnsembleSurface* coll ) const;
|
||||
|
||||
void updateAllViewItems();
|
||||
void syncCollectionsWithView();
|
||||
void syncSurfacesWithView();
|
||||
void syncEnsembleSurfacesWithView();
|
||||
|
||||
caf::PdmProxyValueField<QString> m_collectionName;
|
||||
caf::PdmChildArrayField<RimSurfaceInViewCollection*> m_collectionsInView;
|
||||
caf::PdmChildArrayField<RimSurfaceInView*> m_surfacesInView;
|
||||
caf::PdmPtrField<RimSurfaceCollection*> m_surfaceCollection;
|
||||
caf::PdmChildArrayField<RimEnsembleSurfaceInView*> m_ensembleSurfacesInView;
|
||||
|
||||
caf::PdmPtrField<RimSurfaceCollection*> m_surfaceCollection;
|
||||
};
|
||||
|
@@ -10,6 +10,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RimcStimPlanModelTemplateCollection.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimcStimPlanModelPlotCollection.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimcStimPlanModel.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimcSurfaceCollection.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimcSurface.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimcDataContainerDouble.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimcDataContainerString.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimcDataContainerTime.h
|
||||
@@ -31,6 +32,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RimcStimPlanModelTemplateCollection.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimcStimPlanModelPlotCollection.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimcStimPlanModel.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimcSurfaceCollection.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimcSurface.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimcDataContainerDouble.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimcDataContainerString.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimcDataContainerTime.cpp
|
||||
|
91
ApplicationLibCode/ProjectDataModelCommands/RimcSurface.cpp
Normal file
91
ApplicationLibCode/ProjectDataModelCommands/RimcSurface.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2021- 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 "RimcSurface.h"
|
||||
|
||||
#include "RimCase.h"
|
||||
#include "RimSurface.h"
|
||||
#include "RimcDataContainerString.h"
|
||||
|
||||
#include "RigSurface.h"
|
||||
|
||||
#include "RifSurfaceExporter.h"
|
||||
|
||||
#include "cafPdmFieldScriptingCapability.h"
|
||||
#include "cafPdmObjectScriptingCapability.h"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimSurface, RimcSurface_exportToFile, "ExportToFile" );
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimcSurface_exportToFile::RimcSurface_exportToFile( caf::PdmObjectHandle* self )
|
||||
: caf::PdmObjectMethod( self )
|
||||
{
|
||||
CAF_PDM_InitObject( "Export Surface To fiole", "", "", "Export a surface to file" );
|
||||
CAF_PDM_InitScriptableFieldNoDefault( &m_fileName, "FileName", "", "", "", "Filename to export surface to" );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
caf::PdmObjectHandle* RimcSurface_exportToFile::execute()
|
||||
{
|
||||
RimSurface* surface = self<RimSurface>();
|
||||
|
||||
auto dataObject = new RimcDataContainerString();
|
||||
|
||||
if ( surface )
|
||||
{
|
||||
RigSurface* surfaceData = surface->surfaceData();
|
||||
|
||||
RifSurfaceExporter::writeGocadTSurfFile( m_fileName(),
|
||||
surface->userDescription(),
|
||||
surfaceData->vertices(),
|
||||
surfaceData->triangleIndices() );
|
||||
|
||||
dataObject->m_stringValues = { m_fileName() };
|
||||
}
|
||||
|
||||
return dataObject;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimcSurface_exportToFile::resultIsPersistent() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::unique_ptr<caf::PdmObjectHandle> RimcSurface_exportToFile::defaultResult() const
|
||||
{
|
||||
return std::unique_ptr<caf::PdmObjectHandle>( new RimcDataContainerString() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimcSurface_exportToFile::isNullptrValidResult() const
|
||||
{
|
||||
return true;
|
||||
}
|
48
ApplicationLibCode/ProjectDataModelCommands/RimcSurface.h
Normal file
48
ApplicationLibCode/ProjectDataModelCommands/RimcSurface.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2021- 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 "cafPdmField.h"
|
||||
#include "cafPdmObjectHandle.h"
|
||||
#include "cafPdmObjectMethod.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <memory>
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RimcSurface_exportToFile : public caf::PdmObjectMethod
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimcSurface_exportToFile( caf::PdmObjectHandle* self );
|
||||
|
||||
caf::PdmObjectHandle* execute() override;
|
||||
bool resultIsPersistent() const override;
|
||||
std::unique_ptr<PdmObjectHandle> defaultResult() const override;
|
||||
bool isNullptrValidResult() const override;
|
||||
|
||||
private:
|
||||
caf::PdmField<QString> m_fileName;
|
||||
};
|
@@ -19,7 +19,9 @@
|
||||
|
||||
#include "SurfaceCommands/RicImportSurfacesFeature.h"
|
||||
|
||||
#include "RimCase.h"
|
||||
#include "RimFileSurface.h"
|
||||
#include "RimGridCaseSurface.h"
|
||||
#include "RimSurface.h"
|
||||
#include "RimSurfaceCollection.h"
|
||||
|
||||
@@ -30,6 +32,7 @@
|
||||
|
||||
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimSurfaceCollection, RimcSurfaceCollection_importSurface, "ImportSurface" );
|
||||
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimSurfaceCollection, RimcSurfaceCollection_addFolder, "AddFolder" );
|
||||
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimSurfaceCollection, RimcSurfaceCollection_newSurface, "NewSurface" );
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
@@ -130,3 +133,52 @@ bool RimcSurfaceCollection_addFolder::isNullptrValidResult() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimcSurfaceCollection_newSurface::RimcSurfaceCollection_newSurface( caf::PdmObjectHandle* self )
|
||||
: caf::PdmObjectMethod( self )
|
||||
{
|
||||
CAF_PDM_InitObject( "New Surface", "", "", "Create a new surface" );
|
||||
CAF_PDM_InitScriptableFieldNoDefault( &m_case, "Case", "", "", "", "" );
|
||||
CAF_PDM_InitScriptableFieldNoDefault( &m_kIndex, "KIndex", "", "", "", "" );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
caf::PdmObjectHandle* RimcSurfaceCollection_newSurface::execute()
|
||||
{
|
||||
RimSurfaceCollection* coll = self<RimSurfaceCollection>();
|
||||
if ( coll && m_case )
|
||||
{
|
||||
RimSurface* surface = coll->addGridCaseSurface( m_case(), m_kIndex );
|
||||
return surface;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimcSurfaceCollection_newSurface::resultIsPersistent() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::unique_ptr<caf::PdmObjectHandle> RimcSurfaceCollection_newSurface::defaultResult() const
|
||||
{
|
||||
return std::unique_ptr<caf::PdmObjectHandle>( new RimGridCaseSurface );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimcSurfaceCollection_newSurface::isNullptrValidResult() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -32,6 +32,7 @@
|
||||
|
||||
class RimSurface;
|
||||
class RimSurfaceCollection;
|
||||
class RimCase;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
@@ -70,3 +71,23 @@ public:
|
||||
private:
|
||||
caf::PdmField<QString> m_folderName;
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RimcSurfaceCollection_newSurface : public caf::PdmObjectMethod
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimcSurfaceCollection_newSurface( caf::PdmObjectHandle* self );
|
||||
|
||||
caf::PdmObjectHandle* execute() override;
|
||||
bool resultIsPersistent() const override;
|
||||
std::unique_ptr<PdmObjectHandle> defaultResult() const override;
|
||||
bool isNullptrValidResult() const override;
|
||||
|
||||
private:
|
||||
caf::PdmPtrField<RimCase*> m_case;
|
||||
caf::PdmField<int> m_kIndex;
|
||||
};
|
||||
|
@@ -89,6 +89,8 @@ ${CMAKE_CURRENT_LIST_DIR}/RigTracer.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigStimPlanModelTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSlice2D.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEnsembleParameter.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSurfaceResampler.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSurfaceStatisticsCalculator.h
|
||||
)
|
||||
|
||||
|
||||
@@ -175,6 +177,8 @@ ${CMAKE_CURRENT_LIST_DIR}/RigTracer.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigStimPlanModelTools.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSlice2D.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigEnsembleParameter.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSurfaceResampler.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSurfaceStatisticsCalculator.cpp
|
||||
)
|
||||
|
||||
list(APPEND CODE_HEADER_FILES
|
||||
|
@@ -34,7 +34,7 @@ RigSurface::~RigSurface()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<unsigned>& RigSurface::triangleIndices()
|
||||
const std::vector<unsigned>& RigSurface::triangleIndices() const
|
||||
{
|
||||
return m_triangleIndices;
|
||||
}
|
||||
@@ -42,7 +42,7 @@ const std::vector<unsigned>& RigSurface::triangleIndices()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector<cvf::Vec3d>& RigSurface::vertices()
|
||||
const std::vector<cvf::Vec3d>& RigSurface::vertices() const
|
||||
{
|
||||
return m_vertices;
|
||||
}
|
||||
|
@@ -31,8 +31,8 @@ public:
|
||||
RigSurface();
|
||||
~RigSurface() override;
|
||||
|
||||
const std::vector<unsigned>& triangleIndices();
|
||||
const std::vector<cvf::Vec3d>& vertices();
|
||||
const std::vector<unsigned>& triangleIndices() const;
|
||||
const std::vector<cvf::Vec3d>& vertices() const;
|
||||
|
||||
void setTriangleData( const std::vector<unsigned>& tringleIndices, const std::vector<cvf::Vec3d>& vertices );
|
||||
void addVerticeResult( const QString resultName, const std::vector<float>& resultValues );
|
||||
|
119
ApplicationLibCode/ReservoirDataModel/RigSurfaceResampler.cpp
Normal file
119
ApplicationLibCode/ReservoirDataModel/RigSurfaceResampler.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2021- 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 "RigSurfaceResampler.h"
|
||||
|
||||
#include "cvfGeometryTools.h"
|
||||
|
||||
#include "cvfObject.h"
|
||||
#include <limits>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::ref<RigSurface> RigSurfaceResampler::resampleSurface( cvf::ref<RigSurface> targetSurface, cvf::ref<RigSurface> surface )
|
||||
{
|
||||
cvf::ref<RigSurface> resampledSurface = cvf::make_ref<RigSurface>();
|
||||
|
||||
const std::vector<cvf::Vec3d>& targetVerts = targetSurface->vertices();
|
||||
const std::vector<unsigned>& targetIndices = targetSurface->triangleIndices();
|
||||
|
||||
std::vector<cvf::Vec3d> resampledVerts;
|
||||
|
||||
for ( auto targetVert : targetVerts )
|
||||
{
|
||||
cvf::Vec3d pointAbove = cvf::Vec3d( targetVert.x(), targetVert.y(), 10000.0 );
|
||||
cvf::Vec3d pointBelow = cvf::Vec3d( targetVert.x(), targetVert.y(), -10000.0 );
|
||||
|
||||
cvf::Vec3d intersectionPoint;
|
||||
bool foundMatch =
|
||||
resamplePoint( pointAbove, pointBelow, surface->triangleIndices(), surface->vertices(), intersectionPoint );
|
||||
if ( !foundMatch )
|
||||
intersectionPoint = cvf::Vec3d( targetVert.x(), targetVert.y(), std::numeric_limits<double>::infinity() );
|
||||
|
||||
resampledVerts.push_back( intersectionPoint );
|
||||
}
|
||||
|
||||
resampledSurface->setTriangleData( targetIndices, resampledVerts );
|
||||
|
||||
return resampledSurface;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigSurfaceResampler::resamplePoint( const cvf::Vec3d& pointAbove,
|
||||
const cvf::Vec3d& pointBelow,
|
||||
const std::vector<unsigned int>& indices,
|
||||
const std::vector<cvf::Vec3d>& vertices,
|
||||
cvf::Vec3d& intersectionPoint )
|
||||
{
|
||||
for ( size_t i = 0; i < indices.size(); i += 3 )
|
||||
{
|
||||
bool isLineDirDotNormalNegative = false;
|
||||
if ( cvf::GeometryTools::intersectLineSegmentTriangle( pointAbove,
|
||||
pointBelow,
|
||||
vertices[indices[i]],
|
||||
vertices[indices[i + 1]],
|
||||
vertices[indices[i + 2]],
|
||||
&intersectionPoint,
|
||||
&isLineDirDotNormalNegative ) == 1 )
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle cases where no match is found due to floating point imprecision,
|
||||
// or when falling off resulting grid slightly.
|
||||
double maxDistance = 10.0;
|
||||
return findClosestPointXY( pointAbove, vertices, maxDistance, intersectionPoint );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Find the closest vertex to targetPoint (must be closer than maxDistance) in XY plane.
|
||||
/// Unit maxDistance: meter.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigSurfaceResampler::findClosestPointXY( const cvf::Vec3d& targetPoint,
|
||||
const std::vector<cvf::Vec3d>& vertices,
|
||||
double maxDistance,
|
||||
cvf::Vec3d& intersectionPoint )
|
||||
{
|
||||
// Find closest vertices
|
||||
double shortestDistance = std::numeric_limits<double>::max();
|
||||
double closestZ = std::numeric_limits<double>::infinity();
|
||||
for ( auto v : vertices )
|
||||
{
|
||||
// Ignore height (z) component when finding closest by
|
||||
// moving point to same height as target point above
|
||||
cvf::Vec3d p( v.x(), v.y(), targetPoint.z() );
|
||||
double distance = p.pointDistance( targetPoint );
|
||||
if ( distance < shortestDistance )
|
||||
{
|
||||
shortestDistance = distance;
|
||||
closestZ = v.z();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the closest point is not to far away to be valid
|
||||
if ( shortestDistance < maxDistance )
|
||||
{
|
||||
intersectionPoint = cvf::Vec3d( targetPoint.x(), targetPoint.y(), closestZ );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
43
ApplicationLibCode/ReservoirDataModel/RigSurfaceResampler.h
Normal file
43
ApplicationLibCode/ReservoirDataModel/RigSurfaceResampler.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2021- 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 "RigSurface.h"
|
||||
|
||||
#include "cvfObject.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class RigSurfaceResampler
|
||||
{
|
||||
public:
|
||||
static cvf::ref<RigSurface> resampleSurface( cvf::ref<RigSurface> targetSurface, cvf::ref<RigSurface> surface );
|
||||
|
||||
static bool resamplePoint( const cvf::Vec3d& pointAbove,
|
||||
const cvf::Vec3d& pointBelow,
|
||||
const std::vector<unsigned int>& indices,
|
||||
const std::vector<cvf::Vec3d>& vertices,
|
||||
cvf::Vec3d& intersectionPoint );
|
||||
|
||||
private:
|
||||
static bool findClosestPointXY( const cvf::Vec3d& targetPoint,
|
||||
const std::vector<cvf::Vec3d>& vertices,
|
||||
double maxDistance,
|
||||
cvf::Vec3d& intersectionPoint );
|
||||
};
|
@@ -0,0 +1,129 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2021 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 "RigSurfaceStatisticsCalculator.h"
|
||||
|
||||
#include "RigStatisticsMath.h"
|
||||
|
||||
#include "cafAppEnum.h"
|
||||
#include "cafAssert.h"
|
||||
|
||||
#include "cvfObject.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace caf
|
||||
{
|
||||
template <>
|
||||
void caf::AppEnum<RigSurfaceStatisticsCalculator::StatisticsType>::setUp()
|
||||
{
|
||||
addItem( RigSurfaceStatisticsCalculator::StatisticsType::MEAN, "MEAN", "Mean" );
|
||||
addItem( RigSurfaceStatisticsCalculator::StatisticsType::MIN, "MIN", "Minimum" );
|
||||
addItem( RigSurfaceStatisticsCalculator::StatisticsType::MAX, "MAX", "Maximum" );
|
||||
addItem( RigSurfaceStatisticsCalculator::StatisticsType::P10, "P10", "P10" );
|
||||
addItem( RigSurfaceStatisticsCalculator::StatisticsType::P50, "P50", "P50" );
|
||||
addItem( RigSurfaceStatisticsCalculator::StatisticsType::P90, "P90", "P90" );
|
||||
setDefault( RigSurfaceStatisticsCalculator::StatisticsType::MEAN );
|
||||
}
|
||||
}; // namespace caf
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::ref<RigSurface> RigSurfaceStatisticsCalculator::computeStatistics( const std::vector<cvf::ref<RigSurface>>& surfaces )
|
||||
{
|
||||
// Need at least one surface to generate statistics
|
||||
if ( surfaces.empty() ) return nullptr;
|
||||
|
||||
// Check that the size of all the surfaces are the same
|
||||
if ( !areSurfacesSameSize( surfaces ) ) return nullptr;
|
||||
|
||||
size_t vecSize = surfaces[0]->vertices().size();
|
||||
|
||||
std::vector<float> meanValues( vecSize, std::numeric_limits<double>::infinity() );
|
||||
std::vector<float> minValues( vecSize, std::numeric_limits<double>::infinity() );
|
||||
std::vector<float> maxValues( vecSize, std::numeric_limits<double>::infinity() );
|
||||
std::vector<float> p10Values( vecSize, std::numeric_limits<double>::infinity() );
|
||||
std::vector<float> p50Values( vecSize, std::numeric_limits<double>::infinity() );
|
||||
std::vector<float> p90Values( vecSize, std::numeric_limits<double>::infinity() );
|
||||
|
||||
for ( size_t i = 0; i < vecSize; i++ )
|
||||
{
|
||||
std::vector<double> samples;
|
||||
for ( auto surface : surfaces )
|
||||
{
|
||||
double z = surface->vertices()[i].z();
|
||||
samples.push_back( z );
|
||||
}
|
||||
|
||||
double min;
|
||||
double max;
|
||||
double sum;
|
||||
double range;
|
||||
double mean;
|
||||
double dev;
|
||||
RigStatisticsMath::calculateBasicStatistics( samples, &min, &max, &sum, &range, &mean, &dev );
|
||||
|
||||
double p10;
|
||||
double p50;
|
||||
double p90;
|
||||
RigStatisticsMath::calculateStatisticsCurves( samples, &p10, &p50, &p90, &mean );
|
||||
|
||||
// TODO: improve handling of these cases
|
||||
auto makeValid = []( double val ) {
|
||||
if ( std::isinf( val ) || std::isnan( val ) ) return 0.0;
|
||||
return val;
|
||||
};
|
||||
|
||||
meanValues[i] = makeValid( mean );
|
||||
minValues[i] = makeValid( min );
|
||||
maxValues[i] = makeValid( max );
|
||||
p10Values[i] = makeValid( p10 );
|
||||
p50Values[i] = makeValid( p50 );
|
||||
p90Values[i] = makeValid( p90 );
|
||||
}
|
||||
|
||||
cvf::ref<RigSurface> statSurface = cvf::make_ref<RigSurface>();
|
||||
statSurface->setTriangleData( surfaces[0]->triangleIndices(), surfaces[0]->vertices() );
|
||||
|
||||
auto enumToText = []( auto statisticsType ) { return caf::AppEnum<StatisticsType>::text( statisticsType ); };
|
||||
|
||||
statSurface->addVerticeResult( enumToText( StatisticsType::MEAN ), meanValues );
|
||||
statSurface->addVerticeResult( enumToText( StatisticsType::MIN ), minValues );
|
||||
statSurface->addVerticeResult( enumToText( StatisticsType::MAX ), maxValues );
|
||||
statSurface->addVerticeResult( enumToText( StatisticsType::P10 ), p10Values );
|
||||
statSurface->addVerticeResult( enumToText( StatisticsType::P50 ), p50Values );
|
||||
statSurface->addVerticeResult( enumToText( StatisticsType::P90 ), p90Values );
|
||||
|
||||
return statSurface;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RigSurfaceStatisticsCalculator::areSurfacesSameSize( const std::vector<cvf::ref<RigSurface>>& surfaces )
|
||||
{
|
||||
CAF_ASSERT( !surfaces.empty() );
|
||||
|
||||
size_t vecSize = surfaces[0]->vertices().size();
|
||||
for ( auto surface : surfaces )
|
||||
{
|
||||
if ( vecSize != surface->vertices().size() ) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2021 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 "RigSurface.h"
|
||||
|
||||
#include "cvfObject.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigSurfaceStatisticsCalculator
|
||||
{
|
||||
public:
|
||||
enum class StatisticsType
|
||||
{
|
||||
MIN,
|
||||
MAX,
|
||||
P10,
|
||||
P50,
|
||||
P90,
|
||||
MEAN
|
||||
};
|
||||
|
||||
static cvf::ref<RigSurface> computeStatistics( const std::vector<cvf::ref<RigSurface>>& surfaces );
|
||||
static bool areSurfacesSameSize( const std::vector<cvf::ref<RigSurface>>& surfaces );
|
||||
};
|
@@ -515,13 +515,13 @@ double GeometryTools::linePointSquareDist( const cvf::Vec3d& p1, const cvf::Vec3
|
||||
// dot product (3D) which allows vector operations in arguments
|
||||
#define dot( u, v ) ( ( u ).x() * ( v ).x() + ( u ).y() * ( v ).y() + ( u ).z() * ( v ).z() )
|
||||
|
||||
int GeometryTools::intersectLineSegmentTriangle( const cvf::Vec3d p0,
|
||||
const cvf::Vec3d p1,
|
||||
const cvf::Vec3d t0,
|
||||
const cvf::Vec3d t1,
|
||||
const cvf::Vec3d t2,
|
||||
cvf::Vec3d* intersectionPoint,
|
||||
bool* isLineDirDotNormalNegative )
|
||||
int GeometryTools::intersectLineSegmentTriangle( const cvf::Vec3d& p0,
|
||||
const cvf::Vec3d& p1,
|
||||
const cvf::Vec3d& t0,
|
||||
const cvf::Vec3d& t1,
|
||||
const cvf::Vec3d& t2,
|
||||
cvf::Vec3d* intersectionPoint,
|
||||
bool* isLineDirDotNormalNegative )
|
||||
{
|
||||
CVF_TIGHT_ASSERT( intersectionPoint != nullptr );
|
||||
CVF_TIGHT_ASSERT( isLineDirDotNormalNegative != nullptr );
|
||||
|
@@ -47,13 +47,13 @@ public:
|
||||
double* normalizedIntersection );
|
||||
|
||||
static double linePointSquareDist( const cvf::Vec3d& p1, const cvf::Vec3d& p2, const cvf::Vec3d& p3 );
|
||||
static int intersectLineSegmentTriangle( const cvf::Vec3d p0,
|
||||
const cvf::Vec3d p1,
|
||||
const cvf::Vec3d t0,
|
||||
const cvf::Vec3d t1,
|
||||
const cvf::Vec3d t2,
|
||||
cvf::Vec3d* intersectionPoint,
|
||||
bool* isLineDirDotNormalNegative );
|
||||
static int intersectLineSegmentTriangle( const cvf::Vec3d& p0,
|
||||
const cvf::Vec3d& p1,
|
||||
const cvf::Vec3d& t0,
|
||||
const cvf::Vec3d& t1,
|
||||
const cvf::Vec3d& t2,
|
||||
cvf::Vec3d* intersectionPoint,
|
||||
bool* isLineDirDotNormalNegative );
|
||||
static cvf::Vec3d
|
||||
barycentricCoords( const cvf::Vec3d& t0, const cvf::Vec3d& t1, const cvf::Vec3d& t2, const cvf::Vec3d& p );
|
||||
static cvf::Vec4d barycentricCoords( const cvf::Vec3d& v0,
|
||||
|
@@ -77,6 +77,8 @@ ${CMAKE_CURRENT_LIST_DIR}/RigWellPathGeometryExporter-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifStimPlanModelDeviationFrkExporter-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifSummaryDataReader-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSlice2D-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSurfaceResampler-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigSurfaceStatisticsCalculator-Test.cpp
|
||||
)
|
||||
|
||||
if (RESINSIGHT_ENABLE_GRPC)
|
||||
|
85
ApplicationLibCode/UnitTests/RigSurfaceResampler-Test.cpp
Normal file
85
ApplicationLibCode/UnitTests/RigSurfaceResampler-Test.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2021 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 "RigSurfaceResampler.h"
|
||||
#include "cvfObject.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigSurfaceResamplerTests, sameFlatGeometry )
|
||||
{
|
||||
cvf::ref<RigSurface> targetSurface = cvf::make_ref<RigSurface>();
|
||||
cvf::ref<RigSurface> surface = cvf::make_ref<RigSurface>();
|
||||
|
||||
// Make single triangle, and assign to both surfaces
|
||||
double z = 2.0;
|
||||
std::vector<cvf::Vec3d> vertices = { cvf::Vec3d( -1.0, -1.0, z ), cvf::Vec3d( 1.0, -1.0, z ), cvf::Vec3d( -1.0, 1.0, z ) };
|
||||
std::vector<unsigned int> indices = { 2, 1, 0 };
|
||||
|
||||
targetSurface->setTriangleData( indices, vertices );
|
||||
surface->setTriangleData( indices, vertices );
|
||||
|
||||
cvf::ref<RigSurface> resampledSurface = RigSurfaceResampler::resampleSurface( targetSurface, surface );
|
||||
|
||||
ASSERT_EQ( resampledSurface->triangleIndices().size(), targetSurface->triangleIndices().size() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigSurfaceResamplerTests, flatGeometryLargerSource )
|
||||
{
|
||||
cvf::ref<RigSurface> targetSurface = cvf::make_ref<RigSurface>();
|
||||
cvf::ref<RigSurface> surface = cvf::make_ref<RigSurface>();
|
||||
|
||||
// Make two triangle: target smaller than the surface
|
||||
std::vector<unsigned int> indices = { 2, 1, 0 };
|
||||
|
||||
std::vector<cvf::Vec3d> targetVertices = { cvf::Vec3d( -1.0, -1.0, 1.0 ),
|
||||
cvf::Vec3d( 1.0, -1.0, 1.0 ),
|
||||
cvf::Vec3d( -1.0, 1.0, 1.0 ) };
|
||||
|
||||
targetSurface->setTriangleData( indices, targetVertices );
|
||||
|
||||
std::vector<cvf::Vec3d> surfaceVertices = { cvf::Vec3d( -2.0, -2.0, 2.0 ),
|
||||
cvf::Vec3d( 2.0, -2.0, 2.0 ),
|
||||
cvf::Vec3d( -2.0, 2.0, 2.0 ) };
|
||||
|
||||
surface->setTriangleData( indices, surfaceVertices );
|
||||
|
||||
cvf::ref<RigSurface> resampledSurface = RigSurfaceResampler::resampleSurface( targetSurface, surface );
|
||||
|
||||
ASSERT_EQ( resampledSurface->triangleIndices().size(), targetSurface->triangleIndices().size() );
|
||||
cvf::Vec3d r1 = resampledSurface->vertices()[0];
|
||||
ASSERT_EQ( r1.x(), -1.0 );
|
||||
ASSERT_EQ( r1.y(), -1.0 );
|
||||
ASSERT_EQ( r1.z(), 2.0 );
|
||||
|
||||
cvf::Vec3d r2 = resampledSurface->vertices()[1];
|
||||
ASSERT_EQ( r2.x(), 1.0 );
|
||||
ASSERT_EQ( r2.y(), -1.0 );
|
||||
ASSERT_EQ( r2.z(), 2.0 );
|
||||
|
||||
cvf::Vec3d r3 = resampledSurface->vertices()[2];
|
||||
ASSERT_EQ( r3.x(), -1.0 );
|
||||
ASSERT_EQ( r3.y(), 1.0 );
|
||||
ASSERT_EQ( r3.z(), 2.0 );
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2021 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 "RigSurface.h"
|
||||
#include "RigSurfaceStatisticsCalculator.h"
|
||||
|
||||
#include "cvfObject.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigSurfaceStatisticsTests, computeStatistics )
|
||||
{
|
||||
std::vector<cvf::ref<RigSurface>> surfaces;
|
||||
for ( size_t i = 0; i < 100; i++ )
|
||||
{
|
||||
cvf::ref<RigSurface> surface = cvf::make_ref<RigSurface>();
|
||||
std::vector<unsigned int> indices = { 2, 1, 0 };
|
||||
std::vector<cvf::Vec3d> vertices = { cvf::Vec3d( -1.0, -1.0, i ),
|
||||
cvf::Vec3d( 1.0, -1.0, i ),
|
||||
cvf::Vec3d( -1.0, 1.0, i ) };
|
||||
|
||||
surface->setTriangleData( indices, vertices );
|
||||
surfaces.push_back( surface );
|
||||
}
|
||||
|
||||
cvf::ref<RigSurface> statSurface = RigSurfaceStatisticsCalculator::computeStatistics( surfaces );
|
||||
ASSERT_TRUE( statSurface.notNull() );
|
||||
|
||||
ASSERT_EQ( statSurface->triangleIndices().size(), surfaces[0]->triangleIndices().size() );
|
||||
|
||||
const std::vector<QString>& propertyNames = statSurface->propertyNames();
|
||||
ASSERT_TRUE( std::find( propertyNames.begin(), propertyNames.end(), "MEAN" ) != propertyNames.end() );
|
||||
ASSERT_TRUE( std::find( propertyNames.begin(), propertyNames.end(), "MIN" ) != propertyNames.end() );
|
||||
ASSERT_TRUE( std::find( propertyNames.begin(), propertyNames.end(), "MAX" ) != propertyNames.end() );
|
||||
ASSERT_TRUE( std::find( propertyNames.begin(), propertyNames.end(), "P10" ) != propertyNames.end() );
|
||||
ASSERT_TRUE( std::find( propertyNames.begin(), propertyNames.end(), "P50" ) != propertyNames.end() );
|
||||
ASSERT_TRUE( std::find( propertyNames.begin(), propertyNames.end(), "P90" ) != propertyNames.end() );
|
||||
|
||||
const std::vector<float>& meanValues = statSurface->propertyValues( "MEAN" );
|
||||
const std::vector<float>& minValues = statSurface->propertyValues( "MIN" );
|
||||
const std::vector<float>& maxValues = statSurface->propertyValues( "MAX" );
|
||||
const std::vector<float>& p10Values = statSurface->propertyValues( "P10" );
|
||||
const std::vector<float>& p50Values = statSurface->propertyValues( "P50" );
|
||||
const std::vector<float>& p90Values = statSurface->propertyValues( "P90" );
|
||||
|
||||
// One value per vertex
|
||||
ASSERT_EQ( 3u, meanValues.size() );
|
||||
ASSERT_EQ( 3u, minValues.size() );
|
||||
ASSERT_EQ( 3u, maxValues.size() );
|
||||
ASSERT_EQ( 3u, p10Values.size() );
|
||||
ASSERT_EQ( 3u, p50Values.size() );
|
||||
ASSERT_EQ( 3u, p90Values.size() );
|
||||
|
||||
for ( size_t i = 0; i < 3u; i++ )
|
||||
{
|
||||
ASSERT_EQ( 49.5, meanValues[i] );
|
||||
ASSERT_EQ( 0, minValues[i] );
|
||||
ASSERT_EQ( 99, maxValues[i] );
|
||||
ASSERT_NEAR( 9.1, p10Values[i], 0.0001 );
|
||||
ASSERT_NEAR( 49.5, p50Values[i], 0.0001 );
|
||||
ASSERT_NEAR( 89.9, p90Values[i], 0.0001 );
|
||||
}
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
# Load ResInsight Processing Server Client Library
|
||||
import rips
|
||||
import tempfile
|
||||
from os.path import expanduser
|
||||
from pathlib import Path
|
||||
|
||||
# Connect to ResInsight instance
|
||||
resinsight = rips.Instance.find()
|
||||
|
||||
|
||||
home_dir = expanduser("~")
|
||||
|
||||
export_folder = tempfile.mkdtemp()
|
||||
|
||||
directory_path = "resprojects/webviz-subsurface-testdata/reek_history_match/"
|
||||
|
||||
|
||||
case_file_paths = []
|
||||
num_realizations = 9
|
||||
num_iterations = 4
|
||||
|
||||
|
||||
for realization in range(0, num_realizations):
|
||||
for iteration in range(0, num_iterations):
|
||||
realization_dir = "realization-" + str(realization)
|
||||
iteration_dir = "iter-" + str(iteration)
|
||||
egrid_name = "eclipse/model/5_R001_REEK-" + str(realization) + ".EGRID"
|
||||
path = Path(
|
||||
home_dir, directory_path, realization_dir, iteration_dir, egrid_name
|
||||
)
|
||||
case_file_paths.append(path)
|
||||
|
||||
k_indexes = [4, 10]
|
||||
|
||||
for path in case_file_paths:
|
||||
# Load a case
|
||||
path_name = path.as_posix()
|
||||
case = resinsight.project.load_case(path_name)
|
||||
|
||||
if resinsight.project.has_warnings():
|
||||
for warning in resinsight.project.warnings():
|
||||
print(warning)
|
||||
|
||||
surface_collection = resinsight.project.descendants(rips.SurfaceCollection)[0]
|
||||
|
||||
for k_index in k_indexes:
|
||||
print("Generating surface K layer " + str(k_index) + " for case " + path_name)
|
||||
|
||||
surface = surface_collection.new_surface(case, k_index)
|
||||
print("Surface: ", surface)
|
||||
|
||||
parent_path = path.parent
|
||||
export_folder_path = Path(parent_path, "surfaceexport")
|
||||
export_folder_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
export_file = Path(export_folder_path, "surf_" + str(k_index) + ".ts")
|
||||
print("Exporting to " + export_file.as_posix())
|
||||
surface.export_to_file(export_file.as_posix())
|
||||
|
||||
# Close project to avoid aggregated memory usage
|
||||
# Can be replaced when case.close() is implemented
|
||||
resinsight.project.close()
|
34
GrpcInterface/Python/rips/tests/test_surfaces.py
Normal file
34
GrpcInterface/Python/rips/tests/test_surfaces.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import sys
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
|
||||
sys.path.insert(1, os.path.join(sys.path[0], "../../"))
|
||||
import rips
|
||||
|
||||
import dataroot
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
sys.platform.startswith("linux"),
|
||||
reason="Brugge is currently exceptionally slow on Linux",
|
||||
)
|
||||
def test_create_and_export_surface(rips_instance, initialize_test):
|
||||
case_path = dataroot.PATH + "/Case_with_10_timesteps/Real0/BRUGGE_0000.EGRID"
|
||||
case = rips_instance.project.load_case(path=case_path)
|
||||
assert len(case.grids()) == 1
|
||||
|
||||
surface_collection = rips_instance.project.descendants(rips.SurfaceCollection)[0]
|
||||
|
||||
surface = surface_collection.new_surface(case, 5)
|
||||
assert surface
|
||||
|
||||
with tempfile.TemporaryDirectory(prefix="rips") as tmpdirname:
|
||||
path = Path(tmpdirname, "mysurface.ts")
|
||||
print("Temporary folder: ", path.as_posix())
|
||||
|
||||
fname = surface.export_to_file(path.as_posix())
|
||||
assert len(fname.values) == 1
|
||||
|
||||
assert path.exists()
|
Reference in New Issue
Block a user