diff --git a/.integrated_tests.yaml b/.integrated_tests.yaml index 1730dfbd691..b262b6aa27b 100644 --- a/.integrated_tests.yaml +++ b/.integrated_tests.yaml @@ -1,6 +1,6 @@ baselines: bucket: geosx - baseline: integratedTests/baseline_integratedTests-pr3883-16299-3037085 + baseline: integratedTests/baseline_integratedTests-pr4021-16339-bb862da allow_fail: all: '' diff --git a/BASELINE_NOTES.md b/BASELINE_NOTES.md index 843f5a9564f..e1c07a01361 100644 --- a/BASELINE_NOTES.md +++ b/BASELINE_NOTES.md @@ -5,6 +5,10 @@ This file is designed to track changes to the integrated test baselines. Any developer who updates the baseline ID in the .integrated_tests.yaml file is expected to create an entry in this file with the pull request number, date, and their justification for rebaselining. These notes should be in reverse-chronological order, and use the following time format: (YYYY-MM-DD). +PR #4021 (2026-04-14) +===================== +Add Young Modulus & Poisson import from VTK mesh + PR #3883 (2026-04-10) ===================== Move PVT Driver tests from unit tests to integrated tests diff --git a/src/coreComponents/constitutive/solid/ElasticIsotropic.cpp b/src/coreComponents/constitutive/solid/ElasticIsotropic.cpp index be60e9fd6ee..95ed49740c7 100644 --- a/src/coreComponents/constitutive/solid/ElasticIsotropic.cpp +++ b/src/coreComponents/constitutive/solid/ElasticIsotropic.cpp @@ -19,6 +19,7 @@ #include "ElasticIsotropic.hpp" #include "SolidFields.hpp" +#include "common/MpiWrapper.hpp" namespace geos { @@ -54,6 +55,10 @@ ElasticIsotropic::ElasticIsotropic( string const & name, Group * const parent ): registerField< fields::solid::bulkModulus >( &m_bulkModulus ); registerField< fields::solid::shearModulus >( &m_shearModulus ); + + registerField< fields::solid::youngModulus >( &m_youngModulus ); + + registerField< fields::solid::poissonRatio >( &m_poissonRatio ); } void ElasticIsotropic::postInputInitialization() @@ -147,6 +152,89 @@ void ElasticIsotropic::postInputInitialization() setApplyDefaultValue( m_defaultShearModulus ); } +void ElasticIsotropic::initializePostInitialConditionsPreSubGroups() +{ + SolidBase::initializePostInitialConditionsPreSubGroups(); + + // If per-cell Young's modulus and Poisson's ratio were imported from an external mesh + // convert them to bulk and shear modulus on a cell-by-cell basis. + arrayView1d< real64 const > const youngMod = m_youngModulus; + arrayView1d< real64 const > const nu = m_poissonRatio; + arrayView1d< real64 > const bulkMod = m_bulkModulus; + arrayView1d< real64 > const shearMod = m_shearModulus; + + localIndex numConverted = 0; + localIndex numInvalidE = 0; + localIndex numInvalidNu = 0; + localIndex numNegativeNu = 0; + + for( localIndex k = 0; k < m_youngModulus.size(); ++k ) + { + // youngModulus default is 0: negative values are invalid, zero means not imported + if( youngMod[k] < 0.0 ) + { + ++numInvalidE; + continue; + } + + // youngModulus default is 0: not imported — skip silently + if( !( youngMod[k] > 0.0 ) ) + continue; + + // E was imported and is positive: nu must also be valid + if( nu[k] <= -0.5 || nu[k] >= 0.5 ) + { + ++numInvalidNu; + continue; + } + + // Count negative Poisson's ratio (physically unusual but not always invalid) + if( nu[k] < 0.0 ) + ++numNegativeNu; + + bulkMod[k] = conversions::youngModAndPoissonRatio::toBulkMod( youngMod[k], nu[k] ); + shearMod[k] = conversions::youngModAndPoissonRatio::toShearMod( youngMod[k], nu[k] ); + ++numConverted; + } + + // Aggregate counters across all MPI ranks to report global totals + localIndex const globalConverted = MpiWrapper::sum( numConverted ); + localIndex const globalInvalidE = MpiWrapper::sum( numInvalidE ); + localIndex const globalInvalidNu = MpiWrapper::sum( numInvalidNu ); + localIndex const globalNegativeNu = MpiWrapper::sum( numNegativeNu ); + + if( globalConverted + globalInvalidE + globalInvalidNu > 0 ) + { + GEOS_LOG_RANK_0_IF( globalInvalidE > 0, + GEOS_FMT( "ElasticIsotropic '{}': {} element(s) had negative Young's modulus " + "and were skipped (default bulk/shear modulus used).", + getName(), globalInvalidE ) ); + + GEOS_LOG_RANK_0_IF( globalInvalidNu > 0, + GEOS_FMT( "ElasticIsotropic '{}': {} element(s) had Poisson's ratio outside " + "(-0.5, 0.5) and were skipped (default bulk/shear modulus used).", + getName(), globalInvalidNu ) ); + + GEOS_LOG_RANK_0_IF( globalNegativeNu > 0, + GEOS_FMT( "ElasticIsotropic '{}': {} element(s) have a negative Poisson's ratio. " + "Please verify your input data.", + getName(), globalNegativeNu ) ); + + GEOS_LOG_RANK_0( GEOS_FMT( "ElasticIsotropic '{}': per-cell E/nu conversion — " + "{} converted, {} skipped (invalid E), {} skipped (invalid nu), " + "{} with negative nu.", + getName(), globalConverted, globalInvalidE, globalInvalidNu, globalNegativeNu ) ); + } + + // Back-compute E and nu for all cells from the final K/G so that output fields are meaningful + // for both imported and default cells. + for( localIndex k = 0; k < m_bulkModulus.size(); ++k ) + { + m_youngModulus[k] = conversions::bulkModAndShearMod::toYoungMod( bulkMod[k], shearMod[k] ); + m_poissonRatio[k] = conversions::bulkModAndShearMod::toPoissonRatio( bulkMod[k], shearMod[k] ); + } +} + REGISTER_CATALOG_ENTRY( ConstitutiveBase, ElasticIsotropic, string const &, Group * const ) } } /* namespace geos */ diff --git a/src/coreComponents/constitutive/solid/ElasticIsotropic.hpp b/src/coreComponents/constitutive/solid/ElasticIsotropic.hpp index a6663232fe8..79c186a5c06 100644 --- a/src/coreComponents/constitutive/solid/ElasticIsotropic.hpp +++ b/src/coreComponents/constitutive/solid/ElasticIsotropic.hpp @@ -573,6 +573,9 @@ class ElasticIsotropic : public SolidBase /// Post-process XML data virtual void postInputInitialization() override; + /// Convert per-cell Young's modulus / Poisson's ratio to bulk / shear modulus if imported from mesh + virtual void initializePostInitialConditionsPreSubGroups() override; + /// The default value of the bulk modulus for any new allocations. real64 m_defaultBulkModulus; @@ -585,6 +588,12 @@ class ElasticIsotropic : public SolidBase /// The shear modulus for each upper level dimension (i.e. cell) of *this array1d< real64 > m_shearModulus; + /// Young's modulus per cell (optional; used only when imported from an external mesh) + array1d< real64 > m_youngModulus; + + /// Poisson's ratio per cell (optional; used only when imported from an external mesh) + array1d< real64 > m_poissonRatio; + }; } /* namespace constitutive */ diff --git a/src/coreComponents/constitutive/solid/SolidFields.hpp b/src/coreComponents/constitutive/solid/SolidFields.hpp index dc98487c15a..8ed466eadc7 100644 --- a/src/coreComponents/constitutive/solid/SolidFields.hpp +++ b/src/coreComponents/constitutive/solid/SolidFields.hpp @@ -74,6 +74,22 @@ DECLARE_FIELD( shearModulus, WRITE_AND_READ, "Shear modulus" ); +DECLARE_FIELD( youngModulus, + "youngModulus", + array1d< real64 >, + 0, + NOPLOT, + WRITE_AND_READ, + "Young's modulus (per-cell, used when imported from external mesh; converted to bulk/shear modulus at initialization)" ); + +DECLARE_FIELD( poissonRatio, + "poissonRatio", + array1d< real64 >, + 0, + NOPLOT, + WRITE_AND_READ, + "Poisson's ratio (per-cell, used when imported from external mesh; converted to bulk/shear modulus at initialization)" ); + DECLARE_FIELD( biotCoefficient, "biotCoefficient", array1d< real64 >, diff --git a/src/coreComponents/schema/schema.xsd.other b/src/coreComponents/schema/schema.xsd.other index ffe564b7c6e..c7434b7b99c 100644 --- a/src/coreComponents/schema/schema.xsd.other +++ b/src/coreComponents/schema/schema.xsd.other @@ -2071,12 +2071,16 @@ A field can represent a physical variable. (pressure, temperature, global compos + + + + @@ -2506,6 +2510,8 @@ A field can represent a physical variable. (pressure, temperature, global compos + + @@ -2518,6 +2524,8 @@ A field can represent a physical variable. (pressure, temperature, global compos + + @@ -2548,6 +2556,8 @@ A field can represent a physical variable. (pressure, temperature, global compos + + @@ -2560,6 +2570,8 @@ A field can represent a physical variable. (pressure, temperature, global compos + + @@ -2584,6 +2596,8 @@ A field can represent a physical variable. (pressure, temperature, global compos + + @@ -2596,6 +2610,8 @@ A field can represent a physical variable. (pressure, temperature, global compos + + @@ -2664,6 +2680,8 @@ A field can represent a physical variable. (pressure, temperature, global compos + + @@ -2678,6 +2696,8 @@ A field can represent a physical variable. (pressure, temperature, global compos + + @@ -2696,12 +2716,16 @@ A field can represent a physical variable. (pressure, temperature, global compos + + + + @@ -2710,12 +2734,16 @@ A field can represent a physical variable. (pressure, temperature, global compos + + + + @@ -2808,6 +2836,8 @@ A field can represent a physical variable. (pressure, temperature, global compos + + @@ -2820,6 +2850,8 @@ A field can represent a physical variable. (pressure, temperature, global compos + + @@ -2959,6 +2991,8 @@ A field can represent a physical variable. (pressure, temperature, global compos + + @@ -2967,6 +3001,8 @@ A field can represent a physical variable. (pressure, temperature, global compos + + @@ -3406,12 +3442,16 @@ A field can represent a physical variable. (pressure, temperature, global compos + + + + @@ -3428,6 +3468,8 @@ A field can represent a physical variable. (pressure, temperature, global compos + + @@ -3440,6 +3482,8 @@ A field can represent a physical variable. (pressure, temperature, global compos + +