///////////////////////////////////////////////////////////////////////////////// // // 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 // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RimWellPltPlot.h" #include "RiaDateStringParser.h" #include "RiaLogging.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 "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 "RimWellLogRftCurve.h" #include "RimWellLogTrack.h" #include "RimWellPath.h" #include "RimWellPathCollection.h" #include "RimWellPlotTools.h" #include "cafPdmUiTreeOrdering.h" #include "cafPdmUiTreeSelectionEditor.h" #include "cafVecIjk.h" #include #include #include CAF_PDM_SOURCE_INIT( RimWellPltPlot, "WellPltPlot" ); namespace caf { template <> void caf::AppEnum::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::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()->setUiHidden( 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()->setUiHidden( true ); 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>( { FLOW_PHASE_OIL, FLOW_PHASE_GAS, FLOW_PHASE_WATER } ); m_phases.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN ); m_nameConfig->setCustomName( "PLT Plot" ); 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 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() ) { // Todo: Handle different units in the relevant las channels switch ( source.wellLogFile()->wellLogFileData()->depthUnit() ) { case RiaDefines::DepthUnitType::UNIT_METER: presentUnitSystems.insert( RiaEclipseUnitTools::UnitSystem::UNITS_METRIC ); break; case RiaDefines::DepthUnitType::UNIT_FEET: presentUnitSystems.insert( RiaEclipseUnitTools::UnitSystem::UNITS_FIELD ); break; case RiaDefines::DepthUnitType::UNIT_NONE: presentUnitSystems.insert( RiaEclipseUnitTools::UnitSystem::UNITS_UNKNOWN ); break; } } } } if ( presentUnitSystems.size() > 1 ) { RiaLogging::errorInMessageBox( nullptr, "ResInsight PLT Plot", "Inconsistent units in PLT plot" ); } if ( presentUnitSystems.empty() ) return; RiaEclipseUnitTools::UnitSystem 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->setXAxisTitle( axisTitle ); #if 0 QString unitText; for ( auto unitSet: presentUnitSystems ) { switch ( unitSet ) { case RiaEclipseUnitTools::UNITS_METRIC: unitText += "[Liquid Sm3/day], [Gas kSm3/day]"; break; case RiaEclipseUnitTools::UNITS_FIELD: unitText += "[Liquid BBL/day], [Gas BOE/day]"; break; case RiaEclipseUnitTools::UNITS_LAB: unitText += "[cm3/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( plotByIndex( 0 ) ); CAF_ASSERT( track ); if ( track ) { RimCase* formationNamesCase = track->formationNamesCase(); if ( !formationNamesCase ) { /// Set default case. Todo : Use the first of the selected cases in the plot std::vector cases; proj->allCases( cases ); if ( !cases.empty() ) { formationNamesCase = cases[0]; } } track->setAndUpdateWellPathFormationNamesData( formationNamesCase, wellPath ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::set RimWellPltPlot::selectedCurveDefs() const { std::set 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& pipeBranchCLCoords() { return m_pipeBranchCLCoords; } const std::vector& pipeBranchWellResultPoints() { return m_pipeBranchWellResultPoints; } const std::vector& pipeBranchMeasuredDepths() { return m_pipeBranchMeasuredDepths; } protected: RigEclipseWellLogExtractor* findWellLogExtractor( const QString& wellPathName, RimEclipseResultCase* eclCase ) { RimProject* proj = RimProject::current(); RimWellPath* wellPath = proj->wellPathByName( wellPathName ); RimWellLogPlotCollection* wellLogCollection = proj->mainPlotCollection()->wellLogPlotCollection(); RigEclipseWellLogExtractor* eclExtractor = wellLogCollection->findOrCreateExtractor( wellPath, eclCase ); return eclExtractor; } std::vector m_pipeBranchCLCoords; std::vector m_pipeBranchWellResultPoints; std::vector m_pipeBranchMeasuredDepths; }; //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- class RigRftResultPointCalculator : public RigResultPointCalculator { public: RigRftResultPointCalculator( const QString& wellPathName, RimEclipseResultCase* eclCase, QDateTime m_timeStep ) { RifEclipseRftAddress gasRateAddress( RimWellPlotTools::simWellName( wellPathName ), m_timeStep, RifEclipseRftAddress::GRAT ); RifEclipseRftAddress oilRateAddress( RimWellPlotTools::simWellName( wellPathName ), m_timeStep, RifEclipseRftAddress::ORAT ); RifEclipseRftAddress watRateAddress( RimWellPlotTools::simWellName( wellPathName ), m_timeStep, RifEclipseRftAddress::WRAT ); std::vector 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 gasRates; std::vector oilRates; std::vector watRates; eclCase->rftReader()->values( gasRateAddress, &gasRates ); eclCase->rftReader()->values( oilRateAddress, &oilRates ); eclCase->rftReader()->values( watRateAddress, &watRates ); std::map 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 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 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> 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& 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 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( plotByIndex( 0 ) ); CAF_ASSERT( plotTrack ); if ( !plotTrack ) return; const std::set& curveDefs = selectedCurveDefs(); setPlotXAxisTitles( plotTrack ); // Delete existing curves plotTrack->deleteAllCurves(); int curveGroupId = 0; QString dateFormatString; { std::vector allTimeSteps; for ( const RiaRftPltCurveDefinition& curveDefToAdd : curveDefs ) { allTimeSteps.push_back( curveDefToAdd.timeStep() ); } dateFormatString = RiaQDateTimeTools::createTimeFormatStringFromDates( allTimeSteps ); } // Add curves for ( const RiaRftPltCurveDefinition& curveDefToAdd : curveDefs ) { std::set selectedPhases = std::set( m_phases().begin(), m_phases().end() ); RifDataSourceForRftPlt sourceDef = curveDefToAdd.address(); QDateTime timeStep = curveDefToAdd.timeStep(); std::unique_ptr 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( 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 ) ); } RiaEclipseUnitTools::UnitSystem unitSet = RiaEclipseUnitTools::UnitSystem::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& depthValues = wfTotalAccumulator.pseudoLengthFromTop( 0 ); QString curveUnitText = RimWellPlotTools::flowUnitText( RimWellLogFile::WELL_FLOW_COND_RESERVOIR, unitSet ); const std::vector 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& depthValues = wfPhaseAccumulator.pseudoLengthFromTop( 0 ); std::vector 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& 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; RigWellLogFile* wellLogFileData = sourceDef.wellLogFile()->wellLogFileData(); QStringList channelNames = wellLogFileData->wellLogChannelNames(); std::multiset sortedChannels; std::vector> 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 depthValues = wellLogFileData->depthValues(); RiaEclipseUnitTools::UnitSystem unitSystem = RiaEclipseUnitTools::UnitSystem::UNITS_UNKNOWN; { RiaDefines::DepthUnitType depthUnit = wellLogFileData->depthUnit(); if ( depthUnit == RiaDefines::DepthUnitType::UNIT_FEET ) unitSystem = RiaEclipseUnitTools::UnitSystem::UNITS_FIELD; if ( depthUnit == RiaDefines::DepthUnitType::UNIT_METER ) unitSystem = RiaEclipseUnitTools::UnitSystem::UNITS_METRIC; } 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->setAutoScaleXEnabled( true ); RimWellLogPlot::onLoadDataAndUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPltPlot::addStackedCurve( const QString& curveName, const std::vector& depthValues, const std::vector& 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( RiuQwtSymbol::SYMBOL_NONE ); } else { curve->setDoFillCurve( false ); curve->setSymbol( RimSummaryCurveAppearanceCalculator::cycledSymbol( curveGroupId ) ); } curve->setSymbolSkipDistance( 10 ); plotTrack->addCurve( curve ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimWellPltPlot::selectedSourcesExpanded() const { std::vector 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 RimWellPltPlot::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions, bool* useOptionsOnly ) { QList options = RimWellLogPlot::calculateValueOptions( fieldNeedingOptions, useOptionsOnly ); const QString simWellName = RimWellPlotTools::simWellName( m_wellPathName ); if ( fieldNeedingOptions == &m_wellPathName ) { calculateValueOptionsForWells( options ); } else if ( fieldNeedingOptions == &m_selectedSources ) { std::set optionAddresses; const std::vector rftCases = RimWellPlotTools::rftCasesForWell( simWellName ); std::set availableRftSources; for ( const auto& rftCase : rftCases ) { std::set channelTypesToUse = RifEclipseRftAddress::pltPlotChannelTypes(); std::set 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 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 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( plotByIndex( 0 ) ); CAF_ASSERT( plotTrack ); 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( plotByIndex( 0 ) ); CAF_ASSERT( plotTrack ); if ( plotTrack ) { plotTrack->setAutoScaleXEnabled( true ); } updateZoom(); } if ( changedField == &m_useStandardConditionCurves || changedField == &m_useReservoirConditionCurves || changedField == &m_phases ) { syncCurvesFromUiSelection(); RimWellLogTrack* const plotTrack = dynamic_cast( plotByIndex( 0 ) ); CAF_ASSERT( plotTrack ); if ( plotTrack ) { plotTrack->setAutoScaleXEnabled( 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( plotByIndex( 0 ) ); CAF_ASSERT( track ); if ( track ) { track->uiOrderingForRftPltFormations( uiOrdering ); track->uiOrderingForXAxisSettings( uiOrdering ); caf::PdmUiGroup* depthGroup = uiOrdering.addNewGroup( "Depth Axis Settings" ); uiOrderingForDepthAxis( uiConfigName, *depthGroup ); caf::PdmUiGroup* plotLayoutGroup = uiOrdering.addNewGroup( "Plot Layout" ); plotLayoutGroup->setCollapsedByDefault( true ); RimWellLogPlot::uiOrderingForAutoName( uiConfigName, *plotLayoutGroup ); RimWellLogPlot::uiOrderingForPlotLayout( uiConfigName, *plotLayoutGroup ); } } uiOrdering.skipRemainingFields( true ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPltPlot::defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) { if ( field == &m_phases ) { caf::PdmUiTreeSelectionEditorAttribute* attrib = dynamic_cast( attribute ); attrib->showTextFilter = false; attrib->showToggleAllCheckbox = false; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPltPlot::initAfterRead() { RimViewWindow::initAfterRead(); if ( m_wellLogPlot_OBSOLETE ) { RimWellLogPlot& wellLogPlot = dynamic_cast( *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 selectedSources; for ( RimDataSourceForRftPlt* addr : m_selectedSourcesForIo ) { selectedSources.push_back( addr->address() ); } m_selectedSources = selectedSources; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPltPlot::syncSourcesIoFieldFromGuiField() { m_selectedSourcesForIo.clear(); for ( const RifDataSourceForRftPlt& addr : m_selectedSources() ) { m_selectedSourcesForIo.push_back( new RimDataSourceForRftPlt( addr ) ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPltPlot::calculateValueOptionsForWells( QList& 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( plotByIndex( 0 ) ); CAF_ASSERT( plotTrack ); if ( plotTrack ) { plotTrack->setAnnotationType( RiuPlotAnnotationTool::RegionAnnotationType::FORMATION_ANNOTATIONS ); } } m_isOnLoad = false; } updateMdiWindowVisibility(); updateFormationsOnPlot(); syncCurvesFromUiSelection(); }