///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2016- 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 "RivWellConnectionsPartMgr.h" #include "RimEclipseResultCase.h" #include "RimEclipseView.h" #include "RimSimWellInView.h" #include "RimSimWellInViewCollection.h" #include "RigActiveCellInfo.h" #include "RigEclipseCaseData.h" #include "RigFlowDiagResults.h" #include "RigMainGrid.h" #include "RigSimWellData.h" #include "RigWellResultFrame.h" #include "cafDisplayCoordTransform.h" #include "cafEffectGenerator.h" #include "cvfDrawableGeo.h" #include "cvfModelBasicList.h" #include "cvfPart.h" #include //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RivWellConnectionsPartMgr::RivWellConnectionsPartMgr( RimEclipseView* reservoirView, RimSimWellInView* well ) { m_rimReservoirView = reservoirView; m_rimWell = well; m_useCurvedArrows = true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RivWellConnectionsPartMgr::~RivWellConnectionsPartMgr() { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RivWellConnectionsPartMgr::appendDynamicGeometryPartsToModel( cvf::ModelBasicList* model, size_t frameIndex ) { if ( m_rimReservoirView.isNull() ) return; if ( !m_rimReservoirView->eclipseCase() ) return; if ( !m_rimWell->showWell() ) return; if ( !m_rimWell->simWellData()->hasWellResult( frameIndex ) ) return; if ( !m_rimWell->simWellData()->wellResultFrame( frameIndex )->isOpen() ) return; if ( m_rimWell->simWellData()->wellResultFrame( frameIndex )->productionType() == RiaDefines::WellProductionType::UNDEFINED_PRODUCTION_TYPE ) return; bool isProducer = ( m_rimWell->simWellData()->wellResultFrame( frameIndex )->productionType() == RiaDefines::WellProductionType::PRODUCER ); double pipeRadius = m_rimWell->pipeRadius(); cvf::Vec3d wellHeadTop; cvf::Vec3d wellHeadBottom; double characteristicCellSize; double mainArrowZHeight; cvf::ref displayCordXf; RigFlowDiagResults* flowResults; std::string injectorName; std::string producerName; std::string crossFlowInjectorName; std::string crossFlowProducerName; double fluxWidthScale = 0.0; { RimEclipseResultCase* eclResCase = dynamic_cast( m_rimReservoirView->eclipseCase() ); if ( !eclResCase ) return; if ( !eclResCase->defaultFlowDiagSolution() ) return; flowResults = eclResCase->defaultFlowDiagSolution()->flowDiagResults(); displayCordXf = m_rimReservoirView->displayCoordTransform(); RigEclipseCaseData* rigReservoir = m_rimReservoirView->eclipseCase()->eclipseCaseData(); characteristicCellSize = rigReservoir->mainGrid()->characteristicIJCellSize(); m_rimWell->wellHeadTopBottomPosition( static_cast( frameIndex ), &wellHeadTop, &wellHeadBottom ); wellHeadTop = displayCordXf->transformToDisplayCoord( wellHeadTop ); wellHeadBottom = displayCordXf->transformToDisplayCoord( wellHeadBottom ); wellHeadTop.z() += characteristicCellSize; cvf::Vec3d activeCellsBoundingBoxMax = displayCordXf->transformToDisplayCoord( m_rimReservoirView->currentActiveCellInfo()->geometryBoundingBox().max() ); mainArrowZHeight = activeCellsBoundingBoxMax.z() + 1.5 * characteristicCellSize; // Above the bbox somewhat; if ( isProducer ) { producerName = m_rimWell->name().toStdString(); crossFlowInjectorName = RimFlowDiagSolution::addCrossFlowEnding( m_rimWell->name() ).toStdString(); } else { injectorName = m_rimWell->name().toStdString(); crossFlowProducerName = RimFlowDiagSolution::addCrossFlowEnding( m_rimWell->name() ).toStdString(); } double maxAbsFlux = flowResults->maxAbsPairFlux( static_cast( frameIndex ) ); if ( maxAbsFlux != 0.0 ) fluxWidthScale = characteristicCellSize / maxAbsFlux; } bool enableLighting = !m_rimReservoirView->isLightingDisabled(); RimSimWellInViewCollection* wellColl = m_rimReservoirView->wellCollection(); // Create potentially two the arrows to/from m_rimWell for each of the other wells in the model. // One arrow for the "official" state of the well, and one to account for cross flow contributions for ( RimSimWellInView* otherWell : wellColl->wells ) { if ( otherWell == m_rimWell ) continue; if ( !otherWell->simWellData()->hasWellResult( frameIndex ) ) continue; if ( !otherWell->simWellData()->wellResultFrame( frameIndex )->isOpen() ) continue; if ( otherWell->simWellData()->wellResultFrame( frameIndex )->productionType() == RiaDefines::WellProductionType::UNDEFINED_PRODUCTION_TYPE ) continue; bool isOtherProducer = ( otherWell->simWellData()->wellResultFrame( frameIndex )->productionType() == RiaDefines::WellProductionType::PRODUCER ); { std::string otherWellName = otherWell->name().toStdString(); std::string otherWellXfName = RimFlowDiagSolution::addCrossFlowEnding( otherWell->name() ).toStdString(); if ( isProducer != isOtherProducer ) { if ( isOtherProducer ) { producerName = otherWellName; crossFlowInjectorName = otherWellXfName; } else { injectorName = otherWellName; crossFlowProducerName = otherWellXfName; } } else { if ( isProducer ) { injectorName = otherWellXfName; crossFlowProducerName = otherWellName; } else { producerName = otherWellXfName; crossFlowInjectorName = otherWellName; } } } std::pair injProdFluxPair = flowResults->injectorProducerPairFluxes( injectorName, producerName, static_cast( frameIndex ) ); std::pair injProdFluxPairXF = flowResults->injectorProducerPairFluxes( crossFlowInjectorName, crossFlowProducerName, static_cast( frameIndex ) ); const double fluxThreshold = 0.0; // Todo : Needs threshold in Gui if ( fabs( injProdFluxPair.first ) <= fluxThreshold && fabs( injProdFluxPair.second ) <= fluxThreshold && fabs( injProdFluxPairXF.first ) <= fluxThreshold && fabs( injProdFluxPairXF.second ) <= fluxThreshold ) continue; float width = fluxWidthScale * ( isProducer ? injProdFluxPair.second : injProdFluxPair.first ); float widthXf = fluxWidthScale * ( !isProducer ? injProdFluxPairXF.second : injProdFluxPairXF.first ); cvf::Vec3d otherWellHeadTop; cvf::Vec3d otherWellHeadBottom; { otherWell->wellHeadTopBottomPosition( static_cast( frameIndex ), &otherWellHeadTop, &otherWellHeadBottom ); otherWellHeadTop = displayCordXf->transformToDisplayCoord( otherWellHeadTop ); otherWellHeadBottom = displayCordXf->transformToDisplayCoord( otherWellHeadBottom ); otherWellHeadTop.z() += characteristicCellSize; } { cvf::Vec3f startPoint = cvf::Vec3f( 0.5 * ( wellHeadTop + otherWellHeadTop ) ); if ( m_useCurvedArrows ) startPoint.z() = mainArrowZHeight; cvf::Vec3f endPoint = cvf::Vec3f( wellHeadTop + ( 3 * pipeRadius * ( otherWellHeadTop - wellHeadTop ).getNormalized() ) ); cvf::Color4f arrowColor( otherWell->wellPipeColor() ); if ( fabs( injProdFluxPair.first ) > fluxThreshold && fabs( injProdFluxPair.second ) > fluxThreshold ) { if ( isProducer == isOtherProducer ) { startPoint.z() -= 0.5 * characteristicCellSize; endPoint.z() -= 0.5 * characteristicCellSize; } cvf::ref arrowPart = createArrowPart( startPoint, endPoint, width, isProducer, arrowColor, enableLighting ); model->addPart( arrowPart.p() ); } if ( fabs( injProdFluxPairXF.first ) > fluxThreshold && fabs( injProdFluxPairXF.second ) > fluxThreshold ) { startPoint.z() -= 0.5 * characteristicCellSize; endPoint.z() -= 0.5 * characteristicCellSize; cvf::ref arrowPart = createArrowPart( startPoint, endPoint, widthXf, !isProducer, arrowColor, enableLighting ); model->addPart( arrowPart.p() ); } } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- cvf::ref RivWellConnectionsPartMgr::createArrowPart( const cvf::Vec3f& startPoint, const cvf::Vec3f& endPoint, float width, bool isProducer, const cvf::Color4f& arrowColor, bool enableLighting ) { cvf::ref part = new cvf::Part; part->setName( "RivWellConnectionsPartMgr::createArrowPart" ); cvf::ref geo = createArrowGeometry( startPoint, endPoint, width, isProducer ); part->setDrawable( geo.p() ); caf::SurfaceEffectGenerator surfaceGen( arrowColor, caf::PO_1 ); surfaceGen.enableLighting( enableLighting ); cvf::ref eff = surfaceGen.generateCachedEffect(); part->setEffect( eff.p() ); return part; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- cvf::ref RivWellConnectionsPartMgr::createArrowGeometry( const cvf::Vec3f& startPoint, const cvf::Vec3f& endPoint, double width, bool useArrowEnd ) { // Vertex layout // _ - _ // __________ - - _ // - Producer end: Injector end // 0 2 4 6 8 10 20 12 14 16 16 // : flat : : : : : : : : end 18 18 // 1 3 5 7 9 11 19 13 15 17 17 static const cvf::uint producerArrowFaceList[8 * 5 + 4] = { 4, 0, 1, 3, 2, 4, 2, 3, 5, 4, 4, 4, 5, 7, 6, 4, 6, 7, 9, 8, 4, 8, 9, 11, 10, 4, 10, 11, 20, 19, 4, 19, 20, 13, 12, 4, 12, 13, 15, 14, 3, 16, 17, 18 }; static const cvf::uint injectorArrowFaceList[8 * 5 + 8] = { 4, 0, 1, 3, 2, 4, 2, 3, 5, 4, 4, 4, 5, 7, 6, 4, 6, 7, 9, 8, 4, 8, 9, 11, 10, 4, 10, 11, 20, 19, 4, 19, 20, 13, 12, 4, 12, 13, 15, 14, 3, 14, 18, 16, 3, 18, 15, 17 }; cvf::Vec3f endPointInTopPlane = endPoint; if ( m_useCurvedArrows ) endPointInTopPlane.z() = startPoint.z(); cvf::Vec3f heightDiff = cvf::Vec3f::ZERO; if ( m_useCurvedArrows ) heightDiff.z() = ( startPoint.z() - endPoint.z() ); cvf::Vec3f fromTo = endPointInTopPlane - startPoint; float length = fromTo.length(); float halfWidth = width * 0.5; cvf::Vec3f widthVector = halfWidth * ( fromTo.getNormalized() ^ -cvf::Vec3f::Z_AXIS ); float heightScale = 0.3 * length * 0.15; cvf::Vec3f heightScaleVec = cvf::Vec3f::ZERO; if ( m_useCurvedArrows ) heightScaleVec.z() = heightScale; float endStart = 0.4f; float endStep = ( 1.0f - endStart ) / 7.5f; cvf::ref arrowVertexArray = new cvf::Vec3fArray; arrowVertexArray->resize( 18 + 3 ); ( *arrowVertexArray )[0] = 0.0f * fromTo + startPoint + widthVector; ( *arrowVertexArray )[1] = 0.0f * fromTo + startPoint - widthVector; ( *arrowVertexArray )[2] = endStart * fromTo + startPoint + widthVector; ( *arrowVertexArray )[3] = endStart * fromTo + startPoint - widthVector; ( *arrowVertexArray )[4] = ( 1 * endStep + endStart ) * fromTo + startPoint + widthVector + 0.250f * heightScaleVec; // 0.0250f * // heightDiff; ( *arrowVertexArray )[5] = ( 1 * endStep + endStart ) * fromTo + startPoint - widthVector + 0.250f * heightScaleVec; // 0.0250f * // heightDiff; ( *arrowVertexArray )[6] = ( 2 * endStep + endStart ) * fromTo + startPoint + widthVector + 0.750f * heightScaleVec; // 0.0750f * // heightDiff; ( *arrowVertexArray )[7] = ( 2 * endStep + endStart ) * fromTo + startPoint - widthVector + 0.750f * heightScaleVec; // 0.0750f * // heightDiff; ( *arrowVertexArray )[8] = ( 3 * endStep + endStart ) * fromTo + startPoint + widthVector + 1.000f * heightScaleVec; // 0.1000f * // heightDiff; ( *arrowVertexArray )[9] = ( 3 * endStep + endStart ) * fromTo + startPoint - widthVector + 1.000f * heightScaleVec; // 0.1000f * // heightDiff; ( *arrowVertexArray )[10] = ( 4 * endStep + endStart ) * fromTo + startPoint + widthVector + 0.875f * heightScaleVec; // 0.0875f * // heightDiff; ( *arrowVertexArray )[11] = ( 4 * endStep + endStart ) * fromTo + startPoint - widthVector + 0.875f * heightScaleVec; // 0.0875f * // heightDiff; ( *arrowVertexArray )[19] = ( 4.7f * endStep + endStart ) * fromTo + startPoint + widthVector + 0.400f * heightScaleVec; // 0.0875f * // heightDiff; ( *arrowVertexArray )[20] = ( 4.7f * endStep + endStart ) * fromTo + startPoint - widthVector + 0.400f * heightScaleVec; // 0.0875f * // heightDiff; ( *arrowVertexArray )[12] = ( 5 * endStep + endStart ) * fromTo + startPoint + widthVector; ( *arrowVertexArray )[13] = ( 5 * endStep + endStart ) * fromTo + startPoint - widthVector; ( *arrowVertexArray )[14] = ( 6 * endStep + endStart ) * fromTo + startPoint + widthVector - 0.5f * heightDiff; ( *arrowVertexArray )[15] = ( 6 * endStep + endStart ) * fromTo + startPoint - widthVector - 0.5f * heightDiff; if ( useArrowEnd ) { ( *arrowVertexArray )[16] = ( 6 * endStep + endStart ) * fromTo + startPoint + 1.6f * widthVector - 0.5f * heightDiff; ( *arrowVertexArray )[17] = ( 6 * endStep + endStart ) * fromTo + startPoint - 1.6f * widthVector - 0.5f * heightDiff; ( *arrowVertexArray )[18] = 1.0f * fromTo + startPoint - 1.0f * heightDiff; } else { ( *arrowVertexArray )[16] = 1.0f * fromTo + startPoint + 0.5f * widthVector - 1.0f * heightDiff; ( *arrowVertexArray )[17] = 1.0f * fromTo + startPoint - 0.5f * widthVector - 1.0f * heightDiff; ( *arrowVertexArray )[18] = ( 6 * endStep + endStart ) * fromTo + startPoint - 0.5f * heightDiff; } cvf::ref geo = new cvf::DrawableGeo; geo->setVertexArray( arrowVertexArray.p() ); if ( useArrowEnd ) geo->setFromFaceList( cvf::UIntArray( producerArrowFaceList, 8 * 5 + 4 ) ); else geo->setFromFaceList( cvf::UIntArray( injectorArrowFaceList, 8 * 5 + 8 ) ); geo->computeNormals(); return geo; }