From 5975fe67654cdf9b58c1b8f5ae6eca78fbda023d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Herje?= <82032112+jorgenherje@users.noreply.github.com> Date: Sat, 27 May 2023 10:30:03 +0200 Subject: [PATCH] Rename RimWellLogDiffCurve to RimWellLogCalculatedCurve and additional improvements * Rename from DiffCurve to CalculatedCurve and add operators selection * Add option to select depth source for resampling in CalculatedCurve - Select source for depth values for resampling - Depths from first curve, second curve or union of these - Added unit tests for function creating union depth values and calculate value with selected operator. * Guard divide by zero * Adjust algorithm for union of depths - Prevent duplicated depth values (no need for enter/exist of k-layer when resampling) - Add threshold for depth distance - Update unit tests --- .../Commands/RicWellLogTools.cpp | 8 +- ApplicationLibCode/Commands/RicWellLogTools.h | 4 +- .../WellLogCommands/CMakeLists_files.cmake | 4 +- ...> RicNewWellLogCalculatedCurveFeature.cpp} | 18 +- ... => RicNewWellLogCalculatedCurveFeature.h} | 2 +- .../RimGeoMechFaultReactivationResult.cpp | 17 +- .../RimContextCommandBuilder.cpp | 4 +- .../WellLog/CMakeLists_files.cmake | 4 +- .../WellLog/RimWellLogCalculatedCurve.cpp | 479 ++++++++++++++++++ ...iffCurve.h => RimWellLogCalculatedCurve.h} | 32 +- .../WellLog/RimWellLogDiffCurve.cpp | 300 ----------- .../UnitTests/CMakeLists_files.cmake | 1 + .../RimWellLogCalculatedCurve-Test.cpp | 95 ++++ 13 files changed, 635 insertions(+), 333 deletions(-) rename ApplicationLibCode/Commands/WellLogCommands/{RicNewWellLogDiffCurveFeature.cpp => RicNewWellLogCalculatedCurveFeature.cpp} (79%) rename ApplicationLibCode/Commands/WellLogCommands/{RicNewWellLogDiffCurveFeature.h => RicNewWellLogCalculatedCurveFeature.h} (94%) create mode 100644 ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogCalculatedCurve.cpp rename ApplicationLibCode/ProjectDataModel/WellLog/{RimWellLogDiffCurve.h => RimWellLogCalculatedCurve.h} (72%) delete mode 100644 ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogDiffCurve.cpp create mode 100644 ApplicationLibCode/UnitTests/RimWellLogCalculatedCurve-Test.cpp diff --git a/ApplicationLibCode/Commands/RicWellLogTools.cpp b/ApplicationLibCode/Commands/RicWellLogTools.cpp index 143fdb74d0..6a6ae2a5af 100644 --- a/ApplicationLibCode/Commands/RicWellLogTools.cpp +++ b/ApplicationLibCode/Commands/RicWellLogTools.cpp @@ -30,8 +30,8 @@ #include "RimProject.h" #include "RimSimWellInView.h" #include "RimSummaryCase.h" +#include "RimWellLogCalculatedCurve.h" #include "RimWellLogCurveCommonDataSource.h" -#include "RimWellLogDiffCurve.h" #include "RimWellLogExtractionCurve.h" #include "RimWellLogFile.h" #include "RimWellLogFileChannel.h" @@ -586,12 +586,12 @@ RimWellMeasurementCurve* RicWellLogTools::addWellMeasurementCurve( RimWellLogTra //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -RimWellLogDiffCurve* RicWellLogTools::addWellLogDiffCurve( RimWellLogTrack* plotTrack, bool showPlotWindow ) +RimWellLogCalculatedCurve* RicWellLogTools::addWellLogCalculatedCurve( RimWellLogTrack* plotTrack, bool showPlotWindow ) { CVF_ASSERT( plotTrack ); - RimWellLogDiffCurve* curve = new RimWellLogDiffCurve(); - const cvf::Color3f curveColor = RicWellLogPlotCurveFeatureImpl::curveColorFromTable( plotTrack->curveCount() ); + RimWellLogCalculatedCurve* curve = new RimWellLogCalculatedCurve(); + const cvf::Color3f curveColor = RicWellLogPlotCurveFeatureImpl::curveColorFromTable( plotTrack->curveCount() ); curve->setColor( curveColor ); plotTrack->addCurve( curve ); diff --git a/ApplicationLibCode/Commands/RicWellLogTools.h b/ApplicationLibCode/Commands/RicWellLogTools.h index 52b7dece77..e07348cfda 100644 --- a/ApplicationLibCode/Commands/RicWellLogTools.h +++ b/ApplicationLibCode/Commands/RicWellLogTools.h @@ -38,7 +38,7 @@ class RimWellPath; class RimWellMeasurementCurve; class RimSummaryCase; class RimWellLogCurve; -class RimWellLogDiffCurve; +class RimWellLogCalculatedCurve; //-------------------------------------------------------------------------------------------------- /// @@ -73,7 +73,7 @@ public: bool showPlotWindow = true ); static RimWellMeasurementCurve* addWellMeasurementCurve( RimWellLogTrack* plotTrack, RimWellPath* wellPath, const QString& measurementName, bool showPlotWindow = true ); - static RimWellLogDiffCurve* addWellLogDiffCurve( RimWellLogTrack* plotTrack, bool showPlotWindow = true ); + static RimWellLogCalculatedCurve* addWellLogCalculatedCurve( RimWellLogTrack* plotTrack, bool showPlotWindow = true ); static RimWellLogCurve* addSummaryRftCurve( RimWellLogTrack* plotTrack, RimSummaryCase* rimCase ); static RimWellLogRftCurve* addSummaryRftSegmentCurve( RimWellLogTrack* plotTrack, diff --git a/ApplicationLibCode/Commands/WellLogCommands/CMakeLists_files.cmake b/ApplicationLibCode/Commands/WellLogCommands/CMakeLists_files.cmake index 4dbab412ec..a6df37a9af 100644 --- a/ApplicationLibCode/Commands/WellLogCommands/CMakeLists_files.cmake +++ b/ApplicationLibCode/Commands/WellLogCommands/CMakeLists_files.cmake @@ -30,7 +30,7 @@ set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RicNewRftWellLogPlotFeature.h ${CMAKE_CURRENT_LIST_DIR}/RicNewRftSegmentWellLogPlotFeature.h ${CMAKE_CURRENT_LIST_DIR}/RicNewMultiPhaseRftSegmentPlotFeature.h - ${CMAKE_CURRENT_LIST_DIR}/RicNewWellLogDiffCurveFeature.h + ${CMAKE_CURRENT_LIST_DIR}/RicNewWellLogCalculatedCurveFeature.h ) set(SOURCE_GROUP_SOURCE_FILES @@ -65,7 +65,7 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RicNewRftWellLogPlotFeature.cpp ${CMAKE_CURRENT_LIST_DIR}/RicNewRftSegmentWellLogPlotFeature.cpp ${CMAKE_CURRENT_LIST_DIR}/RicNewMultiPhaseRftSegmentPlotFeature.cpp - ${CMAKE_CURRENT_LIST_DIR}/RicNewWellLogDiffCurveFeature.cpp + ${CMAKE_CURRENT_LIST_DIR}/RicNewWellLogCalculatedCurveFeature.cpp ) list(APPEND COMMAND_CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES}) diff --git a/ApplicationLibCode/Commands/WellLogCommands/RicNewWellLogDiffCurveFeature.cpp b/ApplicationLibCode/Commands/WellLogCommands/RicNewWellLogCalculatedCurveFeature.cpp similarity index 79% rename from ApplicationLibCode/Commands/WellLogCommands/RicNewWellLogDiffCurveFeature.cpp rename to ApplicationLibCode/Commands/WellLogCommands/RicNewWellLogCalculatedCurveFeature.cpp index a5ff434f6f..50cde17f75 100644 --- a/ApplicationLibCode/Commands/WellLogCommands/RicNewWellLogDiffCurveFeature.cpp +++ b/ApplicationLibCode/Commands/WellLogCommands/RicNewWellLogCalculatedCurveFeature.cpp @@ -16,12 +16,12 @@ // ///////////////////////////////////////////////////////////////////////////////// -#include "RicNewWellLogDiffCurveFeature.h" +#include "RicNewWellLogCalculatedCurveFeature.h" #include "RicWellLogTools.h" +#include "RimWellLogCalculatedCurve.h" #include "RimWellLogCurve.h" -#include "RimWellLogDiffCurve.h" #include "RimWellLogTrack.h" #include "RiuPlotMainWindowTools.h" @@ -33,12 +33,12 @@ #include -CAF_CMD_SOURCE_INIT( RicNewWellLogDiffCurveFeature, "RicNewWellLogDiffCurveFeature" ); +CAF_CMD_SOURCE_INIT( RicNewWellLogCalculatedCurveFeature, "RicNewWellLogCalculatedCurveFeature" ); //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -bool RicNewWellLogDiffCurveFeature::isCommandEnabled() +bool RicNewWellLogCalculatedCurveFeature::isCommandEnabled() { std::vector wellLogCurves; caf::SelectionManager::instance()->objectsByType( &wellLogCurves ); @@ -49,12 +49,12 @@ bool RicNewWellLogDiffCurveFeature::isCommandEnabled() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RicNewWellLogDiffCurveFeature::onActionTriggered( bool isChecked ) +void RicNewWellLogCalculatedCurveFeature::onActionTriggered( bool isChecked ) { RimWellLogTrack* wellLogTrack = caf::SelectionManager::instance()->selectedItemOfType(); if ( wellLogTrack ) { - RicWellLogTools::addWellLogDiffCurve( wellLogTrack ); + RicWellLogTools::addWellLogCalculatedCurve( wellLogTrack ); } else { @@ -65,7 +65,7 @@ void RicNewWellLogDiffCurveFeature::onActionTriggered( bool isChecked ) RimWellLogTrack* wellLogTrack = wellLogCurves[0]->firstAncestorOrThisOfType(); if ( !wellLogTrack ) return; - RimWellLogDiffCurve* newCurve = RicWellLogTools::addWellLogDiffCurve( wellLogTrack ); + RimWellLogCalculatedCurve* newCurve = RicWellLogTools::addWellLogCalculatedCurve( wellLogTrack ); newCurve->setWellLogCurves( wellLogCurves[0], wellLogCurves[1] ); newCurve->updateConnectedEditors(); } @@ -75,8 +75,8 @@ void RicNewWellLogDiffCurveFeature::onActionTriggered( bool isChecked ) //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RicNewWellLogDiffCurveFeature::setupActionLook( QAction* actionToSetup ) +void RicNewWellLogCalculatedCurveFeature::setupActionLook( QAction* actionToSetup ) { - actionToSetup->setText( "New Well Log Diff Curve" ); + actionToSetup->setText( "New Well Log Calculated Curve" ); actionToSetup->setIcon( QIcon( ":/WellLogCurve16x16.png" ) ); } diff --git a/ApplicationLibCode/Commands/WellLogCommands/RicNewWellLogDiffCurveFeature.h b/ApplicationLibCode/Commands/WellLogCommands/RicNewWellLogCalculatedCurveFeature.h similarity index 94% rename from ApplicationLibCode/Commands/WellLogCommands/RicNewWellLogDiffCurveFeature.h rename to ApplicationLibCode/Commands/WellLogCommands/RicNewWellLogCalculatedCurveFeature.h index d561273ce5..c36a149afd 100644 --- a/ApplicationLibCode/Commands/WellLogCommands/RicNewWellLogDiffCurveFeature.h +++ b/ApplicationLibCode/Commands/WellLogCommands/RicNewWellLogCalculatedCurveFeature.h @@ -23,7 +23,7 @@ //================================================================================================== /// //================================================================================================== -class RicNewWellLogDiffCurveFeature : public caf::CmdFeature +class RicNewWellLogCalculatedCurveFeature : public caf::CmdFeature { CAF_CMD_HEADER_INIT; diff --git a/ApplicationLibCode/ProjectDataModel/GeoMech/RimGeoMechFaultReactivationResult.cpp b/ApplicationLibCode/ProjectDataModel/GeoMech/RimGeoMechFaultReactivationResult.cpp index 2c3bbe2fd6..f138bf0fed 100644 --- a/ApplicationLibCode/ProjectDataModel/GeoMech/RimGeoMechFaultReactivationResult.cpp +++ b/ApplicationLibCode/ProjectDataModel/GeoMech/RimGeoMechFaultReactivationResult.cpp @@ -35,7 +35,7 @@ #include "RimIntersectionCollection.h" #include "RimMainPlotCollection.h" #include "RimModeledWellPath.h" -#include "RimWellLogDiffCurve.h" +#include "RimWellLogCalculatedCurve.h" #include "RimWellLogExtractionCurve.h" #include "RimWellLogPlotCollection.h" #include "RimWellLogPlotNameConfig.h" @@ -286,8 +286,8 @@ void RimGeoMechFaultReactivationResult::createWellLogCurves() const bool doUpdateAfter = true; RimWellLogTrack* wellLogExtractionDisplacementTrack = RicNewWellLogPlotFeatureImpl::createWellLogPlotTrack( doUpdateAfter, QString( "Fault Reactivation Displacement Curves" ), newPlot ); - RimWellLogTrack* wellLogDiffTrack = - RicNewWellLogPlotFeatureImpl::createWellLogPlotTrack( doUpdateAfter, QString( "Fault Reactivation Displacement Diff" ), newPlot ); + RimWellLogTrack* wellLogCalculatedTrack = + RicNewWellLogPlotFeatureImpl::createWellLogPlotTrack( doUpdateAfter, QString( "Fault Reactivation Displacement Difference" ), newPlot ); RimWellLogTrack* wellLogExtractionFaultmobTrack = RicNewWellLogPlotFeatureImpl::createWellLogPlotTrack( doUpdateAfter, QString( "Fault Reactivation Faultmob Curves" ), newPlot ); @@ -308,11 +308,12 @@ void RimGeoMechFaultReactivationResult::createWellLogCurves() return; } - // Create well log diff curve for m_faceAWellPath and m_faceBWellPath - RimWellLogDiffCurve* faceWellLogDiffCurve = RicWellLogTools::addWellLogDiffCurve( wellLogDiffTrack ); - faceWellLogDiffCurve->setWellLogCurves( faceADisplacementCurve, faceBDisplacementCurve ); - faceWellLogDiffCurve->loadDataAndUpdate( true ); - faceWellLogDiffCurve->updateConnectedEditors(); + // Create well log calculated curve for m_faceAWellPath and m_faceBWellPath + RimWellLogCalculatedCurve* wellLogCalculatedCurve = RicWellLogTools::addWellLogCalculatedCurve( wellLogCalculatedTrack ); + wellLogCalculatedCurve->setOperator( RimWellLogCalculatedCurve::Operators::SUBTRACT ); + wellLogCalculatedCurve->setWellLogCurves( faceADisplacementCurve, faceBDisplacementCurve ); + wellLogCalculatedCurve->loadDataAndUpdate( true ); + wellLogCalculatedCurve->updateConnectedEditors(); // Well log extraction faultmob curves RigFemResultAddress wellLogExtractionFaultmobResult( RigFemResultPosEnum::RIG_ELEMENT_NODAL_FACE, "SE", "FAULTMOB" ); diff --git a/ApplicationLibCode/ProjectDataModel/RimContextCommandBuilder.cpp b/ApplicationLibCode/ProjectDataModel/RimContextCommandBuilder.cpp index a3cc9cccc8..7e2a2314e6 100644 --- a/ApplicationLibCode/ProjectDataModel/RimContextCommandBuilder.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimContextCommandBuilder.cpp @@ -639,7 +639,7 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection() menuBuilder << "RicNewWellLogRftCurveFeature"; menuBuilder << "RicNewWellLogFileCurveFeature"; menuBuilder << "RicNewWellMeasurementCurveFeature"; - menuBuilder << "RicNewWellLogDiffCurveFeature"; + menuBuilder << "RicNewWellLogCalculatedCurveFeature"; menuBuilder << "RicNewEnsembleWellLogCurveSetFeature"; menuBuilder << "Separator"; menuBuilder << "RicDeleteSubPlotFeature"; @@ -1245,7 +1245,7 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection() } else if ( dynamic_cast( firstUiItem ) || dynamic_cast( firstUiItem ) ) { - menuBuilder << "RicNewWellLogDiffCurveFeature"; + menuBuilder << "RicNewWellLogCalculatedCurveFeature"; menuBuilder << "RicExportToLasFileFeature"; menuBuilder << "RicChangeDataSourceFeature"; } diff --git a/ApplicationLibCode/ProjectDataModel/WellLog/CMakeLists_files.cmake b/ApplicationLibCode/ProjectDataModel/WellLog/CMakeLists_files.cmake index aeee3d9fec..932bdec5ac 100644 --- a/ApplicationLibCode/ProjectDataModel/WellLog/CMakeLists_files.cmake +++ b/ApplicationLibCode/ProjectDataModel/WellLog/CMakeLists_files.cmake @@ -26,7 +26,7 @@ set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RimRftTools.h ${CMAKE_CURRENT_LIST_DIR}/RimRftTopologyCurve.h ${CMAKE_CURRENT_LIST_DIR}/RimWellLogCurveInfoTextProvider.h - ${CMAKE_CURRENT_LIST_DIR}/RimWellLogDiffCurve.h + ${CMAKE_CURRENT_LIST_DIR}/RimWellLogCalculatedCurve.h ) set(SOURCE_GROUP_SOURCE_FILES @@ -57,7 +57,7 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RimRftTools.cpp ${CMAKE_CURRENT_LIST_DIR}/RimRftTopologyCurve.cpp ${CMAKE_CURRENT_LIST_DIR}/RimWellLogCurveInfoTextProvider.cpp - ${CMAKE_CURRENT_LIST_DIR}/RimWellLogDiffCurve.cpp + ${CMAKE_CURRENT_LIST_DIR}/RimWellLogCalculatedCurve.cpp ) list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES}) diff --git a/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogCalculatedCurve.cpp b/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogCalculatedCurve.cpp new file mode 100644 index 0000000000..6e90a6a27c --- /dev/null +++ b/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogCalculatedCurve.cpp @@ -0,0 +1,479 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2023- 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 "RimWellLogCalculatedCurve.h" + +#include "RiaDefines.h" +#include "RiaLogging.h" + +#include "RigWellLogCurveData.h" + +#include "RimMainPlotCollection.h" +#include "RimOilField.h" +#include "RimPlot.h" +#include "RimProject.h" +#include "RimWellLogPlot.h" +#include "RimWellLogPlotCollection.h" +#include "RimWellLogTrack.h" +#include "RimWellPathCollection.h" + +#include "RiuPlotCurve.h" + +#include "cafPdmUiComboBoxEditor.h" +#include "cafPdmUiTreeOrdering.h" + +CAF_PDM_SOURCE_INIT( RimWellLogCalculatedCurve, "WellLogCalculatedCurve" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +namespace caf +{ +template <> +void AppEnum::setUp() +{ + addItem( RimWellLogCalculatedCurve::Operators::ADD, "ADD", "+" ); + addItem( RimWellLogCalculatedCurve::Operators::SUBTRACT, "SUBTRACT", "-" ); + addItem( RimWellLogCalculatedCurve::Operators::MULTIPLY, "MULTIPLY", "*" ); + addItem( RimWellLogCalculatedCurve::Operators::DIVIDE, "DIVIDE", "/" ); + setDefault( RimWellLogCalculatedCurve::Operators::SUBTRACT ); +} + +template <> +void AppEnum::setUp() +{ + addItem( RimWellLogCalculatedCurve::DepthSource::FIRST, "FIRST", "First Curve" ); + addItem( RimWellLogCalculatedCurve::DepthSource::SECOND, "SECOND", "Second Curve" ); + addItem( RimWellLogCalculatedCurve::DepthSource::UNION, "UNION", "Union" ); + setDefault( RimWellLogCalculatedCurve::DepthSource::FIRST ); +} +} // namespace caf + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimWellLogCalculatedCurve::RimWellLogCalculatedCurve() +{ + CAF_PDM_InitObject( "Well Log Calculated Curve", ":/WellLogCurve16x16.png" ); + + CAF_PDM_InitFieldNoDefault( &m_operator, "Operator", "Operator" ); + m_operator.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() ); + CAF_PDM_InitFieldNoDefault( &m_depthSource, "DepthSource", "Depth Source" ); + m_depthSource.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() ); + + CAF_PDM_InitFieldNoDefault( &m_firstWellLogCurve, "FirstWellLogCurve", "First Well Log Curve" ); + CAF_PDM_InitFieldNoDefault( &m_secondWellLogCurve, "SecondWellLogCurve", "Second Well Log Curve" ); + + setNamingMethod( RiaDefines::ObjectNamingMethod::AUTO ); + setDeletable( true ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimWellLogCalculatedCurve::~RimWellLogCalculatedCurve() +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogCalculatedCurve::setOperator( Operators operatorValue ) +{ + m_operator = operatorValue; + if ( m_namingMethod() == RiaDefines::ObjectNamingMethod::AUTO ) + { + setAutomaticName(); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogCalculatedCurve::setWellLogCurves( RimWellLogCurve* firstWellLogCurve, RimWellLogCurve* secondWellLogCurve ) +{ + disconnectWellLogCurveChangedFromSlots( m_firstWellLogCurve ); + disconnectWellLogCurveChangedFromSlots( m_secondWellLogCurve ); + if ( firstWellLogCurve ) + { + m_firstWellLogCurve = firstWellLogCurve; + connectWellLogCurveChangedToSlots( m_firstWellLogCurve ); + } + if ( secondWellLogCurve ) + { + m_secondWellLogCurve = secondWellLogCurve; + connectWellLogCurveChangedToSlots( m_secondWellLogCurve ); + } + + if ( m_namingMethod() == RiaDefines::ObjectNamingMethod::AUTO ) + { + setAutomaticName(); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimWellLogCalculatedCurve::createCurveAutoName() +{ + if ( !m_firstWellLogCurve() || !m_secondWellLogCurve() ) return QString( "Not able to find source curves for calculated curve" ); + + const auto& operatorStr = m_operator().uiText(); + return QString( "Calculated (%1 %2 %3)" ).arg( m_firstWellLogCurve->curveName() ).arg( operatorStr ).arg( m_secondWellLogCurve->curveName() ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogCalculatedCurve::onLoadDataAndUpdate( bool updateParentPlot ) +{ + if ( !m_firstWellLogCurve() || !m_secondWellLogCurve() ) return; + + if ( m_namingMethod() == RiaDefines::ObjectNamingMethod::AUTO ) + { + setAutomaticName(); + } + + // Use well A as reference for resampled curve data + auto* firstCurveData = m_firstWellLogCurve()->curveData(); + auto* secondCurveData = m_secondWellLogCurve()->curveData(); + + if ( !firstCurveData || !secondCurveData ) return; + if ( firstCurveData->depthUnit() != secondCurveData->depthUnit() ) + { + RiaLogging::warning( "Well log curve depth units are not the same" ); + } + if ( firstCurveData->propertyValueUnit() != secondCurveData->propertyValueUnit() ) + { + RiaLogging::warning( "Well log curve property value units are not the same" ); + } + + // Find the data for resampling + const auto depthType = RiaDefines::DepthTypeEnum::MEASURED_DEPTH; + const std::vector depthValues = depthValuesFromSource( depthType ); + const auto depthUnit = firstCurveData->depthUnit(); + const auto propertyUnit = firstCurveData->propertyValueUnit(); + + auto firstCurveDepthValues = firstCurveData->depths( depthType ); + auto firstCurvePropertyValues = firstCurveData->propertyValues(); + auto secondCurveDepthValues = secondCurveData->depths( depthType ); + auto secondCurvePropertyValues = secondCurveData->propertyValues(); + + // Resample curve depth and property values if needed + if ( m_depthSource() != DepthSource::FIRST ) + { + cvf::ref firstCurveDataResampled = firstCurveData->calculateResampledCurveData( depthType, depthValues ); + firstCurveDepthValues = firstCurveDataResampled->depths( depthType ); + firstCurvePropertyValues = firstCurveDataResampled->propertyValues(); + } + if ( m_depthSource() != DepthSource::SECOND ) + { + cvf::ref secondCurveDataResampled = secondCurveData->calculateResampledCurveData( depthType, depthValues ); + secondCurveDepthValues = secondCurveDataResampled->depths( depthType ); + secondCurvePropertyValues = secondCurveDataResampled->propertyValues(); + } + + // Verify equal sizes + if ( firstCurveDepthValues.size() != depthValues.size() ) return; + if ( firstCurveDepthValues.size() != firstCurvePropertyValues.size() ) return; + if ( firstCurveDepthValues.size() != secondCurveDepthValues.size() ) return; + if ( firstCurveDepthValues.size() != secondCurvePropertyValues.size() ) return; + + // Calculate curve + std::vector calculatedDepthValues( depthValues.size() ); + std::vector calculatedPropertyValues( depthValues.size() ); + for ( size_t i = 0; i < depthValues.size(); ++i ) + { + calculatedPropertyValues[i] = calculateValue( firstCurvePropertyValues[i], secondCurvePropertyValues[i], m_operator() ); + calculatedDepthValues[i] = depthValues[i]; + } + + const bool useLogarithmicScale = false; + const bool isExtractionCurve = false; + + // Set curve data + auto depthsMap = std::map>(); + depthsMap[depthType] = calculatedDepthValues; + setPropertyValuesAndDepths( calculatedPropertyValues, depthsMap, 0.0, depthUnit, isExtractionCurve, useLogarithmicScale, propertyUnit ); + + // Set curve data to plot + std::vector xPlotValues = curveData()->propertyValuesByIntervals(); + std::vector yPlotValues = curveData()->depthValuesByIntervals( depthType, depthUnit ); + m_plotCurve->setSamplesFromXValuesAndYValues( xPlotValues, yPlotValues, useLogarithmicScale ); + updateCurvePresentation( updateParentPlot ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogCalculatedCurve::setAutomaticName() +{ + m_curveName = createCurveAutoName(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogCalculatedCurve::onWellLogCurveChanged( const SignalEmitter* emitter ) +{ + loadDataAndUpdate( true ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogCalculatedCurve::connectWellLogCurveChangedToSlots( RimWellLogCurve* wellLogCurve ) +{ + if ( !wellLogCurve ) return; + + wellLogCurve->dataChanged.connect( this, &RimWellLogCalculatedCurve::onWellLogCurveChanged ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogCalculatedCurve::disconnectWellLogCurveChangedFromSlots( RimWellLogCurve* wellLogCurve ) +{ + if ( !wellLogCurve ) return; + + wellLogCurve->dataChanged.disconnect( this ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +double RimWellLogCalculatedCurve::calculateValue( double firstValue, double secondValue, Operators operatorValue ) +{ + if ( operatorValue == Operators::ADD ) + { + return firstValue + secondValue; + } + if ( operatorValue == Operators::SUBTRACT ) + { + return firstValue - secondValue; + } + if ( operatorValue == Operators::MULTIPLY ) + { + return firstValue * secondValue; + } + if ( operatorValue == Operators::DIVIDE ) + { + return secondValue == 0.0 ? std::numeric_limits::infinity() : firstValue / secondValue; + } + return 0.0; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimWellLogCalculatedCurve::depthValuesFromSource( RiaDefines::DepthTypeEnum depthType ) const +{ + if ( !m_firstWellLogCurve || !m_firstWellLogCurve->curveData() || !m_secondWellLogCurve || !m_secondWellLogCurve->curveData() ) + { + return {}; + } + + if ( m_depthSource() == DepthSource::FIRST ) + { + return m_firstWellLogCurve->curveData()->depths( depthType ); + } + else if ( m_depthSource() == DepthSource::SECOND ) + { + return m_secondWellLogCurve->curveData()->depths( depthType ); + } + else if ( m_depthSource() == DepthSource::UNION ) + { + return unionDepthValuesFromCurves( depthType ); + } + + return {}; +} + +//-------------------------------------------------------------------------------------------------- +/// As this method creates an union of depths from two vectors, the duplicated depths are neglected. +/// The duplicated depths in a depth vector represent enter and exist of a k-layer. When resampling +/// the curves using the union of depths, one does not know if the new depth values are at enter +/// or exit of a layer. Therefore, the duplicated depths are neglected. +//-------------------------------------------------------------------------------------------------- +std::vector RimWellLogCalculatedCurve::unionDepthValuesFromVectors( const std::vector& firstDepths, + const std::vector& secondDepths, + double threshold ) +{ + auto isWithinThreshold = [&]( double value, const std::vector& existingValues ) -> bool + { + auto calculateDistance = []( double value1, double value2 ) -> double { return std::abs( value1 - value2 ); }; + for ( const auto& existingValue : existingValues ) + { + if ( calculateDistance( value, existingValue ) < threshold ) + { + return true; + } + } + return false; + }; + + // Get union of depths + std::vector unionDepths; + auto insertIntoUnionDepthsVector = [&]( const std::vector& depths, std::vector& unionDepthsVector ) + { + for ( const auto& depth : depths ) + { + if ( isWithinThreshold( depth, unionDepthsVector ) ) continue; + unionDepthsVector.push_back( depth ); + } + }; + insertIntoUnionDepthsVector( firstDepths, unionDepths ); + insertIntoUnionDepthsVector( secondDepths, unionDepths ); + + // Sort vector + std::sort( unionDepths.begin(), unionDepths.end() ); + + return unionDepths; +} + +//-------------------------------------------------------------------------------------------------- +/// When creating an union depth vector, the duplicated depth values are neglected. These duplicates +/// indicates enter and exit of k-layer. However, when curves are resampled, this info is not +/// representative and duplicated depth values are not needed. +//-------------------------------------------------------------------------------------------------- +std::vector RimWellLogCalculatedCurve::unionDepthValuesFromCurves( RiaDefines::DepthTypeEnum depthType ) const +{ + if ( !m_firstWellLogCurve || !m_firstWellLogCurve->curveData() || !m_secondWellLogCurve || !m_secondWellLogCurve->curveData() ) + { + return {}; + } + + constexpr double depthThreshold = 0.1; + + // Get union of depths + const auto firstDepths = m_firstWellLogCurve->curveData()->depths( depthType ); + const auto secondDepths = m_secondWellLogCurve->curveData()->depths( depthType ); + + return unionDepthValuesFromVectors( firstDepths, secondDepths, depthThreshold ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimWellLogCalculatedCurve::wellName() const +{ + return m_curveName; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimWellLogCalculatedCurve::wellLogChannelUiName() const +{ + return m_curveName; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimWellLogCalculatedCurve::wellLogChannelUnits() const +{ + if ( m_firstWellLogCurve->wellLogChannelUnits() != m_secondWellLogCurve->wellLogChannelUnits() ) + { + const auto& operatorStr = m_operator().uiText(); + return QString( "%1 %2 %3" ) + .arg( m_firstWellLogCurve->wellLogChannelUnits() ) + .arg( operatorStr ) + .arg( m_secondWellLogCurve->wellLogChannelUnits() ); + } + return m_firstWellLogCurve->wellLogChannelUnits(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogCalculatedCurve::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) +{ + RimPlotCurve::updateFieldUiState(); + + caf::PdmUiGroup* group = uiOrdering.addNewGroup( "Data Source" ); + group->add( &m_depthSource ); + group->add( &m_firstWellLogCurve ); + group->add( &m_secondWellLogCurve ); + group->add( &m_operator ); + + RimStackablePlotCurve::defaultUiOrdering( uiOrdering ); + + uiOrdering.skipRemainingFields( true ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogCalculatedCurve::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName ) +{ + uiTreeOrdering.skipRemainingChildren( true ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogCalculatedCurve::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) +{ + RimWellLogCurve::fieldChangedByUi( changedField, oldValue, newValue ); + + if ( changedField == &m_firstWellLogCurve || changedField == &m_secondWellLogCurve ) + { + if ( !m_firstWellLogCurve() || !m_secondWellLogCurve() ) return; + + PdmObjectHandle* prevValue = oldValue.value>().rawPtr(); + auto* prevWellLogCurve = dynamic_cast( prevValue ); + disconnectWellLogCurveChangedFromSlots( prevWellLogCurve ); + + if ( changedField == &m_firstWellLogCurve ) connectWellLogCurveChangedToSlots( m_firstWellLogCurve ); + if ( changedField == &m_secondWellLogCurve ) connectWellLogCurveChangedToSlots( m_secondWellLogCurve ); + + loadDataAndUpdate( true ); + } + if ( changedField == &m_operator || changedField == &m_depthSource ) + { + loadDataAndUpdate( true ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QList RimWellLogCalculatedCurve::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) +{ + QList options; + + options = RimWellLogCurve::calculateValueOptions( fieldNeedingOptions ); + if ( !options.empty() ) return options; + + if ( fieldNeedingOptions == &m_firstWellLogCurve || fieldNeedingOptions == &m_secondWellLogCurve ) + { + RimWellLogPlotCollection* wellLogPlotCollection = RimMainPlotCollection::current()->wellLogPlotCollection(); + + if ( !wellLogPlotCollection ) return {}; + + // Find each well log plot in collection + std::vector wellLogCurves = wellLogPlotCollection->descendantsOfType(); + for ( RimWellLogCurve* curve : wellLogCurves ) + { + if ( !curve || curve == this ) continue; + options.push_back( caf::PdmOptionItemInfo( curve->curveName(), curve ) ); + } + } + return options; +} diff --git a/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogDiffCurve.h b/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogCalculatedCurve.h similarity index 72% rename from ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogDiffCurve.h rename to ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogCalculatedCurve.h index c93b0e9896..f401108544 100644 --- a/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogDiffCurve.h +++ b/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogCalculatedCurve.h @@ -30,14 +30,30 @@ class RimWellPath; /// /// //================================================================================================== -class RimWellLogDiffCurve : public RimWellLogCurve +class RimWellLogCalculatedCurve : public RimWellLogCurve { CAF_PDM_HEADER_INIT; public: - RimWellLogDiffCurve(); - ~RimWellLogDiffCurve() override; + enum class Operators + { + ADD, + SUBTRACT, + DIVIDE, + MULTIPLY + }; + enum class DepthSource + { + FIRST, + SECOND, + UNION + }; +public: + RimWellLogCalculatedCurve(); + ~RimWellLogCalculatedCurve() override; + + void setOperator( Operators operatorValue ); void setWellLogCurves( RimWellLogCurve* firstWellLogCurve, RimWellLogCurve* secondWellLogCurve ); // Inherited via RimWellLogCurve @@ -45,6 +61,10 @@ public: QString wellLogChannelUiName() const override; QString wellLogChannelUnits() const override; + static double calculateValue( double firstValue, double secondValue, Operators operatorValue ); + static std::vector + unionDepthValuesFromVectors( const std::vector& firstDepths, const std::vector& secondDepths, double threshold ); + protected: void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override; void defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName = "" ) override; @@ -62,9 +82,15 @@ private: void connectWellLogCurveChangedToSlots( RimWellLogCurve* wellLogCurve ); void disconnectWellLogCurveChangedFromSlots( RimWellLogCurve* wellLogCurve ); + std::vector depthValuesFromSource( RiaDefines::DepthTypeEnum depthType ) const; + std::vector unionDepthValuesFromCurves( RiaDefines::DepthTypeEnum depthType ) const; + private: caf::PdmPtrField m_case; + caf::PdmField> m_operator; + caf::PdmField> m_depthSource; + caf::PdmPtrField m_firstWellLogCurve; caf::PdmPtrField m_secondWellLogCurve; }; diff --git a/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogDiffCurve.cpp b/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogDiffCurve.cpp deleted file mode 100644 index 8f5f678d22..0000000000 --- a/ApplicationLibCode/ProjectDataModel/WellLog/RimWellLogDiffCurve.cpp +++ /dev/null @@ -1,300 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2023- 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 "RimWellLogDiffCurve.h" - -#include "RiaDefines.h" -#include "RiaLogging.h" - -#include "RigWellLogCurveData.h" - -#include "RimMainPlotCollection.h" -#include "RimOilField.h" -#include "RimPlot.h" -#include "RimProject.h" -#include "RimWellLogPlot.h" -#include "RimWellLogPlotCollection.h" -#include "RimWellLogTrack.h" -#include "RimWellPathCollection.h" - -#include "RiuPlotCurve.h" - -#include "cafPdmUiTreeOrdering.h" - -CAF_PDM_SOURCE_INIT( RimWellLogDiffCurve, "WellLogDiffCurve" ); - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RimWellLogDiffCurve::RimWellLogDiffCurve() -{ - CAF_PDM_InitObject( "Well Log Diff Curve", ":/WellLogCurve16x16.png" ); - - CAF_PDM_InitFieldNoDefault( &m_firstWellLogCurve, "FirstWellLogCurve", "First Well Log Curve" ); - CAF_PDM_InitFieldNoDefault( &m_secondWellLogCurve, "SecondWellLogCurve", "Second Well Log Curve" ); - - setNamingMethod( RiaDefines::ObjectNamingMethod::AUTO ); - setDeletable( true ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RimWellLogDiffCurve::~RimWellLogDiffCurve() -{ -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimWellLogDiffCurve::setWellLogCurves( RimWellLogCurve* firstWellLogCurve, RimWellLogCurve* secondWellLogCurve ) -{ - disconnectWellLogCurveChangedFromSlots( m_firstWellLogCurve ); - disconnectWellLogCurveChangedFromSlots( m_secondWellLogCurve ); - if ( firstWellLogCurve ) - { - m_firstWellLogCurve = firstWellLogCurve; - connectWellLogCurveChangedToSlots( m_firstWellLogCurve ); - } - if ( secondWellLogCurve ) - { - m_secondWellLogCurve = secondWellLogCurve; - connectWellLogCurveChangedToSlots( m_secondWellLogCurve ); - } - - if ( m_namingMethod() == RiaDefines::ObjectNamingMethod::AUTO ) - { - setAutomaticName(); - } -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -QString RimWellLogDiffCurve::createCurveAutoName() -{ - if ( !m_firstWellLogCurve() || !m_secondWellLogCurve() ) return QString( "Not able to find source curves for difference curve" ); - return QString( "Diff (%1 - %2)" ).arg( m_firstWellLogCurve->curveName() ).arg( m_secondWellLogCurve->curveName() ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimWellLogDiffCurve::onLoadDataAndUpdate( bool updateParentPlot ) -{ - if ( !m_firstWellLogCurve() || !m_secondWellLogCurve() ) return; - - if ( m_namingMethod() == RiaDefines::ObjectNamingMethod::AUTO ) - { - setAutomaticName(); - } - - // Use well A as reference for resampled curve data - auto* firstCurveData = m_firstWellLogCurve()->curveData(); - auto* secondCurveData = m_secondWellLogCurve()->curveData(); - - if ( !firstCurveData || !secondCurveData ) return; - if ( firstCurveData->depthUnit() != secondCurveData->depthUnit() ) - { - RiaLogging::warning( "Well log curve depth units are not the same" ); - } - if ( firstCurveData->propertyValueUnit() != secondCurveData->propertyValueUnit() ) - { - RiaLogging::warning( "Well log curve property value units are not the same" ); - } - - const auto depthUnit = firstCurveData->depthUnit(); - const auto depthType = RiaDefines::DepthTypeEnum::MEASURED_DEPTH; - const auto propertyUnit = firstCurveData->propertyValueUnit(); - - // Get curve A depths and property values - const auto firstCurveDepthValues = firstCurveData->depths( depthType ); - const auto firstCurvePropertyValues = firstCurveData->propertyValues(); - - // Resample curve B to curve A - cvf::ref secondCurveDataResampled = secondCurveData->calculateResampledCurveData( depthType, firstCurveDepthValues ); - auto secondCurveDepthValuesResampled = secondCurveDataResampled->depths( depthType ); - auto secondCurvePropertyValuesResampled = secondCurveDataResampled->propertyValues(); - - // Verify equal sizes - if ( firstCurveDepthValues.size() != firstCurvePropertyValues.size() ) return; - if ( firstCurveDepthValues.size() != secondCurveDepthValuesResampled.size() ) return; - if ( firstCurveDepthValues.size() != secondCurvePropertyValuesResampled.size() ) return; - - // Calculate diff curve - std::vector curveDiffDepthValues( firstCurveDepthValues.size() ); - std::vector curveDiffPropertyValues( firstCurvePropertyValues.size() ); - for ( size_t i = 0; i < firstCurvePropertyValues.size(); ++i ) - { - curveDiffPropertyValues[i] = firstCurvePropertyValues[i] - secondCurvePropertyValuesResampled[i]; - curveDiffDepthValues[i] = firstCurveDepthValues[i]; - } - - const bool useLogarithmicScale = false; - const bool isExtractionCurve = false; - - // Set curve data - auto depthsMap = std::map>(); - depthsMap[depthType] = curveDiffDepthValues; - setPropertyValuesAndDepths( curveDiffPropertyValues, depthsMap, 0.0, depthUnit, isExtractionCurve, useLogarithmicScale, propertyUnit ); - - // Set curve data to plot - std::vector xPlotValues = curveData()->propertyValuesByIntervals(); - std::vector yPlotValues = curveData()->depthValuesByIntervals( depthType, depthUnit ); - m_plotCurve->setSamplesFromXValuesAndYValues( xPlotValues, yPlotValues, useLogarithmicScale ); - updateCurvePresentation( updateParentPlot ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimWellLogDiffCurve::setAutomaticName() -{ - m_curveName = createCurveAutoName(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimWellLogDiffCurve::onWellLogCurveChanged( const SignalEmitter* emitter ) -{ - onLoadDataAndUpdate( true ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimWellLogDiffCurve::connectWellLogCurveChangedToSlots( RimWellLogCurve* wellLogCurve ) -{ - if ( !wellLogCurve ) return; - - wellLogCurve->dataChanged.connect( this, &RimWellLogDiffCurve::onWellLogCurveChanged ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimWellLogDiffCurve::disconnectWellLogCurveChangedFromSlots( RimWellLogCurve* wellLogCurve ) -{ - if ( !wellLogCurve ) return; - - wellLogCurve->dataChanged.disconnect( this ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -QString RimWellLogDiffCurve::wellName() const -{ - return m_curveName; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -QString RimWellLogDiffCurve::wellLogChannelUiName() const -{ - return m_curveName; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -QString RimWellLogDiffCurve::wellLogChannelUnits() const -{ - CAF_ASSERT( "TO BE IMPLEMETNED!" ); - - if ( m_firstWellLogCurve->wellLogChannelUnits() != m_secondWellLogCurve->wellLogChannelUnits() ) - { - return QString( "%1 - %2" ).arg( m_firstWellLogCurve->wellLogChannelUnits() ).arg( m_secondWellLogCurve->wellLogChannelUnits() ); - } - return m_firstWellLogCurve->wellLogChannelUnits(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimWellLogDiffCurve::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) -{ - RimPlotCurve::updateFieldUiState(); - - caf::PdmUiGroup* group = uiOrdering.addNewGroup( "Data Source" ); - group->add( &m_firstWellLogCurve ); - group->add( &m_secondWellLogCurve ); - - RimStackablePlotCurve::defaultUiOrdering( uiOrdering ); - - uiOrdering.skipRemainingFields( true ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimWellLogDiffCurve::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName ) -{ - uiTreeOrdering.skipRemainingChildren( true ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimWellLogDiffCurve::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) -{ - RimWellLogCurve::fieldChangedByUi( changedField, oldValue, newValue ); - - if ( changedField == &m_firstWellLogCurve || changedField == &m_secondWellLogCurve ) - { - if ( !m_firstWellLogCurve() || !m_secondWellLogCurve() ) return; - - PdmObjectHandle* prevValue = oldValue.value>().rawPtr(); - auto* prevWellLogCurve = dynamic_cast( prevValue ); - disconnectWellLogCurveChangedFromSlots( prevWellLogCurve ); - - if ( changedField == &m_firstWellLogCurve ) connectWellLogCurveChangedToSlots( m_firstWellLogCurve ); - if ( changedField == &m_secondWellLogCurve ) connectWellLogCurveChangedToSlots( m_firstWellLogCurve ); - - onLoadDataAndUpdate( true ); - } -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -QList RimWellLogDiffCurve::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) -{ - QList options; - - options = RimWellLogCurve::calculateValueOptions( fieldNeedingOptions ); - if ( !options.empty() ) return options; - - if ( fieldNeedingOptions == &m_firstWellLogCurve || fieldNeedingOptions == &m_secondWellLogCurve ) - { - RimWellLogPlotCollection* wellLogPlotCollection = RimMainPlotCollection::current()->wellLogPlotCollection(); - - if ( !wellLogPlotCollection ) return {}; - - // Find each well log plot in collection - std::vector wellLogCurves = wellLogPlotCollection->descendantsOfType(); - for ( RimWellLogCurve* curve : wellLogCurves ) - { - if ( !curve || curve == this ) continue; - options.push_back( caf::PdmOptionItemInfo( curve->curveName(), curve ) ); - } - } - return options; -} diff --git a/ApplicationLibCode/UnitTests/CMakeLists_files.cmake b/ApplicationLibCode/UnitTests/CMakeLists_files.cmake index 66bd8d8a35..8daf7224a6 100644 --- a/ApplicationLibCode/UnitTests/CMakeLists_files.cmake +++ b/ApplicationLibCode/UnitTests/CMakeLists_files.cmake @@ -92,6 +92,7 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RiaEnsembleNameTools-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RigDeclineCurveCalculator-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RigWellLogCurveData-Test.cpp + ${CMAKE_CURRENT_LIST_DIR}/RimWellLogCalculatedCurve-Test.cpp ) if(RESINSIGHT_ENABLE_GRPC) diff --git a/ApplicationLibCode/UnitTests/RimWellLogCalculatedCurve-Test.cpp b/ApplicationLibCode/UnitTests/RimWellLogCalculatedCurve-Test.cpp new file mode 100644 index 0000000000..806ebcf9a3 --- /dev/null +++ b/ApplicationLibCode/UnitTests/RimWellLogCalculatedCurve-Test.cpp @@ -0,0 +1,95 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2023- 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 "gtest/gtest.h" + +#include "RimWellLogCalculatedCurve.h" + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RimWellLogCalculatedCurve, unionDepthValuesFromVectors_singleValuesInDepthVectors ) +{ + // Depth vectors without duplicates + const std::vector depthValues1 = { 1.0, 2.0, 3.0, 4.0 }; + const std::vector depthValues2 = { 2.0, 3.0, 4.0, 5.0 }; + const double threshold = 0.1; + + // Expected duplicate occurrence of common depth values + const std::vector expectedUnionDepthValues = { 1.0, 2.0, 3.0, 4.0, 5.0 }; + + // Call the function under test + const std::vector unionDepthValues = + RimWellLogCalculatedCurve::unionDepthValuesFromVectors( depthValues1, depthValues2, threshold ); + + ASSERT_EQ( unionDepthValues, expectedUnionDepthValues ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RimWellLogCalculatedCurve, unionDepthValuesFromVectors_duplicateValuesInDepthVectors ) +{ + // Depth vectors with duplicates + const std::vector depthValues1 = { 0.0, 0.5, 0.5, 1.0, 1.0, 1.5, 1.5 }; + const std::vector depthValues2 = { 1.5, 1.5, 2.0, 2.0, 2.5, 2.5, 3.0 }; + const double threshold = 0.1; + + // Expected no duplicate occurrences of depth values + const std::vector expectedUnionDepthValues = { 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0 }; + + // Call the function under test + const std::vector unionDepthValues = + RimWellLogCalculatedCurve::unionDepthValuesFromVectors( depthValues1, depthValues2, threshold ); + + ASSERT_EQ( unionDepthValues, expectedUnionDepthValues ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RimWellLogCalculatedCurve, unionDepthValuesFromVectors_thresholdVerification ) +{ + // Depth vectors with duplicates + const std::vector depthValues1 = { 0.0, 0.5, 1.0, 1.5 }; + const std::vector depthValues2 = { 2.0, 2.5, 3.0, 4.0 }; + const double threshold = 0.6; + + // Expected every second value to be skipped due to threshold + const std::vector expectedUnionDepthValues = { 0.0, 1.0, 2.0, 3.0, 4.0 }; + + // Call the function under test + const std::vector unionDepthValues = + RimWellLogCalculatedCurve::unionDepthValuesFromVectors( depthValues1, depthValues2, threshold ); + + ASSERT_EQ( unionDepthValues, expectedUnionDepthValues ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RimWellLogCalculatedCurve, calculateValue ) +{ + ASSERT_DOUBLE_EQ( 7.0, RimWellLogCalculatedCurve::calculateValue( 5.0, 2.0, RimWellLogCalculatedCurve::Operators::ADD ) ); + ASSERT_DOUBLE_EQ( 3.0, RimWellLogCalculatedCurve::calculateValue( 5.0, 2.0, RimWellLogCalculatedCurve::Operators::SUBTRACT ) ); + ASSERT_DOUBLE_EQ( 2.5, RimWellLogCalculatedCurve::calculateValue( 5.0, 2.0, RimWellLogCalculatedCurve::Operators::DIVIDE ) ); + ASSERT_DOUBLE_EQ( 10.0, RimWellLogCalculatedCurve::calculateValue( 5.0, 2.0, RimWellLogCalculatedCurve::Operators::MULTIPLY ) ); + + // Divide by zero + ASSERT_DOUBLE_EQ( std::numeric_limits::infinity(), + RimWellLogCalculatedCurve::calculateValue( 5.0, 0.0, RimWellLogCalculatedCurve::Operators::DIVIDE ) ); +}