mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-24 22:27:55 -05:00
#14878: Fix stale well picks, import errors and overlapping zones
This commit is contained in:
1 parent
029759f548
commit
bc62af3bb5
20 files changed
+217
-132
No files matched your search
@@ -18,6 +18,8 @@
|
||||
|
||||
#include "RifWellPathFormationReader.h"
|
||||
|
||||
#include "RifCsvHtmlTableTools.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QStringList>
|
||||
#include <QTextStream>
|
||||
@@ -27,14 +29,6 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// The FMU formations.csv format is comma-separated, the "well pick" formats are semicolon-separated
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QChar detectDelimiter( const QString& line )
|
||||
{
|
||||
return line.contains( ',' ) ? ',' : ';';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -104,7 +98,7 @@ std::expected<RifWellPathFormationReader::WellFormations, QString> RifWellPathFo
|
||||
if ( stream.atEnd() ) return std::unexpected( parseFailure );
|
||||
|
||||
const QString rawHeaderLine = stream.readLine();
|
||||
delimiter = detectDelimiter( rawHeaderLine );
|
||||
delimiter = RifCsvHtmlTableTools::detectDelimiter( rawHeaderLine );
|
||||
header = parseHeader( rawHeaderLine, delimiter );
|
||||
}
|
||||
|
||||
|
||||
@@ -223,14 +223,6 @@ RimEclipseResultCase* RimParameterRftCrossPlot::eclipseCase() const
|
||||
return m_eclipseCase();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimParameterRftCrossPlot::useDepthRange() const
|
||||
{
|
||||
return m_filterMode() != RimRftCrossPlotTools::DepthFilterMode::NONE;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -292,8 +284,7 @@ RiuQwtPlotWidget* RimParameterRftCrossPlot::viewer()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RimRftCrossPlotTools::DepthInterval> RimParameterRftCrossPlot::depthIntervals() const
|
||||
{
|
||||
return RimRftCrossPlotTools::buildDepthIntervals( useDepthRange(),
|
||||
m_filterMode(),
|
||||
return RimRftCrossPlotTools::buildDepthIntervals( m_filterMode(),
|
||||
m_depthRangeMin(),
|
||||
m_depthRangeMax(),
|
||||
m_wellFormations(),
|
||||
@@ -387,15 +378,11 @@ void RimParameterRftCrossPlot::updateAxes()
|
||||
const int axisTitleSize = caf::FontTools::absolutePointSize( RiaPreferences::current()->defaultPlotFontSize(), m_axisTitleFontSize() );
|
||||
const int axisValueSize = caf::FontTools::absolutePointSize( RiaPreferences::current()->defaultPlotFontSize(), m_axisValueFontSize() );
|
||||
|
||||
const QString pressureLabel = m_samplingMode() == SamplingMode::ALL_SAMPLES ? "Pressure" : "Mean Pressure";
|
||||
const QString filterDescription = RimRftCrossPlotTools::depthFilterDescription( useDepthRange(),
|
||||
m_filterMode(),
|
||||
m_depthType(),
|
||||
m_depthRangeMin(),
|
||||
m_depthRangeMax(),
|
||||
m_selectedZones() );
|
||||
const QString depthLabel = filterDescription.isEmpty() ? pressureLabel
|
||||
: QString( "%1 [%2]" ).arg( pressureLabel ).arg( filterDescription );
|
||||
const QString pressureLabel = m_samplingMode() == SamplingMode::ALL_SAMPLES ? "Pressure" : "Mean Pressure";
|
||||
const QString filterDescription =
|
||||
RimRftCrossPlotTools::depthFilterDescription( m_filterMode(), m_depthType(), m_depthRangeMin(), m_depthRangeMax(), m_selectedZones() );
|
||||
const QString depthLabel = filterDescription.isEmpty() ? pressureLabel
|
||||
: QString( "%1 [%2]" ).arg( pressureLabel ).arg( filterDescription );
|
||||
|
||||
m_plotWidget->setAxisTitleText( RiuPlotAxis::defaultLeft(), depthLabel );
|
||||
m_plotWidget->setAxisTitleEnabled( RiuPlotAxis::defaultLeft(), true );
|
||||
@@ -844,12 +831,8 @@ void RimParameterRftCrossPlot::updatePlotTitle()
|
||||
|
||||
if ( m_useAutoPlotTitle && m_ensemble() )
|
||||
{
|
||||
const QString filterDescription = RimRftCrossPlotTools::depthFilterDescription( useDepthRange(),
|
||||
m_filterMode(),
|
||||
m_depthType(),
|
||||
m_depthRangeMin(),
|
||||
m_depthRangeMax(),
|
||||
m_selectedZones() );
|
||||
const QString filterDescription =
|
||||
RimRftCrossPlotTools::depthFilterDescription( m_filterMode(), m_depthType(), m_depthRangeMin(), m_depthRangeMax(), m_selectedZones() );
|
||||
if ( !filterDescription.isEmpty() )
|
||||
{
|
||||
m_description =
|
||||
|
||||
@@ -83,7 +83,6 @@ public:
|
||||
QDateTime selectedTimeStep() const;
|
||||
RimSummaryEnsemble* ensemble() const;
|
||||
RimEclipseResultCase* eclipseCase() const;
|
||||
bool useDepthRange() const;
|
||||
double depthRangeMin() const;
|
||||
double depthRangeMax() const;
|
||||
RiaDefines::DepthType depthType() const;
|
||||
|
||||
@@ -537,7 +537,6 @@ void RimRftCorrelationReportPlot::syncTornadoInputsFromCrossPlot()
|
||||
m_tornadoPlot->setTimeStep( m_parameterRftCrossPlot->selectedTimeStep() );
|
||||
m_tornadoPlot->setSelectedParameter( m_parameterRftCrossPlot->ensembleParameter() );
|
||||
m_tornadoPlot->setEclipseCase( m_parameterRftCrossPlot->eclipseCase() );
|
||||
m_tornadoPlot->setUseDepthRange( m_parameterRftCrossPlot->useDepthRange() );
|
||||
m_tornadoPlot->setDepthRange( m_parameterRftCrossPlot->depthRangeMin(), m_parameterRftCrossPlot->depthRangeMax() );
|
||||
m_tornadoPlot->setDepthType( m_parameterRftCrossPlot->depthType() );
|
||||
m_tornadoPlot->setFilterMode( m_parameterRftCrossPlot->filterMode() );
|
||||
@@ -554,7 +553,6 @@ void RimRftCorrelationReportPlot::applyWellFormationsToSubPlots()
|
||||
|
||||
RimWellFormationsFile* wellFormationsFile = m_parameterRftCrossPlot->wellFormationsFile();
|
||||
|
||||
m_parameterRftCrossPlot->setWellFormations( wellFormationsFile );
|
||||
if ( m_tornadoPlot() ) m_tornadoPlot->setWellFormations( wellFormationsFile );
|
||||
}
|
||||
|
||||
|
||||
@@ -101,8 +101,7 @@ std::vector<double> RimRftCrossPlotTools::filterPressuresByDepthRange( const std
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RimRftCrossPlotTools::DepthInterval> RimRftCrossPlotTools::buildDepthIntervals( bool useFilter,
|
||||
DepthFilterMode mode,
|
||||
std::vector<RimRftCrossPlotTools::DepthInterval> RimRftCrossPlotTools::buildDepthIntervals( DepthFilterMode mode,
|
||||
double depthRangeMin,
|
||||
double depthRangeMax,
|
||||
RimWellFormationsFile* wellFormationsFile,
|
||||
@@ -110,15 +109,15 @@ std::vector<RimRftCrossPlotTools::DepthInterval> RimRftCrossPlotTools::buildDept
|
||||
const std::vector<QString>& selectedZones,
|
||||
RiaDefines::DepthType depthType )
|
||||
{
|
||||
if ( !useFilter || mode == DepthFilterMode::NONE ) return {};
|
||||
if ( mode == DepthFilterMode::NONE ) return {};
|
||||
|
||||
if ( mode == DepthFilterMode::DEPTH_RANGE ) return { DepthInterval{ depthRangeMin, depthRangeMax } };
|
||||
|
||||
// ZONES mode
|
||||
if ( !wellFormationsFile || selectedZones.empty() ) return {};
|
||||
|
||||
std::optional<RigWellPathFormations> formations = wellFormationsFile->formationsForWell( wellName );
|
||||
if ( !formations.has_value() ) return {};
|
||||
const RigWellPathFormations* formations = wellFormationsFile->formationsForWell( wellName );
|
||||
if ( !formations ) return {};
|
||||
|
||||
std::vector<DepthInterval> intervals;
|
||||
for ( size_t i = 0; i < formations->formationCount(); ++i )
|
||||
@@ -169,14 +168,13 @@ std::vector<double> RimRftCrossPlotTools::filterPressuresByDepthIntervals( const
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RimRftCrossPlotTools::depthFilterDescription( bool useFilter,
|
||||
DepthFilterMode mode,
|
||||
QString RimRftCrossPlotTools::depthFilterDescription( DepthFilterMode mode,
|
||||
RiaDefines::DepthType depthType,
|
||||
double depthRangeMin,
|
||||
double depthRangeMax,
|
||||
const std::vector<QString>& selectedZones )
|
||||
{
|
||||
if ( !useFilter || mode == DepthFilterMode::NONE ) return {};
|
||||
if ( mode == DepthFilterMode::NONE ) return {};
|
||||
|
||||
if ( mode == DepthFilterMode::ZONES )
|
||||
{
|
||||
|
||||
@@ -83,10 +83,9 @@ std::vector<double> filterPressuresByDepthRange( const std::vector<double>& dept
|
||||
double depthRangeMax );
|
||||
|
||||
// Builds the active depth filter intervals from either a depth range or a set of selected zones.
|
||||
// Returns an empty vector (meaning "no filter") when useFilter is false, or when ZONES mode is
|
||||
// Returns an empty vector (meaning "no filter") when the mode is NONE, or when ZONES mode is
|
||||
// selected but no well formations file, well name, or matching zones are available.
|
||||
std::vector<DepthInterval> buildDepthIntervals( bool useFilter,
|
||||
DepthFilterMode mode,
|
||||
std::vector<DepthInterval> buildDepthIntervals( DepthFilterMode mode,
|
||||
double depthRangeMin,
|
||||
double depthRangeMax,
|
||||
RimWellFormationsFile* wellFormationsFile,
|
||||
@@ -103,8 +102,7 @@ std::vector<double> filterPressuresByDepthIntervals( const std::vector<double>&
|
||||
|
||||
// Short description of the active depth filter for titles/axis labels/group text, e.g.
|
||||
// "MD 1000 - 2000 m" or "Zones: Valysar, Therys". Empty when filtering is disabled.
|
||||
QString depthFilterDescription( bool useFilter,
|
||||
DepthFilterMode mode,
|
||||
QString depthFilterDescription( DepthFilterMode mode,
|
||||
RiaDefines::DepthType depthType,
|
||||
double depthRangeMin,
|
||||
double depthRangeMax,
|
||||
|
||||
@@ -61,10 +61,9 @@ RimRftTornadoPlot::RimRftTornadoPlot()
|
||||
CAF_PDM_InitField( &m_wellName, "WellName", QString(), "Well Name" );
|
||||
CAF_PDM_InitFieldNoDefault( &m_selectedTimeStep, "TimeStep", "Time Step" );
|
||||
CAF_PDM_InitFieldNoDefault( &m_eclipseCase, "EclipseCase", "Eclipse Case (MD fallback)" );
|
||||
CAF_PDM_InitField( &m_useDepthRange, "UseDepthRange", false, "Filter by Depth Range" );
|
||||
CAF_PDM_InitField( &m_filterMode,
|
||||
"FilterMode",
|
||||
RimRftCrossPlotTools::DepthFilterModeEnum( RimRftCrossPlotTools::DepthFilterMode::DEPTH_RANGE ),
|
||||
RimRftCrossPlotTools::DepthFilterModeEnum( RimRftCrossPlotTools::DepthFilterMode::NONE ),
|
||||
"Filter By" );
|
||||
CAF_PDM_InitField( &m_depthRangeMin, "DepthRangeMin", 0.0, "Min Depth" );
|
||||
CAF_PDM_InitField( &m_depthRangeMax, "DepthRangeMax", 5000.0, "Max Depth" );
|
||||
@@ -138,14 +137,6 @@ void RimRftTornadoPlot::setEclipseCase( RimEclipseResultCase* eclipseCase )
|
||||
m_eclipseCase = eclipseCase;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimRftTornadoPlot::setUseDepthRange( bool useDepthRange )
|
||||
{
|
||||
m_useDepthRange = useDepthRange;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -397,8 +388,7 @@ void RimRftTornadoPlot::onPlotItemSelected( std::shared_ptr<RiuPlotItem> plotIte
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RimRftCrossPlotTools::DepthInterval> RimRftTornadoPlot::depthIntervals() const
|
||||
{
|
||||
return RimRftCrossPlotTools::buildDepthIntervals( m_useDepthRange(),
|
||||
m_filterMode(),
|
||||
return RimRftCrossPlotTools::buildDepthIntervals( m_filterMode(),
|
||||
m_depthRangeMin(),
|
||||
m_depthRangeMax(),
|
||||
m_wellFormations(),
|
||||
@@ -466,14 +456,10 @@ void RimRftTornadoPlot::updatePlotTitle()
|
||||
|
||||
if ( m_useAutoPlotTitle() && m_ensemble() )
|
||||
{
|
||||
const QString filterDescription = RimRftCrossPlotTools::depthFilterDescription( m_useDepthRange(),
|
||||
m_filterMode(),
|
||||
m_depthType(),
|
||||
m_depthRangeMin(),
|
||||
m_depthRangeMax(),
|
||||
m_selectedZones() );
|
||||
const QString rangeStr = filterDescription.isEmpty() ? QString() : QString( " [%1]" ).arg( filterDescription );
|
||||
m_description = QString( "Parameter Correlation vs RFT Pressure%1, %2" ).arg( rangeStr ).arg( m_ensemble->name() );
|
||||
const QString filterDescription =
|
||||
RimRftCrossPlotTools::depthFilterDescription( m_filterMode(), m_depthType(), m_depthRangeMin(), m_depthRangeMax(), m_selectedZones() );
|
||||
const QString rangeStr = filterDescription.isEmpty() ? QString() : QString( " [%1]" ).arg( filterDescription );
|
||||
m_description = QString( "Parameter Correlation vs RFT Pressure%1, %2" ).arg( rangeStr ).arg( m_ensemble->name() );
|
||||
}
|
||||
|
||||
m_plotWidget->setPlotTitle( m_description() );
|
||||
|
||||
@@ -62,7 +62,6 @@ public:
|
||||
void setWellName( const QString& wellName );
|
||||
void setTimeStep( const QDateTime& timeStep );
|
||||
void setEclipseCase( RimEclipseResultCase* eclipseCase );
|
||||
void setUseDepthRange( bool useDepthRange );
|
||||
void setDepthRange( double minMd, double maxMd );
|
||||
void setDepthType( RiaDefines::DepthType depthType );
|
||||
void setFilterMode( RimRftCrossPlotTools::DepthFilterMode filterMode );
|
||||
@@ -107,7 +106,6 @@ private:
|
||||
caf::PdmField<QString> m_wellName;
|
||||
caf::PdmField<QDateTime> m_selectedTimeStep;
|
||||
caf::PdmPtrField<RimEclipseResultCase*> m_eclipseCase;
|
||||
caf::PdmField<bool> m_useDepthRange;
|
||||
caf::PdmField<RimRftCrossPlotTools::DepthFilterModeEnum> m_filterMode;
|
||||
caf::PdmField<double> m_depthRangeMin;
|
||||
caf::PdmField<double> m_depthRangeMax;
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
#include "RimWellFormationsCollection.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
#include "RimWellFormationsFile.h"
|
||||
|
||||
#include "RiuMessageDialog.h"
|
||||
@@ -27,6 +26,8 @@
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <memory>
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimWellFormationsCollection, "WellFormationsCollection" );
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -42,9 +43,10 @@ RimWellFormationsCollection::RimWellFormationsCollection()
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Returns the existing entry for filePath if present, otherwise imports and returns a new one
|
||||
/// Returns the existing entry for filePath if present, otherwise imports and returns a new one. A
|
||||
/// file that cannot be read is not added.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimWellFormationsFile* RimWellFormationsCollection::findOrCreate( const QString& filePath )
|
||||
std::expected<RimWellFormationsFile*, QString> RimWellFormationsCollection::findOrCreate( const QString& filePath )
|
||||
{
|
||||
const QString absoluteFilePath = QFileInfo( filePath ).absoluteFilePath();
|
||||
|
||||
@@ -52,20 +54,25 @@ RimWellFormationsFile* RimWellFormationsCollection::findOrCreate( const QString&
|
||||
{
|
||||
if ( QFileInfo( file->filePath() ).absoluteFilePath() == absoluteFilePath )
|
||||
{
|
||||
// An earlier read may have failed, e.g. if the file was missing or malformed at the time
|
||||
if ( file->wellNames().isEmpty() )
|
||||
{
|
||||
if ( auto result = file->reload(); !result ) return std::unexpected( result.error() );
|
||||
}
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
auto newFile = new RimWellFormationsFile();
|
||||
auto newFile = std::make_unique<RimWellFormationsFile>();
|
||||
newFile->setFilePath( filePath );
|
||||
if ( auto result = newFile->reload(); !result )
|
||||
{
|
||||
RiaLogging::error( result.error().toStdString() );
|
||||
return std::unexpected( result.error() );
|
||||
}
|
||||
|
||||
m_wellFormationsFiles.push_back( newFile );
|
||||
m_wellFormationsFiles.push_back( newFile.get() );
|
||||
|
||||
return newFile;
|
||||
return newFile.release();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -76,7 +83,7 @@ RimWellFormationsFile* RimWellFormationsCollection::findFileForWell( const QStri
|
||||
{
|
||||
for ( RimWellFormationsFile* file : m_wellFormationsFiles )
|
||||
{
|
||||
if ( file->formationsForWell( wellName ).has_value() ) return file;
|
||||
if ( file->formationsForWell( wellName ) ) return file;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@@ -92,15 +99,14 @@ std::vector<RimWellFormationsFile*> RimWellFormationsCollection::importFiles( co
|
||||
|
||||
for ( const QString& filePath : filePaths )
|
||||
{
|
||||
auto newFile = new RimWellFormationsFile();
|
||||
newFile->setFilePath( filePath );
|
||||
if ( auto result = newFile->reload(); !result )
|
||||
auto file = findOrCreate( filePath );
|
||||
if ( !file )
|
||||
{
|
||||
totalErrorMessage += "\nError in: " + filePath + "\n\t" + result.error();
|
||||
totalErrorMessage += "\nError in: " + filePath + "\n\t" + file.error();
|
||||
continue;
|
||||
}
|
||||
|
||||
m_wellFormationsFiles.push_back( newFile );
|
||||
importedFiles.push_back( newFile );
|
||||
importedFiles.push_back( *file );
|
||||
}
|
||||
|
||||
if ( !totalErrorMessage.isEmpty() )
|
||||
@@ -111,6 +117,15 @@ std::vector<RimWellFormationsFile*> RimWellFormationsCollection::importFiles( co
|
||||
return importedFiles;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellFormationsCollection::onChildDeleted( caf::PdmChildArrayFieldHandle* childArray,
|
||||
std::vector<caf::PdmObjectHandle*>& referringObjects )
|
||||
{
|
||||
RimWellFormationsFile::updateReferringObjects( referringObjects );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
#include "cafPdmChildArrayField.h"
|
||||
#include "cafPdmObject.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <expected>
|
||||
|
||||
namespace caf
|
||||
{
|
||||
class CmdFeatureMenuBuilder;
|
||||
@@ -42,13 +46,14 @@ public:
|
||||
|
||||
const caf::PdmChildArrayField<RimWellFormationsFile*>& wellFormationsFiles() const { return m_wellFormationsFiles; }
|
||||
|
||||
RimWellFormationsFile* findOrCreate( const QString& filePath );
|
||||
std::expected<RimWellFormationsFile*, QString> findOrCreate( const QString& filePath );
|
||||
|
||||
RimWellFormationsFile* findFileForWell( const QString& wellName ) const;
|
||||
|
||||
std::vector<RimWellFormationsFile*> importFiles( const QStringList& filePaths );
|
||||
|
||||
protected:
|
||||
void onChildDeleted( caf::PdmChildArrayFieldHandle* childArray, std::vector<caf::PdmObjectHandle*>& referringObjects ) override;
|
||||
void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
#include "RiaLogging.h"
|
||||
#include "RifCsvHtmlTableTools.h"
|
||||
|
||||
#include "RimMainPlotCollection.h"
|
||||
#include "RimProject.h"
|
||||
#include "RimWellPath.h"
|
||||
|
||||
#include "cafPdmUiFilePathEditor.h"
|
||||
#include "cafPdmUiTextEditor.h"
|
||||
#include "cafPdmUiTreeOrdering.h"
|
||||
@@ -82,12 +86,12 @@ std::expected<void, QString> RimWellFormationsFile::reload()
|
||||
if ( !result )
|
||||
{
|
||||
m_wellFormations.clear();
|
||||
updateContentTable();
|
||||
m_contentTable = "";
|
||||
return std::unexpected( result.error() );
|
||||
}
|
||||
|
||||
m_wellFormations = std::move( *result );
|
||||
updateContentTable();
|
||||
m_contentTable = "";
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -125,11 +129,11 @@ QStringList RimWellFormationsFile::zoneNames( const QString& wellName ) const
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::optional<RigWellPathFormations> RimWellFormationsFile::formationsForWell( const QString& wellName ) const
|
||||
const RigWellPathFormations* RimWellFormationsFile::formationsForWell( const QString& wellName ) const
|
||||
{
|
||||
if ( auto it = m_wellFormations.find( wellName ); it != m_wellFormations.end() )
|
||||
{
|
||||
return it->second;
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
// Fall back to a normalized name match, to handle differences between e.g. RFT and FMU well names
|
||||
@@ -138,11 +142,11 @@ std::optional<RigWellPathFormations> RimWellFormationsFile::formationsForWell( c
|
||||
{
|
||||
if ( normalizedWellName( candidateName ) == normalizedTarget )
|
||||
{
|
||||
return formations;
|
||||
return &formations;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -167,9 +171,30 @@ void RimWellFormationsFile::fieldChangedByUi( const caf::PdmFieldHandle* changed
|
||||
{
|
||||
RiaLogging::error( result.error().toStdString() );
|
||||
}
|
||||
|
||||
updateReferringObjects( objectsWithReferringPtrFields() );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Well paths cache the formations resolved from their file, so they must be refreshed when the
|
||||
/// file is reloaded or deleted
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellFormationsFile::updateReferringObjects( const std::vector<caf::PdmObjectHandle*>& referringObjects )
|
||||
{
|
||||
for ( caf::PdmObjectHandle* object : referringObjects )
|
||||
{
|
||||
if ( auto wellPath = dynamic_cast<RimWellPath*>( object ) )
|
||||
{
|
||||
wellPath->refreshFormationsFromFile();
|
||||
wellPath->updateConnectedEditors();
|
||||
}
|
||||
}
|
||||
|
||||
RimProject::current()->scheduleCreateDisplayModelAndRedrawAllViews();
|
||||
RimMainPlotCollection::current()->updatePlotsWithFormations();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -183,6 +208,9 @@ void RimWellFormationsFile::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTree
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellFormationsFile::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
|
||||
{
|
||||
// Built on demand, as the table is only needed when the file is shown in the property editor
|
||||
if ( m_contentTable().isEmpty() ) updateContentTable();
|
||||
|
||||
uiOrdering.add( &m_filePath );
|
||||
uiOrdering.add( &m_contentTable );
|
||||
uiOrdering.skipRemainingFields();
|
||||
@@ -231,7 +259,9 @@ void RimWellFormationsFile::updateUiTreeName()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellFormationsFile::updateContentTable()
|
||||
{
|
||||
auto result = RifCsvHtmlTableTools::generateHtmlTableFromFile( filePath() );
|
||||
const int maxRowCount = 1000;
|
||||
|
||||
auto result = RifCsvHtmlTableTools::generateHtmlTableFromFile( filePath(), maxRowCount );
|
||||
if ( result )
|
||||
{
|
||||
m_contentTable = *result;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
//==================================================================================================
|
||||
/// A single well formations/well picks file (e.g. an FMU formations.csv or a well pick export),
|
||||
@@ -44,11 +45,12 @@ public:
|
||||
|
||||
std::expected<void, QString> reload();
|
||||
|
||||
QStringList wellNames() const;
|
||||
QStringList zoneNames( const QString& wellName ) const;
|
||||
std::optional<RigWellPathFormations> formationsForWell( const QString& wellName ) const;
|
||||
QStringList wellNames() const;
|
||||
QStringList zoneNames( const QString& wellName ) const;
|
||||
const RigWellPathFormations* formationsForWell( const QString& wellName ) const;
|
||||
|
||||
static QString normalizedWellName( const QString& wellName );
|
||||
static void updateReferringObjects( const std::vector<caf::PdmObjectHandle*>& referringObjects );
|
||||
|
||||
protected:
|
||||
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
|
||||
|
||||
+5
-7
@@ -195,13 +195,11 @@ void RimWellLogFormationSettings::setWellNameInFormationsFile( const QString& we
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::optional<RigWellPathFormations> RimWellLogFormationSettings::resolveWellPickFormations() const
|
||||
const RigWellPathFormations* RimWellLogFormationSettings::resolveWellPickFormations() const
|
||||
{
|
||||
if ( m_formationSource() == RiaDefines::WellLogTrackFormationSource::WELL_PICK_FILTER && m_formationWellPathForSourceWellPath() )
|
||||
{
|
||||
const RigWellPathFormations* formations = m_formationWellPathForSourceWellPath->formationsGeometry();
|
||||
if ( formations ) return *formations;
|
||||
return std::nullopt;
|
||||
return m_formationWellPathForSourceWellPath->formationsGeometry();
|
||||
}
|
||||
|
||||
if ( m_formationSource() == RiaDefines::WellLogTrackFormationSource::WELL_PICKS_NO_TRAJECTORY && m_wellFormationsFile() &&
|
||||
@@ -210,7 +208,7 @@ std::optional<RigWellPathFormations> RimWellLogFormationSettings::resolveWellPic
|
||||
return m_wellFormationsFile->formationsForWell( m_wellNameInFormationsFile() );
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -333,7 +331,7 @@ void RimWellLogFormationSettings::uiOrdering( const QString& uiConfigName, caf::
|
||||
// WELL_PICKS_NO_TRAJECTORY source, so this source only ever shows the well path picker.
|
||||
uiOrdering.add( &m_formationWellPathForSourceWellPath );
|
||||
|
||||
if ( resolveWellPickFormations().has_value() )
|
||||
if ( resolveWellPickFormations() )
|
||||
{
|
||||
uiOrdering.add( &m_formationLevel );
|
||||
uiOrdering.add( &m_showFormationFluids );
|
||||
@@ -346,7 +344,7 @@ void RimWellLogFormationSettings::uiOrdering( const QString& uiConfigName, caf::
|
||||
uiOrdering.add( &m_wellFormationsFile );
|
||||
if ( m_wellFormationsFile() ) uiOrdering.add( &m_wellNameInFormationsFile );
|
||||
|
||||
if ( resolveWellPickFormations().has_value() )
|
||||
if ( resolveWellPickFormations() )
|
||||
{
|
||||
uiOrdering.add( &m_formationLevel );
|
||||
uiOrdering.add( &m_showFormationFluids );
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ public:
|
||||
|
||||
// Resolves the formations to show for the WELL_PICK_FILTER source: the selected well path's
|
||||
// own formations if present, otherwise the well looked up directly in wellFormationsFile().
|
||||
std::optional<RigWellPathFormations> resolveWellPickFormations() const;
|
||||
const RigWellPathFormations* resolveWellPickFormations() const;
|
||||
|
||||
// Simulation well
|
||||
QString simWellName() const;
|
||||
|
||||
@@ -2607,7 +2607,7 @@ void RimWellLogTrack::updateFormationNamesOnPlot()
|
||||
m_formationSettings->formationSource() == RiaDefines::WellLogTrackFormationSource::WELL_PICKS_NO_TRAJECTORY )
|
||||
{
|
||||
auto formations = m_formationSettings->resolveWellPickFormations();
|
||||
if ( !formations.has_value() ) return;
|
||||
if ( !formations ) return;
|
||||
|
||||
if ( plot->depthType() != RiaDefines::DepthType::MEASURED_DEPTH && plot->depthType() != RiaDefines::DepthType::TRUE_VERTICAL_DEPTH &&
|
||||
plot->depthType() != RiaDefines::DepthType::TRUE_VERTICAL_DEPTH_RKB )
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "RiaColorTables.h"
|
||||
#include "RiaFieldHandleTools.h"
|
||||
#include "RiaLogging.h"
|
||||
#include "RiaSimWellBranchTools.h"
|
||||
#include "RiaWellNameComparer.h"
|
||||
|
||||
@@ -489,6 +490,7 @@ void RimWellPath::fieldChangedByUi( const caf::PdmFieldHandle* changedField, con
|
||||
{
|
||||
refreshFormationsFromFile();
|
||||
proj->scheduleCreateDisplayModelAndRedrawAllViews();
|
||||
RimMainPlotCollection::current()->updatePlotsWithFormations();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -584,8 +586,14 @@ void RimWellPath::initAfterRead()
|
||||
{
|
||||
if ( !oilField->wellFormationsCollection() ) oilField->wellFormationsCollection = new RimWellFormationsCollection();
|
||||
|
||||
auto* file = oilField->wellFormationsCollection->findOrCreate( m_wellPathFormationFilePath_OBSOLETE().path() );
|
||||
setWellFormationsFile( file, m_formationKeyInFile() );
|
||||
if ( auto file = oilField->wellFormationsCollection->findOrCreate( m_wellPathFormationFilePath_OBSOLETE().path() ) )
|
||||
{
|
||||
setWellFormationsFile( *file, m_formationKeyInFile() );
|
||||
}
|
||||
else
|
||||
{
|
||||
RiaLogging::error( file.error().toStdString() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1058,10 +1066,18 @@ bool RimWellPath::refreshFormationsFromFile()
|
||||
{
|
||||
if ( !m_wellFormationsFile() )
|
||||
{
|
||||
m_wellPathFormations.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_wellPathFormations = m_wellFormationsFile()->formationsForWell( m_formationKeyInFile() );
|
||||
if ( auto formations = m_wellFormationsFile()->formationsForWell( m_formationKeyInFile() ) )
|
||||
{
|
||||
m_wellPathFormations = *formations;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_wellPathFormations.reset();
|
||||
}
|
||||
return m_wellPathFormations.has_value();
|
||||
}
|
||||
|
||||
|
||||
@@ -536,11 +536,19 @@ void RimWellPathCollection::addWellPathFormations( const QStringList& filePaths
|
||||
// The collection is deletable from the project tree
|
||||
if ( !oilField->wellFormationsCollection() ) oilField->wellFormationsCollection = new RimWellFormationsCollection();
|
||||
|
||||
bool fileReadSuccess = false;
|
||||
bool fileReadSuccess = false;
|
||||
QString errorMessage;
|
||||
|
||||
for ( const QString& filePath : filePaths )
|
||||
{
|
||||
RimWellFormationsFile* wellFormationsFile = oilField->wellFormationsCollection->findOrCreate( filePath );
|
||||
auto file = oilField->wellFormationsCollection->findOrCreate( filePath );
|
||||
if ( !file )
|
||||
{
|
||||
errorMessage += "\nError in: " + filePath + "\n\t" + file.error();
|
||||
continue;
|
||||
}
|
||||
|
||||
RimWellFormationsFile* wellFormationsFile = *file;
|
||||
|
||||
for ( const QString& wellName : wellFormationsFile->wellNames() )
|
||||
{
|
||||
@@ -575,6 +583,11 @@ void RimWellPathCollection::addWellPathFormations( const QStringList& filePaths
|
||||
RiuMessageDialog::showError( Riu3DMainWindowTools::mainWindowWidget(), "Well Picks Import", outputMessage );
|
||||
}
|
||||
|
||||
if ( !errorMessage.isEmpty() )
|
||||
{
|
||||
RiuMessageDialog::showError( Riu3DMainWindowTools::mainWindowWidget(), "Well Picks Import", errorMessage );
|
||||
}
|
||||
|
||||
sortWellsByName();
|
||||
updateAllRequiredEditors();
|
||||
}
|
||||
|
||||
@@ -281,16 +281,22 @@ std::pair<std::vector<QString>, std::vector<double>>
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Returns one (name, top, base) range per formation whose level does not exceed the given level,
|
||||
/// for use as shaded zone regions. Unlike depthAndFormationNamesUpToLevel(), this does not merge
|
||||
/// hierarchical levels at coincident depths, so a parent/child formation pair can overlap in the
|
||||
/// result when both satisfy the level filter (e.g. requesting LEVEL1 also includes any GROUP-level
|
||||
/// parent zone covering the same depth range).
|
||||
/// for use as shaded zone regions. Each zone is clipped against the more detailed zones inside it, so
|
||||
/// parent and child zones are not drawn on top of each other.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<std::tuple<QString, double, double>> RigWellPathFormations::depthRangesUpToLevel( FormationLevel level,
|
||||
RiaDefines::DepthType depthType ) const
|
||||
{
|
||||
std::vector<std::tuple<QString, double, double>> result;
|
||||
if ( level == FormationLevel::NONE ) return result;
|
||||
struct Zone
|
||||
{
|
||||
QString name;
|
||||
double top;
|
||||
double base;
|
||||
FormationLevel level;
|
||||
};
|
||||
|
||||
std::vector<Zone> zones;
|
||||
if ( level == FormationLevel::NONE ) return {};
|
||||
|
||||
for ( const auto& [formation, formationLevel] : m_formations )
|
||||
{
|
||||
@@ -300,7 +306,49 @@ std::vector<std::tuple<QString, double, double>> RigWellPathFormations::depthRan
|
||||
auto base = pickDepth( formation, PickPosition::BASE, depthType );
|
||||
if ( !top || !base ) continue;
|
||||
|
||||
result.emplace_back( formation.formationName, *top, *base );
|
||||
zones.push_back( { formation.formationName, *top, *base, formationLevel } );
|
||||
}
|
||||
|
||||
// UNKNOWN has no place in the hierarchy, so it neither hides nor is hidden by other zones
|
||||
auto isMoreDetailed = []( FormationLevel candidate, FormationLevel reference )
|
||||
{ return candidate <= FormationLevel::LEVEL10 && reference <= FormationLevel::LEVEL10 && candidate > reference; };
|
||||
|
||||
const double minThickness = 0.1;
|
||||
|
||||
std::vector<std::tuple<QString, double, double>> result;
|
||||
for ( const auto& zone : zones )
|
||||
{
|
||||
const double lower = std::min( zone.top, zone.base );
|
||||
const double upper = std::max( zone.top, zone.base );
|
||||
|
||||
std::vector<std::pair<double, double>> covered;
|
||||
for ( const auto& other : zones )
|
||||
{
|
||||
if ( !isMoreDetailed( other.level, zone.level ) ) continue;
|
||||
covered.emplace_back( std::min( other.top, other.base ), std::max( other.top, other.base ) );
|
||||
}
|
||||
std::sort( covered.begin(), covered.end() );
|
||||
|
||||
// Keep the parts of the zone not covered by a more detailed zone, in the zone's own direction
|
||||
auto addPart = [&]( double from, double to )
|
||||
{
|
||||
if ( to - from <= minThickness ) return;
|
||||
if ( zone.top <= zone.base )
|
||||
result.emplace_back( zone.name, from, to );
|
||||
else
|
||||
result.emplace_back( zone.name, to, from );
|
||||
};
|
||||
|
||||
double current = lower;
|
||||
for ( const auto& [coveredLower, coveredUpper] : covered )
|
||||
{
|
||||
if ( coveredLower >= upper ) break;
|
||||
if ( coveredUpper <= current ) continue;
|
||||
|
||||
addPart( current, std::min( coveredLower, upper ) );
|
||||
current = std::max( current, coveredUpper );
|
||||
}
|
||||
addPart( current, upper );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -147,10 +147,19 @@ TEST( RigWellPathFormations, DepthRangesUpToLevelReturnsZoneIntervals )
|
||||
}
|
||||
{
|
||||
auto ranges = formations.depthRangesUpToLevel( FormationLevel::LEVEL1, DepthType::MEASURED_DEPTH );
|
||||
ASSERT_EQ( 2u, ranges.size() );
|
||||
EXPECT_EQ( QString( "Garn 1" ), std::get<0>( ranges[0] ) );
|
||||
EXPECT_EQ( QString( "Garn 2" ), std::get<0>( ranges[1] ) );
|
||||
}
|
||||
{
|
||||
// Garn 2 is only partly covered by Garn 2.1, so the remainder is kept
|
||||
auto ranges = formations.depthRangesUpToLevel( FormationLevel::ALL, DepthType::MEASURED_DEPTH );
|
||||
ASSERT_EQ( 3u, ranges.size() );
|
||||
EXPECT_EQ( QString( "GARN" ), std::get<0>( ranges[0] ) );
|
||||
EXPECT_EQ( QString( "Garn 1" ), std::get<0>( ranges[1] ) );
|
||||
EXPECT_EQ( QString( "Garn 2" ), std::get<0>( ranges[2] ) );
|
||||
EXPECT_EQ( QString( "Garn 1" ), std::get<0>( ranges[0] ) );
|
||||
EXPECT_EQ( QString( "Garn 2" ), std::get<0>( ranges[1] ) );
|
||||
EXPECT_DOUBLE_EQ( 175.0, std::get<1>( ranges[1] ) );
|
||||
EXPECT_DOUBLE_EQ( 200.0, std::get<2>( ranges[1] ) );
|
||||
EXPECT_EQ( QString( "Garn 2.1" ), std::get<0>( ranges[2] ) );
|
||||
}
|
||||
{
|
||||
auto ranges = formations.depthRangesUpToLevel( FormationLevel::NONE, DepthType::MEASURED_DEPTH );
|
||||
|
||||
@@ -105,12 +105,11 @@ TEST( RimRftCrossPlotToolsTest, DepthTypeAbbreviation )
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Disabled filtering returns no intervals regardless of mode.
|
||||
/// Disabled filtering returns no intervals.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimRftCrossPlotToolsTest, BuildDepthIntervals_DisabledReturnsEmpty )
|
||||
{
|
||||
auto intervals = RimRftCrossPlotTools::buildDepthIntervals( false,
|
||||
RimRftCrossPlotTools::DepthFilterMode::DEPTH_RANGE,
|
||||
auto intervals = RimRftCrossPlotTools::buildDepthIntervals( RimRftCrossPlotTools::DepthFilterMode::NONE,
|
||||
1000.0,
|
||||
2000.0,
|
||||
nullptr,
|
||||
@@ -125,8 +124,7 @@ TEST( RimRftCrossPlotToolsTest, BuildDepthIntervals_DisabledReturnsEmpty )
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimRftCrossPlotToolsTest, BuildDepthIntervals_DepthRangeProducesSingleInterval )
|
||||
{
|
||||
auto intervals = RimRftCrossPlotTools::buildDepthIntervals( true,
|
||||
RimRftCrossPlotTools::DepthFilterMode::DEPTH_RANGE,
|
||||
auto intervals = RimRftCrossPlotTools::buildDepthIntervals( RimRftCrossPlotTools::DepthFilterMode::DEPTH_RANGE,
|
||||
1000.0,
|
||||
2000.0,
|
||||
nullptr,
|
||||
@@ -143,8 +141,7 @@ TEST( RimRftCrossPlotToolsTest, BuildDepthIntervals_DepthRangeProducesSingleInte
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimRftCrossPlotToolsTest, BuildDepthIntervals_ZonesWithoutFileReturnsEmpty )
|
||||
{
|
||||
auto intervals = RimRftCrossPlotTools::buildDepthIntervals( true,
|
||||
RimRftCrossPlotTools::DepthFilterMode::ZONES,
|
||||
auto intervals = RimRftCrossPlotTools::buildDepthIntervals( RimRftCrossPlotTools::DepthFilterMode::ZONES,
|
||||
1000.0,
|
||||
2000.0,
|
||||
nullptr,
|
||||
@@ -187,8 +184,7 @@ TEST( RimRftCrossPlotToolsTest, FilterPressuresByDepthIntervals_FiltersByMultipl
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimRftCrossPlotToolsTest, DepthFilterDescription_DisabledReturnsEmpty )
|
||||
{
|
||||
QString description = RimRftCrossPlotTools::depthFilterDescription( false,
|
||||
RimRftCrossPlotTools::DepthFilterMode::DEPTH_RANGE,
|
||||
QString description = RimRftCrossPlotTools::depthFilterDescription( RimRftCrossPlotTools::DepthFilterMode::NONE,
|
||||
RiaDefines::DepthType::MEASURED_DEPTH,
|
||||
1000.0,
|
||||
2000.0,
|
||||
@@ -201,8 +197,7 @@ TEST( RimRftCrossPlotToolsTest, DepthFilterDescription_DisabledReturnsEmpty )
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimRftCrossPlotToolsTest, DepthFilterDescription_ZonesListsSelectedZoneNames )
|
||||
{
|
||||
QString description = RimRftCrossPlotTools::depthFilterDescription( true,
|
||||
RimRftCrossPlotTools::DepthFilterMode::ZONES,
|
||||
QString description = RimRftCrossPlotTools::depthFilterDescription( RimRftCrossPlotTools::DepthFilterMode::ZONES,
|
||||
RiaDefines::DepthType::MEASURED_DEPTH,
|
||||
1000.0,
|
||||
2000.0,
|
||||
|
||||
Reference in new issue
Block a user