#include #include #include #include import mean_field; namespace { namespace blocks = mean_field::utils::blocks; namespace eos = mean_field::eos; namespace field = mean_field::field; namespace material = mean_field::material; namespace surface = mean_field::surface; struct Entropy final : eos::ThermodynamicQuantity { static constexpr std::string_view identifier = "mock_entropy"; }; struct Composition final : eos::ThermodynamicQuantity { static constexpr std::string_view identifier = "mock_composition"; }; struct Temperature final : eos::ThermodynamicQuantity { static constexpr std::string_view identifier = "mock_temperature"; }; struct EntropyField final { static constexpr std::string_view name = "mock_entropy"; using PhysicalQuantity = Entropy; }; struct CompositionField final { static constexpr std::string_view name = "mock_composition"; using PhysicalQuantity = Composition; }; struct TemperatureField final { static constexpr std::string_view name = "mock_temperature"; using PhysicalQuantity = Temperature; }; struct AlternateEntropyField final { static constexpr std::string_view name = "alternate_mock_entropy"; using PhysicalQuantity = Entropy; }; struct EntropyValue final : blocks::value_block_base { }; struct EntropyResidual final : blocks::residual_block_base { }; struct CompositionValue final : blocks::value_block_base { }; struct CompositionResidual final : blocks::residual_block_base { }; struct TemperatureValue final : blocks::value_block_base { }; struct TemperatureResidual final : blocks::residual_block_base { }; struct DensityValue final : blocks::value_block_base { }; struct DensityResidual final : blocks::residual_block_base { }; struct EnthalpyValue final : blocks::value_block_base { }; struct EnthalpyResidual final : blocks::residual_block_base { }; struct UnrelatedValue final : blocks::value_block_base { }; struct UnrelatedResidual final : blocks::residual_block_base { }; using EntropyEquation = material::ThermodynamicEquation; using CompositionEquation = material::ThermodynamicEquation; using TemperatureEquation = material::ThermodynamicEquation; using DensityEquation = material::ThermodynamicEquation; using EnthalpyEquation = material::ThermodynamicEquation; using EnthalpyFromPressureEntropyComposition = eos::Relation; class GeneralEquationOfState final { public: using Relations = eos::RelationCatalog; [[nodiscard]] constexpr eos::SpecificEnthalpyValue evaluate( EnthalpyFromPressureEntropyComposition, const eos::PressureValue pressure, const eos::QuantityValue entropy, const eos::QuantityValue composition ) const noexcept { return eos::SpecificEnthalpyValue{ 2.0 * pressure.value() + 3.0 * entropy.value() + 5.0 * composition.value() }; } [[nodiscard]] constexpr eos::PartialDerivative< eos::quantity::SpecificEnthalpy, Entropy> partialDerivative( EnthalpyFromPressureEntropyComposition, eos::WithRespectTo, eos::PressureValue, eos::QuantityValue, eos::QuantityValue ) const noexcept { return eos::PartialDerivative{3.0}; } [[nodiscard]] constexpr eos::PartialDerivative< eos::quantity::SpecificEnthalpy, Composition> partialDerivative( EnthalpyFromPressureEntropyComposition, eos::WithRespectTo, eos::PressureValue, eos::QuantityValue, eos::QuantityValue ) const noexcept { return eos::PartialDerivative{5.0}; } }; class MissingPressureSurfaceRelationEquationOfState final { public: using Relations = eos::RelationCatalog; [[nodiscard]] constexpr eos::PressureValue evaluate( eos::PressureFromDensity, const eos::DensityValue density ) const noexcept { return eos::PressureValue{density.value()}; } }; using EnthalpyFromPressureEntropy = eos::Relation; class AmbiguousEquationOfState final { public: using Relations = eos::RelationCatalog; [[nodiscard]] constexpr eos::SpecificEnthalpyValue evaluate( EnthalpyFromPressureEntropyComposition, const eos::PressureValue pressure, const eos::QuantityValue entropy, const eos::QuantityValue composition ) const noexcept { return eos::SpecificEnthalpyValue{pressure.value() + entropy.value() + composition.value()}; } [[nodiscard]] constexpr eos::SpecificEnthalpyValue evaluate( EnthalpyFromPressureEntropy, const eos::PressureValue pressure, const eos::QuantityValue entropy ) const noexcept { return eos::SpecificEnthalpyValue{pressure.value() + entropy.value()}; } [[nodiscard]] constexpr eos::PartialDerivative< eos::quantity::SpecificEnthalpy, Entropy> partialDerivative( EnthalpyFromPressureEntropyComposition, eos::WithRespectTo, eos::PressureValue, eos::QuantityValue, eos::QuantityValue ) const noexcept { return eos::PartialDerivative{1.0}; } [[nodiscard]] constexpr eos::PartialDerivative< eos::quantity::SpecificEnthalpy, Composition> partialDerivative( EnthalpyFromPressureEntropyComposition, eos::WithRespectTo, eos::PressureValue, eos::QuantityValue, eos::QuantityValue ) const noexcept { return eos::PartialDerivative{1.0}; } [[nodiscard]] constexpr eos::PartialDerivative< eos::quantity::SpecificEnthalpy, Entropy> partialDerivative( EnthalpyFromPressureEntropy, eos::WithRespectTo, eos::PressureValue, eos::QuantityValue ) const noexcept { return eos::PartialDerivative{1.0}; } }; using GeneralForm = blocks::block_form< blocks::type_list, blocks::type_list>; using ReorderedAvailableEquations = material:: ThermodynamicEquationCatalog; using DifferentlyReorderedAvailableEquations = material:: ThermodynamicEquationCatalog; using ExpectedGeneralEquations = material::ThermodynamicEquationCatalog; using GeneralCompilation = material::CompiledThermodynamicEquationsT; using GeneralCompilationFromOtherOrder = material:: CompiledThermodynamicEquationsT; using HalfPresentEquationForm = blocks::block_form< blocks::type_list, blocks::type_list>; using NoThermodynamicEquationForm = blocks::block_form, blocks::type_list>; using DuplicateFieldCatalog = material::ThermodynamicEquationCatalog< EntropyEquation, material::ThermodynamicEquation>; using DuplicateQuantityCatalog = material::ThermodynamicEquationCatalog< EntropyEquation, material::ThermodynamicEquation>; using DensityFromPressureEntropy = eos::Relation; class DensityCarrierEquationOfState final { public: using Relations = eos::RelationCatalog; [[nodiscard]] constexpr eos::DensityValue evaluate( DensityFromPressureEntropy, const eos::PressureValue pressure, const eos::QuantityValue entropy ) const noexcept { return eos::DensityValue{4.0 * pressure.value() + 2.0 * entropy.value()}; } [[nodiscard]] constexpr eos::PartialDerivative< eos::quantity::Density, Entropy> partialDerivative( DensityFromPressureEntropy, eos::WithRespectTo, eos::PressureValue, eos::QuantityValue ) const noexcept { return eos::PartialDerivative{2.0}; } }; using DensityCarrierForm = blocks:: block_form, blocks::type_list>; using DensityCarrierAvailableEquations = material::ThermodynamicEquationCatalog; using DensityCarrierCompilation = material::CompiledThermodynamicEquationsT< DensityCarrierEquationOfState, DensityCarrierForm, DensityCarrierAvailableEquations>; struct DensityCarrierState final { double density; double entropy; [[nodiscard]] constexpr eos::DensityValue value(eos::quantity::Density) const noexcept { return eos::DensityValue{density}; } [[nodiscard]] constexpr eos::QuantityValue value(Entropy) const noexcept { return eos::QuantityValue{entropy}; } }; } // namespace TEST_CASE( "Thermodynamic Equation Compilation Is Inferred From Fields And Canonical Problem Blocks", "[material][thermodynamic_equations][compilation][unit][type_contract]" ) { STATIC_CHECK(material::ThermodynamicField); STATIC_CHECK(material::ThermodynamicField); STATIC_CHECK(material::ThermodynamicField); STATIC_CHECK(material::ThermodynamicEquationType); STATIC_CHECK(material::ValidThermodynamicEquationCatalog); STATIC_CHECK(material::CompiledThermodynamicEquations); STATIC_CHECK(std::same_as); STATIC_CHECK(std::same_as); STATIC_CHECK(GeneralCompilation::Equations::size == 3); STATIC_CHECK( std::same_as< typename GeneralCompilation::StateBindings, surface::SurfaceStateBindings< surface::SurfaceStateBinding, surface::SurfaceStateBinding, surface::SurfaceStateBinding>> ); STATIC_CHECK(std::same_as); } TEST_CASE( "Thermodynamic Equation Compilation Rejects Incomplete Ambiguous And Missing Physics", "[material][thermodynamic_equations][compilation][unit][negative]" ) { STATIC_CHECK_FALSE( material::ThermodynamicEquationsCompilable< GeneralEquationOfState, HalfPresentEquationForm, ReorderedAvailableEquations> ); STATIC_CHECK_FALSE( material::ThermodynamicEquationsCompilable< GeneralEquationOfState, NoThermodynamicEquationForm, ReorderedAvailableEquations> ); STATIC_CHECK_FALSE( material::ThermodynamicEquationsCompilable< MissingPressureSurfaceRelationEquationOfState, GeneralForm, ReorderedAvailableEquations> ); STATIC_CHECK_FALSE( material::ThermodynamicEquationsCompilable ); STATIC_CHECK_FALSE(material::ValidThermodynamicEquationCatalog); STATIC_CHECK_FALSE(material::ValidThermodynamicEquationCatalog); } TEST_CASE( "Pressure Surface Carrier Is Selected By The EOS Relation Rather Than A Preferred Field", "[material][thermodynamic_equations][surface][unit]" ) { using Formulation = DensityCarrierCompilation::PressureSurfaceFormulation; using Constraint = surface::CompiledPressureSurfaceConstraintT; STATIC_CHECK(material::CompiledThermodynamicEquations); STATIC_CHECK(std::same_as); STATIC_CHECK(std::same_as); STATIC_CHECK(std::same_as); STATIC_CHECK( std::same_as< typename Constraint::SurfaceDependencies::StateFieldTypes, field::TypeList> ); constexpr DensityCarrierEquationOfState equationOfState; const surface::ConstantPressureSurface condition{eos::PressureValue{0.25}}; const auto constraint = surface::compilePressureSurfaceConstraint(condition, equationOfState); constexpr DensityCarrierState state{.density = 1.6, .entropy = 0.3}; constexpr DensityCarrierState direction{.density = -0.2, .entropy = 0.4}; CHECK(constraint.residual(state) == 0.0); CHECK(constraint.jacobianAction(state, direction) == -1.0); }