ResInsight/ApplicationLibCode/ProjectDataModel/Flow/RimWellPltPlot.cpp
Magne Sjaastad 493531626f
Rft segment plot adjustments
* #9371 RFT Curves: Add scaling factor
* Make sure isUsingAutoName is guarded by project file date
* Rearrange font and legend group in property editor
2022-10-19 15:13:15 +02:00

1113 lines
47 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2017 Statoil 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 "RimWellPltPlot.h"
#include "RiaDateStringParser.h"
#include "RiaLogging.h"
#include "RiaPlotDefines.h"
#include "RiaQDateTimeTools.h"
#include "RiaWellNameComparer.h"
#include "RifReaderEclipseRft.h"
#include "RigAccWellFlowCalculator.h"
#include "RigCaseCellResultsData.h"
#include "RigEclipseCaseData.h"
#include "RigEclipseWellLogExtractor.h"
#include "RigMainGrid.h"
#include "RigSimWellData.h"
#include "RigWellLogExtractor.h"
#include "RigWellPath.h"
#include "RigWellResultPoint.h"
#include "RimDataSourceForRftPlt.h"
#include "RimEclipseCase.h"
#include "RimEclipseCaseCollection.h"
#include "RimEclipseResultCase.h"
#include "RimEclipseResultDefinition.h"
#include "RimMainPlotCollection.h"
#include "RimOilField.h"
#include "RimProject.h"
#include "RimSummaryCurveAppearanceCalculator.h"
#include "RimWellFlowRateCurve.h"
#include "RimWellLogExtractionCurve.h"
#include "RimWellLogFile.h"
#include "RimWellLogFileChannel.h"
#include "RimWellLogFileCurve.h"
#include "RimWellLogPlot.h"
#include "RimWellLogPlotCollection.h"
#include "RimWellLogPlotNameConfig.h"
#include "RimWellLogRftCurve.h"
#include "RimWellLogTrack.h"
#include "RimWellPath.h"
#include "RimWellPathCollection.h"
#include "RimWellPlotTools.h"
#include "cafPdmUiTreeOrdering.h"
#include "cafPdmUiTreeSelectionEditor.h"
#include "cafVecIjk.h"
#include <algorithm>
#include <iterator>
#include <tuple>
CAF_PDM_SOURCE_INIT( RimWellPltPlot, "WellPltPlot" );
namespace caf
{
template <>
void caf::AppEnum<FlowType>::setUp()
{
addItem( FLOW_TYPE_PHASE_SPLIT, "PHASE_SPLIT", "Phase Split" );
addItem( FLOW_TYPE_TOTAL, "TOTAL", "Total Flow" );
setDefault( FLOW_TYPE_PHASE_SPLIT );
}
template <>
void caf::AppEnum<FlowPhase>::setUp()
{
addItem( FLOW_PHASE_OIL, "PHASE_OIL", "Oil" );
addItem( FLOW_PHASE_GAS, "PHASE_GAS", "Gas" );
addItem( FLOW_PHASE_WATER, "PHASE_WATER", "Water" );
addItem( FLOW_PHASE_TOTAL, "PHASE_TOTAL", "Total" );
}
} // namespace caf
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const char RimWellPltPlot::PLOT_NAME_QFORMAT_STRING[] = "PLT: %1";
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellPltPlot::RimWellPltPlot()
: RimWellLogPlot()
{
CAF_PDM_InitObject( "Well Allocation Plot", ":/WellFlowPlot16x16.png" );
CAF_PDM_InitFieldNoDefault( &m_wellLogPlot_OBSOLETE, "WellLog", "WellLog" );
m_wellLogPlot_OBSOLETE.uiCapability()->setUiTreeHidden( true );
m_wellLogPlot_OBSOLETE.xmlCapability()->setIOWritable( false );
CAF_PDM_InitFieldNoDefault( &m_wellPathName, "WellName", "Well Name" );
CAF_PDM_InitFieldNoDefault( &m_selectedSources, "SourcesInternal", "Sources Internal" );
m_selectedSources.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() );
m_selectedSources.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
m_selectedSources.uiCapability()->setAutoAddingOptionFromValue( false );
m_selectedSources.xmlCapability()->disableIO();
CAF_PDM_InitFieldNoDefault( &m_selectedSourcesForIo, "Sources", "Sources" );
m_selectedSourcesForIo.uiCapability()->setUiTreeHidden( true );
CAF_PDM_InitFieldNoDefault( &m_selectedTimeSteps, "TimeSteps", "Time Steps" );
m_selectedTimeSteps.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() );
m_selectedTimeSteps.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
m_selectedTimeSteps.uiCapability()->setAutoAddingOptionFromValue( false );
CAF_PDM_InitField( &m_useStandardConditionCurves, "UseStandardConditionCurves", true, "Standard Volume" );
CAF_PDM_InitField( &m_useReservoirConditionCurves, "UseReservoirConditionCurves", true, "Reservoir Volume" );
CAF_PDM_InitFieldNoDefault( &m_phases, "Phases", "Phases" );
m_phases.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() );
m_phases = std::vector<caf::AppEnum<FlowPhase>>( { FLOW_PHASE_OIL, FLOW_PHASE_GAS, FLOW_PHASE_WATER } );
m_phases.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
m_nameConfig->setCustomName( "PLT Plot" );
setNamingMethod( RiaDefines::ObjectNamingMethod::CUSTOM );
this->setAsPlotMdiWindow();
m_doInitAfterLoad = false;
m_isOnLoad = true;
m_plotLegendsHorizontal = false;
setAvailableDepthTypes( { RiaDefines::DepthTypeEnum::MEASURED_DEPTH } );
setPlotTitleVisible( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellPltPlot::~RimWellPltPlot()
{
removeMdiWindowFromMdiArea();
deleteViewWidget();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::setPlotXAxisTitles( RimWellLogTrack* plotTrack )
{
std::set<RiaDefines::EclipseUnitSystem> presentUnitSystems;
for ( const RifDataSourceForRftPlt& source : m_selectedSources.v() )
{
if ( source.eclCase() && source.eclCase()->eclipseCaseData() )
{
presentUnitSystems.insert( source.eclCase()->eclipseCaseData()->unitsType() );
}
if ( source.wellLogFile() )
{
if ( source.wellLogFile()->wellLogFileData() )
{
auto eclipseUnit = RiaDefines::fromDepthUnit( source.wellLogFile()->wellLogFileData()->depthUnit() );
presentUnitSystems.insert( eclipseUnit );
}
}
}
if ( presentUnitSystems.size() > 1 )
{
RiaLogging::errorInMessageBox( nullptr, "ResInsight PLT Plot", "Inconsistent units in PLT plot" );
}
if ( presentUnitSystems.empty() ) return;
RiaDefines::EclipseUnitSystem unitSet = *presentUnitSystems.begin();
QString axisTitle;
if ( m_useReservoirConditionCurves )
axisTitle += RimWellPlotTools::flowPlotAxisTitle( RimWellLogFile::WELL_FLOW_COND_RESERVOIR, unitSet );
if ( m_useReservoirConditionCurves && m_useStandardConditionCurves ) axisTitle += " | ";
if ( m_useStandardConditionCurves )
axisTitle += RimWellPlotTools::flowPlotAxisTitle( RimWellLogFile::WELL_FLOW_COND_STANDARD, unitSet );
plotTrack->setPropertyValueAxisTitle( axisTitle );
#if 0
QString unitText;
for ( auto unitSet: presentUnitSystems )
{
switch ( unitSet )
{
case RiaEclipseUnitTools::UNITS_METRIC:
unitText += "[Liquid Sm<sup>3</sup>/day], [Gas kSm<sup>3</sup>/day]";
break;
case RiaEclipseUnitTools::UNITS_FIELD:
unitText += "[Liquid BBL/day], [Gas BOE/day]";
break;
case RiaEclipseUnitTools::UNITS_LAB:
unitText += "[cm<sup>3</sup>/hr]";
break;
default:
unitText += "(unknown unit)";
break;
}
}
plotTrack->setXAxisTitle("Surface Flow Rate " + unitText);
#endif
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::updateFormationsOnPlot() const
{
if ( plotCount() > 0 )
{
RimProject* proj = RimProject::current();
RimWellPath* wellPath = proj->wellPathByName( m_wellPathName );
RimWellLogTrack* track = dynamic_cast<RimWellLogTrack*>( plotByIndex( 0 ) );
if ( track )
{
RimCase* formationNamesCase = track->formationNamesCase();
if ( !formationNamesCase )
{
/// Set default case. Todo : Use the first of the selected cases in the
/// plot
std::vector<RimCase*> cases;
proj->allCases( cases );
if ( !cases.empty() )
{
formationNamesCase = cases[0];
}
}
track->setAndUpdateWellPathFormationNamesData( formationNamesCase, wellPath );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<RiaRftPltCurveDefinition> RimWellPltPlot::selectedCurveDefs() const
{
std::set<RifEclipseRftAddress::RftWellLogChannelType> channelTypesToUse = RifEclipseRftAddress::pltPlotChannelTypes();
return RimWellPlotTools::curveDefsFromTimesteps( RimWellPlotTools::simWellName( m_wellPathName ),
m_selectedTimeSteps.v(),
false,
selectedSourcesExpanded(),
channelTypesToUse );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
class RigResultPointCalculator
{
public:
RigResultPointCalculator() {}
virtual ~RigResultPointCalculator() {}
const std::vector<cvf::Vec3d>& pipeBranchCLCoords() { return m_pipeBranchCLCoords; }
const std::vector<RigWellResultPoint>& pipeBranchWellResultPoints() { return m_pipeBranchWellResultPoints; }
const std::vector<double>& pipeBranchMeasuredDepths() { return m_pipeBranchMeasuredDepths; }
protected:
RigEclipseWellLogExtractor* findWellLogExtractor( const QString& wellPathName, RimEclipseResultCase* eclCase )
{
RimProject* proj = RimProject::current();
RimWellPath* wellPath = proj->wellPathByName( wellPathName );
RimWellLogPlotCollection* wellLogCollection = RimMainPlotCollection::current()->wellLogPlotCollection();
RigEclipseWellLogExtractor* eclExtractor = wellLogCollection->findOrCreateExtractor( wellPath, eclCase );
return eclExtractor;
}
std::vector<cvf::Vec3d> m_pipeBranchCLCoords;
std::vector<RigWellResultPoint> m_pipeBranchWellResultPoints;
std::vector<double> m_pipeBranchMeasuredDepths;
};
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
class RigRftResultPointCalculator : public RigResultPointCalculator
{
public:
RigRftResultPointCalculator( const QString& wellPathName, RimEclipseResultCase* eclCase, QDateTime m_timeStep )
{
RifEclipseRftAddress gasRateAddress =
RifEclipseRftAddress::createAddress( RimWellPlotTools::simWellName( wellPathName ),
m_timeStep,
RifEclipseRftAddress::RftWellLogChannelType::GRAT );
RifEclipseRftAddress oilRateAddress =
RifEclipseRftAddress::createAddress( RimWellPlotTools::simWellName( wellPathName ),
m_timeStep,
RifEclipseRftAddress::RftWellLogChannelType::ORAT );
RifEclipseRftAddress watRateAddress =
RifEclipseRftAddress::createAddress( RimWellPlotTools::simWellName( wellPathName ),
m_timeStep,
RifEclipseRftAddress::RftWellLogChannelType::WRAT );
std::vector<caf::VecIjk> rftIndices;
eclCase->rftReader()->cellIndices( gasRateAddress, &rftIndices );
if ( rftIndices.empty() ) eclCase->rftReader()->cellIndices( oilRateAddress, &rftIndices );
if ( rftIndices.empty() ) eclCase->rftReader()->cellIndices( watRateAddress, &rftIndices );
if ( rftIndices.empty() ) return;
std::vector<double> gasRates;
std::vector<double> oilRates;
std::vector<double> watRates;
eclCase->rftReader()->values( gasRateAddress, &gasRates );
eclCase->rftReader()->values( oilRateAddress, &oilRates );
eclCase->rftReader()->values( watRateAddress, &watRates );
std::map<size_t, size_t> globCellIdxToIdxInRftFile;
const RigMainGrid* mainGrid = eclCase->eclipseCaseData()->mainGrid();
for ( size_t rftCellIdx = 0; rftCellIdx < rftIndices.size(); rftCellIdx++ )
{
caf::VecIjk ijkIndex = rftIndices[rftCellIdx];
size_t globalCellIndex = mainGrid->cellIndexFromIJK( ijkIndex.i(), ijkIndex.j(), ijkIndex.k() );
globCellIdxToIdxInRftFile[globalCellIndex] = rftCellIdx;
}
RigEclipseWellLogExtractor* eclExtractor = findWellLogExtractor( wellPathName, eclCase );
if ( !eclExtractor ) return;
std::vector<WellPathCellIntersectionInfo> intersections = eclExtractor->cellIntersectionInfosAlongWellPath();
for ( size_t wpExIdx = 0; wpExIdx < intersections.size(); wpExIdx++ )
{
size_t globCellIdx = intersections[wpExIdx].globCellIndex;
auto it = globCellIdxToIdxInRftFile.find( globCellIdx );
if ( it == globCellIdxToIdxInRftFile.end() )
{
if ( !m_pipeBranchWellResultPoints.empty() && wpExIdx == ( intersections.size() - 1 ) )
{
m_pipeBranchWellResultPoints.pop_back();
}
continue;
}
m_pipeBranchCLCoords.push_back( intersections[wpExIdx].startPoint );
m_pipeBranchMeasuredDepths.push_back( intersections[wpExIdx].startMD );
m_pipeBranchCLCoords.push_back( intersections[wpExIdx].endPoint );
m_pipeBranchMeasuredDepths.push_back( intersections[wpExIdx].endMD );
RigWellResultPoint resPoint;
resPoint.m_isOpen = true;
resPoint.m_gridIndex = 0; // Always main grid
resPoint.m_gridCellIndex = globCellIdx; // Shortcut, since we only have
// main grid results from RFT
resPoint.m_gasRate =
RiaEclipseUnitTools::convertSurfaceGasFlowRateToOilEquivalents( eclCase->eclipseCaseData()->unitsType(),
gasRates[it->second] );
resPoint.m_oilRate = oilRates[it->second];
resPoint.m_waterRate = watRates[it->second];
m_pipeBranchWellResultPoints.push_back( resPoint );
if ( wpExIdx < intersections.size() - 1 )
{
m_pipeBranchWellResultPoints.push_back( RigWellResultPoint() ); // Invalid res point describing the
// "line" between the cells
}
}
}
};
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
class RigSimWellResultPointCalculator : public RigResultPointCalculator
{
public:
RigSimWellResultPointCalculator( const QString& wellPathName, RimEclipseResultCase* eclCase, QDateTime m_timeStep )
{
// Find timestep index from qdatetime
const std::vector<QDateTime> timeSteps =
eclCase->eclipseCaseData()->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->timeStepDates();
size_t tsIdx = timeSteps.size();
for ( tsIdx = 0; tsIdx < timeSteps.size(); ++tsIdx )
{
if ( timeSteps[tsIdx] == m_timeStep ) break;
}
if ( tsIdx >= timeSteps.size() ) return;
// Build search map into simulation well data
std::map<size_t, std::pair<size_t, size_t>> globCellIdxToIdxInSimWellBranch;
const RimWellPath* wellPath = RimWellPlotTools::wellPathByWellPathNameOrSimWellName( wellPathName );
const RigSimWellData* simWell =
wellPath != nullptr ? eclCase->eclipseCaseData()->findSimWellData( wellPath->associatedSimulationWellName() )
: nullptr;
if ( !simWell ) return;
if ( !simWell->hasWellResult( tsIdx ) ) return;
const RigWellResultFrame* resFrame = simWell->wellResultFrame( tsIdx );
const RigMainGrid* mainGrid = eclCase->eclipseCaseData()->mainGrid();
for ( size_t brIdx = 0; brIdx < resFrame->m_wellResultBranches.size(); ++brIdx )
{
const std::vector<RigWellResultPoint>& branchResPoints =
resFrame->m_wellResultBranches[brIdx].m_branchResultPoints;
for ( size_t wrpIdx = 0; wrpIdx < branchResPoints.size(); wrpIdx++ )
{
const RigGridBase* grid = mainGrid->gridByIndex( branchResPoints[wrpIdx].m_gridIndex );
size_t globalCellIndex = grid->reservoirCellIndex( branchResPoints[wrpIdx].m_gridCellIndex );
globCellIdxToIdxInSimWellBranch[globalCellIndex] = std::make_pair( brIdx, wrpIdx );
}
}
RigEclipseWellLogExtractor* eclExtractor = findWellLogExtractor( wellPathName, eclCase );
if ( !eclExtractor ) return;
std::vector<WellPathCellIntersectionInfo> intersections = eclExtractor->cellIntersectionInfosAlongWellPath();
for ( size_t wpExIdx = 0; wpExIdx < intersections.size(); wpExIdx++ )
{
size_t globCellIdx = intersections[wpExIdx].globCellIndex;
auto it = globCellIdxToIdxInSimWellBranch.find( globCellIdx );
if ( it == globCellIdxToIdxInSimWellBranch.end() )
{
if ( !m_pipeBranchWellResultPoints.empty() && wpExIdx == ( intersections.size() - 1 ) )
{
m_pipeBranchWellResultPoints.pop_back();
}
continue;
}
m_pipeBranchCLCoords.push_back( intersections[wpExIdx].startPoint );
m_pipeBranchMeasuredDepths.push_back( intersections[wpExIdx].startMD );
m_pipeBranchCLCoords.push_back( intersections[wpExIdx].endPoint );
m_pipeBranchMeasuredDepths.push_back( intersections[wpExIdx].endMD );
const RigWellResultPoint& resPoint =
resFrame->m_wellResultBranches[it->second.first].m_branchResultPoints[it->second.second];
m_pipeBranchWellResultPoints.push_back( resPoint );
if ( wpExIdx < intersections.size() - 1 )
{
m_pipeBranchWellResultPoints.push_back( RigWellResultPoint() ); // Invalid res point describing the
// "line" between the cells
}
}
}
};
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::syncCurvesFromUiSelection()
{
RimWellLogTrack* plotTrack = dynamic_cast<RimWellLogTrack*>( plotByIndex( 0 ) );
if ( !plotTrack ) return;
const std::set<RiaRftPltCurveDefinition>& curveDefs = selectedCurveDefs();
setPlotXAxisTitles( plotTrack );
// Delete existing curves
plotTrack->deleteAllCurves();
int curveGroupId = 0;
QString dateFormatString;
{
std::vector<QDateTime> allTimeSteps;
for ( const RiaRftPltCurveDefinition& curveDefToAdd : curveDefs )
{
allTimeSteps.push_back( curveDefToAdd.timeStep() );
}
dateFormatString = RiaQDateTimeTools::createTimeFormatStringFromDates( allTimeSteps );
}
// Add curves
for ( const RiaRftPltCurveDefinition& curveDefToAdd : curveDefs )
{
std::set<FlowPhase> selectedPhases = std::set<FlowPhase>( m_phases().begin(), m_phases().end() );
RifDataSourceForRftPlt sourceDef = curveDefToAdd.address();
QDateTime timeStep = curveDefToAdd.timeStep();
std::unique_ptr<RigResultPointCalculator> resultPointCalc;
QString curveName;
{
curveName += sourceDef.eclCase() ? sourceDef.eclCase()->caseUserDescription() : "";
curveName += sourceDef.wellLogFile() ? sourceDef.wellLogFile()->name() : "";
if ( sourceDef.sourceType() == RifDataSourceForRftPlt::RFT ) curveName += ", RFT";
curveName += ", " + RiaQDateTimeTools::toStringUsingApplicationLocale( timeStep, dateFormatString );
}
RimEclipseResultCase* rimEclipseResultCase = dynamic_cast<RimEclipseResultCase*>( sourceDef.eclCase() );
if ( sourceDef.sourceType() == RifDataSourceForRftPlt::RFT )
{
resultPointCalc.reset( new RigRftResultPointCalculator( m_wellPathName, rimEclipseResultCase, timeStep ) );
}
else if ( sourceDef.sourceType() == RifDataSourceForRftPlt::GRID )
{
resultPointCalc.reset( new RigSimWellResultPointCalculator( m_wellPathName, rimEclipseResultCase, timeStep ) );
}
RiaDefines::EclipseUnitSystem unitSet = RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
if ( rimEclipseResultCase )
{
unitSet = rimEclipseResultCase->eclipseCaseData()->unitsType();
}
if ( resultPointCalc != nullptr )
{
if ( !resultPointCalc->pipeBranchCLCoords().empty() )
{
if ( selectedPhases.count( FLOW_PHASE_TOTAL ) && m_useReservoirConditionCurves() &&
sourceDef.sourceType() == RifDataSourceForRftPlt::GRID )
{
RigAccWellFlowCalculator wfTotalAccumulator( resultPointCalc->pipeBranchCLCoords(),
resultPointCalc->pipeBranchWellResultPoints(),
resultPointCalc->pipeBranchMeasuredDepths(),
true );
const std::vector<double>& depthValues = wfTotalAccumulator.pseudoLengthFromTop( 0 );
QString curveUnitText =
RimWellPlotTools::flowUnitText( RimWellLogFile::WELL_FLOW_COND_RESERVOIR, unitSet );
const std::vector<double> accFlow =
wfTotalAccumulator.accumulatedTracerFlowPrPseudoLength( RIG_FLOW_TOTAL_NAME, 0 );
addStackedCurve( curveName + ", " + RIG_FLOW_TOTAL_NAME + " " + curveUnitText,
depthValues,
accFlow,
plotTrack,
cvf::Color3f::DARK_GRAY,
curveGroupId,
false );
curveGroupId++;
}
if ( m_useStandardConditionCurves() )
{
RigAccWellFlowCalculator wfPhaseAccumulator( resultPointCalc->pipeBranchCLCoords(),
resultPointCalc->pipeBranchWellResultPoints(),
resultPointCalc->pipeBranchMeasuredDepths(),
false );
const std::vector<double>& depthValues = wfPhaseAccumulator.pseudoLengthFromTop( 0 );
std::vector<QString> tracerNames = wfPhaseAccumulator.tracerNames();
for ( const QString& tracerName : tracerNames )
{
auto color = tracerName == RIG_FLOW_OIL_NAME
? cvf::Color3f::DARK_GREEN
: tracerName == RIG_FLOW_GAS_NAME
? cvf::Color3f::DARK_RED
: tracerName == RIG_FLOW_WATER_NAME ? cvf::Color3f::BLUE
: cvf::Color3f::DARK_GRAY;
if ( tracerName == RIG_FLOW_OIL_NAME && selectedPhases.count( FLOW_PHASE_OIL ) ||
tracerName == RIG_FLOW_GAS_NAME && selectedPhases.count( FLOW_PHASE_GAS ) ||
tracerName == RIG_FLOW_WATER_NAME && selectedPhases.count( FLOW_PHASE_WATER ) )
{
FlowPhase flowPhase = FLOW_PHASE_NONE;
if ( tracerName == RIG_FLOW_OIL_NAME )
flowPhase = FLOW_PHASE_OIL;
else if ( tracerName == RIG_FLOW_GAS_NAME )
flowPhase = FLOW_PHASE_GAS;
else if ( tracerName == RIG_FLOW_WATER_NAME )
flowPhase = FLOW_PHASE_WATER;
QString curveUnitText =
RimWellPlotTools::curveUnitText( RimWellLogFile::WELL_FLOW_COND_STANDARD, unitSet, flowPhase );
const std::vector<double>& accFlow =
wfPhaseAccumulator.accumulatedTracerFlowPrPseudoLength( tracerName, 0 );
addStackedCurve( curveName + ", " + tracerName + " " + curveUnitText,
depthValues,
accFlow,
plotTrack,
color,
curveGroupId,
false );
}
}
}
}
}
else if ( sourceDef.sourceType() == RifDataSourceForRftPlt::OBSERVED )
{
if ( sourceDef.wellLogFile() && sourceDef.wellLogFile()->wellLogFileData() )
{
RimWellLogFile::WellFlowCondition flowCondition = sourceDef.wellLogFile()->wellFlowRateCondition();
if ( ( m_useStandardConditionCurves() && flowCondition == RimWellLogFile::WELL_FLOW_COND_STANDARD ) ||
( m_useReservoirConditionCurves() && flowCondition == RimWellLogFile::WELL_FLOW_COND_RESERVOIR ) )
{
using ChannelValNameIdxTuple = std::tuple<double, QString, int>;
RigWellLogFile* wellLogFileData = sourceDef.wellLogFile()->wellLogFileData();
QStringList channelNames = wellLogFileData->wellLogChannelNames();
std::multiset<ChannelValNameIdxTuple> sortedChannels;
std::vector<std::vector<double>> channelData;
channelData.resize( channelNames.size() );
for ( int chIdx = 0; chIdx < channelNames.size(); ++chIdx )
{
QString channelName = channelNames[chIdx];
channelData[chIdx] = wellLogFileData->values( channelName );
if ( !channelData[chIdx].empty() )
{
sortedChannels.insert(
ChannelValNameIdxTuple( -fabs( channelData[chIdx].front() ), channelName, chIdx ) );
}
}
std::vector<double> depthValues = wellLogFileData->depthValues();
RiaDefines::EclipseUnitSystem unitSystem = RiaDefines::fromDepthUnit( wellLogFileData->depthUnit() );
for ( const ChannelValNameIdxTuple& channelInfo : sortedChannels )
{
const auto& channelName = std::get<1>( channelInfo );
if ( selectedPhases.count( RimWellPlotTools::flowPhaseFromChannelName( channelName ) ) > 0 )
{
auto color = RimWellPlotTools::isOilFlowChannel( channelName )
? cvf::Color3f::DARK_GREEN
: RimWellPlotTools::isGasFlowChannel( channelName )
? cvf::Color3f::DARK_RED
: RimWellPlotTools::isWaterFlowChannel( channelName )
? cvf::Color3f::BLUE
: cvf::Color3f::DARK_GRAY;
FlowPhase flowPhase = FLOW_PHASE_NONE;
if ( RimWellPlotTools::isOilFlowChannel( channelName ) )
flowPhase = FLOW_PHASE_OIL;
else if ( RimWellPlotTools::isGasFlowChannel( channelName ) )
flowPhase = FLOW_PHASE_GAS;
else if ( RimWellPlotTools::isWaterFlowChannel( channelName ) )
flowPhase = FLOW_PHASE_WATER;
QString curveUnitText = RimWellPlotTools::curveUnitText( flowCondition, unitSystem, flowPhase );
addStackedCurve( curveName + ", " + channelName + " " + curveUnitText,
depthValues,
channelData[std::get<2>( channelInfo )],
plotTrack,
color,
curveGroupId,
true );
// Total flow channel will end up first, so just increment the
// group idx to make the rest of the phases group together
if ( RimWellPlotTools::isTotalFlowChannel( channelName ) ) curveGroupId++;
}
}
}
}
}
curveGroupId++;
}
plotTrack->setAutoScalePropertyValuesEnabled( true );
RimWellLogPlot::onLoadDataAndUpdate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::addStackedCurve( const QString& curveName,
const std::vector<double>& depthValues,
const std::vector<double>& accFlow,
RimWellLogTrack* plotTrack,
cvf::Color3f color,
int curveGroupId,
bool doFillCurve )
{
RimWellFlowRateCurve* curve = new RimWellFlowRateCurve;
curve->setFlowValuesPrDepthValue( curveName, depthType(), depthValues, accFlow );
curve->setColor( color );
curve->setGroupId( curveGroupId );
if ( curveGroupId == 0 )
{
curve->setDoFillCurve( true );
curve->setSymbol( RiuPlotCurveSymbol::SYMBOL_NONE );
}
else
{
curve->setDoFillCurve( false );
curve->setSymbol( RimSummaryCurveAppearanceCalculator::cycledSymbol( curveGroupId ) );
}
curve->setSymbolSkipDistance( 10 );
plotTrack->addCurve( curve );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RifDataSourceForRftPlt> RimWellPltPlot::selectedSourcesExpanded() const
{
std::vector<RifDataSourceForRftPlt> sources;
for ( const RifDataSourceForRftPlt& addr : m_selectedSources() )
{
if ( addr.sourceType() == RifDataSourceForRftPlt::OBSERVED )
{
for ( RimWellLogFile* const wellLogFile : RimWellPlotTools::wellLogFilesContainingFlow( m_wellPathName ) )
{
sources.push_back( RifDataSourceForRftPlt( RifDataSourceForRftPlt::OBSERVED, wellLogFile ) );
}
}
else
sources.push_back( addr );
}
return sources;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::setCurrentWellName( const QString& currWellName )
{
m_wellPathName = currWellName;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const char* RimWellPltPlot::plotNameFormatString()
{
return PLOT_NAME_QFORMAT_STRING;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QList<caf::PdmOptionItemInfo> RimWellPltPlot::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions )
{
QList<caf::PdmOptionItemInfo> options = RimWellLogPlot::calculateValueOptions( fieldNeedingOptions );
const QString simWellName = RimWellPlotTools::simWellName( m_wellPathName );
if ( fieldNeedingOptions == &m_wellPathName )
{
calculateValueOptionsForWells( options );
}
else if ( fieldNeedingOptions == &m_selectedSources )
{
std::set<RifDataSourceForRftPlt> optionAddresses;
const std::vector<RimEclipseResultCase*> rftCases = RimWellPlotTools::rftCasesForWell( simWellName );
std::set<RifDataSourceForRftPlt> availableRftSources;
for ( const auto& rftCase : rftCases )
{
std::set<RifEclipseRftAddress::RftWellLogChannelType> channelTypesToUse =
RifEclipseRftAddress::pltPlotChannelTypes();
std::set<QDateTime> rftTimes = rftCase->rftReader()->availableTimeSteps( simWellName, channelTypesToUse );
if ( !rftTimes.empty() )
{
availableRftSources.insert( RifDataSourceForRftPlt( RifDataSourceForRftPlt::RFT, rftCase ) );
}
}
if ( !availableRftSources.empty() )
{
options.push_back( caf::PdmOptionItemInfo::createHeader( RifDataSourceForRftPlt::sourceTypeUiText(
RifDataSourceForRftPlt::RFT ),
true ) );
for ( const auto& addr : availableRftSources )
{
auto item = caf::PdmOptionItemInfo( addr.eclCase()->caseUserDescription(), QVariant::fromValue( addr ) );
item.setLevel( 1 );
options.push_back( item );
}
}
const std::vector<RimEclipseResultCase*> gridCases = RimWellPlotTools::gridCasesForWell( simWellName );
if ( !gridCases.empty() )
{
options.push_back( caf::PdmOptionItemInfo::createHeader( RifDataSourceForRftPlt::sourceTypeUiText(
RifDataSourceForRftPlt::GRID ),
true ) );
}
for ( const auto& gridCase : gridCases )
{
auto addr = RifDataSourceForRftPlt( RifDataSourceForRftPlt::GRID, gridCase );
auto item = caf::PdmOptionItemInfo( gridCase->caseUserDescription(), QVariant::fromValue( addr ) );
item.setLevel( 1 );
options.push_back( item );
}
if ( !RimWellPlotTools::wellLogFilesContainingFlow( m_wellPathName ).empty() )
{
options.push_back( caf::PdmOptionItemInfo::createHeader( RifDataSourceForRftPlt::sourceTypeUiText(
RifDataSourceForRftPlt::OBSERVED ),
true ) );
auto addr = RifDataSourceForRftPlt( RifDataSourceForRftPlt::OBSERVED );
auto item = caf::PdmOptionItemInfo( "Observed Data", QVariant::fromValue( addr ) );
item.setLevel( 1 );
options.push_back( item );
optionAddresses.insert( addr );
}
}
else if ( fieldNeedingOptions == &m_selectedTimeSteps )
{
std::set<RifEclipseRftAddress::RftWellLogChannelType> channelTypesToUse =
RifEclipseRftAddress::pltPlotChannelTypes();
RimWellPlotTools::calculateValueOptionsForTimeSteps( RimWellPlotTools::simWellName( m_wellPathName ),
selectedSourcesExpanded(),
channelTypesToUse,
options );
}
if ( fieldNeedingOptions == &m_phases )
{
options.push_back( caf::PdmOptionItemInfo( "Oil", FLOW_PHASE_OIL ) );
options.push_back( caf::PdmOptionItemInfo( "Gas", FLOW_PHASE_GAS ) );
options.push_back( caf::PdmOptionItemInfo( "Water", FLOW_PHASE_WATER ) );
options.push_back( caf::PdmOptionItemInfo( "Total", FLOW_PHASE_TOTAL ) );
}
return options;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::fieldChangedByUi( const caf::PdmFieldHandle* changedField,
const QVariant& oldValue,
const QVariant& newValue )
{
RimWellLogPlot::fieldChangedByUi( changedField, oldValue, newValue );
if ( changedField == &m_wellPathName )
{
m_nameConfig->setCustomName( QString( plotNameFormatString() ).arg( m_wellPathName ) );
}
if ( changedField == &m_wellPathName )
{
RimWellLogTrack* const plotTrack = dynamic_cast<RimWellLogTrack*>( plotByIndex( 0 ) );
if ( plotTrack )
{
plotTrack->deleteAllCurves();
m_selectedSources.v().clear();
m_selectedTimeSteps.v().clear();
updateFormationsOnPlot();
}
}
else if ( changedField == &m_selectedSources )
{
RimProject* project = RimProject::current();
RimWellPath* wellPath = project->wellPathByName( m_wellPathName() );
if ( wellPath && !wellPath->wellPathGeometry() )
{
for ( const RifDataSourceForRftPlt& address : m_selectedSources() )
{
if ( address.sourceType() == RifDataSourceForRftPlt::RFT ||
address.sourceType() == RifDataSourceForRftPlt::GRID )
{
if ( !wellPath->wellPathGeometry() )
{
QString tmp = QString( "Display of Measured Depth (MD) for Grid or "
"RFT curves is not possible without a "
"well log path, and the curve will be hidden "
"in this mode.\n\n" );
RiaLogging::errorInMessageBox( nullptr, "Grid/RFT curve without MD", tmp );
// Do not show multiple dialogs
break;
}
}
}
}
}
if ( changedField == &m_selectedSources || changedField == &m_selectedTimeSteps )
{
updateFormationsOnPlot();
syncSourcesIoFieldFromGuiField();
syncCurvesFromUiSelection();
RimWellLogTrack* const plotTrack = dynamic_cast<RimWellLogTrack*>( plotByIndex( 0 ) );
if ( plotTrack )
{
plotTrack->setAutoScalePropertyValuesEnabled( true );
}
updateZoom();
}
if ( changedField == &m_useStandardConditionCurves || changedField == &m_useReservoirConditionCurves ||
changedField == &m_phases )
{
syncCurvesFromUiSelection();
RimWellLogTrack* const plotTrack = dynamic_cast<RimWellLogTrack*>( plotByIndex( 0 ) );
if ( plotTrack )
{
plotTrack->setAutoScalePropertyValuesEnabled( true );
}
updateZoom();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName )
{
uiTreeOrdering.skipRemainingChildren( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
uiOrdering.add( &m_wellPathName );
caf::PdmUiGroup* sourcesGroup = uiOrdering.addNewGroupWithKeyword( "Sources", "Sources" );
sourcesGroup->add( &m_selectedSources );
caf::PdmUiGroup* timeStepsGroup = uiOrdering.addNewGroupWithKeyword( "Time Steps", "TimeSteps" );
timeStepsGroup->add( &m_selectedTimeSteps );
caf::PdmUiGroup* flowGroup = uiOrdering.addNewGroupWithKeyword( "Curve Selection", "PhaseSelection" );
flowGroup->add( &m_useStandardConditionCurves );
flowGroup->add( &m_useReservoirConditionCurves );
flowGroup->add( &m_phases );
if ( plotCount() > 0 )
{
RimWellLogTrack* const track = dynamic_cast<RimWellLogTrack*>( plotByIndex( 0 ) );
if ( track )
{
track->uiOrderingForRftPltFormations( uiOrdering );
track->uiOrderingForPropertyAxisSettings( uiOrdering );
caf::PdmUiGroup* depthGroup = uiOrdering.addNewGroup( "Depth Axis Settings" );
uiOrderingForDepthAxis( uiConfigName, *depthGroup );
caf::PdmUiGroup* nameGroup = uiOrdering.addNewGroup( "Plot Name" );
nameGroup->setCollapsedByDefault();
RimWellLogPlot::uiOrderingForAutoName( uiConfigName, *nameGroup );
RimPlotWindow::uiOrderingForLegendsAndFonts( uiConfigName, uiOrdering );
}
}
uiOrdering.skipRemainingFields( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::defineEditorAttribute( const caf::PdmFieldHandle* field,
QString uiConfigName,
caf::PdmUiEditorAttribute* attribute )
{
if ( field == &m_phases )
{
caf::PdmUiTreeSelectionEditorAttribute* attrib = dynamic_cast<caf::PdmUiTreeSelectionEditorAttribute*>( attribute );
attrib->showTextFilter = false;
attrib->showToggleAllCheckbox = false;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::initAfterRead()
{
RimDepthTrackPlot::initAfterRead();
if ( m_wellLogPlot_OBSOLETE )
{
RimWellLogPlot& wellLogPlot = dynamic_cast<RimWellLogPlot&>( *this );
wellLogPlot = std::move( *m_wellLogPlot_OBSOLETE.value() );
delete m_wellLogPlot_OBSOLETE;
m_wellLogPlot_OBSOLETE = nullptr;
}
RimWellLogPlot::initAfterRead();
// Postpone init until data has been loaded
m_doInitAfterLoad = true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::setupBeforeSave()
{
syncSourcesIoFieldFromGuiField();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::initAfterLoad()
{
std::vector<RifDataSourceForRftPlt> selectedSources;
for ( RimDataSourceForRftPlt* addr : m_selectedSourcesForIo )
{
selectedSources.push_back( addr->address() );
}
m_selectedSources = selectedSources;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::syncSourcesIoFieldFromGuiField()
{
m_selectedSourcesForIo.deleteChildren();
for ( const RifDataSourceForRftPlt& addr : m_selectedSources() )
{
m_selectedSourcesForIo.push_back( new RimDataSourceForRftPlt( addr ) );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::calculateValueOptionsForWells( QList<caf::PdmOptionItemInfo>& options )
{
RimProject* proj = RimProject::current();
if ( proj != nullptr )
{
// Observed wells
for ( const RimWellPath* const wellPath : proj->allWellPaths() )
{
const QString wellName = wellPath->name();
if ( wellPath->wellPathGeometry() || RimWellPlotTools::hasFlowData( wellPath ) )
options.push_back( caf::PdmOptionItemInfo( wellName, wellName ) );
}
}
options.push_back( caf::PdmOptionItemInfo( "None", "" ) );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::onLoadDataAndUpdate()
{
if ( m_doInitAfterLoad )
{
initAfterLoad();
m_doInitAfterLoad = false;
}
if ( m_isOnLoad )
{
if ( plotCount() > 0 )
{
RimWellLogTrack* const plotTrack = dynamic_cast<RimWellLogTrack*>( plotByIndex( 0 ) );
if ( plotTrack )
{
plotTrack->setAnnotationType( RiaDefines::RegionAnnotationType::FORMATION_ANNOTATIONS );
}
}
m_isOnLoad = false;
}
updateMdiWindowVisibility();
updateFormationsOnPlot();
syncCurvesFromUiSelection();
}