diff --git a/src/core/io/src/4C_io_input_field.hpp b/src/core/io/src/4C_io_input_field.hpp index a25b59a91d..c7624a9640 100644 --- a/src/core/io/src/4C_io_input_field.hpp +++ b/src/core/io/src/4C_io_input_field.hpp @@ -200,6 +200,7 @@ namespace Core::IO !std::is_same_v; public: + using value_type = T; using IndexType = int; using MapType = std::unordered_map; using PointMapType = PointDataMap; diff --git a/src/core/io/src/4C_io_input_spec_builders.hpp b/src/core/io/src/4C_io_input_spec_builders.hpp index 8b7b1e8be1..f82d0fd9b0 100644 --- a/src/core/io/src/4C_io_input_spec_builders.hpp +++ b/src/core/io/src/4C_io_input_spec_builders.hpp @@ -826,6 +826,17 @@ namespace Core::IO * used to implement input_field(). */ StoreFunction transform_data{nullptr}; + + /** + * Whether the selection is required or optional. + */ + bool required{true}; + + /** + * An optional function that stores a default value into the container when the selection + * is absent from the input. Setting this implies has_default_value = true. + */ + std::optional> default_value_setter{}; }; template @@ -884,6 +895,19 @@ namespace Core::IO * store the InputField in a struct. */ StoreFunction store{nullptr}; + + /** + * Whether the InputField is required or optional. If false, the field may be absent from + * the input. If a default_value is set, it is stored when the field is absent. + */ + bool required{true}; + + /** + * An optional default value of the underlying value type T. When the InputField is absent + * from the input, a constant InputField wrapping this value is stored automatically. + * Implies required = false. + */ + std::optional default_value{}; }; template @@ -986,6 +1010,9 @@ namespace Core::IO std::function init_my_storage{}; InputSpecBuilders::StoreFunction move_my_storage{}; + bool required{true}; + std::optional> default_value_setter{}; + void parse(ValueParser& parser, InputParameterContainer& container) const; bool match(ConstYamlNodeRef node, InputSpecBuilders::Storage& container, IO::Internal::MatchEntry& match_entry) const; @@ -2259,6 +2286,19 @@ bool Core::IO::Internal::SelectionSpec::match(ConstYamlNodeRef node, if (!group_exists_nested && !group_node_is_input) { + if (default_value_setter.has_value()) + { + match_entry.state = IO::Internal::MatchEntry::State::defaulted; + set_default_value(container); + return true; + } + + if (!required) + { + match_entry.state = IO::Internal::MatchEntry::State::not_required; + return true; + } + return false; } @@ -2352,7 +2392,7 @@ void Core::IO::Internal::SelectionSpec::emit_metadata( { emit_value_as_yaml(node.wrap(node.node["description"]), data.description); } - emit_value_as_yaml(node.wrap(node.node["required"]), true); + emit_value_as_yaml(node.wrap(node.node["required"]), required); node.node["choices"] |= ryml::SEQ; for (const auto& [choice, spec] : choices) @@ -2397,7 +2437,9 @@ template void Core::IO::Internal::SelectionSpec::set_default_value( InputSpecBuilders::Storage& container) const { - FOUR_C_THROW("Internal error: set_default_value() called on a SelectionSpec."); + FOUR_C_ASSERT(default_value_setter.has_value(), + "Internal error: set_default_value() called on a SelectionSpec without a default value."); + (*default_value_setter)(container); } @@ -2521,8 +2563,8 @@ Core::IO::InputSpec Core::IO::InputSpecBuilders::selection( Internal::InputSpecImpl::CommonData common_data{ .name = name, .description = data.description, - .required = true, - .has_default_value = false, + .required = data.required && !data.default_value_setter.has_value(), + .has_default_value = data.default_value_setter.has_value(), .type = Internal::InputSpecType::selection, .stores_to = data.transform_data ? &data.transform_data.stores_to() : &move_my_storage.stores_to(), @@ -2552,6 +2594,8 @@ Core::IO::InputSpec Core::IO::InputSpecBuilders::selection( }, .init_my_storage = [](Storage& storage) { storage.emplace(); }, .move_my_storage = std::move(move_my_storage), + .required = data.required && !data.default_value_setter.has_value(), + .default_value_setter = std::move(data.default_value_setter), }, common_data); } @@ -2562,6 +2606,19 @@ Core::IO::InputSpec Core::IO::InputSpecBuilders::input_field( const std::string name, InputFieldData> data) { auto store = data.store ? data.store : in_container>(name); + + std::optional> default_value_setter; + if (data.default_value.has_value()) + { + default_value_setter = [store_fn = store, default_value = *data.default_value]( + Storage& container) + { + InputField default_field(default_value); + [[maybe_unused]] auto [ok, _] = store_fn(container, std::move(default_field)); + FOUR_C_ASSERT(ok, "Internal error: could not set default value for input field."); + }; + } + auto spec = selection(name, { parameter("constant", {.description = "Constant value for the field."}), @@ -2575,6 +2632,8 @@ Core::IO::InputSpec Core::IO::InputSpecBuilders::input_field( { .description = data.description, .transform_data = Internal::make_input_field_data_transform(name, std::move(store)), + .required = data.required && !data.default_value.has_value(), + .default_value_setter = std::move(default_value_setter), }); return spec; }; @@ -2585,6 +2644,20 @@ Core::IO::InputSpec Core::IO::InputSpecBuilders::interpolated_input_field( { auto store = data.store ? data.store : in_container>(name); + + std::optional> default_value_setter; + if (data.default_value.has_value()) + { + default_value_setter = [store_fn = store, default_value = *data.default_value]( + Storage& container) + { + InterpolatedInputField default_field(default_value); + [[maybe_unused]] auto [ok, _] = store_fn(container, std::move(default_field)); + FOUR_C_ASSERT( + ok, "Internal error: could not set default value for interpolated input field."); + }; + } + auto spec = selection(name, { parameter("constant", {.description = "Constant value for the field."}), @@ -2598,6 +2671,8 @@ Core::IO::InputSpec Core::IO::InputSpecBuilders::interpolated_input_field( { .description = data.description, .transform_data = Internal::make_input_field_data_transform(name, std::move(store)), + .required = data.required && !data.default_value.has_value(), + .default_value_setter = std::move(default_value_setter), }); return spec; }; diff --git a/src/core/io/tests/4C_io_input_field_test.cpp b/src/core/io/tests/4C_io_input_field_test.cpp index 924f38c822..3ad168b593 100644 --- a/src/core/io/tests/4C_io_input_field_test.cpp +++ b/src/core/io/tests/4C_io_input_field_test.cpp @@ -174,4 +174,45 @@ namespace EXPECT_EQ(data.stiffness.at(3), 5.5); } } + TEST(InputField, DefaultValueUsedWhenFieldAbsent) + { + auto spec = input_field( + "stiffness", {.description = "A stiffness field", .default_value = 10.0}); + + EXPECT_FALSE(spec.impl().required()); + EXPECT_TRUE(spec.impl().has_default_value()); + + ryml::Tree tree = init_yaml_tree_with_exceptions(); + ryml::NodeRef root = tree.rootref(); + ryml::parse_in_arena("{}", root); + + ConstYamlNodeRef node(root, ""); + InputParameterContainer container; + spec.match(node, container); + + const auto& field = container.get>("stiffness"); + EXPECT_EQ(field.at(0), 10.0); + EXPECT_EQ(field.at(99), 10.0); + } + + TEST(InputField, DefaultValueUsedWhenInterpolatedFieldAbsent) + { + auto spec = interpolated_input_field( + "scalar1", {.description = "A scalar field", .default_value = 12.00}); + + EXPECT_FALSE(spec.impl().required()); + EXPECT_TRUE(spec.impl().has_default_value()); + + ryml::Tree tree = init_yaml_tree_with_exceptions(); + ryml::NodeRef root = tree.rootref(); + ryml::parse_in_arena("{}", root); + + ConstYamlNodeRef node(root, ""); + InputParameterContainer container; + spec.match(node, container); + + const auto& field = container.get>("scalar1"); + EXPECT_NEAR(field.interpolate(0, std::array{0.0, 0.0, 0.0}), 12.00, 1e-12); + } + } // namespace diff --git a/src/global_legacy_module/4C_global_legacy_module_validmaterials.cpp b/src/global_legacy_module/4C_global_legacy_module_validmaterials.cpp index 61aff7740e..9935dfaaec 100644 --- a/src/global_legacy_module/4C_global_legacy_module_validmaterials.cpp +++ b/src/global_legacy_module/4C_global_legacy_module_validmaterials.cpp @@ -13,6 +13,7 @@ #include "4C_io_input_field.hpp" #include "4C_io_input_spec_builders.hpp" #include "4C_io_input_spec_validators.hpp" +#include "4C_linalg_tensor_generators.hpp" #include "4C_linalg_utils_densematrix_funct.hpp" #include "4C_mat_electrode.hpp" #include "4C_mat_fiber_interpolation.hpp" @@ -4172,6 +4173,10 @@ std::unordered_map Global::v parameter( "ISOCHORIC", {.description = "Flag whether prestretch tensor is isochoric", .default_value = false}), + interpolated_input_field>("PRESTRETCH", + {.description = "Optional initial prestretch tensor used as starting value. " + "If not provided, the identity tensor is used.", + .default_value = Core::LinAlg::TensorGenerators::identity}), }, {.description = "Simple iterative prestress strategy for any geometry. Needed to be " "used within the mixture framework."}); diff --git a/src/mixture/src/4C_mixture_prestress_strategy_iterative.cpp b/src/mixture/src/4C_mixture_prestress_strategy_iterative.cpp index 0d482fa194..8af539d87e 100644 --- a/src/mixture/src/4C_mixture_prestress_strategy_iterative.cpp +++ b/src/mixture/src/4C_mixture_prestress_strategy_iterative.cpp @@ -9,7 +9,6 @@ #include "4C_linalg_fixedsizematrix_generators.hpp" #include "4C_linalg_symmetric_tensor.hpp" -#include "4C_linalg_tensor_generators.hpp" #include "4C_linalg_tensor_svd.hpp" #include "4C_mat_anisotropy.hpp" #include "4C_mat_elast_isoneohooke.hpp" @@ -25,7 +24,10 @@ Mixture::PAR::IterativePrestressStrategy::IterativePrestressStrategy( const Core::Mat::PAR::Parameter::Data& matdata) : PrestressStrategy(matdata), isochoric_(matdata.parameters.get("ISOCHORIC")), - is_active_(matdata.parameters.get("ACTIVE")) + is_active_(matdata.parameters.get("ACTIVE")), + initial_prestretch_(matdata.parameters + .get>>( + "PRESTRETCH")) { } @@ -55,8 +57,7 @@ void Mixture::IterativePrestressStrategy::evaluate_prestress(const MixtureRule& const Teuchos::ParameterList& params, const Mat::EvaluationContext<3>& context, int gp, int eleGID) { - // Start with zero prestretch - G = Core::LinAlg::TensorGenerators::identity; + G = params_->initial_prestretch_.interpolate(eleGID, context.xi->as_span()); } void Mixture::IterativePrestressStrategy::update( diff --git a/src/mixture/src/4C_mixture_prestress_strategy_iterative.hpp b/src/mixture/src/4C_mixture_prestress_strategy_iterative.hpp index 97cb185881..dc5023ffcb 100644 --- a/src/mixture/src/4C_mixture_prestress_strategy_iterative.hpp +++ b/src/mixture/src/4C_mixture_prestress_strategy_iterative.hpp @@ -11,7 +11,9 @@ #include "4C_config.hpp" #include "4C_comm_pack_buffer.hpp" +#include "4C_io_input_field.hpp" #include "4C_linalg_fixedsizematrix.hpp" +#include "4C_linalg_symmetric_tensor.hpp" #include "4C_material_parameter_base.hpp" #include "4C_mixture_prestress_strategy.hpp" @@ -50,6 +52,9 @@ namespace Mixture const bool isochoric_; /// Flag whether the prestretch tensor should be updated const bool is_active_; + /// Initial prestretch tensor. Defaults to the identity tensor if not specified in the input. + const Core::IO::InterpolatedInputField> + initial_prestretch_; /// @} }; } // namespace PAR diff --git a/tests/input_files/mixture_prestress_iterative_prescribed.4C.yaml b/tests/input_files/mixture_prestress_iterative_prescribed.4C.yaml new file mode 100644 index 0000000000..6408be68f6 --- /dev/null +++ b/tests/input_files/mixture_prestress_iterative_prescribed.4C.yaml @@ -0,0 +1,213 @@ +TITLE: + - "This test covers the functionality of the iterative prestress strategy with a prescribed initial + prestretch. The test represents the prestressing performed on a single element in Maes & Famaey 2023 + https://doi.org/10.1016/j.jmbbm.2023.105733 paragraph 2.6." +PROBLEM TYPE: + PROBLEMTYPE: "Structure" +IO: + STRUCT_DISP: true + VERBOSITY: "Standard" + STRUCT_STRAIN: "gl" + STRUCT_STRESS: "cauchy" + WRITE_FINAL_STATE: true + WRITE_INITIAL_STATE: false +IO/RUNTIME VTK OUTPUT: + INTERVAL_STEPS: 1 +IO/RUNTIME VTK OUTPUT/STRUCTURE: + OUTPUT_STRUCTURE: true + DISPLACEMENT: true + ELEMENT_OWNER: true + STRESS_STRAIN: true + GAUSS_POINT_DATA_OUTPUT_TYPE: "gauss_points" +SOLVER 1: + NAME: "Structure_Solver" + SOLVER: "MUMPS" +STRUCTURAL DYNAMIC: + INT_STRATEGY: "Standard" + DYNAMICTYPE: "Statics" + RESULTSEVERY: 0 + RESTARTEVERY: 0 + TIMESTEP: 0.1 + NUMSTEP: 2000 + MAXTIME: 200.0000000001 + TOLDISP: 1e-09 + TOLRES: 1e-09 + LINEAR_SOLVER: 1 + LOADLIN: true + PRESTRESS: "material_iterative" + PRESTRESSTIME: 200.0000000001 + PRESTRESSTOLDISP: 1.0 # This is set large to ensure that the desired MINLOADSTEPS below from the paper are met + PRESTRESSMINLOADSTEPS: 20 +STRUCTURE GEOMETRY: + FILE: "vtu/homogenized-constrained-mixture-patch.vtu" + ELEMENT_BLOCKS: + - ID: 1 + SOLID: + HEX8: + MAT: 1 + KINEM: "nonlinear" +MATERIALS: + - MAT: 1 + MAT_Mixture: + MATIDSCONST: [11, 12, 13, 14, 15] + MATIDMIXTURERULE: 10 + - MAT: 10 + MIX_GrowthRemodelMixtureRule: + GROWTH_STRATEGY: 100 + MASSFRAC: [0.8, 0.05, 0.05, 0.05, 0.05] + DENS: 1.0 + - MAT: 100 + MIX_GrowthStrategy_Anisotropic: + GROWTH_DIRECTION: + constant: [1.0, 0.0, 0.0] + - MAT: 11 + MIX_Constituent_ElastHyper: + NUMMAT: 2 + MATIDS: [111, 112] + PRESTRESS_STRATEGY: 119 + - MAT: 111 + ELAST_IsoNeoHooke: + MUE: + constant: 0.07625 + - MAT: 112 + ELAST_VolSussmanBathe: + KAPPA: 0.7625 + - MAT: 119 + MIX_Prestress_Strategy_Iterative: + ISOCHORIC: false + ACTIVE: true + PRESTRETCH: + constant: [[0.912870929, 0.0, 0.0], [0.0, 0.912870929, 0.0], [0.0, 0.0, 1.2]] + - MAT: 12 + MIX_Constituent_ExplicitRemodelFiber: + FIBER_MATERIAL_ID: 110 + DECAY_TIME: 101.0 + GROWTH_CONSTANT: 0.0009900990099009901 + DEPOSITION_STRETCH: 1.1 + ENABLE_GROWTH: false + ENABLE_BASAL_MASS_PRODUCTION: true + ORIENTATION: + constant: [0.0, 1.0, 0.0] + INELASTIC_GROWTH: true + - MAT: 13 + MIX_Constituent_ExplicitRemodelFiber: + FIBER_MATERIAL_ID: 110 + DECAY_TIME: 101.0 + GROWTH_CONSTANT: 0.0009900990099009901 + DEPOSITION_STRETCH: 1.1 + ENABLE_GROWTH: false + ENABLE_BASAL_MASS_PRODUCTION: true + ORIENTATION: + constant: [0.0, 0.0, 1.0] + INELASTIC_GROWTH: true + - MAT: 130 + MIX_Constituent_RemodelFiber_Material_Exponential: + K1: 0.578 + K2: 1.23 + COMPRESSION: false + - MAT: 14 + MIX_Constituent_ExplicitRemodelFiber: + FIBER_MATERIAL_ID: 110 + DECAY_TIME: 101.0 + GROWTH_CONSTANT: 0.0009900990099009901 + DEPOSITION_STRETCH: 1.1 + ENABLE_GROWTH: false + ENABLE_BASAL_MASS_PRODUCTION: true + ORIENTATION: + constant: [0.0, 0.9238795325112867, 0.3826834323650898] + INELASTIC_GROWTH: true + - MAT: 140 + MIX_Constituent_RemodelFiber_Material_Exponential: + K1: 0.578 + K2: 1.23 + COMPRESSION: false + - MAT: 15 + MIX_Constituent_ExplicitRemodelFiber: + FIBER_MATERIAL_ID: 110 + DECAY_TIME: 101.0 + GROWTH_CONSTANT: 0.0009900990099009901 + DEPOSITION_STRETCH: 1.1 + ENABLE_GROWTH: false + ENABLE_BASAL_MASS_PRODUCTION: true + ORIENTATION: + constant: [0.0, 0.9238795325112867, -0.3826834323650898] + INELASTIC_GROWTH: true + - MAT: 110 + MIX_Constituent_RemodelFiber_Material_Exponential: + K1: 0.578 + K2: 1.23 + COMPRESSION: false +DESIGN SURF DIRICH CONDITIONS: + - E: 2 + ENTITY_TYPE: "node_set_id" + NUMDOF: 3 + ONOFF: [1, 0, 0] + VAL: [0.0, 0.0, 0.0] + FUNCT: [0, 0, 0] + - E: 3 + ENTITY_TYPE: "node_set_id" + NUMDOF: 3 + ONOFF: [0, 0, 1] + VAL: [0.0, 0.0, 0.0] + FUNCT: [0, 0, 0] + - E: 5 + ENTITY_TYPE: "node_set_id" + NUMDOF: 3 + ONOFF: [0, 0, 1] + VAL: [0.0, 0.0, 0.0] + FUNCT: [0, 0, 0] + - E: 6 + ENTITY_TYPE: "node_set_id" + NUMDOF: 3 + ONOFF: [0, 1, 0] + VAL: [0.0, 0.0, 0.0] + FUNCT: [0, 0, 0] +DESIGN SURF NEUMANN CONDITIONS: + - E: 4 + ENTITY_TYPE: "node_set_id" + NUMDOF: 3 + ONOFF: [1, 0, 0] + VAL: [1.0, 0.0, 0.0] + FUNCT: [1, 0, 0] + TYPE: "orthopressure" +FUNCT1: + - SYMBOLIC_FUNCTION_OF_SPACE_TIME: "v" + - VARIABLE: 0 + NAME: "v" + TYPE: "linearinterpolation" + NUMPOINTS: 3 + TIMES: [0, 1, 200.10000000009998] + VALUES: [0.1, 0.1, 0.1] +FUNCT2: + - SYMBOLIC_FUNCTION_OF_TIME: "v" + - VARIABLE: 0 + NAME: "v" + TYPE: "linearinterpolation" + NUMPOINTS: 3 + TIMES: [0.0, 1.0, 200.10000000009998] + VALUES: [1.1, 1.1, 1.1] +RESULT DESCRIPTION: + - STRUCTURE: + DIS: "structure" + NODE: 1 + QUANTITY: "dispx" + VALUE: 1.84976129836196318e-04 + TOLERANCE: 1e-06 + - STRUCTURE: + DIS: "structure" + NODE: 6 + QUANTITY: "dispy" + VALUE: 2.11809010640181855e-04 + TOLERANCE: 1e-06 + - STRUCTURE: + DIS: "structure" + NODE: 8 + QUANTITY: "dispy" + VALUE: 2.11809010639350950e-04 + TOLERANCE: 1e-06 + - STRUCTURE: + DIS: "structure" + NODE: 2 + QUANTITY: "dispx" + VALUE: 0 + TOLERANCE: 1.84976129836364044e-04 diff --git a/tests/input_files/vtu/homogenized-constrained-mixture-patch.vtu b/tests/input_files/vtu/homogenized-constrained-mixture-patch.vtu new file mode 100644 index 0000000000..7730f29648 --- /dev/null +++ b/tests/input_files/vtu/homogenized-constrained-mixture-patch.vtu @@ -0,0 +1,147 @@ + + + + + + 0 1 0 0 1 0 + 0 1 0 0 1 0 + 0 1 0 0 1 0 + 0 1 0 0 1 0 + + + 1 + + + 1 + + + + + 1 0 0 1 0 0 + 1 0 0 1 0 0 + 1 0 0 1 0 0 + 1 0 0 1 0 0 + + + 1 + + + 1 + + + + + 0 0 1 0 0 1 + 0 0 1 0 0 1 + 0 0 1 0 0 1 + 0 0 1 0 0 1 + + + 1 + + + 1 + + + + + 0 0.923879532511 0.382683432365 0 0.923879532511 0.382683432365 + 0 0.923879532511 0.382683432365 0 0.923879532511 0.382683432365 + 0 0.923879532511 0.382683432365 0 0.923879532511 0.382683432365 + 0 0.923879532511 0.382683432365 0 0.923879532511 0.382683432365 + + + 1 + + + 1 + + + + + 0 0.923879532511 -0.382683432365 0 0.923879532511 -0.382683432365 + 0 0.923879532511 -0.382683432365 0 0.923879532511 -0.382683432365 + 0 0.923879532511 -0.382683432365 0 0.923879532511 -0.382683432365 + 0 0.923879532511 -0.382683432365 0 0.923879532511 -0.382683432365 + + + 1 + + + 1 + + + + + 1 1 1 1 0 0 + 0 0 + + + 1 1 0 0 1 1 + 0 0 + + + 1 0 1 0 1 0 + 1 0 + + + 1 1 1 1 1 1 + 1 1 + + + 0 1 0 1 0 1 + 0 1 + + + 0 0 1 1 0 0 + 1 1 + + + 0 0 0 0 1 1 + 1 1 + + + + + 1 + + + + + 0 0 0 0 1 0 + 0 0 1 0 1 1 + 1 0 0 1 1 0 + 1 0 1 1 1 1 + + + 0 + + + 1.7320508076 + + + + + 0 + + + 1.7320508076 + + + + + + + 0 4 5 1 2 6 + 7 3 + + + 8 + + + 12 + + + + + diff --git a/tests/list_of_tests.cmake b/tests/list_of_tests.cmake index 661ba440c2..8b7c61dfea 100644 --- a/tests/list_of_tests.cmake +++ b/tests/list_of_tests.cmake @@ -2554,6 +2554,7 @@ four_c_test(TEST_FILE xfsi_push_1D_1st_order.4C.yaml) four_c_test(TEST_FILE xfsi_push_1D_2nd_order.4C.yaml) # Tests requiring VTK +four_c_test(TEST_FILE mixture_prestress_iterative_prescribed.4C.yaml NP 2 REQUIRED_DEPENDENCIES VTK) four_c_test(TEST_FILE ssi_homogenized_constraint_mixture_qcyl.4C.yaml NP 2 REQUIRED_DEPENDENCIES VTK) four_c_test(TEST_FILE ssi_homogenized_constraint_mixture_prestress_qcyl.4C.yaml NP 2 REQUIRED_DEPENDENCIES VTK) four_c_test(TEST_FILE solid_vtu_input.4C.yaml NP 2 REQUIRED_DEPENDENCIES VTK RETURN_AS current)