diff --git a/ApplicationCode/Commands/FlowCommands/RicPlotProductionRateFeature.cpp b/ApplicationCode/Commands/FlowCommands/RicPlotProductionRateFeature.cpp index 2c1fae6022..7ffcf683de 100644 --- a/ApplicationCode/Commands/FlowCommands/RicPlotProductionRateFeature.cpp +++ b/ApplicationCode/Commands/FlowCommands/RicPlotProductionRateFeature.cpp @@ -34,6 +34,7 @@ #include "RimOilField.h" #include "RimProject.h" #include "RimSimWellInView.h" +#include "RimSimWellInViewTools.h" #include "RimSummaryCaseMainCollection.h" #include "RimSummaryCurve.h" #include "RimSummaryCurveAppearanceCalculator.h" @@ -58,7 +59,7 @@ bool RicPlotProductionRateFeature::isCommandEnabled() for ( RimSimWellInView* well : collection ) { - RimGridSummaryCase* gridSummaryCase = RicPlotProductionRateFeature::gridSummaryCaseForWell( well ); + RimGridSummaryCase* gridSummaryCase = RimSimWellInViewTools::gridSummaryCaseForWell( well ); if ( gridSummaryCase ) { return true; @@ -90,12 +91,12 @@ void RicPlotProductionRateFeature::onActionTriggered( bool isChecked ) for ( RimSimWellInView* well : collection ) { - RimGridSummaryCase* gridSummaryCase = RicPlotProductionRateFeature::gridSummaryCaseForWell( well ); + RimGridSummaryCase* gridSummaryCase = RimSimWellInViewTools::gridSummaryCaseForWell( well ); if ( !gridSummaryCase ) continue; QString description = "Well Production Rates : "; - if ( isInjector( well ) ) + if ( RimSimWellInViewTools::isInjector( well ) ) { description = "Well Injection Rates : "; } @@ -103,7 +104,7 @@ void RicPlotProductionRateFeature::onActionTriggered( bool isChecked ) description += well->name(); RimSummaryPlot* plot = summaryPlotColl->createNamedSummaryPlot( description ); - if ( isInjector( well ) ) + if ( RimSimWellInViewTools::isInjector( well ) ) { // Left Axis @@ -239,63 +240,6 @@ void RicPlotProductionRateFeature::setupActionLook( QAction* actionToSetup ) actionToSetup->setText( "Plot Production Rates" ); } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RimGridSummaryCase* RicPlotProductionRateFeature::gridSummaryCaseForWell( RimSimWellInView* well ) -{ - RimProject* project = RiaApplication::instance()->project(); - if ( !project ) return nullptr; - - RimSummaryCaseMainCollection* sumCaseColl = project->activeOilField() - ? project->activeOilField()->summaryCaseMainCollection() - : nullptr; - if ( !sumCaseColl ) return nullptr; - - RimEclipseResultCase* eclCase = nullptr; - well->firstAncestorOrThisOfType( eclCase ); - if ( eclCase ) - { - RimGridSummaryCase* gridSummaryCase = dynamic_cast( - sumCaseColl->findSummaryCaseFromEclipseResultCase( eclCase ) ); - if ( gridSummaryCase ) - { - return gridSummaryCase; - } - } - - return nullptr; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -bool RicPlotProductionRateFeature::isInjector( RimSimWellInView* well ) -{ - RigSimWellData* wRes = well->simWellData(); - if ( wRes ) - { - Rim3dView* rimView = nullptr; - well->firstAncestorOrThisOfTypeAsserted( rimView ); - - int currentTimeStep = rimView->currentTimeStep(); - - if ( wRes->hasWellResult( currentTimeStep ) ) - { - const RigWellResultFrame& wrf = wRes->wellResultFrame( currentTimeStep ); - - if ( wrf.m_productionType == RigWellResultFrame::OIL_INJECTOR || - wrf.m_productionType == RigWellResultFrame::GAS_INJECTOR || - wrf.m_productionType == RigWellResultFrame::WATER_INJECTOR ) - { - return true; - } - } - } - - return false; -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/ModelVisualization/CMakeLists_files.cmake b/ApplicationCode/ModelVisualization/CMakeLists_files.cmake index d57e2efa0d..22f34001ff 100644 --- a/ApplicationCode/ModelVisualization/CMakeLists_files.cmake +++ b/ApplicationCode/ModelVisualization/CMakeLists_files.cmake @@ -55,6 +55,8 @@ ${CMAKE_CURRENT_LIST_DIR}/RivPolylinesAnnotationSourceInfo.h ${CMAKE_CURRENT_LIST_DIR}/RivPolylineGenerator.h ${CMAKE_CURRENT_LIST_DIR}/RivMeasurementPartMgr.h ${CMAKE_CURRENT_LIST_DIR}/RivTextLabelSourceInfo.h +${CMAKE_CURRENT_LIST_DIR}/RivDiskGeometryGenerator.h +${CMAKE_CURRENT_LIST_DIR}/RivWellDiskPartMgr.h ) set (SOURCE_GROUP_SOURCE_FILES @@ -108,6 +110,8 @@ ${CMAKE_CURRENT_LIST_DIR}/RivPolylinesAnnotationSourceInfo.cpp ${CMAKE_CURRENT_LIST_DIR}/RivPolylineGenerator.cpp ${CMAKE_CURRENT_LIST_DIR}/RivMeasurementPartMgr.cpp ${CMAKE_CURRENT_LIST_DIR}/RivTextLabelSourceInfo.cpp +${CMAKE_CURRENT_LIST_DIR}/RivDiskGeometryGenerator.cpp +${CMAKE_CURRENT_LIST_DIR}/RivWellDiskPartMgr.cpp ) list(APPEND CODE_HEADER_FILES diff --git a/ApplicationCode/ModelVisualization/RivDiskGeometryGenerator.cpp b/ApplicationCode/ModelVisualization/RivDiskGeometryGenerator.cpp new file mode 100644 index 0000000000..2e0bb92243 --- /dev/null +++ b/ApplicationCode/ModelVisualization/RivDiskGeometryGenerator.cpp @@ -0,0 +1,141 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2019- 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RivDiskGeometryGenerator.h" + +#include "cvfArray.h" +#include "cvfBase.h" +#include "cvfMath.h" + +#include "cvfGeometryBuilder.h" +#include "cvfGeometryUtils.h" + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RivDiskGeometryGenerator::RivDiskGeometryGenerator() + : m_relativeRadius( 0.085f ) + , m_relativeLength( 0.25f ) + , m_numSlices( 20 ) +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RivDiskGeometryGenerator::setRelativeRadius( float relativeRadius ) +{ + m_relativeRadius = relativeRadius; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RivDiskGeometryGenerator::setRelativeLength( float relativeLength ) +{ + m_relativeLength = relativeLength; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RivDiskGeometryGenerator::setNumSlices( unsigned int numSlices ) +{ + m_numSlices = numSlices; +} + +//-------------------------------------------------------------------------------------------------- +/// Create a disc centered at origin with its normal along positive z-axis +/// +/// \param radius Outer radius of the disc +/// \param numSlices The number of subdivisions around the z-axis. Must be >= 4 +/// \param builder Geometry builder to use when creating geometry +/// +/// Creates a disc on the z = 0 plane, centered at origin and with its surface normal pointing +/// along the positive z-axis. +/// +/// The disk is subdivided around the z axis into numSlices (as in pizza slices). +/// +/// Each slice is a triangle which does not share any vertices with other slices. This +/// is the main different between cvf::GeometryUtils::createDisc. This method generates 3x +/// more vertices, but the result is easier to use if you need to color each slice separately. +/// +/// The sourceNodes that will be produced by this method: +/// +/// The following triangle connectivities will be produced: +/// (0,1,2) (3,4,5) (6,7,8) ... +//-------------------------------------------------------------------------------------------------- +void createDisc( double radius, size_t numSlices, cvf::GeometryBuilder* builder ) +{ + CVF_ASSERT( numSlices >= 4 ); + CVF_ASSERT( builder ); + + double da = 2 * cvf::PI_D / numSlices; + + // Find the start point on the circle for each slice + cvf::Vec3fArray points; + points.reserve( numSlices ); + for ( size_t i = 0; i < numSlices; i++ ) + { + // Precompute this one (A = i*da;) + double sinA = cvf::Math::sin( i * da ); + double cosA = cvf::Math::cos( i * da ); + + cvf::Vec3f point = cvf::Vec3f::ZERO; + point.x() = static_cast( -sinA * radius ); + point.y() = static_cast( cosA * radius ); + points.add( point ); + } + + // Create independent vertices per slice + cvf::Vec3fArray verts; + verts.reserve( numSlices * 3 ); + for ( size_t i = 0; i < numSlices; i++ ) + { + verts.add( cvf::Vec3f::ZERO ); + verts.add( points[i] ); + if ( i == numSlices - 1 ) + { + // Last slice complete the circle. + verts.add( points[0] ); + } + else + verts.add( points[i + 1] ); + } + + cvf::uint baseNodeIdx = builder->addVertices( verts ); + + // Build the triangles for each slice + for ( cvf::uint i = 0; i < static_cast( numSlices ); i++ ) + { + cvf::uint v1 = baseNodeIdx + ( i * 3 ) + 0; + cvf::uint v2 = baseNodeIdx + ( i * 3 ) + 1; + cvf::uint v3 = baseNodeIdx + ( i * 3 ) + 2; + + builder->addTriangle( v1, v2, v3 ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RivDiskGeometryGenerator::generate( cvf::GeometryBuilder* builder ) +{ + const unsigned int numPolysZDir = 1; + createDisc( m_relativeRadius, m_numSlices, builder ); +} diff --git a/ApplicationCode/ModelVisualization/RivDiskGeometryGenerator.h b/ApplicationCode/ModelVisualization/RivDiskGeometryGenerator.h new file mode 100644 index 0000000000..2031811011 --- /dev/null +++ b/ApplicationCode/ModelVisualization/RivDiskGeometryGenerator.h @@ -0,0 +1,43 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2019- 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +namespace cvf +{ +class GeometryBuilder; +} + +class RivDiskGeometryGenerator +{ +public: + RivDiskGeometryGenerator(); + + void setRelativeRadius( float relativeRadius ); + void setRelativeLength( float relativeLength ); + + void setNumSlices( unsigned int numSlices ); + + void generate( cvf::GeometryBuilder* builder ); + +private: + float m_relativeRadius; + float m_relativeLength; + + unsigned int m_numSlices; +}; diff --git a/ApplicationCode/ModelVisualization/RivReservoirSimWellsPartMgr.cpp b/ApplicationCode/ModelVisualization/RivReservoirSimWellsPartMgr.cpp index 8c7a445f6a..7af198d6fc 100644 --- a/ApplicationCode/ModelVisualization/RivReservoirSimWellsPartMgr.cpp +++ b/ApplicationCode/ModelVisualization/RivReservoirSimWellsPartMgr.cpp @@ -31,6 +31,7 @@ #include "RivSimWellPipesPartMgr.h" #include "RivWellConnectionsPartMgr.h" +#include "RivWellDiskPartMgr.h" #include "RivWellHeadPartMgr.h" #include "RivWellSpheresPartMgr.h" @@ -63,6 +64,7 @@ void RivReservoirSimWellsPartMgr::clearGeometryCache() m_wellPipesPartMgrs.clear(); m_wellHeadPartMgrs.clear(); m_wellSpheresPartMgrs.clear(); + m_wellDiskPartMgrs.clear(); } //-------------------------------------------------------------------------------------------------- @@ -112,6 +114,25 @@ void RivReservoirSimWellsPartMgr::appendDynamicGeometryPartsToModel( cvf::ModelB m_reservoirView->displayCoordTransform().p() ); } + // Well disks + if ( m_reservoirView->wellCollection()->wells.size() != m_wellDiskPartMgrs.size() ) + { + clearGeometryCache(); + + for ( size_t i = 0; i < m_reservoirView->wellCollection()->wells.size(); ++i ) + { + RivWellDiskPartMgr* wellDiskMgr = new RivWellDiskPartMgr( m_reservoirView->wellCollection()->wells[i] ); + m_wellDiskPartMgrs.push_back( wellDiskMgr ); + } + } + + for ( size_t wIdx = 0; wIdx != m_wellDiskPartMgrs.size(); ++wIdx ) + { + m_wellDiskPartMgrs[wIdx]->appendDynamicGeometryPartsToModel( model, + frameIndex, + m_reservoirView->displayCoordTransform().p() ); + } + // Well spheres if ( m_reservoirView->wellCollection()->wells.size() != m_wellSpheresPartMgrs.size() ) diff --git a/ApplicationCode/ModelVisualization/RivReservoirSimWellsPartMgr.h b/ApplicationCode/ModelVisualization/RivReservoirSimWellsPartMgr.h index f3ee113ce2..ca33b3d8e6 100644 --- a/ApplicationCode/ModelVisualization/RivReservoirSimWellsPartMgr.h +++ b/ApplicationCode/ModelVisualization/RivReservoirSimWellsPartMgr.h @@ -33,6 +33,7 @@ class RimEclipseView; class RivSimWellPipesPartMgr; class RivWellHeadPartMgr; class RivWellSpheresPartMgr; +class RivWellDiskPartMgr; class RivWellConnectionsPartMgr; //-------------------------------------------------------------------------------------------------- @@ -59,5 +60,6 @@ private: cvf::Collection m_wellPipesPartMgrs; cvf::Collection m_wellHeadPartMgrs; cvf::Collection m_wellSpheresPartMgrs; + cvf::Collection m_wellDiskPartMgrs; cvf::Collection m_wellConnPartMgrs; }; diff --git a/ApplicationCode/ModelVisualization/RivWellDiskPartMgr.cpp b/ApplicationCode/ModelVisualization/RivWellDiskPartMgr.cpp new file mode 100644 index 0000000000..85c4aa2172 --- /dev/null +++ b/ApplicationCode/ModelVisualization/RivWellDiskPartMgr.cpp @@ -0,0 +1,568 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2019- 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RivWellDiskPartMgr.h" + +#include "RiaColorTools.h" +#include "RiaGuiApplication.h" + +#include "RigActiveCellInfo.h" +#include "RigCell.h" +#include "RigSimWellData.h" + +#include "RimEclipseCase.h" +#include "RimEclipseView.h" +#include "RimSimWellInView.h" +#include "RimSimWellInViewCollection.h" + +#include "RivDiskGeometryGenerator.h" +#include "RivPartPriority.h" +#include "RivSectionFlattner.h" +#include "RivSimWellPipeSourceInfo.h" +#include "RivTextLabelSourceInfo.h" + +#include "cafDisplayCoordTransform.h" +#include "cafEffectGenerator.h" + +#include "cvfDrawableGeo.h" +#include "cvfDrawableText.h" +#include "cvfEffect.h" +#include "cvfGeometryBuilderDrawableGeo.h" +#include "cvfGeometryBuilderFaceList.h" +#include "cvfGeometryBuilderTriangles.h" +#include "cvfGeometryUtils.h" +#include "cvfModelBasicList.h" +#include "cvfPart.h" +#include "cvfRenderState_FF.h" +#include "cvfShaderProgram.h" +#include "cvfShaderProgramGenerator.h" +#include "cvfShaderSourceProvider.h" +#include "cvfShaderSourceRepository.h" +#include "cvfTransform.h" +#include "cvfUniform.h" +#include "cvfqtUtils.h" + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RivWellDiskPartMgr::RivWellDiskPartMgr( RimSimWellInView* well ) + : m_rimWell( well ) +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RivWellDiskPartMgr::~RivWellDiskPartMgr() {} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RivWellDiskPartMgr::buildWellDiskParts( size_t frameIndex, const caf::DisplayCoordTransform* displayXf ) +{ + clearAllGeometry(); + + if ( !viewWithSettings() ) return; + + RimSimWellInView* well = m_rimWell; + + double characteristicCellSize = viewWithSettings()->ownerCase()->characteristicCellSize(); + + cvf::Vec3d whEndPos; + cvf::Vec3d whStartPos; + { + well->wellHeadTopBottomPosition( static_cast( frameIndex ), &whEndPos, &whStartPos ); + + whEndPos = displayXf->transformToDisplayCoord( whEndPos ); + whEndPos.z() += characteristicCellSize; + } + + if ( !well->simWellData()->hasWellResult( frameIndex ) ) return; + + auto productionType = well->simWellData()->wellResultFrame( frameIndex ).m_productionType; + + double pipeRadius = m_rimWell->pipeRadius(); + unsigned int numSectors = 100; + + // Upper part of simulation well pipe is defined to use branch index 0 + cvf::ref sourceInfo = new RivSimWellPipeSourceInfo( m_rimWell, 0 ); + + // Well disk geometry + double arrowLength = characteristicCellSize * simWellInViewCollection()->wellHeadScaleFactor() * + m_rimWell->wellHeadScaleFactor(); + + cvf::Vec3d diskPosition = whEndPos; + diskPosition.z() += pipeRadius + arrowLength * 2.0; + + cvf::Vec3d textPosition = diskPosition; + textPosition.z() += 0.1; + + double ijScaleFactor = arrowLength / 6; + + cvf::ref geo1 = new cvf::DrawableGeo; + { + cvf::Mat4f matr; + + matr( 0, 0 ) *= ijScaleFactor; + matr( 1, 1 ) *= ijScaleFactor; + matr( 2, 2 ) *= ijScaleFactor; + + matr.setTranslation( cvf::Vec3f( diskPosition ) ); + + cvf::GeometryBuilderFaceList builder; + { + RivDiskGeometryGenerator gen; + gen.setRelativeRadius( 2.5f * ( m_rimWell->diskScale() ) ); + gen.setRelativeLength( 0.1f ); + gen.setNumSlices( numSectors ); + gen.generate( &builder ); + } + + cvf::ref vertices = builder.vertices(); + cvf::ref faceList = builder.faceList(); + + for ( size_t i = 0; i < vertices->size(); i++ ) + { + cvf::Vec3f v = vertices->get( i ); + v.transformPoint( matr ); + vertices->set( i, v ); + } + + geo1->setVertexArray( vertices.p() ); + geo1->setFromFaceList( *faceList ); + geo1->computeNormals(); + } + + // Create the fixed function effect + { + m_fixedFuncEffect = new cvf::Effect; + + cvf::ref mat = new cvf::RenderStateMaterial_FF( cvf::Color3::BLUE ); + mat->enableColorMaterial( true ); + m_fixedFuncEffect->setRenderState( mat.p() ); + + cvf::ref lighting = new cvf::RenderStateLighting_FF; + m_fixedFuncEffect->setRenderState( lighting.p() ); + } + + // Create effect with shader program + { + m_shaderEffect = new cvf::Effect; + + cvf::ShaderProgramGenerator gen( "PerVertexColor", cvf::ShaderSourceProvider::instance() ); + gen.addVertexCode( cvf::ShaderSourceRepository::vs_Standard ); + + gen.addFragmentCode( cvf::ShaderSourceRepository::src_VaryingColorGlobalAlpha ); + gen.addFragmentCode( cvf::ShaderSourceRepository::fs_Unlit ); + m_shaderProg = gen.generate(); + + m_shaderProg->setDefaultUniform( new cvf::UniformFloat( "u_alpha", 1.0f ) ); + + m_shaderEffect->setShaderProgram( m_shaderProg.p() ); + } + + cvf::ref effectToUse = RiaGuiApplication::instance()->useShaders() ? m_shaderEffect : m_fixedFuncEffect; + + const cvf::Color3ub colorTable[] = {cvf::Color3ub( cvf::Color3::DARK_GREEN ), + cvf::Color3ub( cvf::Color3::DARK_RED ), + cvf::Color3ub( cvf::Color3::DARK_BLUE )}; + + size_t vertexCount = geo1->vertexCount(); + cvf::ref colorArray = new cvf::Color3ubArray; + colorArray->resize( vertexCount ); + colorArray->setAll( cvf::Color3::WHITE ); + CVF_ASSERT( vertexCount == numSectors * 3 ); + + std::vector> labelsWithPosition; + + int numberPrecision = 2; + + double accumulatedPropertyValue = 0.0; + + QString labelText = m_rimWell->name(); + RigWellDiskData diskData = m_rimWell->wellDiskData(); + if ( diskData.isSingleProperty() ) + { + // Set color for the triangle vertices + for ( size_t i = 0; i < numSectors * 3; i++ ) + { + cvf::Color3ub c = cvf::Color3::OLIVE; + colorArray->set( i, c ); + } + + accumulatedPropertyValue = diskData.singlePropertyValue(); + + if ( simWellInViewCollection()->showWellDiskQuantityLables() ) + { + const double singleProperty = diskData.singlePropertyValue(); + labelText += QString( "\n%2" ).arg( singleProperty, 0, 'g', numberPrecision ); + } + } + else + { + const double oil = diskData.oil(); + const double gas = diskData.gas(); + const double water = diskData.water(); + + const double total = diskData.total(); + const double oilFraction = oil / total; + const double gasFraction = gas / total; + const double waterFraction = water / total; + + accumulatedPropertyValue = total; + + const double threshold = 1e-6; + if ( total > threshold ) + { + double aggregatedFraction = 0.0; + + { + auto p = createTextAndLocation( oilFraction / 2.0, diskPosition, ijScaleFactor, oil, numberPrecision ); + labelsWithPosition.push_back( p ); + aggregatedFraction += oilFraction; + } + + { + auto p = createTextAndLocation( aggregatedFraction + gasFraction / 2.0, + diskPosition, + ijScaleFactor, + gas, + numberPrecision ); + labelsWithPosition.push_back( p ); + aggregatedFraction += gasFraction; + } + + { + auto p = createTextAndLocation( aggregatedFraction + waterFraction / 2.0, + diskPosition, + ijScaleFactor, + water, + numberPrecision ); + + labelsWithPosition.push_back( p ); + aggregatedFraction += waterFraction; + } + } + + for ( size_t i = 0; i < numSectors; i++ ) + { + int colorIdx = 0; + + // Find the color for this sector + double lim = ( i + 1 ) / static_cast( numSectors ); + if ( lim <= oilFraction ) + { + colorIdx = 0; + } + else if ( lim <= oilFraction + gasFraction ) + { + colorIdx = 1; + } + else + { + colorIdx = 2; + } + + // Set color for the triangle vertices + for ( int t = 0; t < 3; t++ ) + { + cvf::Color3ub c = colorTable[colorIdx]; + colorArray->set( i * 3 + t, c ); + } + } + } + geo1->setColorArray( colorArray.p() ); + + double threshold = 0.1; + if ( accumulatedPropertyValue > threshold ) + { + { + cvf::ref part = new cvf::Part; + part->setName( "RivWellDiskPartMgr: disk " + cvfqt::Utils::toString( well->name() ) ); + part->setDrawable( geo1.p() ); + + part->setEffect( effectToUse.p() ); + part->setSourceInfo( sourceInfo.p() ); + + m_wellDiskPart = part; + } + + // Add visual indicator for well type: producer or injector + if ( productionType == RigWellResultFrame::PRODUCER ) + { + const uint numPolysZDir = 1; + float bottomRadius = 0.5f; + float topRadius = 0.5f; + float height = 0.1f; + float topOffsetX = 0.0f; + float topOffsetY = 0.0f; + + cvf::GeometryBuilderFaceList builder; + cvf::GeometryUtils::createObliqueCylinder( bottomRadius, + topRadius, + height, + topOffsetX, + topOffsetY, + 20, + true, + true, + true, + numPolysZDir, + &builder ); + + cvf::ref vertices = builder.vertices(); + cvf::ref faceList = builder.faceList(); + + cvf::Mat4f matr; + matr( 0, 0 ) *= ijScaleFactor; + matr( 1, 1 ) *= ijScaleFactor; + matr( 2, 2 ) *= ijScaleFactor; + matr.setTranslation( cvf::Vec3f( diskPosition ) ); + + for ( size_t i = 0; i < vertices->size(); i++ ) + { + cvf::Vec3f v = vertices->get( i ); + v.transformPoint( matr ); + vertices->set( i, v ); + } + + caf::SurfaceEffectGenerator surfaceGen( cvf::Color4f( cvf::Color3::BLACK ), caf::PO_1 ); + surfaceGen.enableLighting( false ); + cvf::ref eff = surfaceGen.generateCachedEffect(); + + cvf::ref injectorGeo = new cvf::DrawableGeo; + injectorGeo->setVertexArray( vertices.p() ); + injectorGeo->setFromFaceList( *faceList ); + injectorGeo->computeNormals(); + + cvf::ref part = new cvf::Part; + part->setName( "RivWellDiskPartMgr: producer " + cvfqt::Utils::toString( well->name() ) ); + part->setDrawable( injectorGeo.p() ); + + part->setEffect( eff.p() ); + part->setSourceInfo( sourceInfo.p() ); + + m_wellDiskInjectorPart = part; + } + else if ( productionType == RigWellResultFrame::OIL_INJECTOR || + productionType == RigWellResultFrame::GAS_INJECTOR || + productionType == RigWellResultFrame::WATER_INJECTOR ) + { + cvf::GeometryBuilderFaceList builder; + cvf::Vec3f pos( 0.0, 0.0, 0.0 ); + + // Construct a cross using to "bars" + cvf::GeometryUtils::createBox( pos, 0.2f, 0.8f, 0.1f, &builder ); + cvf::GeometryUtils::createBox( pos, 0.8f, 0.2f, 0.1f, &builder ); + + cvf::ref vertices = builder.vertices(); + cvf::ref faceList = builder.faceList(); + + cvf::Mat4f matr; + matr( 0, 0 ) *= ijScaleFactor; + matr( 1, 1 ) *= ijScaleFactor; + matr( 2, 2 ) *= ijScaleFactor; + matr.setTranslation( cvf::Vec3f( diskPosition ) ); + + for ( size_t i = 0; i < vertices->size(); i++ ) + { + cvf::Vec3f v = vertices->get( i ); + v.transformPoint( matr ); + vertices->set( i, v ); + } + + cvf::Color4f injectorMarkerColor = getWellInjectionColor( productionType ); + caf::SurfaceEffectGenerator surfaceGen( injectorMarkerColor, caf::PO_1 ); + surfaceGen.enableLighting( false ); + cvf::ref eff = surfaceGen.generateCachedEffect(); + + cvf::ref injectorGeo = new cvf::DrawableGeo; + injectorGeo->setVertexArray( vertices.p() ); + injectorGeo->setFromFaceList( *faceList ); + injectorGeo->computeNormals(); + + cvf::ref part = new cvf::Part; + part->setName( "RivWellDiskPartMgr: injector " + cvfqt::Utils::toString( well->name() ) ); + part->setDrawable( injectorGeo.p() ); + + part->setEffect( eff.p() ); + part->setSourceInfo( sourceInfo.p() ); + + m_wellDiskInjectorPart = part; + } + } + + bool showTextLabels = simWellInViewCollection()->showWellDiskQuantityLables() || + ( well->showWellLabel() && well->showWellDisks() && !well->name().isEmpty() ); + + if ( showTextLabels ) + { + cvf::Font* font = RiaGuiApplication::instance()->defaultWellLabelFont(); + + cvf::ref drawableText = new cvf::DrawableText; + drawableText->setFont( font ); + drawableText->setCheckPosVisible( false ); + drawableText->setDrawBorder( false ); + + drawableText->setDrawBackground( simWellInViewCollection()->showWellDiskLabelBackground() ); + drawableText->setVerticalAlignment( cvf::TextDrawer::CENTER ); + + auto textColor = simWellInViewCollection()->wellLabelColor(); + drawableText->setTextColor( textColor ); + + auto bgColor = RiaColorTools::contrastColor( textColor ); + drawableText->setBackgroundColor( bgColor ); + + cvf::String cvfString = cvfqt::Utils::toString( labelText ); + + cvf::Vec3f textCoord( textPosition ); + drawableText->addText( cvfString, textCoord ); + + if ( simWellInViewCollection()->showWellDiskQuantityLables() ) + { + for ( const auto& t : labelsWithPosition ) + { + drawableText->addText( t.first, t.second ); + } + } + + cvf::ref part = new cvf::Part; + part->setName( "RivWellDiskPartMgr: text " + cvfString ); + part->setDrawable( drawableText.p() ); + + cvf::ref eff = new cvf::Effect; + + part->setEffect( eff.p() ); + part->setPriority( RivPartPriority::PartType::Text ); + + m_wellDiskLabelPart = part; + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::pair RivWellDiskPartMgr::createTextAndLocation( const double aggregatedFraction, + cvf::Vec3d diskPosition, + double ijScaleFactor, + const double fraction, + int precision ) +{ + double sinA = cvf::Math::sin( aggregatedFraction * 2.0 * cvf::PI_F ); + double cosA = cvf::Math::cos( aggregatedFraction * 2.0 * cvf::PI_F ); + + cvf::Vec3f v = cvf::Vec3f( diskPosition ); + + double radius = 2.5f * ( m_rimWell->diskScale() ); + radius *= ijScaleFactor; + radius *= 1.1; // Put label outside the disk + + v.x() = v.x() + static_cast( -sinA * radius ); + v.y() = v.y() + static_cast( cosA * radius ); + + auto s = QString::number( fraction, 'g', precision ); + cvf::String text = cvf::String( s.toStdString() ); + + auto p = std::make_pair( text, v ); + + return p; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RivWellDiskPartMgr::clearAllGeometry() +{ + m_wellDiskPart = nullptr; + m_wellDiskLabelPart = nullptr; + m_wellDiskInjectorPart = nullptr; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RivWellDiskPartMgr::appendDynamicGeometryPartsToModel( cvf::ModelBasicList* model, + size_t frameIndex, + const caf::DisplayCoordTransform* displayXf ) +{ + if ( m_rimWell.isNull() ) return; + if ( !viewWithSettings() ) return; + + if ( !m_rimWell->isWellPipeVisible( frameIndex ) ) return; + if ( !m_rimWell->isValidDisk() ) return; + + buildWellDiskParts( frameIndex, displayXf ); + + if ( m_rimWell->showWellDisks() && m_wellDiskLabelPart.notNull() ) + { + model->addPart( m_wellDiskLabelPart.p() ); + } + + if ( m_rimWell->showWellDisks() && m_wellDiskPart.notNull() ) + { + model->addPart( m_wellDiskPart.p() ); + } + + if ( m_rimWell->showWellDisks() && m_wellDiskInjectorPart.notNull() ) + { + model->addPart( m_wellDiskInjectorPart.p() ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +Rim3dView* RivWellDiskPartMgr::viewWithSettings() +{ + Rim3dView* view = nullptr; + if ( m_rimWell ) m_rimWell->firstAncestorOrThisOfType( view ); + + return view; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimSimWellInViewCollection* RivWellDiskPartMgr::simWellInViewCollection() +{ + RimSimWellInViewCollection* wellCollection = nullptr; + if ( m_rimWell ) m_rimWell->firstAncestorOrThisOfType( wellCollection ); + + return wellCollection; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +cvf::Color4f RivWellDiskPartMgr::getWellInjectionColor( RigWellResultFrame::WellProductionType productionType ) +{ + if ( productionType == RigWellResultFrame::OIL_INJECTOR ) + { + return cvf::Color4f( cvf::Color3::ORANGE ); + } + else if ( productionType == RigWellResultFrame::GAS_INJECTOR ) + { + return cvf::Color4f( cvf::Color3::RED ); + } + else if ( productionType == RigWellResultFrame::WATER_INJECTOR ) + { + return cvf::Color4f( cvf::Color3::BLUE ); + } + + return cvf::Color4f( cvf::Color3::BLACK ); +} diff --git a/ApplicationCode/ModelVisualization/RivWellDiskPartMgr.h b/ApplicationCode/ModelVisualization/RivWellDiskPartMgr.h new file mode 100644 index 0000000000..9a407557d7 --- /dev/null +++ b/ApplicationCode/ModelVisualization/RivWellDiskPartMgr.h @@ -0,0 +1,81 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2019 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "RigWellResultPoint.h" + +#include "cafPdmPointer.h" + +#include "cvfObject.h" +#include "cvfString.h" + +namespace cvf +{ +class Part; +class ModelBasicList; +class Effect; +class ShaderProgram; +class Color4f; +} // namespace cvf + +namespace caf +{ +class DisplayCoordTransform; +} + +class Rim3dView; +class RimSimWellInView; +class RimSimWellInViewCollection; + +class RivWellDiskPartMgr : public cvf::Object +{ +public: + RivWellDiskPartMgr( RimSimWellInView* well ); + ~RivWellDiskPartMgr() override; + + void appendDynamicGeometryPartsToModel( cvf::ModelBasicList* model, + size_t frameIndex, + const caf::DisplayCoordTransform* displayXf ); + +private: + void buildWellDiskParts( size_t frameIndex, const caf::DisplayCoordTransform* displayXf ); + + std::pair createTextAndLocation( const double aggregatedFraction, + cvf::Vec3d diskPosition, + double ijScaleFactor, + const double fraction, + int precision ); + + void clearAllGeometry(); + Rim3dView* viewWithSettings(); + RimSimWellInViewCollection* simWellInViewCollection(); + + static cvf::Color4f getWellInjectionColor( RigWellResultFrame::WellProductionType productionType ); + +private: + caf::PdmPointer m_rimWell; + + cvf::ref m_wellDiskPart; + cvf::ref m_wellDiskLabelPart; + cvf::ref m_wellDiskInjectorPart; + + cvf::ref m_shaderProg; + cvf::ref m_fixedFuncEffect; + cvf::ref m_shaderEffect; +}; diff --git a/ApplicationCode/ModelVisualization/RivWellHeadPartMgr.cpp b/ApplicationCode/ModelVisualization/RivWellHeadPartMgr.cpp index 76a4eed3ec..e0a2912f59 100644 --- a/ApplicationCode/ModelVisualization/RivWellHeadPartMgr.cpp +++ b/ApplicationCode/ModelVisualization/RivWellHeadPartMgr.cpp @@ -287,7 +287,9 @@ void RivWellHeadPartMgr::buildWellHeadParts( size_t f m_wellHeadArrowPart = part; } - if ( well->showWellLabel() && !well->name().isEmpty() ) + // Show labels for well heads only when well disks are disabled: + // well disk labels are prefered since they have more info. + if ( well->showWellLabel() && !well->name().isEmpty() && !well->showWellDisks() ) { cvf::Font* font = RiaGuiApplication::instance()->defaultWellLabelFont(); diff --git a/ApplicationCode/ProjectDataModel/CMakeLists_files.cmake b/ApplicationCode/ProjectDataModel/CMakeLists_files.cmake index 1b8174301c..061a931b4f 100644 --- a/ApplicationCode/ProjectDataModel/CMakeLists_files.cmake +++ b/ApplicationCode/ProjectDataModel/CMakeLists_files.cmake @@ -23,6 +23,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RimEclipseCellColors.h ${CMAKE_CURRENT_LIST_DIR}/RimCellEdgeColors.h ${CMAKE_CURRENT_LIST_DIR}/RimSimWellInView.h ${CMAKE_CURRENT_LIST_DIR}/RimSimWellInViewCollection.h +${CMAKE_CURRENT_LIST_DIR}/RimSimWellInViewTools.h ${CMAKE_CURRENT_LIST_DIR}/RimWellPath.h ${CMAKE_CURRENT_LIST_DIR}/RimFileWellPath.h ${CMAKE_CURRENT_LIST_DIR}/RimModeledWellPath.h @@ -149,6 +150,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RimWellMeasurementCurve.h ${CMAKE_CURRENT_LIST_DIR}/RimWellMeasurementFilter.h ${CMAKE_CURRENT_LIST_DIR}/RimWellMeasurementInViewCollection.h ${CMAKE_CURRENT_LIST_DIR}/RimWellMeasurementInView.h +${CMAKE_CURRENT_LIST_DIR}/RimWellDiskConfig.h ) @@ -176,6 +178,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RimEclipseCellColors.cpp ${CMAKE_CURRENT_LIST_DIR}/RimCellEdgeColors.cpp ${CMAKE_CURRENT_LIST_DIR}/RimSimWellInView.cpp ${CMAKE_CURRENT_LIST_DIR}/RimSimWellInViewCollection.cpp +${CMAKE_CURRENT_LIST_DIR}/RimSimWellInViewTools.cpp ${CMAKE_CURRENT_LIST_DIR}/RimWellPath.cpp ${CMAKE_CURRENT_LIST_DIR}/RimFileWellPath.cpp ${CMAKE_CURRENT_LIST_DIR}/RimModeledWellPath.cpp @@ -302,6 +305,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RimWellMeasurementCurve.cpp ${CMAKE_CURRENT_LIST_DIR}/RimWellMeasurementFilter.cpp ${CMAKE_CURRENT_LIST_DIR}/RimWellMeasurementInViewCollection.cpp ${CMAKE_CURRENT_LIST_DIR}/RimWellMeasurementInView.cpp +${CMAKE_CURRENT_LIST_DIR}/RimWellDiskConfig.cpp ) list(APPEND CODE_HEADER_FILES diff --git a/ApplicationCode/ProjectDataModel/Rim3dOverlayInfoConfig.cpp b/ApplicationCode/ProjectDataModel/Rim3dOverlayInfoConfig.cpp index b122f29549..0cc92b3fdd 100644 --- a/ApplicationCode/ProjectDataModel/Rim3dOverlayInfoConfig.cpp +++ b/ApplicationCode/ProjectDataModel/Rim3dOverlayInfoConfig.cpp @@ -716,6 +716,12 @@ QString Rim3dOverlayInfoConfig::resultInfoText( const HistogramData& histData, infoText += QString( "%1
" ).arg( diffResString ); } + const RimSimWellInViewCollection* wellCollection = eclipseView->wellCollection(); + if ( wellCollection && wellCollection->isActive() && wellCollection->isWellDisksVisible() ) + { + infoText += QString( "Well Disk Property: %1
" ).arg( wellCollection->wellDiskPropertyUiText() ); + } + if ( eclipseView->cellResult()->hasDualPorFractureResult() ) { QString porosityModelText = caf::AppEnum::uiText( diff --git a/ApplicationCode/ProjectDataModel/RimEclipseView.cpp b/ApplicationCode/ProjectDataModel/RimEclipseView.cpp index 6072791393..e4ec47c27e 100644 --- a/ApplicationCode/ProjectDataModel/RimEclipseView.cpp +++ b/ApplicationCode/ProjectDataModel/RimEclipseView.cpp @@ -633,6 +633,8 @@ void RimEclipseView::onUpdateDisplayModelForCurrentTimeStep() m_overlayInfoConfig()->update3DInfo(); + wellCollection()->updateWellDisks(); + // Invisible Wells are marked as read only when "show wells intersecting visible cells" is enabled // Visibility of wells differ betweeen time steps, so trigger a rebuild of tree state items wellCollection()->updateConnectedEditors(); @@ -922,6 +924,8 @@ void RimEclipseView::onLoadDataAndUpdate() this->faultCollection()->syncronizeFaults(); + this->m_wellCollection->updateWellDisks(); + scheduleReservoirGridGeometryRegen(); m_simWellsPartManager->clearGeometryCache(); diff --git a/ApplicationCode/ProjectDataModel/RimSimWellInView.cpp b/ApplicationCode/ProjectDataModel/RimSimWellInView.cpp index 95fe17094f..a4e83258ef 100644 --- a/ApplicationCode/ProjectDataModel/RimSimWellInView.cpp +++ b/ApplicationCode/ProjectDataModel/RimSimWellInView.cpp @@ -37,6 +37,8 @@ #include "RimSimWellFracture.h" #include "RimSimWellFractureCollection.h" #include "RimSimWellInViewCollection.h" +#include "RimSimWellInViewTools.h" +#include "RimWellDiskConfig.h" #include "RiuMainWindow.h" @@ -67,6 +69,7 @@ RimSimWellInView::RimSimWellInView() CAF_PDM_InitField( &showWellHead, "ShowWellHead", true, "Well Head", "", "", "" ); CAF_PDM_InitField( &showWellPipe, "ShowWellPipe", true, "Pipe", "", "", "" ); CAF_PDM_InitField( &showWellSpheres, "ShowWellSpheres", false, "Spheres", "", "", "" ); + CAF_PDM_InitField( &showWellDisks, "ShowWellDisks", false, "Disks", "", "", "" ); CAF_PDM_InitField( &wellHeadScaleFactor, "WellHeadScaleFactor", 1.0, "Well Head Scale", "", "", "" ); CAF_PDM_InitField( &pipeScaleFactor, "WellPipeRadiusScale", 1.0, "Pipe Radius Scale", "", "", "" ); @@ -113,7 +116,7 @@ void RimSimWellInView::fieldChangedByUi( const caf::PdmFieldHandle* changedField if ( reservoirView ) { if ( &showWellLabel == changedField || &showWellHead == changedField || &showWellPipe == changedField || - &showWellSpheres == changedField || &wellPipeColor == changedField ) + &showWellSpheres == changedField || &showWellDisks == changedField || &wellPipeColor == changedField ) { reservoirView->scheduleCreateDisplayModelAndRedraw(); schedule2dIntersectionViewUpdate(); @@ -401,6 +404,7 @@ void RimSimWellInView::defineUiOrdering( QString uiConfigName, caf::PdmUiOrderin appearanceGroup->add( &showWellHead ); appearanceGroup->add( &showWellPipe ); appearanceGroup->add( &showWellSpheres ); + appearanceGroup->add( &showWellDisks ); caf::PdmUiGroup* filterGroup = uiOrdering.addNewGroup( "Well Cells and Fence" ); filterGroup->add( &showWellCells ); @@ -580,6 +584,14 @@ bool RimSimWellInView::isUsingCellCenterForPipe() const return ( wellColl && wellColl->wellPipeCoordType() == RimSimWellInViewCollection::WELLPIPE_CELLCENTER ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RigWellDiskData RimSimWellInView::wellDiskData() const +{ + return m_wellDiskData; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -613,6 +625,128 @@ size_t RimSimWellInView::resultWellIndex() const return m_resultWellIndex; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +double RimSimWellInView::calculateInjectionProductionFractions( const RimWellDiskConfig& wellDiskConfig, bool* isOk ) +{ + const RimEclipseView* reservoirView = nullptr; + this->firstAncestorOrThisOfType( reservoirView ); + if ( !reservoirView ) return false; + + size_t timeStep = static_cast( reservoirView->currentTimeStep() ); + std::vector caseTimeSteps = reservoirView->eclipseCase()->timeStepDates(); + QDateTime currentDate; + if ( timeStep < caseTimeSteps.size() ) + { + currentDate = caseTimeSteps[timeStep]; + } + else + { + currentDate = caseTimeSteps.back(); + } + + RimGridSummaryCase* gridSummaryCase = RimSimWellInViewTools::gridSummaryCaseForWell( this ); + + if ( wellDiskConfig.isSingleProperty() ) + { + double singleProperty = RimSimWellInViewTools::extractValueForTimeStep( gridSummaryCase, + name(), + wellDiskConfig.getSingleProperty(), + currentDate, + isOk ); + if ( !( *isOk ) ) + { + m_isValidDisk = false; + return -1.0; + } + + m_wellDiskData.setSinglePropertyValue( singleProperty ); + } + else + { + m_isInjector = RimSimWellInViewTools::gridSummaryCaseForWell( this ); + + double oil = RimSimWellInViewTools::extractValueForTimeStep( gridSummaryCase, + name(), + wellDiskConfig.getOilProperty(), + currentDate, + isOk ); + if ( !( *isOk ) ) + { + m_isValidDisk = false; + return -1.0; + } + + double gas = RimSimWellInViewTools::extractValueForTimeStep( gridSummaryCase, + name(), + wellDiskConfig.getGasProperty(), + currentDate, + isOk ) / + 1000.0; + if ( !( *isOk ) ) + { + m_isValidDisk = false; + return -1.0; + } + + double water = RimSimWellInViewTools::extractValueForTimeStep( gridSummaryCase, + name(), + wellDiskConfig.getWaterProperty(), + currentDate, + isOk ); + if ( !( *isOk ) ) + { + m_isValidDisk = false; + return -1.0; + } + + m_wellDiskData.setOilGasWater( oil, gas, water ); + } + + m_isValidDisk = true; + + return m_wellDiskData.total(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSimWellInView::scaleDisk( double minValue, double maxValue ) +{ + if ( m_isValidDisk ) + { + m_diskScale = 1.0 + ( m_wellDiskData.total() - minValue ) / ( maxValue - minValue ); + } + else + { + m_diskScale = 1.0; + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimSimWellInView::isValidDisk() const +{ + return m_isValidDisk; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +double RimSimWellInView::diskScale() const +{ + if ( m_isValidDisk ) + { + return m_diskScale; + } + else + { + return 1.0; + } +} + //-------------------------------------------------------------------------------------------------- /// Internal functions //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/ProjectDataModel/RimSimWellInView.h b/ApplicationCode/ProjectDataModel/RimSimWellInView.h index 92f91ede08..bf73b9db89 100644 --- a/ApplicationCode/ProjectDataModel/RimSimWellInView.h +++ b/ApplicationCode/ProjectDataModel/RimSimWellInView.h @@ -20,6 +20,8 @@ #pragma once +#include "RigWellDiskData.h" + #include "cafAppEnum.h" #include "cafPdmChildField.h" #include "cafPdmField.h" @@ -38,6 +40,7 @@ struct RigWellResultPoint; class RimSimWellFractureCollection; class RigWellPath; +class RimWellDiskConfig; //================================================================================================== /// @@ -61,6 +64,10 @@ public: bool isWellSpheresVisible( size_t frameIndex ) const; bool isUsingCellCenterForPipe() const; + RigWellDiskData wellDiskData() const; + bool isValidDisk() const; + double diskScale() const; + caf::PdmFieldHandle* userDescriptionField() override; caf::PdmFieldHandle* objectToggleField() override; @@ -83,6 +90,7 @@ public: caf::PdmField showWellHead; caf::PdmField showWellPipe; caf::PdmField showWellSpheres; + caf::PdmField showWellDisks; caf::PdmField wellHeadScaleFactor; caf::PdmField pipeScaleFactor; @@ -94,6 +102,9 @@ public: caf::PdmChildField simwellFractureCollection; + double calculateInjectionProductionFractions( const RimWellDiskConfig& wellDiskConfig, bool* isOk ); + void scaleDisk( double minValue, double maxValue ); + protected: void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, @@ -110,4 +121,9 @@ private: private: cvf::ref m_simWellData; size_t m_resultWellIndex; + bool m_isInjector; + + RigWellDiskData m_wellDiskData; + bool m_isValidDisk; + double m_diskScale; }; diff --git a/ApplicationCode/ProjectDataModel/RimSimWellInViewCollection.cpp b/ApplicationCode/ProjectDataModel/RimSimWellInViewCollection.cpp index a9230ac866..2d1cbdba89 100644 --- a/ApplicationCode/ProjectDataModel/RimSimWellInViewCollection.cpp +++ b/ApplicationCode/ProjectDataModel/RimSimWellInViewCollection.cpp @@ -32,19 +32,28 @@ #include "RimEclipseContourMapView.h" #include "RimEclipseResultCase.h" #include "RimEclipseView.h" +#include "RimGridSummaryCase.h" #include "RimIntersectionCollection.h" #include "RimProject.h" #include "RimSimWellFractureCollection.h" #include "RimSimWellInView.h" +#include "RimSimWellInViewTools.h" #include "RimWellAllocationPlot.h" +#include "RimWellDiskConfig.h" + +#include "RifSummaryReaderInterface.h" #include "RiuMainWindow.h" +#include "RiuSummaryQuantityNameInfoProvider.h" #include "RivReservoirViewPartMgr.h" #include "cafPdmUiCheckBoxTristateEditor.h" +#include "cafPdmUiListEditor.h" #include "cafPdmUiPushButtonEditor.h" +#include + namespace caf { // OBSOLETE enum @@ -117,6 +126,30 @@ void RimSimWellInViewCollection::WellPipeColorsEnum::setUp() } } // namespace caf +namespace caf +{ +template <> +void AppEnum::setUp() +{ + addItem( RimSimWellInViewCollection::PROPERTY_TYPE_PREDEFINED, " PROPERTY_TYPE_PREDEFINED", "Predefined" ); + addItem( RimSimWellInViewCollection::PROPERTY_TYPE_SINGLE, "ANY_SINGLE_PROPERTY", "Single Property" ); + setDefault( RimSimWellInViewCollection::PROPERTY_TYPE_PREDEFINED ); +} +} // namespace caf + +namespace caf +{ +template <> +void AppEnum::setUp() +{ + addItem( RimSimWellInViewCollection::PRODUCTION_RATES, "PRODUCTION_RATES", "Production Rates" ); + addItem( RimSimWellInViewCollection::INJECTION_RATES, "INJECTION_RATES", "Injection Rates" ); + addItem( RimSimWellInViewCollection::CUMULATIVE_PRODUCTION_RATES, "CUMULATIVE_PRODUCTION_RATES", "Production Total" ); + addItem( RimSimWellInViewCollection::CUMULATIVE_INJECTION_RATES, "CUMULATIVE_INJECTION_RATES", "Injection Total" ); + setDefault( RimSimWellInViewCollection::PRODUCTION_RATES ); +} +} // namespace caf + CAF_PDM_SOURCE_INIT( RimSimWellInViewCollection, "Wells" ); //-------------------------------------------------------------------------------------------------- @@ -146,6 +179,7 @@ RimSimWellInViewCollection::RimSimWellInViewCollection() CAF_PDM_InitFieldNoDefault( &m_showWellLabel, "ShowWellLabelTristate", "Label", "", "", "" ); CAF_PDM_InitFieldNoDefault( &m_showWellPipe, "ShowWellPipe", "Pipe", "", "", "" ); CAF_PDM_InitFieldNoDefault( &m_showWellSpheres, "ShowWellSpheres", "Spheres", "", "", "" ); + CAF_PDM_InitFieldNoDefault( &m_showWellDisks, "ShowWellDisks", "Disks", "", "", "" ); m_showWellHead.uiCapability()->setUiEditorTypeName( caf::PdmUiCheckBoxTristateEditor::uiEditorTypeName() ); m_showWellHead.xmlCapability()->disableIO(); @@ -159,6 +193,9 @@ RimSimWellInViewCollection::RimSimWellInViewCollection() m_showWellSpheres.uiCapability()->setUiEditorTypeName( caf::PdmUiCheckBoxTristateEditor::uiEditorTypeName() ); m_showWellSpheres.xmlCapability()->disableIO(); + m_showWellDisks.uiCapability()->setUiEditorTypeName( caf::PdmUiCheckBoxTristateEditor::uiEditorTypeName() ); + m_showWellDisks.xmlCapability()->disableIO(); + // Scaling CAF_PDM_InitField( &wellHeadScaleFactor, "WellHeadScale", 1.0, "Well Head Scale", "", "", "" ); CAF_PDM_InitField( &pipeScaleFactor, "WellPipeRadiusScale", 0.1, "Pipe Radius Scale ", "", "", "" ); @@ -220,6 +257,27 @@ RimSimWellInViewCollection::RimSimWellInViewCollection() m_showWellCellFence.uiCapability()->setUiEditorTypeName( caf::PdmUiCheckBoxTristateEditor::uiEditorTypeName() ); m_showWellCellFence.xmlCapability()->disableIO(); + CAF_PDM_InitField( &m_wellDiskQuantity, "WellDiskQuantity", QString( "WOPT" ), "Disk Quantity", "", "", "" ); + m_wellDiskQuantity.uiCapability()->setUiEditorTypeName( caf::PdmUiListEditor::uiEditorTypeName() ); + m_wellDiskQuantity.uiCapability()->setAutoAddingOptionFromValue( false ); + + CAF_PDM_InitFieldNoDefault( &m_wellDiskPropertyType, "WellDiskPropertyType", "Property Type", "", "", "" ); + CAF_PDM_InitFieldNoDefault( &m_wellDiskPropertyConfigType, + "WellDiskPropertyConfigType", + "Property Config Type", + "", + "", + "" ); + + CAF_PDM_InitField( &m_wellDiskShowQuantityLabels, "WellDiskShowQuantityLabels", true, "Show Quantity Labels", "", "", "" ); + CAF_PDM_InitField( &m_wellDiskshowLabelsBackground, + "WellDiskShowLabelsBackground", + false, + "Show Label Background", + "", + "", + "" ); + CAF_PDM_InitField( &obsoleteField_wellPipeVisibility, "GlobalWellPipeVisibility", WellVisibilityEnum( PIPES_INDIVIDUALLY ), @@ -405,6 +463,15 @@ void RimSimWellInViewCollection::fieldChangedByUi( const caf::PdmFieldHandle* ch } } + if ( &m_showWellDisks == changedField ) + { + for ( RimSimWellInView* w : wells ) + { + w->showWellDisks = !( m_showWellDisks().isFalse() ); + w->updateConnectedEditors(); + } + } + if ( &m_showWellCells == changedField ) { for ( RimSimWellInView* w : wells ) @@ -435,8 +502,16 @@ void RimSimWellInViewCollection::fieldChangedByUi( const caf::PdmFieldHandle* ch { m_reservoirView->scheduleCreateDisplayModelAndRedraw(); } + else if ( &m_wellDiskQuantity == changedField || &m_wellDiskPropertyType == changedField || + &m_wellDiskPropertyConfigType == changedField || &m_wellDiskshowLabelsBackground == changedField || + &m_wellDiskShowQuantityLabels == changedField ) + { + RimWellDiskConfig wellDiskConfig = getActiveWellDiskConfig(); + updateWellDisks( wellDiskConfig ); + m_reservoirView->updateDisplayModelForCurrentTimeStepAndRedraw(); + } else if ( &spheresScaleFactor == changedField || &m_showWellSpheres == changedField || - &showConnectionStatusColors == changedField ) + &m_showWellDisks == changedField || &showConnectionStatusColors == changedField ) { m_reservoirView->scheduleSimWellGeometryRegen(); m_reservoirView->scheduleCreateDisplayModelAndRedraw(); @@ -505,6 +580,64 @@ void RimSimWellInViewCollection::fieldChangedByUi( const caf::PdmFieldHandle* ch } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QList + RimSimWellInViewCollection::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions, + bool* useOptionsOnly ) +{ + if ( fieldNeedingOptions == &m_wellDiskQuantity ) + { + QList options; + if ( !wells.empty() ) + { + // Assume that the wells share the grid summary case + RimGridSummaryCase* summaryCase = RimSimWellInViewTools::gridSummaryCaseForWell( wells[0] ); + + std::set summaries; + if ( summaryCase ) + { + auto addresses = summaryCase->summaryReader()->allResultAddresses(); + for ( auto addr : addresses ) + { + if ( addr.category() == RifEclipseSummaryAddress::SUMMARY_WELL ) + { + summaries.insert( addr.quantityName() ); + } + } + } + + for ( const auto& itemName : summaries ) + { + QString displayName; + + std::string longVectorName = RiuSummaryQuantityNameInfoProvider::instance()->longNameFromQuantityName( + itemName ); + + if ( longVectorName.empty() ) + { + displayName = QString::fromStdString( itemName ); + } + else + { + displayName = QString::fromStdString( longVectorName ); + displayName += QString( " (%1)" ).arg( QString::fromStdString( itemName ) ); + } + + auto optionItem = caf::PdmOptionItemInfo( displayName, QString::fromStdString( itemName ) ); + options.push_back( optionItem ); + } + } + + if ( useOptionsOnly ) *useOptionsOnly = true; + + return options; + } + + return QList(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -567,6 +700,7 @@ void RimSimWellInViewCollection::defineUiOrdering( QString uiConfigName, caf::Pd appearanceGroup->add( &m_showWellHead ); appearanceGroup->add( &m_showWellPipe ); appearanceGroup->add( &m_showWellSpheres ); + appearanceGroup->add( &m_showWellDisks ); appearanceGroup->add( &m_showWellCommunicationLines ); if ( !isContourMap ) @@ -604,6 +738,19 @@ void RimSimWellInViewCollection::defineUiOrdering( QString uiConfigName, caf::Pd advancedGroup->add( &wellHeadPosition ); } + caf::PdmUiGroup* wellDiskGroup = uiOrdering.addNewGroup( "Well Disk" ); + wellDiskGroup->add( &m_wellDiskPropertyType ); + if ( m_wellDiskPropertyType() == PROPERTY_TYPE_PREDEFINED ) + { + wellDiskGroup->add( &m_wellDiskPropertyConfigType ); + } + else + { + wellDiskGroup->add( &m_wellDiskQuantity ); + } + wellDiskGroup->add( &m_wellDiskShowQuantityLabels ); + wellDiskGroup->add( &m_wellDiskshowLabelsBackground ); + RimEclipseResultCase* ownerCase = nullptr; firstAncestorOrThisOfType( ownerCase ); if ( ownerCase ) @@ -626,6 +773,7 @@ void RimSimWellInViewCollection::updateStateForVisibilityCheckboxes() size_t showWellHeadCount = 0; size_t showPipeCount = 0; size_t showSphereCount = 0; + size_t showDiskCount = 0; size_t showWellCellsCount = 0; size_t showWellCellFenceCount = 0; @@ -635,6 +783,7 @@ void RimSimWellInViewCollection::updateStateForVisibilityCheckboxes() if ( w->showWellHead() ) showWellHeadCount++; if ( w->showWellPipe() ) showPipeCount++; if ( w->showWellSpheres() ) showSphereCount++; + if ( w->showWellDisks() ) showDiskCount++; if ( w->showWellCells() ) showWellCellsCount++; if ( w->showWellCellFence() ) showWellCellFenceCount++; } @@ -643,6 +792,7 @@ void RimSimWellInViewCollection::updateStateForVisibilityCheckboxes() updateStateFromEnabledChildCount( showWellHeadCount, &m_showWellHead ); updateStateFromEnabledChildCount( showPipeCount, &m_showWellPipe ); updateStateFromEnabledChildCount( showSphereCount, &m_showWellSpheres ); + updateStateFromEnabledChildCount( showDiskCount, &m_showWellDisks ); updateStateFromEnabledChildCount( showWellCellsCount, &m_showWellCells ); updateStateFromEnabledChildCount( showWellCellFenceCount, &m_showWellCellFence ); } @@ -805,3 +955,119 @@ void RimSimWellInViewCollection::sortWellsByName() { std::sort( wells.begin(), wells.end(), lessEclipseWell ); } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimSimWellInViewCollection::wellDiskPropertyUiText() const +{ + if ( m_wellDiskPropertyType() == RimSimWellInViewCollection::PROPERTY_TYPE_PREDEFINED ) + { + return m_wellDiskPropertyConfigType().uiText(); + } + + return m_wellDiskQuantity; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimSimWellInViewCollection::isWellDisksVisible() const +{ + return m_showWellDisks.v().isTrue() || m_showWellDisks.v().isPartiallyTrue(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimSimWellInViewCollection::showWellDiskLabelBackground() const +{ + return m_wellDiskshowLabelsBackground(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimSimWellInViewCollection::showWellDiskQuantityLables() const +{ + return m_wellDiskShowQuantityLabels(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSimWellInViewCollection::updateWellDisks() +{ + RimWellDiskConfig wellDiskConfig = getActiveWellDiskConfig(); + updateWellDisks( wellDiskConfig ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSimWellInViewCollection::updateWellDisks( const RimWellDiskConfig& wellDiskConfig ) +{ + double minValue = std::numeric_limits::max(); + double maxValue = -minValue; + for ( RimSimWellInView* w : wells ) + { + bool isOk = true; + double value = w->calculateInjectionProductionFractions( wellDiskConfig, &isOk ); + if ( isOk ) + { + minValue = std::min( minValue, value ); + maxValue = std::max( maxValue, value ); + } + } + + if ( maxValue > minValue ) + { + for ( RimSimWellInView* w : wells ) + { + w->scaleDisk( minValue, maxValue ); + } + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimWellDiskConfig RimSimWellInViewCollection::getActiveWellDiskConfig() const +{ + RimWellDiskConfig wellDiskConfig; + if ( m_wellDiskPropertyType() == RimSimWellInViewCollection::PROPERTY_TYPE_PREDEFINED ) + { + WellDiskPropertyConfigType configType = m_wellDiskPropertyConfigType(); + + if ( configType == PRODUCTION_RATES ) + { + wellDiskConfig.setOilProperty( "WOPR" ); + wellDiskConfig.setGasProperty( "WGPR" ); + wellDiskConfig.setWaterProperty( "WWPR" ); + } + else if ( configType == INJECTION_RATES ) + { + wellDiskConfig.setOilProperty( "" ); + wellDiskConfig.setGasProperty( "WGIR" ); + wellDiskConfig.setWaterProperty( "WWIR" ); + } + else if ( configType == CUMULATIVE_PRODUCTION_RATES ) + { + wellDiskConfig.setOilProperty( "WOPT" ); + wellDiskConfig.setGasProperty( "WGPT" ); + wellDiskConfig.setWaterProperty( "WWPT" ); + } + else if ( configType == CUMULATIVE_INJECTION_RATES ) + { + wellDiskConfig.setOilProperty( "" ); + wellDiskConfig.setGasProperty( "WGIT" ); + wellDiskConfig.setWaterProperty( "WWIT" ); + } + } + else + { + wellDiskConfig.setSingleProperty( m_wellDiskQuantity.v().toStdString() ); + } + + return wellDiskConfig; +} diff --git a/ApplicationCode/ProjectDataModel/RimSimWellInViewCollection.h b/ApplicationCode/ProjectDataModel/RimSimWellInViewCollection.h index c0b3fa5155..d3c62e98a7 100644 --- a/ApplicationCode/ProjectDataModel/RimSimWellInViewCollection.h +++ b/ApplicationCode/ProjectDataModel/RimSimWellInViewCollection.h @@ -30,6 +30,7 @@ class RimEclipseView; class RimSimWellInView; +class RimWellDiskConfig; //================================================================================================== /// @@ -91,6 +92,20 @@ public: WELLPIPE_COLOR_UNIFORM }; + enum WellDiskPropertyType + { + PROPERTY_TYPE_PREDEFINED, + PROPERTY_TYPE_SINGLE + }; + + enum WellDiskPropertyConfigType + { + PRODUCTION_RATES, + INJECTION_RATES, + CUMULATIVE_PRODUCTION_RATES, + CUMULATIVE_INJECTION_RATES + }; + typedef caf::AppEnum WellPipeColorsEnum; caf::PdmField isActive; @@ -121,6 +136,11 @@ public: caf::PdmField isAutoDetectingBranches; + QString wellDiskPropertyUiText() const; + bool isWellDisksVisible() const; + bool showWellDiskLabelBackground() const; + bool showWellDiskQuantityLables() const; + caf::PdmChildArrayField wells; RimSimWellInView* findWell( QString name ); @@ -136,6 +156,8 @@ public: static void updateWellAllocationPlots(); + void updateWellDisks(); + protected: void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, @@ -143,12 +165,17 @@ protected: void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override; + QList calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions, + bool* useOptionsOnly ); + caf::PdmFieldHandle* objectToggleField() override; void initAfterRead() override; private: void calculateWellGeometryVisibility( size_t frameIndex ); void updateStateFromEnabledChildCount( size_t showLabelCount, caf::PdmField* fieldToUpdate ); + void updateWellDisks( const RimWellDiskConfig& wellDiskConfig ); + RimWellDiskConfig getActiveWellDiskConfig() const; private: RimEclipseView* m_reservoirView; @@ -162,11 +189,19 @@ private: caf::PdmField m_showWellHead; caf::PdmField m_showWellPipe; caf::PdmField m_showWellSpheres; + caf::PdmField m_showWellDisks; caf::PdmField m_showWellCells; caf::PdmField m_showWellCellFence; caf::PdmField m_showWellCommunicationLines; + // Well Discs + caf::PdmField> m_wellDiskPropertyType; + caf::PdmField> m_wellDiskPropertyConfigType; + caf::PdmField m_wellDiskQuantity; + caf::PdmField m_wellDiskShowQuantityLabels; + caf::PdmField m_wellDiskshowLabelsBackground; + // Obsolete fields caf::PdmField obsoleteField_wellPipeVisibility; caf::PdmField obsoleteField_wellCellsToRangeFilterMode; diff --git a/ApplicationCode/ProjectDataModel/RimSimWellInViewTools.cpp b/ApplicationCode/ProjectDataModel/RimSimWellInViewTools.cpp new file mode 100644 index 0000000000..4b3973d032 --- /dev/null +++ b/ApplicationCode/ProjectDataModel/RimSimWellInViewTools.cpp @@ -0,0 +1,167 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2019- 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RimSimWellInViewTools.h" + +#include "RiaApplication.h" +#include "RiaSummaryTools.h" +#include "RiaTimeHistoryCurveResampler.h" + +#include "RifEclipseSummaryAddress.h" +#include "RifSummaryReaderInterface.h" + +#include "RigSimWellData.h" + +#include "Rim3dView.h" +#include "RimEclipseResultCase.h" +#include "RimGridSummaryCase.h" +#include "RimOilField.h" +#include "RimProject.h" +#include "RimSimWellInView.h" +#include "RimSimWellInViewCollection.h" +#include "RimSummaryCaseMainCollection.h" + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimGridSummaryCase* RimSimWellInViewTools::gridSummaryCaseForWell( RimSimWellInView* well ) +{ + RimProject* project = RiaApplication::instance()->project(); + if ( !project ) return nullptr; + + RimSummaryCaseMainCollection* sumCaseColl = project->activeOilField() + ? project->activeOilField()->summaryCaseMainCollection() + : nullptr; + if ( !sumCaseColl ) return nullptr; + + RimEclipseResultCase* eclCase = nullptr; + well->firstAncestorOrThisOfType( eclCase ); + if ( eclCase ) + { + RimGridSummaryCase* gridSummaryCase = dynamic_cast( + sumCaseColl->findSummaryCaseFromEclipseResultCase( eclCase ) ); + if ( gridSummaryCase ) + { + return gridSummaryCase; + } + } + + return nullptr; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimSimWellInViewTools::isInjector( RimSimWellInView* well ) +{ + RigSimWellData* wRes = well->simWellData(); + if ( wRes ) + { + Rim3dView* rimView = nullptr; + well->firstAncestorOrThisOfTypeAsserted( rimView ); + + int currentTimeStep = rimView->currentTimeStep(); + + if ( wRes->hasWellResult( currentTimeStep ) ) + { + const RigWellResultFrame& wrf = wRes->wellResultFrame( currentTimeStep ); + + if ( wrf.m_productionType == RigWellResultFrame::OIL_INJECTOR || + wrf.m_productionType == RigWellResultFrame::GAS_INJECTOR || + wrf.m_productionType == RigWellResultFrame::WATER_INJECTOR ) + { + return true; + } + } + } + + return false; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +double RimSimWellInViewTools::extractValueForTimeStep( RimGridSummaryCase* gridSummaryCase, + const QString& wellName, + const std::string& vectorName, + const QDateTime& currentDate, + bool* isOk ) + +{ + if ( vectorName.empty() ) + { + *isOk = true; + return 0.0; + } + + RifEclipseSummaryAddress addr( RifEclipseSummaryAddress::SUMMARY_WELL, + vectorName, + -1, + -1, + "", + wellName.toStdString(), + -1, + "", + -1, + -1, + -1, + -1, + false, + -1 ); + + RifSummaryReaderInterface* reader = gridSummaryCase->summaryReader(); + if ( !gridSummaryCase->summaryReader()->hasAddress( addr ) ) + { + // TODO: better error handling + std::cerr << "ERROR: no address found for well " << wellName.toStdString() << " " << vectorName << std::endl; + *isOk = false; + return 0.0; + } + + std::vector values; + reader->values( addr, &values ); + std::vector timeSteps = reader->timeSteps( addr ); + + RiaTimeHistoryCurveResampler resampler; + resampler.setCurveData( values, timeSteps ); + if ( RiaSummaryTools::hasAccumulatedData( addr ) ) + { + resampler.resampleAndComputePeriodEndValues( DateTimePeriod::DAY ); + } + else + { + resampler.resampleAndComputeWeightedMeanValues( DateTimePeriod::DAY ); + } + + // Find the data point which best matches the selected time step + std::vector resampledTimeSteps = resampler.resampledTimeSteps(); + std::vector resampledValues = resampler.resampledValues(); + for ( unsigned int i = 0; i < resampledTimeSteps.size(); i++ ) + { + QDateTime t = QDateTime::fromTime_t( resampledTimeSteps[i] ); + if ( t > currentDate ) + { + *isOk = true; + return resampledValues[i]; + } + } + + std::cerr << "ERROR: no resampled value found for well " << wellName.toStdString() << " " << vectorName << std::endl; + *isOk = false; + return -1; +} diff --git a/ApplicationCode/ProjectDataModel/RimSimWellInViewTools.h b/ApplicationCode/ProjectDataModel/RimSimWellInViewTools.h new file mode 100644 index 0000000000..c6a4881407 --- /dev/null +++ b/ApplicationCode/ProjectDataModel/RimSimWellInViewTools.h @@ -0,0 +1,44 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2019- 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include + +class QString; +class QDateTime; + +class RimSimWellInView; +class RimGridSummaryCase; + +//================================================================================================== +/// +/// +//================================================================================================== +class RimSimWellInViewTools +{ +public: + static RimGridSummaryCase* gridSummaryCaseForWell( RimSimWellInView* well ); + static bool isInjector( RimSimWellInView* well ); + + static double extractValueForTimeStep( RimGridSummaryCase* gridSummaryCase, + const QString& wellName, + const std::string& vectorName, + const QDateTime& currentDate, + bool* isOk ); +}; diff --git a/ApplicationCode/ProjectDataModel/RimWellDiskConfig.cpp b/ApplicationCode/ProjectDataModel/RimWellDiskConfig.cpp new file mode 100644 index 0000000000..26591a7e5e --- /dev/null +++ b/ApplicationCode/ProjectDataModel/RimWellDiskConfig.cpp @@ -0,0 +1,75 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2019- 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RimWellDiskConfig.h" + +RimWellDiskConfig::RimWellDiskConfig() + : m_isSingleProperty( false ) +{ +} + +RimWellDiskConfig::~RimWellDiskConfig() {} + +bool RimWellDiskConfig::isSingleProperty() const +{ + return m_isSingleProperty; +} + +std::string RimWellDiskConfig::getSingleProperty() const +{ + return m_singleProperty; +} + +void RimWellDiskConfig::setSingleProperty( const std::string& singleProperty ) +{ + m_isSingleProperty = true; + m_singleProperty = singleProperty; +} + +void RimWellDiskConfig::setOilProperty( const std::string& oilProperty ) +{ + m_isSingleProperty = false; + m_oilProperty = oilProperty; +} + +std::string RimWellDiskConfig::getOilProperty() const +{ + return m_oilProperty; +} + +void RimWellDiskConfig::setGasProperty( const std::string& gasProperty ) +{ + m_isSingleProperty = false; + m_gasProperty = gasProperty; +} + +std::string RimWellDiskConfig::getGasProperty() const +{ + return m_gasProperty; +} + +void RimWellDiskConfig::setWaterProperty( const std::string& waterProperty ) +{ + m_isSingleProperty = false; + m_waterProperty = waterProperty; +} + +std::string RimWellDiskConfig::getWaterProperty() const +{ + return m_waterProperty; +} diff --git a/ApplicationCode/ProjectDataModel/RimWellDiskConfig.h b/ApplicationCode/ProjectDataModel/RimWellDiskConfig.h new file mode 100644 index 0000000000..34aa16e633 --- /dev/null +++ b/ApplicationCode/ProjectDataModel/RimWellDiskConfig.h @@ -0,0 +1,52 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2019- 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include + +//================================================================================================== +/// +/// +//================================================================================================== +class RimWellDiskConfig +{ +public: + RimWellDiskConfig(); + ~RimWellDiskConfig(); + + bool isSingleProperty() const; + std::string getSingleProperty() const; + void setSingleProperty( const std::string& singleProperty ); + + void setOilProperty( const std::string& oilProperty ); + std::string getOilProperty() const; + + void setGasProperty( const std::string& gasProperty ); + std::string getGasProperty() const; + + void setWaterProperty( const std::string& waterProperty ); + std::string getWaterProperty() const; + +private: + bool m_isSingleProperty; + std::string m_singleProperty; + std::string m_oilProperty; + std::string m_gasProperty; + std::string m_waterProperty; +}; diff --git a/ApplicationCode/ReservoirDataModel/CMakeLists_files.cmake b/ApplicationCode/ReservoirDataModel/CMakeLists_files.cmake index dc1df6eb5f..4f310adfe0 100644 --- a/ApplicationCode/ReservoirDataModel/CMakeLists_files.cmake +++ b/ApplicationCode/ReservoirDataModel/CMakeLists_files.cmake @@ -77,6 +77,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RigWbsParameter.h ${CMAKE_CURRENT_LIST_DIR}/RigEclipseAllenFaultsStatCalc.h ${CMAKE_CURRENT_LIST_DIR}/RigCellFaceGeometryTools.h ${CMAKE_CURRENT_LIST_DIR}/RigNncConnection.h +${CMAKE_CURRENT_LIST_DIR}/RigWellDiskData.h ) @@ -151,6 +152,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RigWbsParameter.cpp ${CMAKE_CURRENT_LIST_DIR}/RigEclipseAllenFaultsStatCalc.cpp ${CMAKE_CURRENT_LIST_DIR}/RigCellFaceGeometryTools.cpp ${CMAKE_CURRENT_LIST_DIR}/RigNncConnection.cpp +${CMAKE_CURRENT_LIST_DIR}/RigWellDiskData.cpp ) list(APPEND CODE_HEADER_FILES diff --git a/ApplicationCode/ReservoirDataModel/RigWellDiskData.cpp b/ApplicationCode/ReservoirDataModel/RigWellDiskData.cpp new file mode 100644 index 0000000000..77c658d1c9 --- /dev/null +++ b/ApplicationCode/ReservoirDataModel/RigWellDiskData.cpp @@ -0,0 +1,107 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2019- 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RigWellDiskData.h" + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RigWellDiskData::RigWellDiskData() + : m_isSingleProperty( false ) + , m_singlePropertyValue( 0.0 ) + , m_oilValue( 0.0 ) + , m_waterValue( 0.0 ) + , m_gasValue( 0.0 ) +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RigWellDiskData::setSinglePropertyValue( double value ) +{ + m_isSingleProperty = true; + m_singlePropertyValue = value; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RigWellDiskData::setOilGasWater( double oil, double gas, double water ) +{ + m_isSingleProperty = false; + + m_oilValue = oil; + m_gasValue = gas; + m_waterValue = water; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +double RigWellDiskData::total() const +{ + if ( m_isSingleProperty ) + { + return m_singlePropertyValue; + } + else + { + return m_oilValue + m_gasValue + m_waterValue; + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +double RigWellDiskData::oil() const +{ + return m_oilValue; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +double RigWellDiskData::gas() const +{ + return m_gasValue; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +double RigWellDiskData::water() const +{ + return m_waterValue; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +double RigWellDiskData::singlePropertyValue() const +{ + return m_singlePropertyValue; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RigWellDiskData::isSingleProperty() const +{ + return m_isSingleProperty; +} diff --git a/ApplicationCode/ReservoirDataModel/RigWellDiskData.h b/ApplicationCode/ReservoirDataModel/RigWellDiskData.h new file mode 100644 index 0000000000..7282ae568b --- /dev/null +++ b/ApplicationCode/ReservoirDataModel/RigWellDiskData.h @@ -0,0 +1,46 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2019- 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +//================================================================================================== +/// +/// +//================================================================================================== +class RigWellDiskData +{ +public: + RigWellDiskData(); + + void setSinglePropertyValue( double value ); + void setOilGasWater( double oil, double gas, double water ); + + double total() const; + double oil() const; + double gas() const; + double water() const; + double singlePropertyValue() const; + bool isSingleProperty() const; + +private: + bool m_isSingleProperty; + double m_singlePropertyValue; + double m_oilValue; + double m_waterValue; + double m_gasValue; +};