diff --git a/ApplicationCode/Commands/RicImportElementPropertyFeature.cpp b/ApplicationCode/Commands/RicImportElementPropertyFeature.cpp index 4acae520f9..97292d4bfd 100644 --- a/ApplicationCode/Commands/RicImportElementPropertyFeature.cpp +++ b/ApplicationCode/Commands/RicImportElementPropertyFeature.cpp @@ -43,6 +43,14 @@ bool RicImportElementPropertyFeature::isCommandEnabled() /// //-------------------------------------------------------------------------------------------------- void RicImportElementPropertyFeature::onActionTriggered( bool isChecked ) +{ + importElementProperties(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicImportElementPropertyFeature::importElementProperties() { RiaApplication* app = RiaApplication::instance(); diff --git a/ApplicationCode/Commands/RicImportElementPropertyFeature.h b/ApplicationCode/Commands/RicImportElementPropertyFeature.h index e32ff49fd2..5797b8b987 100644 --- a/ApplicationCode/Commands/RicImportElementPropertyFeature.h +++ b/ApplicationCode/Commands/RicImportElementPropertyFeature.h @@ -27,6 +27,8 @@ class RicImportElementPropertyFeature : public caf::CmdFeature { CAF_CMD_HEADER_INIT; + static void importElementProperties(); + protected: bool isCommandEnabled() override; void onActionTriggered( bool isChecked ) override; diff --git a/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultsCollection.cpp b/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultsCollection.cpp index 513af98b73..32fdfc7dc9 100644 --- a/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultsCollection.cpp +++ b/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultsCollection.cpp @@ -19,6 +19,8 @@ #include "RigFemPartResultsCollection.h" +#include "RiaLogging.h" + #include "RifElementPropertyReader.h" #include "RifGeoMechReaderInterface.h" @@ -104,6 +106,9 @@ RigFemPartResultsCollection::RigFemPartResultsCollection( RifGeoMechReaderInterf m_cohesion = 10.0; m_frictionAngleRad = cvf::Math::toRadians( 30.0 ); m_normalizationAirGap = 0.0; + + m_biotFixedFactor = 1.0; + m_biotResultAddress = ""; } //-------------------------------------------------------------------------------------------------- @@ -213,6 +218,25 @@ std::vector return addressesToRemove; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::map + RigFemPartResultsCollection::addressesInElementPropertyFiles( const std::vector& filenames ) +{ + std::map fieldsInFile; + for ( const QString& filename : filenames ) + { + std::vector fields = m_elementPropertyReader->fieldsInFile( filename.toStdString() ); + for ( const std::string& field : fields ) + { + fieldsInFile[field] = filename; + } + } + + return fieldsInFile; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -233,6 +257,72 @@ void RigFemPartResultsCollection::setCalculationParameters( double cohesion, dou RigFemResultAddress( RIG_INTEGRATION_POINT, "SE", "FOS", RigFemResultAddress::allTimeLapsesValue() ) ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RigFemPartResultsCollection::setBiotCoefficientParameters( double biotFixedFactor, const QString& biotResultAddress ) +{ + m_biotFixedFactor = biotFixedFactor; + m_biotResultAddress = biotResultAddress; + + // Invalidate all results which depends on biot coefficient (directly or indirectly) + // Shear results are independent of pore pressure and biot coefficient. + bool includeShear = false; + std::vector componentNames = getStressComponentNames( includeShear ); + componentNames.push_back( "S1inc" ); + componentNames.push_back( "S1azi" ); + componentNames.push_back( "S2inc" ); + componentNames.push_back( "S2azi" ); + componentNames.push_back( "S3inc" ); + componentNames.push_back( "S3azi" ); + + for ( auto elementType : {RIG_ELEMENT_NODAL, RIG_INTEGRATION_POINT} ) + { + for ( auto fieldName : {"SE", "ST"} ) + { + for ( auto componentName : componentNames ) + { + deleteResult( + RigFemResultAddress( elementType, fieldName, componentName, RigFemResultAddress::allTimeLapsesValue() ) ); + } + } + + // SE only: depends on SE.S1 and SE.S3 + deleteResult( RigFemResultAddress( elementType, "SE", "SFI", RigFemResultAddress::allTimeLapsesValue() ) ); + deleteResult( RigFemResultAddress( elementType, "SE", "DSM", RigFemResultAddress::allTimeLapsesValue() ) ); + deleteResult( RigFemResultAddress( elementType, "SE", "SEM", RigFemResultAddress::allTimeLapsesValue() ) ); + + // SE only: depends on SE.DSM + deleteResult( RigFemResultAddress( elementType, "SE", "FOS", RigFemResultAddress::allTimeLapsesValue() ) ); + + // ST only: depends on ST.S1 and ST.S3 + deleteResult( RigFemResultAddress( elementType, "ST", "Q", RigFemResultAddress::allTimeLapsesValue() ) ); + deleteResult( RigFemResultAddress( elementType, "ST", "STM", RigFemResultAddress::allTimeLapsesValue() ) ); + } + + for ( auto fieldName : {"SE", "ST"} ) + { + // Surface aligned stress + for ( auto componentName : {"SN", "TP", "TPinc", "TPH", "TPQV", "FAULTMOB", "PCRIT"} ) + { + deleteResult( RigFemResultAddress( RIG_ELEMENT_NODAL_FACE, + fieldName, + componentName, + RigFemResultAddress::allTimeLapsesValue() ) ); + } + + // Stress gradient components + const std::vector stressGradientComponentNames = getStressGradientComponentNames( includeShear ); + for ( auto componentName : stressGradientComponentNames ) + { + deleteResult( RigFemResultAddress( RIG_DIFFERENTIALS, + fieldName, + componentName, + RigFemResultAddress::allTimeLapsesValue() ) ); + } + } +} + //-------------------------------------------------------------------------------------------------- /// Will always return a valid object, but it can be empty //-------------------------------------------------------------------------------------------------- @@ -338,8 +428,9 @@ RigFemScalarResultFrames* RigFemPartResultsCollection::findOrLoadScalarResult( i if ( !frames ) { - frames = m_femPartResults[partIndex]->createScalarResult( resVarAddr ); // Create a dummy empty result, if the - // request did not specify the component. + frames = m_femPartResults[partIndex]->createScalarResult( resVarAddr ); // Create a dummy empty result, if + // the request did not specify the + // component. } return frames; @@ -710,7 +801,8 @@ RigFemScalarResultFrames* RigFemPartResultsCollection::calculateTimeLapseResult( } else { - // Gamma time lapse needs to be calculated as ST_dt / POR_dt and not Gamma - Gamma_baseFrame see github issue #937 + // Gamma time lapse needs to be calculated as ST_dt / POR_dt and not Gamma - Gamma_baseFrame see github + // issue #937 caf::ProgressInfo frameCountProgress( this->frameCount() * 3, "" ); frameCountProgress.setProgressDescription( @@ -2131,7 +2223,8 @@ RigFemScalarResultFrames* RigFemPartResultsCollection::calculateNE( int partInde //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -RigFemScalarResultFrames* RigFemPartResultsCollection::calculateSE( int partIndex, const RigFemResultAddress& resVarAddr ) +RigFemScalarResultFrames* RigFemPartResultsCollection::calculateSE_12_13_23( int partIndex, + const RigFemResultAddress& resVarAddr ) { caf::ProgressInfo frameCountProgress( this->frameCount() * 3, "" ); frameCountProgress.setProgressDescription( @@ -2143,8 +2236,6 @@ RigFemScalarResultFrames* RigFemPartResultsCollection::calculateSE( int partInde RigFemResultAddress( resVarAddr.resultPosType, "S-Bar", resVarAddr.componentName ) ); frameCountProgress.incrementProgress(); frameCountProgress.setNextProgressIncrement( this->frameCount() ); - RigFemScalarResultFrames* srcPORDataFrames = - this->findOrLoadScalarResult( partIndex, RigFemResultAddress( RIG_NODAL, "POR-Bar", "" ) ); RigFemScalarResultFrames* dstDataFrames = m_femPartResults[partIndex]->createScalarResult( resVarAddr ); frameCountProgress.incrementProgress(); @@ -2153,6 +2244,7 @@ RigFemScalarResultFrames* RigFemPartResultsCollection::calculateSE( int partInde float inf = std::numeric_limits::infinity(); int frameCount = srcDataFrames->frameCount(); + for ( int fIdx = 0; fIdx < frameCount; ++fIdx ) { const std::vector& srcSFrameData = srcDataFrames->frameData( fIdx ); @@ -2160,8 +2252,6 @@ RigFemScalarResultFrames* RigFemPartResultsCollection::calculateSE( int partInde size_t valCount = srcSFrameData.size(); dstFrameData.resize( valCount ); - const std::vector& srcPORFrameData = srcPORDataFrames->frameData( fIdx ); - int elementCount = femPart->elementCount(); #pragma omp parallel for @@ -2201,6 +2291,131 @@ RigFemScalarResultFrames* RigFemPartResultsCollection::calculateSE( int partInde return dstDataFrames; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RigFemScalarResultFrames* RigFemPartResultsCollection::calculateSE_11_22_33( int partIndex, + const RigFemResultAddress& resVarAddr ) +{ + caf::ProgressInfo frameCountProgress( this->frameCount() * 3, "" ); + frameCountProgress.setProgressDescription( + "Calculating " + QString::fromStdString( resVarAddr.fieldName + ": " + resVarAddr.componentName ) ); + frameCountProgress.setNextProgressIncrement( this->frameCount() ); + + RigFemScalarResultFrames* srcDataFrames = + this->findOrLoadScalarResult( partIndex, + RigFemResultAddress( resVarAddr.resultPosType, "S-Bar", resVarAddr.componentName ) ); + frameCountProgress.incrementProgress(); + frameCountProgress.setNextProgressIncrement( this->frameCount() ); + RigFemScalarResultFrames* srcPORDataFrames = + this->findOrLoadScalarResult( partIndex, RigFemResultAddress( RIG_NODAL, "POR-Bar", "" ) ); + RigFemScalarResultFrames* dstDataFrames = m_femPartResults[partIndex]->createScalarResult( resVarAddr ); + + frameCountProgress.incrementProgress(); + + // Biot porelastic coeffisient (alpha) + RigFemScalarResultFrames* biotCoefficient = nullptr; + if ( !m_biotResultAddress.isEmpty() ) + { + biotCoefficient = + this->findOrLoadScalarResult( partIndex, + RigFemResultAddress( RIG_ELEMENT, m_biotResultAddress.toStdString(), "" ) ); + } + + const RigFemPart* femPart = m_femParts->part( partIndex ); + float inf = std::numeric_limits::infinity(); + + int frameCount = srcDataFrames->frameCount(); + + for ( int fIdx = 0; fIdx < frameCount; ++fIdx ) + { + const std::vector& srcSFrameData = srcDataFrames->frameData( fIdx ); + std::vector& dstFrameData = dstDataFrames->frameData( fIdx ); + size_t valCount = srcSFrameData.size(); + dstFrameData.resize( valCount ); + + const std::vector& initialPORFrameData = srcPORDataFrames->frameData( 0 ); + + int elementCount = femPart->elementCount(); + + std::vector biotData; + if ( biotCoefficient ) + { + biotData = biotCoefficient->frameData( fIdx ); + if ( !isValidBiotData( biotData, elementCount ) ) + { + deleteResult( resVarAddr ); + return nullptr; + } + } + +#pragma omp parallel for + for ( int elmIdx = 0; elmIdx < elementCount; ++elmIdx ) + { + RigElementType elmType = femPart->elementType( elmIdx ); + + int elmNodeCount = RigFemTypes::elmentNodeCount( femPart->elementType( elmIdx ) ); + + if ( elmType == HEX8P ) + { + for ( int elmNodIdx = 0; elmNodIdx < elmNodeCount; ++elmNodIdx ) + { + size_t elmNodResIdx = femPart->elementNodeResultIdx( elmIdx, elmNodIdx ); + if ( elmNodResIdx < srcSFrameData.size() ) + { + double SE_abacus = -srcSFrameData[elmNodResIdx]; + if ( fIdx == 0 ) + { + // Geostatic step: biot coefficient == 1.0 + dstFrameData[elmNodResIdx] = SE_abacus; + } + else + { + // Use biot coefficient for all other (not Geostatic) timesteps + double biotCoefficient = 1.0; + if ( biotData.empty() ) + { + biotCoefficient = m_biotFixedFactor; + } + else + { + // Use coefficient from element property table + biotCoefficient = biotData[elmIdx]; + } + + // SE = St - alpha * porePressure - (1 - alpha) * initialPorePressure + // ST = SE_abaqus + alpha * porePressure + // Can be simplified: + // SE = SE_abaqus - (1-alpha) * initialPorePressure + // SE_abaqus is called S-Bar + int nodeIdx = femPart->nodeIdxFromElementNodeResultIdx( elmNodResIdx ); + double initialPorePressure = initialPORFrameData[nodeIdx]; + if ( initialPorePressure == inf ) initialPorePressure = 0.0f; + + dstFrameData[elmNodResIdx] = SE_abacus - ( 1.0 - biotCoefficient ) * initialPorePressure; + } + } + } + } + else + { + for ( int elmNodIdx = 0; elmNodIdx < elmNodeCount; ++elmNodIdx ) + { + size_t elmNodResIdx = femPart->elementNodeResultIdx( elmIdx, elmNodIdx ); + if ( elmNodResIdx < dstFrameData.size() ) + { + dstFrameData[elmNodResIdx] = inf; + } + } + } + } + + frameCountProgress.incrementProgress(); + } + + return dstDataFrames; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -2221,6 +2436,15 @@ RigFemScalarResultFrames* RigFemPartResultsCollection::calculateST_11_22_33( int RigFemScalarResultFrames* srcPORDataFrames = this->findOrLoadScalarResult( partIndex, RigFemResultAddress( RIG_NODAL, "POR-Bar", "" ) ); + // Biot porelastic coeffisient (alpha) + RigFemScalarResultFrames* biotCoefficient = nullptr; + if ( !m_biotResultAddress.isEmpty() ) + { + biotCoefficient = + this->findOrLoadScalarResult( partIndex, + RigFemResultAddress( RIG_ELEMENT, m_biotResultAddress.toStdString(), "" ) ); + } + RigFemScalarResultFrames* dstDataFrames = m_femPartResults[partIndex]->createScalarResult( resVarAddr ); const RigFemPart* femPart = m_femParts->part( partIndex ); int frameCount = srcSDataFrames->frameCount(); @@ -2234,13 +2458,24 @@ RigFemScalarResultFrames* RigFemPartResultsCollection::calculateST_11_22_33( int const std::vector& srcSFrameData = srcSDataFrames->frameData( fIdx ); const std::vector& srcPORFrameData = srcPORDataFrames->frameData( fIdx ); + int elementCount = femPart->elementCount(); + + std::vector biotData; + if ( biotCoefficient ) + { + biotData = biotCoefficient->frameData( fIdx ); + if ( !isValidBiotData( biotData, elementCount ) ) + { + deleteResult( resVarAddr ); + return nullptr; + } + } + std::vector& dstFrameData = dstDataFrames->frameData( fIdx ); size_t valCount = srcSFrameData.size(); dstFrameData.resize( valCount ); - int elementCount = femPart->elementCount(); - #pragma omp parallel for for ( int elmIdx = 0; elmIdx < elementCount; ++elmIdx ) { @@ -2260,7 +2495,31 @@ RigFemScalarResultFrames* RigFemPartResultsCollection::calculateST_11_22_33( int float por = srcPORFrameData[nodeIdx]; if ( por == inf ) por = 0.0f; - dstFrameData[elmNodResIdx] = -srcSFrameData[elmNodResIdx] + por; + // ST = SE_abacus + alpha * porePressure + // where alpha is biot coefficient, and porePressure is POR-Bar. + + double SE_abacus = -srcSFrameData[elmNodResIdx]; + if ( fIdx == 0 ) + { + // Geostatic step: biot coefficient == 1.0 + dstFrameData[elmNodResIdx] = SE_abacus + por; + } + else + { + // Use biot coefficient for all other (not Geostatic) timesteps + double biotCoefficient = 1.0; + if ( biotData.empty() ) + { + biotCoefficient = m_biotFixedFactor; + } + else + { + // Use coefficient from element property table + biotCoefficient = biotData[elmIdx]; + } + + dstFrameData[elmNodResIdx] = SE_abacus + biotCoefficient * por; + } } } } @@ -2555,11 +2814,16 @@ RigFemScalarResultFrames* RigFemPartResultsCollection::calculateDerivedResult( i return calculatePrincipalStrainValues( partIndex, resVarAddr ); } - if ( ( resVarAddr.fieldName == "SE" ) && ( resVarAddr.componentName == "S11" || resVarAddr.componentName == "S22" || - resVarAddr.componentName == "S33" || resVarAddr.componentName == "S12" || - resVarAddr.componentName == "S13" || resVarAddr.componentName == "S23" ) ) + if ( ( resVarAddr.fieldName == "SE" ) && + ( resVarAddr.componentName == "S11" || resVarAddr.componentName == "S22" || resVarAddr.componentName == "S33" ) ) { - return calculateSE( partIndex, resVarAddr ); + return calculateSE_11_22_33( partIndex, resVarAddr ); + } + + if ( ( resVarAddr.fieldName == "SE" ) && + ( resVarAddr.componentName == "S12" || resVarAddr.componentName == "S13" || resVarAddr.componentName == "S23" ) ) + { + return calculateSE_12_13_23( partIndex, resVarAddr ); } if ( ( resVarAddr.fieldName == "SE" || resVarAddr.fieldName == "ST" ) && @@ -3488,18 +3752,26 @@ void findReferenceElementForNode( const RigFemPart& part, size_t nodeIdx, size_t //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -std::vector RigFemPartResultsCollection::getStressComponentNames() +std::vector RigFemPartResultsCollection::getStressComponentNames( bool includeShear ) { - return {"S11", "S22", "S33", "S12", "S13", "S23", "S1", "S2", "S3"}; + std::vector componentNames = {"S11", "S22", "S33", "S1", "S2", "S3"}; + if ( includeShear ) + { + componentNames.push_back( "S12" ); + componentNames.push_back( "S13" ); + componentNames.push_back( "S23" ); + } + + return componentNames; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -std::vector RigFemPartResultsCollection::getStressGradientComponentNames() +std::vector RigFemPartResultsCollection::getStressGradientComponentNames( bool includeShear ) { std::vector directions = {"X", "Y", "Z"}; - std::vector stressComponentNames = getStressComponentNames(); + std::vector stressComponentNames = getStressComponentNames( includeShear ); std::vector stressGradientComponentNames; for ( auto& s : stressComponentNames ) @@ -3512,3 +3784,29 @@ std::vector RigFemPartResultsCollection::getStressGradientComponent return stressGradientComponentNames; } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RigFemPartResultsCollection::isValidBiotData( const std::vector& biotData, size_t elementCount ) const +{ + if ( biotData.size() != elementCount ) + { + RiaLogging::error( QString( "Unexpected size of biot coefficient element properties: %1 (expected: %2)" ) + .arg( biotData.size() ) + .arg( elementCount ) ); + return false; + } + + for ( float b : biotData ) + { + if ( !std::isinf( b ) && ( b < 0.0 || b > 1.0 ) ) + { + RiaLogging::error( + QString( "Found unexpected biot coefficient. The value must be in the [0, 1] interval." ) ); + return false; + } + } + + return true; +} diff --git a/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultsCollection.h b/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultsCollection.h index b2b65189e1..90ad690f2f 100644 --- a/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultsCollection.h +++ b/ApplicationCode/GeoMech/GeoMechDataModel/RigFemPartResultsCollection.h @@ -61,11 +61,14 @@ public: void addElementPropertyFiles( const std::vector& filenames ); std::vector removeElementPropertyFiles( const std::vector& filenames ); + std::map addressesInElementPropertyFiles( const std::vector& filenames ); void setCalculationParameters( double cohesion, double frictionAngleRad ); double parameterCohesion() const { return m_cohesion; } double parameterFrictionAngleRad() const { return m_frictionAngleRad; } + void setBiotCoefficientParameters( double fixedFactor, const QString& biotResultAddress ); + std::map> scalarFieldAndComponentNames( RigFemResultPosEnum resPos ); std::vector filteredStepNames() const; bool assertResultsLoaded( const RigFemResultAddress& resVarAddr ); @@ -150,7 +153,8 @@ private: RigFemScalarResultFrames* calculatePrincipalStrainValues( int partIndex, const RigFemResultAddress& resVarAddr ); RigFemScalarResultFrames* calculateCompactionValues( int partIndex, const RigFemResultAddress& resVarAddr ); RigFemScalarResultFrames* calculateNE( int partIndex, const RigFemResultAddress& resVarAddr ); - RigFemScalarResultFrames* calculateSE( int partIndex, const RigFemResultAddress& resVarAddr ); + RigFemScalarResultFrames* calculateSE_11_22_33( int partIndex, const RigFemResultAddress& resVarAddr ); + RigFemScalarResultFrames* calculateSE_12_13_23( int partIndex, const RigFemResultAddress& resVarAddr ); RigFemScalarResultFrames* calculateST_11_22_33( int partIndex, const RigFemResultAddress& resVarAddr ); RigFemScalarResultFrames* calculateST_12_13_23( int partIndex, const RigFemResultAddress& resVarAddr ); RigFemScalarResultFrames* calculateGamma( int partIndex, const RigFemResultAddress& resVarAddr ); @@ -161,6 +165,8 @@ private: const RigFormationNames* activeFormationNames() const; + bool isValidBiotData( const std::vector& biotData, size_t elementCount ) const; + private: cvf::Collection m_femPartResults; cvf::ref m_readerInterface; @@ -172,12 +178,15 @@ private: double m_frictionAngleRad; double m_normalizationAirGap; + double m_biotFixedFactor; + QString m_biotResultAddress; + RigStatisticsDataCache* statistics( const RigFemResultAddress& resVarAddr ); std::vector getResAddrToComponentsToRead( const RigFemResultAddress& resVarAddr ); std::map> m_resultStatistics; - static std::vector getStressComponentNames(); - static std::vector getStressGradientComponentNames(); + static std::vector getStressComponentNames( bool includeShear = true ); + static std::vector getStressGradientComponentNames( bool includeShear = true ); }; class RigFemPart; diff --git a/ApplicationCode/ProjectDataModel/Rim3dOverlayInfoConfig.cpp b/ApplicationCode/ProjectDataModel/Rim3dOverlayInfoConfig.cpp index d5a71ad651..07932986f2 100644 --- a/ApplicationCode/ProjectDataModel/Rim3dOverlayInfoConfig.cpp +++ b/ApplicationCode/ProjectDataModel/Rim3dOverlayInfoConfig.cpp @@ -861,6 +861,24 @@ QString Rim3dOverlayInfoConfig::resultInfoText( const HistogramData& histData, R QString( "Cell result: %1, %2, %3
" ).arg( resultPos ).arg( fieldName ).arg( compName ); } + if ( geoMechView->cellResultResultDefinition()->isBiotCoefficientDependent() ) + { + if ( geoMechCase->biotCoefficientType() == RimGeoMechCase::BiotCoefficientType::BIOT_NONE ) + { + infoText += QString( "Biot Coefficient: 1.0 (None)
" ); + } + else if ( geoMechCase->biotCoefficientType() == RimGeoMechCase::BiotCoefficientType::BIOT_FIXED ) + { + infoText += + QString( "Biot Coefficient: %1 (Fixed)
" ).arg( geoMechCase->biotFixedCoefficient() ); + } + else if ( geoMechCase->biotCoefficientType() == RimGeoMechCase::BiotCoefficientType::BIOT_PER_ELEMENT ) + { + infoText += QString( "Biot Coefficient: %1 (From element property)
" ) + .arg( geoMechCase->biotResultAddress() ); + } + } + const RimGeoMechContourMapView* contourMapView = dynamic_cast( geoMechView ); if ( contourMapView ) { diff --git a/ApplicationCode/ProjectDataModel/RimGeoMechCase.cpp b/ApplicationCode/ProjectDataModel/RimGeoMechCase.cpp index 0cc884ca73..126ae01c71 100644 --- a/ApplicationCode/ProjectDataModel/RimGeoMechCase.cpp +++ b/ApplicationCode/ProjectDataModel/RimGeoMechCase.cpp @@ -23,6 +23,7 @@ #include "RiaLogging.h" #include "RiaPreferences.h" +#include "RicImportElementPropertyFeature.h" #include "RicfCommandObject.h" #include "RifOdbReader.h" @@ -49,8 +50,11 @@ #include "RimTools.h" #include "RimWellLogPlotCollection.h" +#include "cafCmdFeatureManager.h" #include "cafPdmFieldIOScriptability.h" #include "cafPdmObjectScriptability.h" +#include "cafPdmUiDoubleValueEditor.h" +#include "cafPdmUiListEditor.h" #include "cafPdmUiPropertyViewDialog.h" #include "cafPdmUiPushButtonEditor.h" #include "cafPdmUiTreeOrdering.h" @@ -58,13 +62,27 @@ #include "cvfVector3.h" -#include -#include +#include #include CAF_PDM_SOURCE_INIT( RimGeoMechCase, "ResInsightGeoMechCase" ); +namespace caf +{ +template <> +void caf::AppEnum::setUp() +{ + addItem( RimGeoMechCase::BiotCoefficientType::BIOT_NONE, "BIOT_NONE", "None" ); + addItem( RimGeoMechCase::BiotCoefficientType::BIOT_FIXED, "BIOT_FIXED", "Fixed biot coefficient" ); + addItem( RimGeoMechCase::BiotCoefficientType::BIOT_PER_ELEMENT, + "BIOT_PER_ELEMENT", + "Biot coefficient from element properties" ); + setDefault( RimGeoMechCase::BiotCoefficientType::BIOT_NONE ); +} + +} // End namespace caf + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -106,12 +124,23 @@ RimGeoMechCase::RimGeoMechCase( void ) "" ); m_elementPropertyFileNameIndexUiSelection.xmlCapability()->disableIO(); + CAF_PDM_InitField( &m_importElementPropertyFileCommand, "importElementPropertyFileCommad", false, "", "", "", "" ); + caf::PdmUiPushButtonEditor::configureEditorForField( &m_importElementPropertyFileCommand ); + CAF_PDM_InitField( &m_closeElementPropertyFileCommand, "closeElementPropertyFileCommad", false, "", "", "", "" ); caf::PdmUiPushButtonEditor::configureEditorForField( &m_closeElementPropertyFileCommand ); CAF_PDM_InitField( &m_reloadElementPropertyFileCommand, "reloadElementPropertyFileCommand", false, "", "", "", "" ); caf::PdmUiPushButtonEditor::configureEditorForField( &m_reloadElementPropertyFileCommand ); + caf::AppEnum defaultBiotCoefficientType = RimGeoMechCase::BiotCoefficientType::BIOT_NONE; + CAF_PDM_InitField( &m_biotCoefficientType, "BiotCoefficientType", defaultBiotCoefficientType, "Biot Coefficient", "", "", "" ); + CAF_PDM_InitField( &m_biotFixedCoefficient, "BiotFixedCoefficient", 1.0, "Fixed coefficient", "", "", "" ); + m_biotFixedCoefficient.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleValueEditor::uiEditorTypeName() ); + + CAF_PDM_InitField( &m_biotResultAddress, "BiotResultAddress", QString( "" ), "Value", "", "", "" ); + m_biotResultAddress.uiCapability()->setUiEditorTypeName( caf::PdmUiListEditor::uiEditorTypeName() ); + CAF_PDM_InitFieldNoDefault( &m_contourMapCollection, "ContourMaps", "2d Contour Maps", "", "", "" ); m_contourMapCollection = new RimGeoMechContourMapViewCollection; m_contourMapCollection.uiCapability()->setUiTreeHidden( true ); @@ -564,6 +593,30 @@ double RimGeoMechCase::frictionAngleDeg() const return m_frictionAngleDeg; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimGeoMechCase::BiotCoefficientType RimGeoMechCase::biotCoefficientType() const +{ + return m_biotCoefficientType(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +double RimGeoMechCase::biotFixedCoefficient() const +{ + return m_biotFixedCoefficient; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimGeoMechCase::biotResultAddress() const +{ + return m_biotResultAddress; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -636,14 +689,56 @@ void RimGeoMechCase::fieldChangedByUi( const caf::PdmFieldHandle* changedField, cvf::Math::toRadians( m_frictionAngleDeg() ) ); } - std::vector views = this->views(); - for ( Rim3dView* view : views ) + updateConnectedViews(); + } + else if ( changedField == &m_biotFixedCoefficient || changedField == &m_biotCoefficientType || + changedField == &m_biotResultAddress ) + { + RigGeoMechCaseData* rigCaseData = geoMechData(); + if ( rigCaseData && rigCaseData->femPartResults() ) { - if ( view ) // Todo: only those using the variable actively + if ( m_biotCoefficientType() == RimGeoMechCase::BiotCoefficientType::BIOT_NONE ) { - view->scheduleCreateDisplayModelAndRedraw(); + rigCaseData->femPartResults()->setBiotCoefficientParameters( 1.0, "" ); + } + else if ( m_biotCoefficientType() == RimGeoMechCase::BiotCoefficientType::BIOT_FIXED ) + { + rigCaseData->femPartResults()->setBiotCoefficientParameters( m_biotFixedCoefficient(), "" ); + } + else if ( m_biotCoefficientType() == RimGeoMechCase::BiotCoefficientType::BIOT_PER_ELEMENT ) + { + if ( changedField == &m_biotCoefficientType ) + { + // 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 biot coefficients from file (typically called alpha.inp) by " + "selecting 'Import Element Property Table' on the Geomechanical Model." ); + RiaLogging::info( importMessage ); + // Set back to default value + m_biotCoefficientType = RimGeoMechCase::BiotCoefficientType::BIOT_NONE; + return; + } + } + + if ( biotResultAddress().isEmpty() ) + { + // Automatically select the first available property element if empty + std::vector elementProperties = possibleElementPropertyFieldNames(); + if ( !elementProperties.empty() ) + { + m_biotResultAddress = QString::fromStdString( elementProperties[0] ); + } + } + + rigCaseData->femPartResults()->setBiotCoefficientParameters( 1.0, biotResultAddress() ); } } + + updateConnectedViews(); } else if ( changedField == &m_reloadElementPropertyFileCommand ) { @@ -657,6 +752,12 @@ void RimGeoMechCase::fieldChangedByUi( const caf::PdmFieldHandle* changedField, closeSelectedElementPropertyFiles(); updateConnectedEditors(); } + else if ( changedField == &m_importElementPropertyFileCommand ) + { + m_importElementPropertyFileCommand = false; + importElementPropertyFile(); + updateConnectedEditors(); + } } //-------------------------------------------------------------------------------------------------- @@ -795,6 +896,12 @@ void RimGeoMechCase::closeSelectedElementPropertyFiles() view->cellResult()->setResultAddress( RigFemResultAddress() ); } + if ( address.fieldName == biotResultAddress().toStdString() ) + { + // If the used biot value is being removed we need to change the biot type back to default + m_biotCoefficientType = RimGeoMechCase::BiotCoefficientType::BIOT_NONE; + } + for ( RimGeoMechPropertyFilter* propertyFilter : view->geoMechPropertyFilterCollection()->propertyFilters() ) { if ( address == propertyFilter->resultDefinition->resultAddress() ) @@ -834,6 +941,14 @@ void RimGeoMechCase::reloadSelectedElementPropertyFiles() } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimGeoMechCase::importElementPropertyFile() +{ + RicImportElementPropertyFeature::importElementProperties(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -850,9 +965,19 @@ void RimGeoMechCase::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& caf::PdmUiGroup* elmPropGroup = uiOrdering.addNewGroup( "Element Properties" ); elmPropGroup->add( &m_elementPropertyFileNameIndexUiSelection ); + elmPropGroup->add( &m_importElementPropertyFileCommand ); elmPropGroup->add( &m_reloadElementPropertyFileCommand ); elmPropGroup->add( &m_closeElementPropertyFileCommand ); + caf::PdmUiGroup* biotGroup = uiOrdering.addNewGroup( "Biot Coefficient" ); + biotGroup->add( &m_biotCoefficientType ); + biotGroup->add( &m_biotFixedCoefficient ); + biotGroup->add( &m_biotResultAddress ); + m_biotFixedCoefficient.uiCapability()->setUiHidden( m_biotCoefficientType != + RimGeoMechCase::BiotCoefficientType::BIOT_FIXED ); + m_biotResultAddress.uiCapability()->setUiHidden( m_biotCoefficientType != + RimGeoMechCase::BiotCoefficientType::BIOT_PER_ELEMENT ); + caf::PdmUiGroup* timeStepFilterGroup = uiOrdering.addNewGroup( "Time Step Filter" ); timeStepFilterGroup->setCollapsedByDefault( true ); m_timeStepFilter->uiOrdering( uiConfigName, *timeStepFilterGroup ); @@ -865,13 +990,27 @@ void RimGeoMechCase::defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) { + if ( field == &m_importElementPropertyFileCommand ) + { + dynamic_cast( attribute )->m_buttonText = "Import Element Property"; + } if ( field == &m_reloadElementPropertyFileCommand ) { - dynamic_cast( attribute )->m_buttonText = "Reload Case(s)"; + dynamic_cast( attribute )->m_buttonText = "Reload Element Property"; } if ( field == &m_closeElementPropertyFileCommand ) { - dynamic_cast( attribute )->m_buttonText = "Close Case(s)"; + dynamic_cast( attribute )->m_buttonText = "Close Element Property"; + } + + if ( field == &m_biotFixedCoefficient ) + { + auto uiDoubleValueEditorAttr = dynamic_cast( attribute ); + if ( uiDoubleValueEditorAttr ) + { + uiDoubleValueEditorAttr->m_decimals = 2; + uiDoubleValueEditorAttr->m_validator = new QDoubleValidator( 0.0, 1.0, 2 ); + } } } @@ -892,6 +1031,80 @@ QList RimGeoMechCase::calculateValueOptions( const caf:: options.push_back( caf::PdmOptionItemInfo( m_elementPropertyFileNames.v().at( i ).path(), (int)i, true ) ); } } + else if ( fieldNeedingOptions == &m_biotResultAddress ) + { + std::vector elementProperties = possibleElementPropertyFieldNames(); + + std::vector paths; + for ( auto path : m_elementPropertyFileNames.v() ) + { + paths.push_back( path.path() ); + } + + std::map addressesInFile = + geoMechData()->femPartResults()->addressesInElementPropertyFiles( paths ); + + for ( const std::string elementProperty : elementProperties ) + { + QString result = QString::fromStdString( elementProperty ); + QString filename = findFileNameForElementProperty( elementProperty, addressesInFile ); + options.push_back( caf::PdmOptionItemInfo( result + " (" + filename + ")", result ) ); + } + } return options; } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimGeoMechCase::findFileNameForElementProperty( const std::string& elementProperty, + const std::map addressesInFiles ) const +{ + auto it = addressesInFiles.find( elementProperty ); + if ( it != addressesInFiles.end() ) + { + QFileInfo fileInfo( it->second ); + return fileInfo.fileName(); + } + else + { + return QString( "Unknown file" ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimGeoMechCase::updateConnectedViews() +{ + std::vector views = this->views(); + for ( Rim3dView* view : views ) + { + if ( view ) + { + view->scheduleCreateDisplayModelAndRedraw(); + } + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimGeoMechCase::possibleElementPropertyFieldNames() +{ + std::vector fieldNames; + + if ( geoMechData() ) + { + std::map> fieldWithComponentNames = + geoMechData()->femPartResults()->scalarFieldAndComponentNames( RIG_ELEMENT ); + + std::map>::const_iterator fieldIt; + for ( fieldIt = fieldWithComponentNames.begin(); fieldIt != fieldWithComponentNames.end(); ++fieldIt ) + { + fieldNames.push_back( fieldIt->first ); + } + } + return fieldNames; +} diff --git a/ApplicationCode/ProjectDataModel/RimGeoMechCase.h b/ApplicationCode/ProjectDataModel/RimGeoMechCase.h index b41b65ada9..4eb2475e13 100644 --- a/ApplicationCode/ProjectDataModel/RimGeoMechCase.h +++ b/ApplicationCode/ProjectDataModel/RimGeoMechCase.h @@ -52,6 +52,14 @@ public: CASE_OPEN_CANCELLED, CASE_OPEN_ERROR }; + + enum class BiotCoefficientType + { + BIOT_NONE = 0, + BIOT_FIXED, + BIOT_PER_ELEMENT + }; + RimGeoMechCase( void ); ~RimGeoMechCase( void ) override; @@ -88,6 +96,10 @@ public: // Fields: caf::PdmChildArrayField geoMechViews; + BiotCoefficientType biotCoefficientType() const; + double biotFixedCoefficient() const; + QString biotResultAddress() const; + private: cvf::Vec3d displayModelOffset() const override; static std::vector vectorOfValidDateTimesFromTimeStepStrings( const QStringList& timeStepStrings ); @@ -102,14 +114,20 @@ private: QList calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions, bool* useOptionsOnly ) override; + QString findFileNameForElementProperty( const std::string& elementProperty, + const std::map addressesInFiles ) const; + void updateFormationNamesData() override; void initAfterRead() override; static QString subStringOfDigits( const QString& timeStepString, int numberOfDigitsToFind ); - void closeSelectedElementPropertyFiles(); - void reloadSelectedElementPropertyFiles(); - std::vector allSpecialViews() const override; + void importElementPropertyFile(); + void closeSelectedElementPropertyFiles(); + void reloadSelectedElementPropertyFiles(); + std::vector allSpecialViews() const override; + void updateConnectedViews(); + std::vector possibleElementPropertyFieldNames(); private: cvf::ref m_geoMechCaseData; @@ -117,9 +135,14 @@ private: caf::PdmField m_frictionAngleDeg; caf::PdmField> m_elementPropertyFileNames; caf::PdmField> m_elementPropertyFileNameIndexUiSelection; + caf::PdmField m_importElementPropertyFileCommand; caf::PdmField m_closeElementPropertyFileCommand; caf::PdmField m_reloadElementPropertyFileCommand; + caf::PdmField> m_biotCoefficientType; + caf::PdmField m_biotFixedCoefficient; + caf::PdmField m_biotResultAddress; + caf::PdmChildField m_contourMapCollection; bool m_applyTimeFilter; diff --git a/ApplicationCode/ProjectDataModel/RimGeoMechResultDefinition.cpp b/ApplicationCode/ProjectDataModel/RimGeoMechResultDefinition.cpp index e873cbb17f..af22501218 100644 --- a/ApplicationCode/ProjectDataModel/RimGeoMechResultDefinition.cpp +++ b/ApplicationCode/ProjectDataModel/RimGeoMechResultDefinition.cpp @@ -931,3 +931,11 @@ void RimGeoMechResultDefinition::updateLegendTextAndRanges( RimRegularLegendConf legendConfigToUpdate->setTitle( legendTitle ); } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimGeoMechResultDefinition::isBiotCoefficientDependent() const +{ + return ( this->resultFieldName() == "SE" || this->resultFieldName() == "ST" ); +} diff --git a/ApplicationCode/ProjectDataModel/RimGeoMechResultDefinition.h b/ApplicationCode/ProjectDataModel/RimGeoMechResultDefinition.h index 072a51ead1..e524a7cf82 100644 --- a/ApplicationCode/ProjectDataModel/RimGeoMechResultDefinition.h +++ b/ApplicationCode/ProjectDataModel/RimGeoMechResultDefinition.h @@ -80,6 +80,8 @@ public: const QString& legendHeading, int timeStepIndex ); + bool isBiotCoefficientDependent() const; + protected: virtual void updateLegendCategorySettings(){}; void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;