diff --git a/ApplicationCode/GeoMech/GeoMechDataModel/CMakeLists.txt b/ApplicationCode/GeoMech/GeoMechDataModel/CMakeLists.txt index 12401cd752..407677f59e 100644 --- a/ApplicationCode/GeoMech/GeoMechDataModel/CMakeLists.txt +++ b/ApplicationCode/GeoMech/GeoMechDataModel/CMakeLists.txt @@ -94,6 +94,8 @@ add_library( ${PROJECT_NAME} RigFemPartResultCalculatorStressAnisotropy.cpp RigFemPartResultCalculatorPoreCompressibility.h RigFemPartResultCalculatorPoreCompressibility.cpp + RigFemPartResultCalculatorPorosityPermeability.h + RigFemPartResultCalculatorPorosityPermeability.cpp RimGeoMechGeometrySelectionItem.h RimGeoMechGeometrySelectionItem.cpp ) diff --git a/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultCalculatorPorosityPermeability.cpp b/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultCalculatorPorosityPermeability.cpp new file mode 100644 index 0000000000..7d3abde0e8 --- /dev/null +++ b/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultCalculatorPorosityPermeability.cpp @@ -0,0 +1,243 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2020- 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 "RigFemPartResultCalculatorPorosityPermeability.h" + +#include "RiaEclipseUnitTools.h" +#include "RiaLogging.h" + +#include "RigFemPart.h" +#include "RigFemPartCollection.h" +#include "RigFemPartResultsCollection.h" +#include "RigFemResultAddress.h" +#include "RigFemScalarResultFrames.h" + +#include "cafProgressInfo.h" + +#include + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RigFemPartResultCalculatorPorosityPermeability::RigFemPartResultCalculatorPorosityPermeability( + RigFemPartResultsCollection& collection ) + : RigFemPartResultCalculator( collection ) +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RigFemPartResultCalculatorPorosityPermeability::~RigFemPartResultCalculatorPorosityPermeability() +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RigFemPartResultCalculatorPorosityPermeability::isMatching( const RigFemResultAddress& resVarAddr ) const +{ + return ( resVarAddr.fieldName == "POROSITY-PERMEABILITY" && + ( resVarAddr.componentName == "PHI" || resVarAddr.componentName == "DPHI" || + resVarAddr.componentName == "PERM" ) ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RigFemScalarResultFrames* + RigFemPartResultCalculatorPorosityPermeability::calculate( int partIndex, const RigFemResultAddress& resVarAddr ) +{ + caf::ProgressInfo frameCountProgress( m_resultCollection->frameCount() * 6, "" ); + frameCountProgress.setProgressDescription( "Calculating Porosity/Permeability" ); + + frameCountProgress.setNextProgressIncrement( m_resultCollection->frameCount() ); + + RigFemScalarResultFrames* srcPorePressureDataFrames = + m_resultCollection->findOrLoadScalarResult( partIndex, RigFemResultAddress( RIG_NODAL, "POR-Bar", "" ) ); + frameCountProgress.incrementProgress(); + + // Volumetric Strain + frameCountProgress.setNextProgressIncrement( m_resultCollection->frameCount() ); + RigFemScalarResultFrames* srcEVDataFrames = + m_resultCollection->findOrLoadScalarResult( partIndex, RigFemResultAddress( resVarAddr.resultPosType, "NE", "EV" ) ); + + frameCountProgress.incrementProgress(); + + // Pore Compressibility + frameCountProgress.setNextProgressIncrement( m_resultCollection->frameCount() ); + RigFemScalarResultFrames* poreCompressibilityFrames = + m_resultCollection->findOrLoadScalarResult( partIndex, + RigFemResultAddress( resVarAddr.resultPosType, "COMPRESSIBILITY", "PORE" ) ); + if ( poreCompressibilityFrames->frameData( 0 ).empty() ) + { + RiaLogging::error( "Missing pore compressibility data." ); + return nullptr; + } + + frameCountProgress.incrementProgress(); + + // Initial permeability (k0) + frameCountProgress.setNextProgressIncrement( m_resultCollection->frameCount() ); + RigFemScalarResultFrames* initialPermeabilityFrames = nullptr; + if ( !m_resultCollection->initialPermeabilityAddress().isEmpty() ) + { + initialPermeabilityFrames = + m_resultCollection + ->findOrLoadScalarResult( partIndex, + RigFemResultAddress( RIG_ELEMENT, + m_resultCollection->initialPermeabilityAddress().toStdString(), + "" ) ); + } + frameCountProgress.incrementProgress(); + + frameCountProgress.setNextProgressIncrement( m_resultCollection->frameCount() ); + + RigFemScalarResultFrames* voidRatioFrames = + m_resultCollection->findOrLoadScalarResult( partIndex, + RigFemResultAddress( resVarAddr.resultPosType, "VOIDR", "" ) ); + + RigFemScalarResultFrames* porosityFrames = + m_resultCollection->createScalarResult( partIndex, + RigFemResultAddress( resVarAddr.resultPosType, resVarAddr.fieldName, "PHI" ) ); + RigFemScalarResultFrames* porosityDeltaFrames = + m_resultCollection->createScalarResult( partIndex, + RigFemResultAddress( resVarAddr.resultPosType, resVarAddr.fieldName, "DPHI" ) ); + RigFemScalarResultFrames* permeabilityFrames = + m_resultCollection->createScalarResult( partIndex, + RigFemResultAddress( resVarAddr.resultPosType, resVarAddr.fieldName, "PERM" ) ); + frameCountProgress.incrementProgress(); + + const RigFemPart* femPart = m_resultCollection->parts()->part( partIndex ); + float inf = std::numeric_limits::infinity(); + + frameCountProgress.setNextProgressIncrement( 1u ); + + int referenceFrameIdx = m_resultCollection->referenceTimeStep(); + + int frameCount = srcEVDataFrames->frameCount(); + for ( int fIdx = 0; fIdx < frameCount; ++fIdx ) + { + const std::vector& evData = srcEVDataFrames->frameData( fIdx ); + const std::vector& referenceEvData = srcEVDataFrames->frameData( referenceFrameIdx ); + const std::vector& voidRatioData = voidRatioFrames->frameData( 0 ); + const std::vector& referencePorFrameData = srcPorePressureDataFrames->frameData( referenceFrameIdx ); + const std::vector& porFrameData = srcPorePressureDataFrames->frameData( fIdx ); + const std::vector& poreCompressibilityFrameData = poreCompressibilityFrames->frameData( fIdx ); + + std::vector& porosityFrameData = porosityFrames->frameData( fIdx ); + std::vector& porosityDeltaFrameData = porosityDeltaFrames->frameData( fIdx ); + std::vector& permeabilityFrameData = permeabilityFrames->frameData( fIdx ); + + size_t valCount = evData.size(); + porosityFrameData.resize( valCount ); + porosityDeltaFrameData.resize( valCount ); + permeabilityFrameData.resize( valCount ); + + int elementCount = femPart->elementCount(); + + std::vector initialPermeabilityData; + if ( initialPermeabilityFrames ) + { + initialPermeabilityData = initialPermeabilityFrames->frameData( fIdx ); + } + +#pragma omp parallel for + for ( int elmIdx = 0; elmIdx < elementCount; ++elmIdx ) + { + RigElementType elmType = femPart->elementType( elmIdx ); + + int elmNodeCount = RigFemTypes::elementNodeCount( femPart->elementType( elmIdx ) ); + + if ( elmType == HEX8P ) + { + for ( int elmNodIdx = 0; elmNodIdx < elmNodeCount; ++elmNodIdx ) + { + size_t elmNodResIdx = femPart->elementNodeResultIdx( elmIdx, elmNodIdx ); + if ( elmNodResIdx < evData.size() ) + { + // User provides initial permeability + double initialPermeability = 1.0; + if ( initialPermeabilityData.empty() ) + { + // 1. Same value for all cells + initialPermeability = m_resultCollection->initialPermeabilityFixed(); + } + else + { + // 2. From element property table + initialPermeability = initialPermeabilityData[elmIdx]; + } + + int nodeIdx = femPart->nodeIdxFromElementNodeResultIdx( elmNodResIdx ); + + // Calculate initial porosity + double voidr = voidRatioData[elmNodResIdx]; + double initialPorosity = voidr / ( 1.0 + voidr ); + + // Calculate difference in pore pressure between reference state and this state, + // and convert unit from Bar to Pascal. + double referencePorePressure = referencePorFrameData[nodeIdx]; + double framePorePressure = porFrameData[nodeIdx]; + double deltaPorePressure = + RiaEclipseUnitTools::barToPascal( framePorePressure - referencePorePressure ); + + // Pore compressibility. Convert from 1/GPa to 1/Pa. + double poreCompressibility = poreCompressibilityFrameData[elmNodResIdx] / 1.0e9; + + // Volumetric strain + double deltaEv = evData[elmNodResIdx] - referenceEvData[elmNodResIdx]; + + // Porosity change between reference state and initial state (geostatic) + double deltaPorosity = initialPorosity * ( poreCompressibility * deltaPorePressure + deltaEv ); + + // Current porosity + double currentPorosity = initialPorosity + deltaPorosity; + + // Permeability. Formula from Petunin, 2011. + double permeabilityExponent = m_resultCollection->permeabilityExponent(); + double permeability = + initialPermeability * std::pow( currentPorosity / initialPorosity, permeabilityExponent ); + + porosityFrameData[elmNodResIdx] = currentPorosity; + porosityDeltaFrameData[elmNodResIdx] = deltaPorosity; + permeabilityFrameData[elmNodResIdx] = permeability; + } + } + } + else + { + for ( int elmNodIdx = 0; elmNodIdx < elmNodeCount; ++elmNodIdx ) + { + size_t elmNodResIdx = femPart->elementNodeResultIdx( elmIdx, elmNodIdx ); + if ( elmNodResIdx < poreCompressibilityFrameData.size() ) + { + porosityFrameData[elmNodResIdx] = inf; + porosityDeltaFrameData[elmNodResIdx] = inf; + permeabilityFrameData[elmNodResIdx] = inf; + } + } + } + } + + frameCountProgress.incrementProgress(); + } + + RigFemScalarResultFrames* requestedResultFrames = m_resultCollection->findOrLoadScalarResult( partIndex, resVarAddr ); + return requestedResultFrames; +} diff --git a/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultCalculatorPorosityPermeability.h b/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultCalculatorPorosityPermeability.h new file mode 100644 index 0000000000..d94741c11b --- /dev/null +++ b/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultCalculatorPorosityPermeability.h @@ -0,0 +1,37 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2020- Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "RigFemPartResultCalculator.h" + +class RigFemPartResultsCollection; +class RigFemScalarResultFrames; +class RigFemResultAddress; + +//================================================================================================== +/// +//================================================================================================== +class RigFemPartResultCalculatorPorosityPermeability : public RigFemPartResultCalculator +{ +public: + explicit RigFemPartResultCalculatorPorosityPermeability( RigFemPartResultsCollection& collection ); + virtual ~RigFemPartResultCalculatorPorosityPermeability(); + bool isMatching( const RigFemResultAddress& resVarAddr ) const override; + RigFemScalarResultFrames* calculate( int partIndex, const RigFemResultAddress& resVarAddr ) override; +}; diff --git a/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultsCollection.cpp b/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultsCollection.cpp index dae9c98071..f427b2263a 100644 --- a/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultsCollection.cpp +++ b/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultsCollection.cpp @@ -23,7 +23,6 @@ #include "RifElementPropertyReader.h" #include "RifGeoMechReaderInterface.h" -#include "RigFemPartResultCalculatorPoreCompressibility.h" #ifdef USE_ODB_API #include "RifOdbReader.h" @@ -45,6 +44,8 @@ #include "RigFemPartResultCalculatorNormalSE.h" #include "RigFemPartResultCalculatorNormalST.h" #include "RigFemPartResultCalculatorNormalized.h" +#include "RigFemPartResultCalculatorPoreCompressibility.h" +#include "RigFemPartResultCalculatorPorosityPermeability.h" #include "RigFemPartResultCalculatorPrincipalStrain.h" #include "RigFemPartResultCalculatorPrincipalStress.h" #include "RigFemPartResultCalculatorQ.h" @@ -113,6 +114,10 @@ RigFemPartResultsCollection::RigFemPartResultsCollection( RifGeoMechReaderInterf m_referenceTimeStep = 0; + m_initialPermeabilityFixed = 1.0; + m_initialPermeabilityResultAddress = ""; + m_permeabilityExponent = 1.0; + m_resultCalculators.push_back( std::unique_ptr( new RigFemPartResultCalculatorTimeLapse( *this ) ) ); m_resultCalculators.push_back( @@ -166,6 +171,8 @@ RigFemPartResultsCollection::RigFemPartResultsCollection( RifGeoMechReaderInterf std::unique_ptr( new RigFemPartResultCalculatorStressAnisotropy( *this ) ) ); m_resultCalculators.push_back( std::unique_ptr( new RigFemPartResultCalculatorPoreCompressibility( *this ) ) ); + m_resultCalculators.push_back( + std::unique_ptr( new RigFemPartResultCalculatorPorosityPermeability( *this ) ) ); m_resultCalculators.push_back( std::unique_ptr( new RigFemPartResultCalculatorFormationIndices( *this ) ) ); } @@ -374,6 +381,13 @@ void RigFemPartResultsCollection::setBiotCoefficientParameters( double biotFixed deleteResult( RigFemResultAddress( elementType, "ST", "Q", RigFemResultAddress::allTimeLapsesValue() ) ); } + // Depends on COMRESSIBILITY.PORE which depends on biot coefficient + std::set initPermResults = initialPermeabilityDependentResults(); + for ( auto result : initPermResults ) + { + deleteResult( result ); + } + for ( auto fieldName : {"SE", "ST"} ) { // Surface aligned stress @@ -419,6 +433,48 @@ int RigFemPartResultsCollection::referenceTimeStep() const return m_referenceTimeStep; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RigFemPartResultsCollection::setPermeabilityParameters( double fixedInitalPermeability, + const QString& initialPermeabilityAddress, + double permeabilityExponent ) +{ + m_initialPermeabilityFixed = fixedInitalPermeability; + m_initialPermeabilityResultAddress = initialPermeabilityAddress; + m_permeabilityExponent = permeabilityExponent; + + std::set results = initialPermeabilityDependentResults(); + for ( auto result : results ) + { + deleteResult( result ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +double RigFemPartResultsCollection::initialPermeabilityFixed() const +{ + return m_initialPermeabilityFixed; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RigFemPartResultsCollection::initialPermeabilityAddress() const +{ + return m_initialPermeabilityResultAddress; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +double RigFemPartResultsCollection::permeabilityExponent() const +{ + return m_permeabilityExponent; +} + //-------------------------------------------------------------------------------------------------- /// Will always return a valid object, but it can be empty //-------------------------------------------------------------------------------------------------- @@ -636,6 +692,10 @@ std::map> fieldCompNames["COMPRESSIBILITY"].push_back( "PORE" ); fieldCompNames["COMPRESSIBILITY"].push_back( "VERTICAL" ); fieldCompNames["COMPRESSIBILITY"].push_back( "VERTICAL-RATIO" ); + + fieldCompNames["POROSITY-PERMEABILITY"].push_back( "PHI" ); + fieldCompNames["POROSITY-PERMEABILITY"].push_back( "DPHI" ); + fieldCompNames["POROSITY-PERMEABILITY"].push_back( "PERM" ); } else if ( resPos == RIG_INTEGRATION_POINT ) { @@ -715,6 +775,10 @@ std::map> fieldCompNames["COMPRESSIBILITY"].push_back( "PORE" ); fieldCompNames["COMPRESSIBILITY"].push_back( "VERTICAL" ); fieldCompNames["COMPRESSIBILITY"].push_back( "VERTICAL-RATIO" ); + + fieldCompNames["POROSITY-PERMEABILITY"].push_back( "PHI" ); + fieldCompNames["POROSITY-PERMEABILITY"].push_back( "DPHI" ); + fieldCompNames["POROSITY-PERMEABILITY"].push_back( "PERM" ); } else if ( resPos == RIG_ELEMENT_NODAL_FACE ) { @@ -1242,11 +1306,35 @@ std::set RigFemPartResultsCollection::referenceCaseDependen "COMPRESSIBILITY", "VERTICAL-RATIO", RigFemResultAddress::allTimeLapsesValue() ) ); + results.insert( + RigFemResultAddress( elementType, "POROSITY-PERMEABILITY", "PHI", RigFemResultAddress::allTimeLapsesValue() ) ); + results.insert( + RigFemResultAddress( elementType, "POROSITY-PERMEABILITY", "DPHI", RigFemResultAddress::allTimeLapsesValue() ) ); + results.insert( + RigFemResultAddress( elementType, "POROSITY-PERMEABILITY", "PERM", RigFemResultAddress::allTimeLapsesValue() ) ); } return results; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::set RigFemPartResultsCollection::initialPermeabilityDependentResults() +{ + std::set results; + for ( auto elementType : {RIG_ELEMENT_NODAL, RIG_INTEGRATION_POINT} ) + { + results.insert( + RigFemResultAddress( elementType, "POROSITY-PERMEABILITY", "PHI", RigFemResultAddress::allTimeLapsesValue() ) ); + results.insert( + RigFemResultAddress( elementType, "POROSITY-PERMEABILITY", "DPHI", RigFemResultAddress::allTimeLapsesValue() ) ); + results.insert( + RigFemResultAddress( elementType, "POROSITY-PERMEABILITY", "PERM", RigFemResultAddress::allTimeLapsesValue() ) ); + return results; + } +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultsCollection.h b/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultsCollection.h index f4170236f0..9538dd28b2 100644 --- a/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultsCollection.h +++ b/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultsCollection.h @@ -73,6 +73,13 @@ public: double biotFixedFactor() const { return m_biotFixedFactor; } QString biotResultAddress() const { return m_biotResultAddress; } + void setPermeabilityParameters( double fixedInitalPermeability, + const QString& initialPermeabilityAddress, + double permeabilityExponent ); + double initialPermeabilityFixed() const; + QString initialPermeabilityAddress() const; + double permeabilityExponent() const; + std::map> scalarFieldAndComponentNames( RigFemResultPosEnum resPos ); std::vector filteredStepNames() const; bool assertResultsLoaded( const RigFemResultAddress& resVarAddr ); @@ -136,6 +143,8 @@ public: static std::set referenceCaseDependentResults(); static bool isReferenceCaseDependentResult( const RigFemResultAddress& result ); + static std::set initialPermeabilityDependentResults(); + RigFemScalarResultFrames* findOrLoadScalarResult( int partIndex, const RigFemResultAddress& resVarAddr ); RigFemScalarResultFrames* createScalarResult( int partIndex, const RigFemResultAddress& resVarAddr ); @@ -162,6 +171,10 @@ private: double m_biotFixedFactor; QString m_biotResultAddress; + double m_initialPermeabilityFixed; + QString m_initialPermeabilityResultAddress; + double m_permeabilityExponent; + int m_referenceTimeStep; std::vector> m_resultCalculators; diff --git a/ApplicationCode/ProjectDataModel/RimGeoMechCase.cpp b/ApplicationCode/ProjectDataModel/RimGeoMechCase.cpp index f7eb653b73..c1c8bfc4d5 100644 --- a/ApplicationCode/ProjectDataModel/RimGeoMechCase.cpp +++ b/ApplicationCode/ProjectDataModel/RimGeoMechCase.cpp @@ -80,6 +80,18 @@ void caf::AppEnum::setUp() setDefault( RimGeoMechCase::BiotCoefficientType::BIOT_NONE ); } +template <> +void caf::AppEnum::setUp() +{ + addItem( RimGeoMechCase::InitialPermeabilityType::INITIAL_PERMEABILITY_FIXED, + "INITIAL_PERMEABILITY_FIXED", + "Fixed initial permeability" ); + addItem( RimGeoMechCase::InitialPermeabilityType::INITIAL_PERMEABILITY_PER_ELEMENT, + "INITIAL_PERMEABILITY_PER_ELEMENT", + "Initial permeability from element properties" ); + setDefault( RimGeoMechCase::InitialPermeabilityType::INITIAL_PERMEABILITY_FIXED ); +} + } // End namespace caf //-------------------------------------------------------------------------------------------------- @@ -140,6 +152,24 @@ RimGeoMechCase::RimGeoMechCase( void ) CAF_PDM_InitField( &m_biotResultAddress, "BiotResultAddress", QString( "" ), "Value", "", "", "" ); m_biotResultAddress.uiCapability()->setUiEditorTypeName( caf::PdmUiListEditor::uiEditorTypeName() ); + caf::AppEnum defaultInitialPermeabilityType = + RimGeoMechCase::InitialPermeabilityType::INITIAL_PERMEABILITY_FIXED; + CAF_PDM_InitField( &m_initialPermeabilityType, + "InitialPermeabilityType", + defaultInitialPermeabilityType, + "Initial Permeability", + "", + "", + "" ); + CAF_PDM_InitField( &m_initialPermeabilityFixed, "InitialPermeabilityFixed", 1.0, "Fixed Initial Permeability", "", "", "" ); + m_initialPermeabilityFixed.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleValueEditor::uiEditorTypeName() ); + + CAF_PDM_InitField( &m_initialPermeabilityResultAddress, "InitialPermeabilityAddress", QString( "" ), "Value", "", "", "" ); + m_initialPermeabilityResultAddress.uiCapability()->setUiEditorTypeName( caf::PdmUiListEditor::uiEditorTypeName() ); + + CAF_PDM_InitField( &m_permeabilityExponent, "PermeabilityExponent", 1.0, "Permeability Exponent", "", "", "" ); + m_permeabilityExponent.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleValueEditor::uiEditorTypeName() ); + CAF_PDM_InitFieldNoDefault( &m_contourMapCollection, "ContourMaps", "2d Contour Maps", "", "", "" ); m_contourMapCollection = new RimGeoMechContourMapViewCollection; m_contourMapCollection.uiCapability()->setUiTreeHidden( true ); @@ -616,6 +646,38 @@ QString RimGeoMechCase::biotResultAddress() const return m_biotResultAddress; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimGeoMechCase::InitialPermeabilityType RimGeoMechCase::initialPermeabilityType() const +{ + return m_initialPermeabilityType(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +double RimGeoMechCase::initialPermeabilityFixed() const +{ + return m_initialPermeabilityFixed; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimGeoMechCase::initialPermeabilityAddress() const +{ + return m_initialPermeabilityResultAddress; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +double RimGeoMechCase::permeabilityExponent() const +{ + return m_permeabilityExponent; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -739,6 +801,56 @@ void RimGeoMechCase::fieldChangedByUi( const caf::PdmFieldHandle* changedField, updateConnectedViews(); } + else if ( changedField == &m_initialPermeabilityFixed || changedField == &m_initialPermeabilityType || + changedField == &m_initialPermeabilityResultAddress || changedField == &m_permeabilityExponent ) + { + RigGeoMechCaseData* rigCaseData = geoMechData(); + if ( rigCaseData && rigCaseData->femPartResults() ) + { + if ( m_initialPermeabilityType() == RimGeoMechCase::InitialPermeabilityType::INITIAL_PERMEABILITY_FIXED ) + { + rigCaseData->femPartResults()->setPermeabilityParameters( initialPermeabilityFixed(), + "", + permeabilityExponent() ); + } + else if ( m_initialPermeabilityType() == + RimGeoMechCase::InitialPermeabilityType::INITIAL_PERMEABILITY_PER_ELEMENT ) + { + if ( changedField == &m_initialPermeabilityType ) + { + // Show info message to user when selecting "from file" option before + // an element property has been imported + std::vector elementProperties = possibleElementPropertyFieldNames(); + if ( elementProperties.empty() ) + { + QString importMessage = + QString( "Please import initial permeability from file (typically called perm.inp) by " + "selecting 'Import Element Property Table' on the Geomechanical Model." ); + RiaLogging::info( importMessage ); + // Set back to default value + m_initialPermeabilityType = RimGeoMechCase::InitialPermeabilityType::INITIAL_PERMEABILITY_FIXED; + return; + } + } + + if ( initialPermeabilityAddress().isEmpty() ) + { + // Automatically select the first available property element if empty + std::vector elementProperties = possibleElementPropertyFieldNames(); + if ( !elementProperties.empty() ) + { + m_initialPermeabilityResultAddress = QString::fromStdString( elementProperties[0] ); + } + } + + rigCaseData->femPartResults()->setPermeabilityParameters( initialPermeabilityFixed(), + initialPermeabilityAddress(), + permeabilityExponent() ); + } + } + + updateConnectedViews(); + } else if ( changedField == &m_reloadElementPropertyFileCommand ) { m_reloadElementPropertyFileCommand = false; @@ -977,6 +1089,16 @@ void RimGeoMechCase::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& m_biotResultAddress.uiCapability()->setUiHidden( m_biotCoefficientType != RimGeoMechCase::BiotCoefficientType::BIOT_PER_ELEMENT ); + caf::PdmUiGroup* permeabilityGroup = uiOrdering.addNewGroup( "Permeability" ); + permeabilityGroup->add( &m_initialPermeabilityType ); + permeabilityGroup->add( &m_initialPermeabilityFixed ); + permeabilityGroup->add( &m_initialPermeabilityResultAddress ); + m_initialPermeabilityFixed.uiCapability()->setUiHidden( + m_initialPermeabilityType != RimGeoMechCase::InitialPermeabilityType::INITIAL_PERMEABILITY_FIXED ); + m_initialPermeabilityResultAddress.uiCapability()->setUiHidden( + m_initialPermeabilityType != RimGeoMechCase::InitialPermeabilityType::INITIAL_PERMEABILITY_PER_ELEMENT ); + permeabilityGroup->add( &m_permeabilityExponent ); + caf::PdmUiGroup* timeStepFilterGroup = uiOrdering.addNewGroup( "Time Step Filter" ); timeStepFilterGroup->setCollapsedByDefault( true ); m_timeStepFilter->uiOrdering( uiConfigName, *timeStepFilterGroup ); @@ -1030,7 +1152,7 @@ QList RimGeoMechCase::calculateValueOptions( const caf:: options.push_back( caf::PdmOptionItemInfo( m_elementPropertyFileNames.v().at( i ).path(), (int)i, true ) ); } } - else if ( fieldNeedingOptions == &m_biotResultAddress ) + else if ( fieldNeedingOptions == &m_biotResultAddress || fieldNeedingOptions == &m_initialPermeabilityResultAddress ) { std::vector elementProperties = possibleElementPropertyFieldNames(); diff --git a/ApplicationCode/ProjectDataModel/RimGeoMechCase.h b/ApplicationCode/ProjectDataModel/RimGeoMechCase.h index 4eb2475e13..d9793441f8 100644 --- a/ApplicationCode/ProjectDataModel/RimGeoMechCase.h +++ b/ApplicationCode/ProjectDataModel/RimGeoMechCase.h @@ -60,6 +60,12 @@ public: BIOT_PER_ELEMENT }; + enum class InitialPermeabilityType + { + INITIAL_PERMEABILITY_FIXED = 0, + INITIAL_PERMEABILITY_PER_ELEMENT + }; + RimGeoMechCase( void ); ~RimGeoMechCase( void ) override; @@ -100,6 +106,11 @@ public: double biotFixedCoefficient() const; QString biotResultAddress() const; + InitialPermeabilityType initialPermeabilityType() const; + double initialPermeabilityFixed() const; + QString initialPermeabilityAddress() const; + double permeabilityExponent() const; + private: cvf::Vec3d displayModelOffset() const override; static std::vector vectorOfValidDateTimesFromTimeStepStrings( const QStringList& timeStepStrings ); @@ -143,6 +154,11 @@ private: caf::PdmField m_biotFixedCoefficient; caf::PdmField m_biotResultAddress; + caf::PdmField> m_initialPermeabilityType; + caf::PdmField m_initialPermeabilityFixed; + caf::PdmField m_initialPermeabilityResultAddress; + caf::PdmField m_permeabilityExponent; + caf::PdmChildField m_contourMapCollection; bool m_applyTimeFilter;