Files
ResInsight/ApplicationLibCode/ProjectDataModel/WellLog/WellLogTrack/RimWellLogFormationSettings.cpp
T
Magne Sjaastad 5812826db6 #14880: RFT correlation plot formation workflow
* Add Add Well Picks button to well log formation settings
* Expose formation zone colors and shading alpha from RFT track
* Average observed pressures per formation with full min/max range
* Show formation-colored observed pressure and depth filter in RFT correlation plots
* Default new RFT correlation plots to formation filter and ensemble parameter coloring
* Move Qwt code out of RFT correlation Rim classes
* Align RFT correlation depth filter bars and click zones with displayed track formations
2026-10-08 17:32:23 +02:00

506 lines
21 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026- Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RimWellLogFormationSettings.h"
#include "RiaSimWellBranchTools.h"
#include "RiaWellLogTrackDefines.h"
#include "RimCase.h"
#include "RimEclipseCase.h"
#include "RimOilField.h"
#include "RimProject.h"
#include "RimTools.h"
#include "RimWellLogTrack.h"
#include "RimWellPath.h"
#include "RicImportWellFormationsFeature.h"
#include "Formations/RimWellFormationsCollection.h"
#include "Formations/RimWellFormationsFile.h"
#include "RigEclipseCaseData.h"
#include "cafPdmUiComboBoxEditor.h"
#include "cafPdmUiGroup.h"
CAF_PDM_SOURCE_INIT( RimWellLogFormationSettings, "RimWellLogFormationSettings" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellLogFormationSettings::RimWellLogFormationSettings()
{
CAF_PDM_InitObject( "Formation Settings" );
CAF_PDM_InitFieldNoDefault( &m_formationSource, "FormationSource", "Source" );
CAF_PDM_InitFieldNoDefault( &m_formationTrajectoryType, "FormationTrajectoryType", "Trajectory" );
CAF_PDM_InitFieldNoDefault( &m_formationWellPathForSourceCase, "FormationWellPath", "Well Path" );
m_formationWellPathForSourceCase.uiCapability()->setUiTreeChildrenHidden( true );
CAF_PDM_InitFieldNoDefault( &m_formationWellPathForSourceWellPath, "FormationWellPathForSourceWellPath", "Well Path" );
m_formationWellPathForSourceWellPath.uiCapability()->setUiTreeChildrenHidden( true );
CAF_PDM_InitFieldNoDefault( &m_wellFormationsFile, "FormationWellFormationsFile", "Formations File" );
m_wellFormationsFile.uiCapability()->setUiTreeChildrenHidden( true );
CAF_PDM_InitField( &m_wellNameInFormationsFile, "FormationWellNameInFile", QString(), "Well" );
m_wellNameInFormationsFile.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() );
CAF_PDM_InitField( &m_formationSimWellName, "FormationSimulationWellName", QString( "None" ), "Simulation Well" );
CAF_PDM_InitField( &m_formationBranchIndex, "FormationBranchIndex", 0, " " );
CAF_PDM_InitField( &m_formationBranchDetection,
"FormationBranchDetection",
true,
"Branch Detection",
"",
"Compute branches based on how simulation well cells are organized",
"" );
CAF_PDM_InitFieldNoDefault( &m_formationCase, "FormationCase", "Formation Case" );
m_formationCase.uiCapability()->setUiTreeChildrenHidden( true );
CAF_PDM_InitFieldNoDefault( &m_formationLevel, "FormationLevel", "Well Pick Filter" );
CAF_PDM_InitField( &m_showFormationFluids, "ShowFormationFluids", false, "Show Fluids" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaDefines::WellLogTrackFormationSource RimWellLogFormationSettings::formationSource() const
{
return m_formationSource();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellLogFormationSettings::setFormationSource( RiaDefines::WellLogTrackFormationSource source )
{
m_formationSource = source;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimCase* RimWellLogFormationSettings::formationCase() const
{
return m_formationCase();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellLogFormationSettings::setFormationCase( RimCase* rimCase )
{
m_formationCase = rimCase;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaDefines::WellLogTrackTrajectoryType RimWellLogFormationSettings::trajectoryType() const
{
return m_formationTrajectoryType();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellLogFormationSettings::setTrajectoryType( RiaDefines::WellLogTrackTrajectoryType trajectoryType )
{
m_formationTrajectoryType = trajectoryType;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellPath* RimWellLogFormationSettings::wellPathForSourceCase() const
{
return m_formationWellPathForSourceCase();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellLogFormationSettings::setWellPathForSourceCase( RimWellPath* wellPath )
{
m_formationWellPathForSourceCase = wellPath;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellPath* RimWellLogFormationSettings::wellPathForSourceWellPath() const
{
return m_formationWellPathForSourceWellPath();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellLogFormationSettings::setWellPathForSourceWellPath( RimWellPath* wellPath )
{
m_formationWellPathForSourceWellPath = wellPath;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellFormationsFile* RimWellLogFormationSettings::wellFormationsFile() const
{
return m_wellFormationsFile();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellLogFormationSettings::setWellFormationsFile( RimWellFormationsFile* file )
{
m_wellFormationsFile = file;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimWellLogFormationSettings::wellNameInFormationsFile() const
{
return m_wellNameInFormationsFile();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellLogFormationSettings::setWellNameInFormationsFile( const QString& wellName )
{
m_wellNameInFormationsFile = wellName;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const RigWellPathFormations* RimWellLogFormationSettings::resolveWellPickFormations() const
{
if ( m_formationSource() == RiaDefines::WellLogTrackFormationSource::WELL_PICK_FILTER && m_formationWellPathForSourceWellPath() )
{
return m_formationWellPathForSourceWellPath->formationsGeometry();
}
if ( m_formationSource() == RiaDefines::WellLogTrackFormationSource::WELL_PICKS_NO_TRAJECTORY && m_wellFormationsFile() &&
!m_wellNameInFormationsFile().isEmpty() )
{
return m_wellFormationsFile->formationsForWell( m_wellNameInFormationsFile() );
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimWellLogFormationSettings::simWellName() const
{
return m_formationSimWellName();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellLogFormationSettings::setSimWellName( const QString& simWellName )
{
m_formationSimWellName = simWellName;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RimWellLogFormationSettings::branchIndex() const
{
return m_formationBranchIndex();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellLogFormationSettings::setBranchIndex( int branchIndex )
{
m_formationBranchIndex = branchIndex;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimWellLogFormationSettings::branchDetection() const
{
return m_formationBranchDetection();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellLogFormationSettings::setBranchDetection( bool branchDetection )
{
m_formationBranchDetection = branchDetection;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaDefines::WellLogTrackFormationLevel RimWellLogFormationSettings::formationLevel() const
{
return m_formationLevel();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellLogFormationSettings::setFormationLevel( RiaDefines::WellLogTrackFormationLevel level )
{
m_formationLevel = level;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimWellLogFormationSettings::showFormationFluids() const
{
return m_showFormationFluids();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellLogFormationSettings::setShowFormationFluids( bool show )
{
m_showFormationFluids = show;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellLogFormationSettings::uiOrdering( const QString& uiConfigName, caf::PdmUiOrdering& uiOrdering, bool formationsForCaseWithSimWellOnly )
{
if ( !formationsForCaseWithSimWellOnly )
{
uiOrdering.add( &m_formationSource );
}
if ( m_formationSource() == RiaDefines::WellLogTrackFormationSource::CASE )
{
uiOrdering.add( &m_formationCase );
if ( !formationsForCaseWithSimWellOnly )
{
uiOrdering.add( &m_formationTrajectoryType );
if ( m_formationTrajectoryType() == RiaDefines::WellLogTrackTrajectoryType::WELL_PATH )
{
uiOrdering.add( &m_formationWellPathForSourceCase );
}
}
if ( formationsForCaseWithSimWellOnly || m_formationTrajectoryType() == RiaDefines::WellLogTrackTrajectoryType::SIMULATION_WELL )
{
uiOrdering.add( &m_formationSimWellName );
RiaSimWellBranchTools::appendSimWellBranchFieldsIfRequiredFromSimWellName( &uiOrdering,
m_formationSimWellName,
m_formationBranchDetection,
m_formationBranchIndex );
}
}
else if ( m_formationSource() == RiaDefines::WellLogTrackFormationSource::WELL_PICK_FILTER )
{
// The direct Formations File fallback (no well path selected) has moved to the dedicated
// WELL_PICKS_NO_TRAJECTORY source, so this source only ever shows the well path picker.
uiOrdering.add( &m_formationWellPathForSourceWellPath );
if ( resolveWellPickFormations() )
{
uiOrdering.add( &m_formationLevel );
uiOrdering.add( &m_showFormationFluids );
}
}
else if ( m_formationSource() == RiaDefines::WellLogTrackFormationSource::WELL_PICKS_NO_TRAJECTORY )
{
// Well picks use explicit top/base depths and do not require a well path trajectory, so
// this source can be used e.g. for an RFT well with no associated/modelled well path.
uiOrdering.add( &m_wellFormationsFile, { .newRow = true, .totalColumnSpan = 2, .leftLabelColumnSpan = 1 } );
uiOrdering.addNewButton( "Add Well Picks",
[this]() { onAddWellPicksClicked(); },
{ .newRow = false, .totalColumnSpan = 1, .leftLabelColumnSpan = 0 } );
if ( m_wellFormationsFile() ) uiOrdering.add( &m_wellNameInFormationsFile );
if ( resolveWellPickFormations() )
{
uiOrdering.add( &m_formationLevel );
uiOrdering.add( &m_showFormationFluids );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellLogFormationSettings::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
{
if ( changedField == &m_formationSource && m_formationSource() == RiaDefines::WellLogTrackFormationSource::WELL_PICK_FILTER )
{
std::vector<RimWellPath*> wellPaths;
RimTools::wellPathWithFormations( &wellPaths );
for ( RimWellPath* wellPath : wellPaths )
{
if ( wellPath == m_formationWellPathForSourceCase )
{
m_formationWellPathForSourceWellPath = m_formationWellPathForSourceCase();
break;
}
}
}
else if ( changedField == &m_formationSource && m_formationSource() == RiaDefines::WellLogTrackFormationSource::WELL_PICKS_NO_TRAJECTORY )
{
// The WELL_PICKS_NO_TRAJECTORY source always resolves formations directly from
// m_wellFormationsFile, so clear any well path left over from a previous WELL_PICK_FILTER
// selection.
m_formationWellPathForSourceWellPath = nullptr;
}
else if ( changedField == &m_wellFormationsFile )
{
// Reset the selected well name when the file changes, picking the first well as a
// convenient default (mirrors RimWellPath's behavior when linking a formations file).
QStringList wellNames = m_wellFormationsFile() ? m_wellFormationsFile->wellNames() : QStringList();
m_wellNameInFormationsFile = wellNames.isEmpty() ? QString() : wellNames.first();
}
auto track = firstAncestorOrThisOfType<RimWellLogTrack>();
if ( track )
{
track->loadDataAndUpdate();
track->updateConnectedEditors();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellLogFormationSettings::onAddWellPicksClicked()
{
std::vector<RimWellFormationsFile*> importedFiles = RicImportWellFormationsFeature::importFilesWithDialog();
if ( importedFiles.empty() ) return;
RimWellFormationsFile* file = importedFiles.back();
m_wellFormationsFile = file;
QStringList wellNames = file->wellNames();
m_wellNameInFormationsFile = wellNames.isEmpty() ? QString() : wellNames.first();
if ( auto track = firstAncestorOrThisOfType<RimWellLogTrack>() )
{
track->loadDataAndUpdate();
track->updateEditors();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QList<caf::PdmOptionItemInfo> RimWellLogFormationSettings::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions )
{
QList<caf::PdmOptionItemInfo> options;
if ( fieldNeedingOptions == &m_formationWellPathForSourceCase )
{
RimTools::wellPathOptionItems( &options );
}
else if ( fieldNeedingOptions == &m_formationWellPathForSourceWellPath )
{
RimTools::wellPathWithFormationsOptionItems( &options );
}
else if ( fieldNeedingOptions == &m_wellFormationsFile )
{
options.push_back( caf::PdmOptionItemInfo( "None", static_cast<RimWellFormationsFile*>( nullptr ) ) );
RimProject* proj = RimProject::current();
if ( proj && proj->activeOilField() && proj->activeOilField()->wellFormationsCollection() )
{
for ( RimWellFormationsFile* file : proj->activeOilField()->wellFormationsCollection()->wellFormationsFiles() )
{
options.push_back( caf::PdmOptionItemInfo( file->shortName(), file, false, file->uiCapability()->uiIconProvider() ) );
}
}
}
else if ( fieldNeedingOptions == &m_wellNameInFormationsFile )
{
if ( m_wellFormationsFile() )
{
for ( const QString& wellName : m_wellFormationsFile->wellNames() )
{
options.push_back( caf::PdmOptionItemInfo( wellName, wellName ) );
}
}
}
else if ( fieldNeedingOptions == &m_formationCase )
{
RimTools::caseOptionItems( &options );
}
else if ( fieldNeedingOptions == &m_formationSimWellName )
{
std::set<QString> sortedWellNames;
RimEclipseCase* eclipseCase = dynamic_cast<RimEclipseCase*>( m_formationCase.value() );
if ( eclipseCase && eclipseCase->eclipseCaseData() )
{
sortedWellNames = eclipseCase->eclipseCaseData()->findSortedWellNames();
}
caf::IconProvider simWellIcon( ":/Well.svg" );
for ( const QString& wname : sortedWellNames )
{
options.push_back( caf::PdmOptionItemInfo( wname, wname, false, simWellIcon ) );
}
}
else if ( fieldNeedingOptions == &m_formationBranchIndex )
{
auto simulationWellBranches = RiaSimWellBranchTools::simulationWellBranches( m_formationSimWellName(), m_formationBranchDetection );
options = RiaSimWellBranchTools::valueOptionsForBranchIndexField( simulationWellBranches );
}
else if ( fieldNeedingOptions == &m_formationLevel )
{
if ( auto formations = resolveWellPickFormations() )
{
using FormationLevelEnum = caf::AppEnum<RiaDefines::WellLogTrackFormationLevel>;
options.push_back( caf::PdmOptionItemInfo( FormationLevelEnum::uiText( RiaDefines::WellLogTrackFormationLevel::NONE ),
RiaDefines::WellLogTrackFormationLevel::NONE ) );
options.push_back( caf::PdmOptionItemInfo( FormationLevelEnum::uiText( RiaDefines::WellLogTrackFormationLevel::ALL ),
RiaDefines::WellLogTrackFormationLevel::ALL ) );
for ( const auto& level : formations->formationsLevelsPresent() )
{
options.push_back( caf::PdmOptionItemInfo( FormationLevelEnum::uiText( level ), level ) );
}
}
}
return options;
}