///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2024- 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 "RimWellTargetMapping.h" #include "RiaGuiApplication.h" #include "RiaLogging.h" #include "RiaPorosityModel.h" #include "RiaResultNames.h" #include "RiuMainWindow.h" #include "RigCaseCellResultsData.h" #include "RigEclipseCaseData.h" #include "RigEclipseResultAddress.h" #include "RigFloodingSettings.h" #include "RigStatisticsMath.h" #include "Well/RigWellTargetMapping.h" #include "RimCellFilter.h" #include "RimDataFilterCollection.h" #include "RimEclipseCase.h" #include "RimEclipseCaseCollection.h" #include "RimEclipseCaseEnsemble.h" #include "RimEclipseCellColors.h" #include "RimEclipsePropertyFilterCollection.h" #include "RimEclipseResultDefinition.h" #include "RimEclipseView.h" #include "RimEclipseViewCollection.h" #include "RimRegularGridCase.h" #include "RimReservoirGridEnsemble.h" #include "RimTools.h" #include "cafPdmUiDoubleSliderEditor.h" #include "cafPdmUiSliderTools.h" #include "cafPdmUiTreeOrdering.h" #include #include CAF_PDM_SOURCE_INIT( RimWellTargetMapping, "RimWellTargetMapping" ); namespace caf { template <> void caf::AppEnum::setUp() { addItem( RigWellTargetMapping::VolumeType::OIL, "OIL", "Oil" ); addItem( RigWellTargetMapping::VolumeType::GAS, "GAS", "Gas" ); addItem( RigWellTargetMapping::VolumeType::HYDROCARBON, "HYDROCARBON", "Hydrocarbon" ); setDefault( RigWellTargetMapping::VolumeType::OIL ); } template <> void caf::AppEnum::setUp() { addItem( RigWellTargetMapping::VolumeResultType::MOBILE, "MOBILE", "Mobile" ); addItem( RigWellTargetMapping::VolumeResultType::TOTAL, "TOTAL", "Total" ); setDefault( RigWellTargetMapping::VolumeResultType::TOTAL ); } template <> void caf::AppEnum::setUp() { addItem( RigWellTargetMapping::VolumesType::RESERVOIR_VOLUMES, "RESERVOIR_VOLUMES", "Reservoir Volumes (RFIPOIL, RFIPGAS)" ); addItem( RigWellTargetMapping::VolumesType::SURFACE_VOLUMES_SFIP, "SURFACE_VOLUMES_SFIP", "Surface Volumes (SFIPOIL, SFIPGAS)" ); addItem( RigWellTargetMapping::VolumesType::SURFACE_VOLUMES_FIP, "SURFACE_VOLUMES_FIP", "Surface Volumes (FIPOIL, FIPGAS)" ); addItem( RigWellTargetMapping::VolumesType::RESERVOIR_VOLUMES_COMPUTED, "RESERVOIR_VOLUMES_COMPUTED", "Reservoir Volumes (PORV*SOIL, PORV*SGAS)" ); setDefault( RigWellTargetMapping::VolumesType::RESERVOIR_VOLUMES_COMPUTED ); } } // End namespace caf //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimWellTargetMapping::RimWellTargetMapping() { CAF_PDM_InitObject( "Well Target Mapping", ":/WellTargets.png" ); setName( "Well Target Mapping" ); CAF_PDM_InitField( &m_timeStep, "TimeStep", 0, "Time Step" ); CAF_PDM_InitFieldNoDefault( &m_volumeType, "VolumeType", "Volume" ); CAF_PDM_InitFieldNoDefault( &m_volumeResultType, "VolumeResultType", "Result" ); CAF_PDM_InitFieldNoDefault( &m_volumesType, "VolumesType", "" ); CAF_PDM_InitFieldNoDefault( &m_oilFloodingType, "OilFloodingType", "Residual Oil Given By" ); m_oilFloodingType.setValue( RigFloodingSettings::FloodingType::WATER_FLOODING ); CAF_PDM_InitField( &m_userDefinedFloodingOil, "UserDefinedFloodingOil", 0.0, "" ); m_userDefinedFloodingOil.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleSliderEditor::uiEditorTypeName() ); m_userDefinedFloodingOil.uiCapability()->setAttribute( caf::PdmUiDoubleSliderEditor::Keys::MINIMUM, 0.0 ); m_userDefinedFloodingOil.uiCapability()->setAttribute( caf::PdmUiDoubleSliderEditor::Keys::MAXIMUM, 1.0 ); m_userDefinedFloodingOil.uiCapability()->setAttribute( caf::PdmUiDoubleSliderEditor::Keys::SLIDER_TICK_COUNT, 20 ); CAF_PDM_InitField( &m_gasFloodingType, "GasFloodingType", RigFloodingSettings::FloodingType::GAS_FLOODING, "Residual Oil-in-Gas Given By" ); caf::AppEnum::setEnumSubset( &m_gasFloodingType, { RigFloodingSettings::FloodingType::GAS_FLOODING, RigFloodingSettings::FloodingType::USER_DEFINED } ); CAF_PDM_InitField( &m_userDefinedFloodingGas, "UserDefinedFloodingGas", 0.0, "" ); m_userDefinedFloodingGas.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleSliderEditor::uiEditorTypeName() ); m_userDefinedFloodingGas.uiCapability()->setAttribute( caf::PdmUiDoubleSliderEditor::Keys::MINIMUM, 0.0 ); m_userDefinedFloodingGas.uiCapability()->setAttribute( caf::PdmUiDoubleSliderEditor::Keys::MAXIMUM, 1.0 ); m_userDefinedFloodingGas.uiCapability()->setAttribute( caf::PdmUiDoubleSliderEditor::Keys::SLIDER_TICK_COUNT, 20 ); CAF_PDM_InitField( &m_saturationOil, "SaturationOil", 0.0, "Saturation Oil" ); m_saturationOil.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleSliderEditor::uiEditorTypeName() ); CAF_PDM_InitField( &m_saturationGas, "SaturationGas", 0.0, "Saturation Gas" ); m_saturationGas.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleSliderEditor::uiEditorTypeName() ); CAF_PDM_InitField( &m_pressure, "Pressure", 0.0, "Pressure" ); m_pressure.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleSliderEditor::uiEditorTypeName() ); CAF_PDM_InitField( &m_permeability, "Permeability", 0.0, "Permeability [Kh]" ); m_permeability.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleSliderEditor::uiEditorTypeName() ); CAF_PDM_InitField( &m_transmissibility, "Transmissibility", 0.0, "Transmissibility" ); m_transmissibility.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleSliderEditor::uiEditorTypeName() ); CAF_PDM_InitField( &m_maxIterations, "Iterations", 100000, "Max Iterations" ); CAF_PDM_InitField( &m_maxNumTargets, "MaxNumTargets", 5, "Maximum Number of Well Targets" ); CAF_PDM_InitFieldNoDefault( &m_resultDefinition, "ResultDefinition", "" ); m_resultDefinition.uiCapability()->setUiTreeChildrenHidden( true ); m_resultDefinition = new RimEclipseResultDefinition; m_resultDefinition->findField( "MResultType" )->uiCapability()->setUiName( "Result" ); m_resultDefinition->setResultType( RiaDefines::ResultCatType::DYNAMIC_NATIVE ); m_resultDefinition->setResultVariable( "SOIL" ); CAF_PDM_InitField( &m_cellCountI, "CellCountI", 100, "Cell Count I" ); CAF_PDM_InitField( &m_cellCountJ, "CellCountJ", 100, "Cell Count J" ); CAF_PDM_InitField( &m_cellCountK, "CellCountK", 10, "Cell Count K" ); CAF_PDM_InitField( &m_expandBoundingBoxXYPercent, "ExpandBoundingBoxXYPercent", 5.0, "Expand Bounding Box XY [%]", "", "How much to increase the bounding box of the first case to cover for any grid size differences across the " "ensemble." ); CAF_PDM_InitField( &m_expandBoundingBoxZPercent, "ExpandBoundingBoxZPercent", 10.0, "Expand Bounding Box Z [%]", "", "How much to increase the bounding box of the first case to cover for any grid size differences across the " "ensemble." ); // "DataFilter" is the grow filter; keeping the keyword keeps old project files working (an empty start filter falls back to it). CAF_PDM_InitFieldNoDefault( &m_startDataFilter, "StartDataFilter", "Start Data Filter", "", "Limits where starting cells are searched for. If empty, the grow data filter is used." ); CAF_PDM_InitFieldNoDefault( &m_dataFilter, "DataFilter", "Grow Data Filter", "", "Limits where clusters can grow. If empty, clusters can grow anywhere in the model." ); CAF_PDM_InitField( &m_avoidFaults, "AvoidFaults", false, "Avoid Growing Across Faults", "", "Do not grow clusters across fault faces or fault NNCs. Also generates a result with the number of fault faces per " "cell." ); CAF_PDM_InitFieldNoDefault( &m_ensembleStatisticsCase, "EnsembleStatisticsCase", "Ensemble Statistics Case" ); m_minimumSaturationOil = cvf::UNDEFINED_DOUBLE; m_maximumSaturationOil = cvf::UNDEFINED_DOUBLE; m_defaultSaturationOil = cvf::UNDEFINED_DOUBLE; m_minimumSaturationGas = cvf::UNDEFINED_DOUBLE; m_maximumSaturationGas = cvf::UNDEFINED_DOUBLE; m_defaultSaturationGas = cvf::UNDEFINED_DOUBLE; m_minimumPressure = cvf::UNDEFINED_DOUBLE; m_maximumPressure = cvf::UNDEFINED_DOUBLE; m_defaultPressure = cvf::UNDEFINED_DOUBLE; m_minimumPermeability = cvf::UNDEFINED_DOUBLE; m_maximumPermeability = cvf::UNDEFINED_DOUBLE; m_defaultPermeability = cvf::UNDEFINED_DOUBLE; m_minimumTransmissibility = cvf::UNDEFINED_DOUBLE; m_maximumTransmissibility = cvf::UNDEFINED_DOUBLE; m_defaultTransmissibility = cvf::UNDEFINED_DOUBLE; setDeletable( true ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimWellTargetMapping::~RimWellTargetMapping() { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellTargetMapping::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) { if ( changedField != nameField() ) { updateAllBoundaries(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QList RimWellTargetMapping::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) { QList options; if ( fieldNeedingOptions == &m_timeStep ) { if ( auto fc = firstCase() ) { RimTools::timeStepsForCase( fc, &options ); } } else if ( fieldNeedingOptions == &m_volumesType ) { if ( auto fc = firstCase() ) { caf::AppEnum::setEnumSubset( &m_volumesType, findAvailableVolumesTypes( fc ) ); } } else if ( fieldNeedingOptions == &m_dataFilter || fieldNeedingOptions == &m_startDataFilter ) { options.push_back( caf::PdmOptionItemInfo( "None", nullptr ) ); if ( auto collection = dataFilterCollection() ) { for ( auto filter : collection->filters() ) { options.push_back( caf::PdmOptionItemInfo( filter->name(), filter ) ); } } } return options; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellTargetMapping::updateAllBoundaries() { RimEclipseCase* eclipseCase = firstCase(); if ( !eclipseCase ) return; eclipseCase->ensureReservoirCaseIsOpen(); auto resultsData = eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL ); if ( !resultsData ) return; const int timeStepIdx = m_timeStep(); auto updateBoundaryValues = []( auto resultsData, const std::vector& addresses, size_t timeStepIdx ) -> std::tuple { double globalMin = std::numeric_limits::max(); double globalMax = -std::numeric_limits::max(); std::vector allValues; for ( auto address : addresses ) { double currentMinimum; double currentMaximum; if ( resultsData->ensureKnownResultLoaded( address ) ) { resultsData->minMaxCellScalarValues( address, timeStepIdx, currentMinimum, currentMaximum ); globalMin = std::min( globalMin, currentMinimum ); globalMax = std::max( globalMax, currentMaximum ); const std::vector& values = resultsData->cellScalarResults( address, timeStepIdx ); allValues.insert( allValues.end(), values.begin(), values.end() ); } } double p10, p50, p90, mean; RigStatisticsMath::calculateStatisticsCurves( allValues, &p10, &p50, &p90, &mean, RigStatisticsMath::PercentileStyle::SWITCHED ); return { globalMin, globalMax, p90 }; }; std::tie( m_minimumSaturationOil, m_maximumSaturationOil, m_defaultSaturationOil ) = updateBoundaryValues( resultsData, { RigEclipseResultAddress( RiaDefines::ResultCatType::DYNAMIC_NATIVE, RiaResultNames::soil() ) }, timeStepIdx ); m_defaultSaturationOil = 0.3; std::tie( m_minimumSaturationGas, m_maximumSaturationGas, m_defaultSaturationGas ) = updateBoundaryValues( resultsData, { RigEclipseResultAddress( RiaDefines::ResultCatType::DYNAMIC_NATIVE, RiaResultNames::sgas() ) }, timeStepIdx ); m_defaultSaturationGas = 0.3; std::tie( m_minimumPressure, m_maximumPressure, m_defaultPressure ) = updateBoundaryValues( resultsData, { RigEclipseResultAddress( RiaDefines::ResultCatType::DYNAMIC_NATIVE, RiaResultNames::pressure() ) }, timeStepIdx ); std::tie( m_minimumPermeability, m_maximumPermeability, m_defaultPermeability ) = updateBoundaryValues( resultsData, { RigEclipseResultAddress( RiaDefines::ResultCatType::STATIC_NATIVE, "PERMX" ) }, 0 ); std::tie( m_minimumTransmissibility, m_maximumTransmissibility, m_defaultTransmissibility ) = updateBoundaryValues( resultsData, { RigEclipseResultAddress( RiaDefines::ResultCatType::STATIC_NATIVE, "TRANX" ), RigEclipseResultAddress( RiaDefines::ResultCatType::STATIC_NATIVE, "TRANY" ), RigEclipseResultAddress( RiaDefines::ResultCatType::STATIC_NATIVE, "TRANZ" ) }, 0 ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- cvf::Vec3st RimWellTargetMapping::getResultGridCellCount() const { return cvf::Vec3st( m_cellCountI, m_cellCountJ, m_cellCountK ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellTargetMapping::generateCandidates( RimEclipseCase* eclipseCase, bool setTimeStepInView ) { RigWellTargetMapping::ClusteringLimits limits = getClusteringLimits(); RigFloodingSettings floodingSettings( m_oilFloodingType(), m_userDefinedFloodingOil(), m_gasFloodingType(), m_userDefinedFloodingGas() ); bool skipUndefinedResults = true; RigWellTargetMapping::generateCandidates( eclipseCase, m_timeStep(), m_volumeType(), m_volumesType(), m_volumeResultType(), floodingSettings, limits, skipUndefinedResults, setTimeStepInView ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellTargetMapping::generateEnsembleStatistics() { auto ensemble = firstAncestorOrThisOfType(); if ( !ensemble ) return; const cvf::Vec3st& resultGridCellCount = getResultGridCellCount(); RigWellTargetMapping::ClusteringLimits limits = getClusteringLimits(); RigFloodingSettings floodingSettings( m_oilFloodingType(), m_userDefinedFloodingOil(), m_gasFloodingType(), m_userDefinedFloodingGas() ); double expandXY = ensemble->hasSharedGrid() ? 0.0 : m_expandBoundingBoxXYPercent(); double expandZ = ensemble->hasSharedGrid() ? 0.0 : m_expandBoundingBoxZPercent(); RimRegularGridCase* regularGridCase = RigWellTargetMapping::generateEnsembleCandidates( ensemble->cases(), m_timeStep(), resultGridCellCount, m_volumeType(), m_volumesType(), m_volumeResultType(), floodingSettings, limits, expandXY, expandZ ); regularGridCase->setCustomCaseName( "Ensemble Grid" ); regularGridCase->setDeletable( false ); if ( m_ensembleStatisticsCase() != nullptr ) { delete m_ensembleStatisticsCase(); } m_ensembleStatisticsCase = regularGridCase; auto eclipseView = regularGridCase->createAndAddReservoirView(); eclipseView->cellResult()->setResultType( RiaDefines::ResultCatType::GENERATED ); eclipseView->cellResult()->setResultVariable( "TOTAL_PORV_SOIL_P10" ); eclipseView->loadDataAndUpdate(); updateConnectedEditors(); m_ensembleStatisticsCase->updateConnectedEditors(); ensemble->reloadMetaDataIfNeeded(); if ( RiaGuiApplication::isRunning() || RiuMainWindow::instance() ) { RiuMainWindow::instance()->selectAsCurrentItem( eclipseView->cellResult() ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellTargetMapping::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) { uiOrdering.add( nameField() ); uiOrdering.addNewButton( "Compute", [this]() { onGenerateButtonClicked(); } ); caf::PdmUiGroup* resultGroup = uiOrdering.addNewGroup( "Result" ); resultGroup->add( &m_timeStep ); resultGroup->add( &m_volumeType ); resultGroup->add( &m_volumeResultType ); bool showGasOptions = m_volumeType() == RigWellTargetMapping::VolumeType::GAS || m_volumeType() == RigWellTargetMapping::VolumeType::HYDROCARBON; bool showOilOptions = m_volumeType() == RigWellTargetMapping::VolumeType::OIL || m_volumeType() == RigWellTargetMapping::VolumeType::HYDROCARBON; if ( m_volumeResultType() == RigWellTargetMapping::VolumeResultType::MOBILE ) { if ( showOilOptions ) { resultGroup->add( &m_oilFloodingType ); if ( m_oilFloodingType() == RigFloodingSettings::FloodingType::USER_DEFINED ) { resultGroup->add( &m_userDefinedFloodingOil ); } } if ( showGasOptions ) { resultGroup->add( &m_gasFloodingType ); if ( m_gasFloodingType() == RigFloodingSettings::FloodingType::USER_DEFINED ) { resultGroup->add( &m_userDefinedFloodingGas ); } } } resultGroup->add( &m_volumesType ); auto gridEnsemble = firstAncestorOrThisOfType(); auto hasEnsembleParent = firstAncestorOrThisOfType() != nullptr || gridEnsemble != nullptr; uiOrdering.add( &m_startDataFilter ); uiOrdering.add( &m_dataFilter ); uiOrdering.add( &m_avoidFaults ); auto setSliderRange = []( auto& field, double minimum, double maximum ) { if ( minimum == cvf::UNDEFINED_DOUBLE || maximum == cvf::UNDEFINED_DOUBLE ) caf::PdmUiDoubleSliderEditor::clearRange( field.uiCapability() ); else caf::PdmUiDoubleSliderEditor::setRange( field.uiCapability(), minimum, maximum ); field.uiCapability()->setAttribute( caf::PdmUiDoubleSliderEditor::Keys::DECIMALS, 3 ); }; setSliderRange( m_saturationOil, m_minimumSaturationOil, m_maximumSaturationOil ); setSliderRange( m_saturationGas, m_minimumSaturationGas, m_maximumSaturationGas ); setSliderRange( m_pressure, m_minimumPressure, m_maximumPressure ); setSliderRange( m_permeability, m_minimumPermeability, m_maximumPermeability ); setSliderRange( m_transmissibility, m_minimumTransmissibility, m_maximumTransmissibility ); caf::PdmUiGroup* minimumCellValuesGroup = uiOrdering.addNewGroup( "Minimum Cell Values" ); if ( showOilOptions ) minimumCellValuesGroup->add( &m_saturationOil ); if ( showGasOptions ) minimumCellValuesGroup->add( &m_saturationGas ); minimumCellValuesGroup->add( &m_pressure ); minimumCellValuesGroup->add( &m_permeability ); minimumCellValuesGroup->add( &m_transmissibility ); minimumCellValuesGroup->addNewButton( "Reset to Default", [this]() { resetMinimumCellValuesToDefault(); } ); if ( hasEnsembleParent ) { caf::PdmUiGroup* ensembleGridGroup = uiOrdering.addNewGroup( "Ensemble Statistics Grid" ); ensembleGridGroup->add( &m_cellCountI ); ensembleGridGroup->add( &m_cellCountJ ); ensembleGridGroup->add( &m_cellCountK ); if ( gridEnsemble && !gridEnsemble->hasSharedGrid() ) { ensembleGridGroup->add( &m_expandBoundingBoxXYPercent ); ensembleGridGroup->add( &m_expandBoundingBoxZPercent ); } } caf::PdmUiGroup* advancedGroup = uiOrdering.addNewGroup( "Advanced" ); advancedGroup->add( &m_maxNumTargets ); advancedGroup->setCollapsedByDefault(); uiOrdering.skipRemainingFields(); if ( m_minimumPressure == cvf::UNDEFINED_DOUBLE || m_maximumPressure == cvf::UNDEFINED_DOUBLE ) { updateAllBoundaries(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellTargetMapping::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName ) { if ( ensembleStatisticsCase() ) { uiTreeOrdering.add( &m_ensembleStatisticsCase ); } uiTreeOrdering.skipRemainingChildren(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigWellTargetMapping::ClusteringLimits RimWellTargetMapping::getClusteringLimits() const { return { .saturationOil = m_saturationOil, .saturationGas = m_saturationGas, .permeability = m_permeability, .pressure = m_pressure, .transmissibility = m_transmissibility, .maxNumTargets = m_maxNumTargets, .maxIterations = m_maxIterations, .avoidFaults = m_avoidFaults, .filterAddress = m_resultDefinition->eclipseResultAddress(), .startDataFilter = m_startDataFilter(), .growDataFilter = m_dataFilter() }; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimDataFilterCollection* RimWellTargetMapping::dataFilterCollection() const { if ( auto ensemble = firstAncestorOrThisOfType() ) { return ensemble->dataFilterCollection(); } if ( auto eclipseCase = firstAncestorOrThisOfType() ) { return eclipseCase->dataFilterCollection(); } return nullptr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimEclipseCase* RimWellTargetMapping::firstCase() const { auto ensemble = firstAncestorOrThisOfType(); if ( ensemble && !ensemble->cases().empty() ) return ensemble->cases()[0]; auto reservoirGridEnsemble = firstAncestorOrThisOfType(); if ( reservoirGridEnsemble && !reservoirGridEnsemble->cases().empty() ) return reservoirGridEnsemble->cases()[0]; return firstAncestorOrThisOfType(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellTargetMapping::initAfterRead() { if ( firstAncestorOrThisOfType() ) { return; // no need to to do anything for ensembles, as the data have been cached } else if ( RimEclipseCase* eclipseCase = firstCase() ) { // Automatically generate results on project load m_resultDefinition->setEclipseCase( eclipseCase ); bool setTimeStepInView = false; generateCandidates( eclipseCase, setTimeStepInView ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellTargetMapping::updateResultDefinition() { RimEclipseCase* eclipseCase = firstCase(); if ( eclipseCase ) m_resultDefinition->setEclipseCase( eclipseCase ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimEclipseCase* RimWellTargetMapping::ensembleStatisticsCase() const { return m_ensembleStatisticsCase; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellTargetMapping::setDefaults() { auto findFirstByPriority = []( const std::vector& priority, const std::vector& available ) { for ( auto pri : priority ) { if ( std::find( available.begin(), available.end(), pri ) != available.end() ) return pri; } return RigWellTargetMapping::VolumesType::RESERVOIR_VOLUMES_COMPUTED; }; // Use last available time step if ( RimEclipseCase* eclipseCase = firstCase() ) { m_timeStep = static_cast( eclipseCase->timeStepDates().size() ) - 1; std::vector volumesTypesByPriority = { RigWellTargetMapping::VolumesType::RESERVOIR_VOLUMES, RigWellTargetMapping::VolumesType::SURFACE_VOLUMES_SFIP, RigWellTargetMapping::VolumesType::SURFACE_VOLUMES_FIP, RigWellTargetMapping::VolumesType::RESERVOIR_VOLUMES_COMPUTED }; std::vector availableVolumesTypes = findAvailableVolumesTypes( eclipseCase ); m_volumesType = findFirstByPriority( volumesTypesByPriority, availableVolumesTypes ); updateAllBoundaries(); resetMinimumCellValuesToDefault(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellTargetMapping::resetMinimumCellValuesToDefault() { auto clampIfValid = []( double defaultValue, double minValue, double maxValue ) -> double { // Only clamp if we have valid bounds (min <= max) if ( minValue <= maxValue ) { return std::clamp( defaultValue, minValue, maxValue ); } // If bounds are invalid (no data), just return the default value return defaultValue; }; m_saturationOil = clampIfValid( m_defaultSaturationOil, m_minimumSaturationOil, m_maximumSaturationOil ); m_saturationGas = clampIfValid( m_defaultSaturationGas, m_minimumSaturationGas, m_maximumSaturationGas ); m_pressure = clampIfValid( m_defaultPressure, m_minimumPressure, m_maximumPressure ); m_permeability = clampIfValid( m_defaultPermeability, m_minimumPermeability, m_maximumPermeability ); m_transmissibility = clampIfValid( m_defaultTransmissibility, std::max( m_minimumTransmissibility, 0.1 ), m_maximumTransmissibility ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellTargetMapping::onGenerateButtonClicked() { if ( firstAncestorOrThisOfType() != nullptr ) { generateEnsembleStatistics(); } else if ( auto eclipseCase = firstCase() ) { generateCandidates( eclipseCase ); if ( auto views = eclipseCase->reservoirViews(); !views.empty() ) { auto eclipseView = views.front(); eclipseView->cellResult()->setResultType( RiaDefines::ResultCatType::GENERATED ); eclipseView->cellResult()->setResultVariable( RigWellTargetMapping::wellTargetResultName() ); eclipseView->cellResult()->updateConnectedEditors(); if ( eclipseView->eclipsePropertyFilterCollection()->propertyFilters().empty() ) { eclipseView->eclipsePropertyFilterCollection()->addFilterLinkedToCellResult(); eclipseView->eclipsePropertyFilterCollection()->updateConnectedEditors(); } if ( RiaGuiApplication::isRunning() || RiuMainWindow::instance() ) { RiuMainWindow::instance()->selectAsCurrentItem( eclipseView->cellResult() ); } } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimWellTargetMapping::findAvailableVolumesTypes( RimEclipseCase* eclipseCase ) { auto hasResult = []( RigCaseCellResultsData& resultsData, const QString& resultName ) -> bool { RigEclipseResultAddress address( RiaDefines::ResultCatType::DYNAMIC_NATIVE, resultName ); return resultsData.ensureKnownResultLoaded( address ); }; auto resultsData = eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL ); if ( !resultsData ) return {}; std::vector availableVolumesTypes; if ( hasResult( *resultsData, RiaResultNames::riPorvSoil() ) || hasResult( *resultsData, RiaResultNames::riPorvSgas() ) ) availableVolumesTypes.push_back( RigWellTargetMapping::VolumesType::RESERVOIR_VOLUMES_COMPUTED ); if ( hasResult( *resultsData, "RFIPOIL" ) || hasResult( *resultsData, "RFIPGAS" ) ) availableVolumesTypes.push_back( RigWellTargetMapping::VolumesType::RESERVOIR_VOLUMES ); if ( hasResult( *resultsData, "SFIPOIL" ) || hasResult( *resultsData, "SFIPGAS" ) ) availableVolumesTypes.push_back( RigWellTargetMapping::VolumesType::SURFACE_VOLUMES_SFIP ); if ( hasResult( *resultsData, "FIPOIL" ) || hasResult( *resultsData, "FIPGAS" ) ) availableVolumesTypes.push_back( RigWellTargetMapping::VolumesType::SURFACE_VOLUMES_FIP ); return availableVolumesTypes; }