Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/core/io/src/4C_io_input_field.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ namespace Core::IO
!std::is_same_v<Interpolation, NoInterpolation>;

public:
using value_type = T;
using IndexType = int;
using MapType = std::unordered_map<IndexType, T>;
using PointMapType = PointDataMap<IndexType, T>;
Expand Down
83 changes: 79 additions & 4 deletions src/core/io/src/4C_io_input_spec_builders.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,17 @@ namespace Core::IO
* used to implement input_field().
*/
StoreFunction<DefaultStorage> 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<std::function<void(Storage&)>> default_value_setter{};
};

template <typename T>
Expand Down Expand Up @@ -884,6 +895,19 @@ namespace Core::IO
* store the InputField in a struct.
*/
StoreFunction<InputField> 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<typename InputField::value_type> default_value{};
};

template <typename Number, Utils::CompileTimeString... variables>
Expand Down Expand Up @@ -986,6 +1010,9 @@ namespace Core::IO
std::function<void(InputSpecBuilders::Storage& my_storage)> init_my_storage{};
InputSpecBuilders::StoreFunction<InputSpecBuilders::Storage> move_my_storage{};

bool required{true};
std::optional<std::function<void(InputSpecBuilders::Storage&)>> default_value_setter{};

void parse(ValueParser& parser, InputParameterContainer& container) const;
bool match(ConstYamlNodeRef node, InputSpecBuilders::Storage& container,
IO::Internal::MatchEntry& match_entry) const;
Expand Down Expand Up @@ -2259,6 +2286,19 @@ bool Core::IO::Internal::SelectionSpec<T>::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;
}

Expand Down Expand Up @@ -2352,7 +2392,7 @@ void Core::IO::Internal::SelectionSpec<T>::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)
Expand Down Expand Up @@ -2397,7 +2437,9 @@ template <typename T>
void Core::IO::Internal::SelectionSpec<T>::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);
}


Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -2552,6 +2594,8 @@ Core::IO::InputSpec Core::IO::InputSpecBuilders::selection(
},
.init_my_storage = [](Storage& storage) { storage.emplace<StorageType>(); },
.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);
}
Expand All @@ -2562,6 +2606,19 @@ Core::IO::InputSpec Core::IO::InputSpecBuilders::input_field(
const std::string name, InputFieldData<InputField<T>> data)
{
auto store = data.store ? data.store : in_container<InputField<T>>(name);

std::optional<std::function<void(Storage&)>> default_value_setter;
if (data.default_value.has_value())
{
default_value_setter = [store_fn = store, default_value = *data.default_value](
Storage& container)
{
InputField<T> 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<Internal::InputFieldType>(name,
{
parameter<T>("constant", {.description = "Constant value for the field."}),
Expand All @@ -2575,6 +2632,8 @@ Core::IO::InputSpec Core::IO::InputSpecBuilders::input_field(
{
.description = data.description,
.transform_data = Internal::make_input_field_data_transform<T>(name, std::move(store)),
.required = data.required && !data.default_value.has_value(),
.default_value_setter = std::move(default_value_setter),
});
return spec;
};
Expand All @@ -2585,6 +2644,20 @@ Core::IO::InputSpec Core::IO::InputSpecBuilders::interpolated_input_field(
{
auto store =
data.store ? data.store : in_container<InterpolatedInputField<T, Interpolation>>(name);

std::optional<std::function<void(Storage&)>> default_value_setter;
if (data.default_value.has_value())
{
default_value_setter = [store_fn = store, default_value = *data.default_value](
Storage& container)
{
InterpolatedInputField<T, Interpolation> 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<Internal::InputFieldType>(name,
{
parameter<T>("constant", {.description = "Constant value for the field."}),
Expand All @@ -2598,6 +2671,8 @@ Core::IO::InputSpec Core::IO::InputSpecBuilders::interpolated_input_field(
{
.description = data.description,
.transform_data = Internal::make_input_field_data_transform<T>(name, std::move(store)),
.required = data.required && !data.default_value.has_value(),
.default_value_setter = std::move(default_value_setter),
});
return spec;
};
Expand Down
41 changes: 41 additions & 0 deletions src/core/io/tests/4C_io_input_field_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,45 @@ namespace
EXPECT_EQ(data.stiffness.at(3), 5.5);
}
}
TEST(InputField, DefaultValueUsedWhenFieldAbsent)
{
auto spec = input_field<double>(
"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<InputField<double>>("stiffness");
EXPECT_EQ(field.at(0), 10.0);
EXPECT_EQ(field.at(99), 10.0);
}

TEST(InputField, DefaultValueUsedWhenInterpolatedFieldAbsent)
{
auto spec = interpolated_input_field<double>(
"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<InterpolatedInputField<double>>("scalar1");
EXPECT_NEAR(field.interpolate(0, std::array{0.0, 0.0, 0.0}), 12.00, 1e-12);
}

} // namespace
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -4172,6 +4173,10 @@ std::unordered_map<Core::Materials::MaterialType, Core::IO::InputSpec> Global::v
parameter<bool>(
"ISOCHORIC", {.description = "Flag whether prestretch tensor is isochoric",
.default_value = false}),
interpolated_input_field<Core::LinAlg::SymmetricTensor<double, 3, 3>>("PRESTRETCH",
{.description = "Optional initial prestretch tensor used as starting value. "
"If not provided, the identity tensor is used.",
.default_value = Core::LinAlg::TensorGenerators::identity<double, 3, 3>}),
},
{.description = "Simple iterative prestress strategy for any geometry. Needed to be "
"used within the mixture framework."});
Expand Down
9 changes: 5 additions & 4 deletions src/mixture/src/4C_mixture_prestress_strategy_iterative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -25,7 +24,10 @@ Mixture::PAR::IterativePrestressStrategy::IterativePrestressStrategy(
const Core::Mat::PAR::Parameter::Data& matdata)
: PrestressStrategy(matdata),
isochoric_(matdata.parameters.get<bool>("ISOCHORIC")),
is_active_(matdata.parameters.get<bool>("ACTIVE"))
is_active_(matdata.parameters.get<bool>("ACTIVE")),
initial_prestretch_(matdata.parameters
.get<Core::IO::InterpolatedInputField<Core::LinAlg::SymmetricTensor<double, 3, 3>>>(
"PRESTRETCH"))
{
}

Expand Down Expand Up @@ -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<double, 3, 3>;
G = params_->initial_prestretch_.interpolate(eleGID, context.xi->as_span());
}

void Mixture::IterativePrestressStrategy::update(
Expand Down
5 changes: 5 additions & 0 deletions src/mixture/src/4C_mixture_prestress_strategy_iterative.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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<Core::LinAlg::SymmetricTensor<double, 3, 3>>
initial_prestretch_;
/// @}
};
} // namespace PAR
Expand Down
Loading
Loading