feat(cpp-object-oriented-modular-refactoring): step 19 - boundary-condition-policy
This commit is contained in:
@@ -8,6 +8,7 @@ add_executable(
|
||||
unit/assembly/parallel_for_test.cpp
|
||||
unit/assembly/load_assembler_test.cpp
|
||||
unit/assembly/sparse_assembler_test.cpp
|
||||
unit/constraints/boundary_condition_test.cpp
|
||||
unit/constraints/essential_constraints_test.cpp
|
||||
unit/core/ascii_test.cpp
|
||||
unit/core/diagnostic_test.cpp
|
||||
|
||||
@@ -107,7 +107,7 @@ TEST(AnalysisModel, ClassifiesActiveEntitiesInStableOrder) {
|
||||
EXPECT_EQ(model.ActiveSections(),
|
||||
(std::vector<fesa::EntityIndex>{0U, 1U, 2U}));
|
||||
EXPECT_EQ(model.ActiveBoundaryConditions(),
|
||||
(std::vector<fesa::EntityIndex>{0U, 1U}));
|
||||
(std::vector<fesa::EntityIndex>{0U, 1U, 2U, 3U, 4U, 5U}));
|
||||
EXPECT_EQ(model.ActiveLoads(), (std::vector<fesa::EntityIndex>{0U, 1U, 2U}));
|
||||
ASSERT_EQ(model.Step().Loads().size(), 3U);
|
||||
EXPECT_EQ(&model.Step().Loads()[0U].get(),
|
||||
|
||||
@@ -27,10 +27,10 @@ struct LoadFixture {
|
||||
std::unique_ptr<fesa::DofManager> dofs;
|
||||
};
|
||||
|
||||
LoadFixture MakeFixture(const std::size_t node_count,
|
||||
std::vector<fesa::NodeSet> node_sets,
|
||||
std::vector<fesa::BoundaryCondition> boundaries,
|
||||
std::vector<fesa::NodalLoad> loads) {
|
||||
LoadFixture MakeFixture(
|
||||
const std::size_t node_count, std::vector<fesa::NodeSet> node_sets,
|
||||
std::vector<fesa::PrescribedDisplacementDefinition> boundaries,
|
||||
std::vector<fesa::NodalLoad> loads) {
|
||||
const std::filesystem::path source{"models/load-assembly.inp"};
|
||||
fesa::ModelDefinition definition{};
|
||||
definition.source_path = source;
|
||||
@@ -73,8 +73,9 @@ LoadFixture MakeFixture(const std::size_t node_count,
|
||||
return {std::move(domain), std::move(model), std::move(dofs)};
|
||||
}
|
||||
|
||||
LoadFixture MakeShellFixture(std::vector<fesa::BoundaryCondition> boundaries,
|
||||
std::vector<fesa::NodalLoad> loads) {
|
||||
LoadFixture MakeShellFixture(
|
||||
std::vector<fesa::PrescribedDisplacementDefinition> boundaries,
|
||||
std::vector<fesa::NodalLoad> loads) {
|
||||
const std::filesystem::path source{"models/shell-load-assembly.inp"};
|
||||
fesa::ModelDefinition definition{};
|
||||
definition.source_path = source;
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
#include "fesa/constraints/boundary_condition.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "fesa/analysis/analysis_model.h"
|
||||
#include "fesa/constraints/prescribed_displacement.h"
|
||||
#include "fesa/fem/dof_manager.h"
|
||||
#include "fesa/model/domain.h"
|
||||
#include "fesa/model/source_target_resolver.h"
|
||||
|
||||
namespace {
|
||||
|
||||
struct BoundaryFixture {
|
||||
std::unique_ptr<fesa::Domain> domain;
|
||||
std::unique_ptr<fesa::AnalysisModel> model;
|
||||
std::unique_ptr<fesa::DofManager> dofs;
|
||||
};
|
||||
|
||||
fesa::ModelDefinition MakeDefinition(
|
||||
std::vector<fesa::PrescribedDisplacementDefinition> boundaries = {}) {
|
||||
const std::filesystem::path source{"models/boundary-condition.inp"};
|
||||
fesa::ModelDefinition definition{};
|
||||
definition.source_path = source;
|
||||
definition.source_content_identity = "fnv1a64:boundary";
|
||||
definition.nodes = {{{"Beam-1", 10, "10"}, {0.0, 0.0, 0.0}, {source, 2U}},
|
||||
{{"Beam-1", 20, "20"}, {1.0, 0.0, 0.0}, {source, 3U}}};
|
||||
definition.node_sets = {
|
||||
{"Pair", std::string{"Beam-1"}, {1U, 0U}, {source, 4U}}};
|
||||
definition.steps = {{"Step-1",
|
||||
std::move(boundaries),
|
||||
{},
|
||||
0.1,
|
||||
1.0,
|
||||
0.01,
|
||||
1.0,
|
||||
{source, 10U}}};
|
||||
return definition;
|
||||
}
|
||||
|
||||
BoundaryFixture MakeFixture(
|
||||
std::vector<fesa::PrescribedDisplacementDefinition> boundaries = {}) {
|
||||
auto domain_result =
|
||||
fesa::Domain::Create(MakeDefinition(std::move(boundaries)));
|
||||
EXPECT_TRUE(domain_result.HasValue());
|
||||
auto domain =
|
||||
std::make_unique<fesa::Domain>(std::move(domain_result.Value()));
|
||||
|
||||
auto model_result = fesa::AnalysisModel::Create(*domain);
|
||||
EXPECT_TRUE(model_result.HasValue());
|
||||
auto model =
|
||||
std::make_unique<fesa::AnalysisModel>(std::move(model_result.Value()));
|
||||
|
||||
auto dof_result = fesa::DofManager::Create(*model);
|
||||
EXPECT_TRUE(dof_result.HasValue());
|
||||
auto dofs = std::make_unique<fesa::DofManager>(std::move(dof_result.Value()));
|
||||
return {std::move(domain), std::move(model), std::move(dofs)};
|
||||
}
|
||||
|
||||
class FakeBoundaryCondition final : public fesa::BoundaryCondition {
|
||||
public:
|
||||
explicit FakeBoundaryCondition(
|
||||
std::vector<fesa::ConstraintDefinition> definitions)
|
||||
: definitions_{std::move(definitions)} {}
|
||||
|
||||
fesa::Result<std::vector<fesa::ConstraintDefinition>> ResolveConstraints(
|
||||
const fesa::BoundaryConditionContext&) const override {
|
||||
return fesa::Result<std::vector<fesa::ConstraintDefinition>>::Success(
|
||||
definitions_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<fesa::ConstraintDefinition> definitions_;
|
||||
};
|
||||
|
||||
void ExpectFailureCode(const fesa::Status& status, const std::string& code) {
|
||||
ASSERT_FALSE(status.IsOk());
|
||||
ASSERT_EQ(status.Diagnostics().size(), 1U);
|
||||
EXPECT_EQ(status.Diagnostics()[0U].code, code);
|
||||
}
|
||||
|
||||
void ExpectCandidate(const fesa::DofManager& dofs,
|
||||
const std::vector<std::size_t>& constrained_dofs,
|
||||
const std::vector<double>& prescribed_values) {
|
||||
EXPECT_EQ(dofs.ConstrainedDofs(), constrained_dofs);
|
||||
ASSERT_EQ(dofs.PrescribedValues().Size(), prescribed_values.size());
|
||||
for (std::size_t index = 0U; index < prescribed_values.size(); ++index) {
|
||||
EXPECT_DOUBLE_EQ(dofs.PrescribedValues()[index], prescribed_values[index]);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// C-BC-001
|
||||
TEST(BoundaryCondition, ResolvesStableNonzeroDefinitionsThroughBase) {
|
||||
static_assert(std::has_virtual_destructor_v<fesa::BoundaryCondition>);
|
||||
auto fixture = MakeFixture();
|
||||
const fesa::SourceTargetIndex target_index =
|
||||
fesa::SourceTargetIndex::FromDomain(*fixture.domain);
|
||||
const fesa::SourceTargetResolver resolver{target_index};
|
||||
const fesa::BoundaryConditionContext context{*fixture.domain, *fixture.dofs,
|
||||
resolver};
|
||||
const fesa::PrescribedDisplacementBoundaryCondition prescribed(
|
||||
{fesa::SourceEntityKind::kNode, "Beam-1", "pair"},
|
||||
fesa::DofComponent::kUz, 2.5, 4U);
|
||||
const fesa::BoundaryCondition& boundary = prescribed;
|
||||
|
||||
const auto definitions = boundary.ResolveConstraints(context);
|
||||
|
||||
ASSERT_TRUE(definitions.HasValue());
|
||||
ASSERT_EQ(definitions.Value().size(), 2U);
|
||||
EXPECT_EQ(definitions.Value()[0U].source_order, 4U);
|
||||
EXPECT_EQ(definitions.Value()[0U].full_dof_index, 8U);
|
||||
EXPECT_DOUBLE_EQ(definitions.Value()[0U].prescribed_value, 2.5);
|
||||
EXPECT_EQ(definitions.Value()[1U].source_order, 4U);
|
||||
EXPECT_EQ(definitions.Value()[1U].full_dof_index, 2U);
|
||||
EXPECT_DOUBLE_EQ(definitions.Value()[1U].prescribed_value, 2.5);
|
||||
}
|
||||
|
||||
TEST(BoundaryCondition, DomainOwnsExpandedPolymorphicDefinitionsStably) {
|
||||
const std::filesystem::path source{"models/boundary-condition.inp"};
|
||||
auto fixture = MakeFixture({{"Pair", 1, 2, 1.25, {source, 12U}}});
|
||||
|
||||
const auto& boundaries = fixture.model->Step().BoundaryConditions();
|
||||
ASSERT_EQ(boundaries.size(), 2U);
|
||||
EXPECT_EQ(fixture.model->ActiveBoundaryConditions(),
|
||||
(std::vector<fesa::EntityIndex>{0U, 1U}));
|
||||
|
||||
const fesa::SourceTargetIndex target_index =
|
||||
fesa::SourceTargetIndex::FromDomain(*fixture.domain);
|
||||
const fesa::SourceTargetResolver resolver{target_index};
|
||||
const fesa::BoundaryConditionContext context{*fixture.domain, *fixture.dofs,
|
||||
resolver};
|
||||
const auto first = boundaries[0U].get().ResolveConstraints(context);
|
||||
const auto second = boundaries[1U].get().ResolveConstraints(context);
|
||||
ASSERT_TRUE(first.HasValue());
|
||||
ASSERT_TRUE(second.HasValue());
|
||||
EXPECT_EQ(first.Value()[0U].source_order, 0U);
|
||||
EXPECT_EQ(first.Value()[0U].full_dof_index, 6U);
|
||||
EXPECT_EQ(second.Value()[0U].source_order, 1U);
|
||||
EXPECT_EQ(second.Value()[0U].full_dof_index, 7U);
|
||||
EXPECT_DOUBLE_EQ(first.Value()[0U].prescribed_value, 1.25);
|
||||
EXPECT_DOUBLE_EQ(second.Value()[0U].prescribed_value, 1.25);
|
||||
}
|
||||
|
||||
TEST(BoundaryCondition,
|
||||
RejectsDuplicateConflictAndNonfiniteBeforeCandidateCommit) {
|
||||
auto fixture = MakeFixture();
|
||||
const fesa::ElementView elements;
|
||||
FakeBoundaryCondition initial({{0U, 1U, 3.0}});
|
||||
ASSERT_TRUE(
|
||||
fixture.dofs->Build(*fixture.model, elements, {std::cref(initial)})
|
||||
.IsOk());
|
||||
ExpectCandidate(*fixture.dofs, {1U}, {3.0});
|
||||
|
||||
FakeBoundaryCondition duplicate({{0U, 2U, 4.0}, {0U, 2U, 4.0}});
|
||||
ExpectFailureCode(
|
||||
fixture.dofs->Build(*fixture.model, elements, {std::cref(duplicate)}),
|
||||
"duplicate-constraint-definition");
|
||||
ExpectCandidate(*fixture.dofs, {1U}, {3.0});
|
||||
|
||||
FakeBoundaryCondition first({{0U, 2U, 4.0}});
|
||||
FakeBoundaryCondition conflicting({{1U, 2U, -4.0}});
|
||||
ExpectFailureCode(
|
||||
fixture.dofs->Build(*fixture.model, elements,
|
||||
{std::cref(first), std::cref(conflicting)}),
|
||||
"conflicting-boundary-condition");
|
||||
ExpectCandidate(*fixture.dofs, {1U}, {3.0});
|
||||
|
||||
FakeBoundaryCondition nonfinite(
|
||||
{{0U, 2U, std::numeric_limits<double>::quiet_NaN()}});
|
||||
ExpectFailureCode(
|
||||
fixture.dofs->Build(*fixture.model, elements, {std::cref(nonfinite)}),
|
||||
"nonfinite-prescribed-value");
|
||||
ExpectCandidate(*fixture.dofs, {1U}, {3.0});
|
||||
}
|
||||
|
||||
TEST(BoundaryCondition, AcceptsIdenticalOverlapAcrossSourceDefinitions) {
|
||||
auto fixture = MakeFixture();
|
||||
FakeBoundaryCondition first({{0U, 4U, -2.0}});
|
||||
FakeBoundaryCondition second({{1U, 4U, -2.0}});
|
||||
|
||||
const fesa::Status status = fixture.dofs->Build(
|
||||
*fixture.model, {}, {std::cref(first), std::cref(second)});
|
||||
|
||||
ASSERT_TRUE(status.IsOk());
|
||||
ExpectCandidate(*fixture.dofs, {4U}, {-2.0});
|
||||
}
|
||||
|
||||
TEST(BoundaryCondition, ConcreteRejectsNonfinitePrescribedValue) {
|
||||
auto fixture = MakeFixture();
|
||||
const fesa::SourceTargetIndex target_index =
|
||||
fesa::SourceTargetIndex::FromDomain(*fixture.domain);
|
||||
const fesa::SourceTargetResolver resolver{target_index};
|
||||
const fesa::BoundaryConditionContext context{*fixture.domain, *fixture.dofs,
|
||||
resolver};
|
||||
const fesa::PrescribedDisplacementBoundaryCondition prescribed(
|
||||
{fesa::SourceEntityKind::kNode, "Beam-1", "10"}, fesa::DofComponent::kUx,
|
||||
std::numeric_limits<double>::infinity(), 0U);
|
||||
|
||||
const auto result = prescribed.ResolveConstraints(context);
|
||||
|
||||
ASSERT_FALSE(result.HasValue());
|
||||
ASSERT_EQ(result.GetStatus().Diagnostics().size(), 1U);
|
||||
EXPECT_EQ(result.GetStatus().Diagnostics()[0U].code,
|
||||
"nonfinite-prescribed-value");
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
#include "fesa/constraints/essential_constraints.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cstddef>
|
||||
@@ -10,12 +8,14 @@
|
||||
#include <vector>
|
||||
|
||||
#include "fesa/analysis/analysis_model.h"
|
||||
#include "fesa/constraints/essential_constraint_policy.h"
|
||||
#include "fesa/fem/dof_manager.h"
|
||||
#include "fesa/model/domain.h"
|
||||
|
||||
namespace {
|
||||
|
||||
fesa::DofManager MakeDofs(std::vector<fesa::BoundaryCondition> boundaries) {
|
||||
fesa::DofManager MakeDofs(
|
||||
std::vector<fesa::PrescribedDisplacementDefinition> boundaries) {
|
||||
const std::filesystem::path source{"models/essential-constraints.inp"};
|
||||
fesa::ModelDefinition definition{};
|
||||
definition.source_path = source;
|
||||
@@ -40,7 +40,7 @@ fesa::DofManager MakeDofs(std::vector<fesa::BoundaryCondition> boundaries) {
|
||||
}
|
||||
|
||||
fesa::DofManager MakeShellSizedDofs(
|
||||
std::vector<fesa::BoundaryCondition> boundaries) {
|
||||
std::vector<fesa::PrescribedDisplacementDefinition> boundaries) {
|
||||
const std::filesystem::path source{"models/shell-essential-constraints.inp"};
|
||||
fesa::ModelDefinition definition{};
|
||||
definition.source_path = source;
|
||||
@@ -124,93 +124,98 @@ TEST(EssentialConstraints, ExtractsHandComputedBlocksInStableOrder) {
|
||||
full_values[2U * 6U + 4U] = 0.0;
|
||||
const auto full = MakeMatrix(6U, 6U, full_values);
|
||||
|
||||
auto result = fesa::EssentialConstraints::Partition(full, dofs);
|
||||
const fesa::EssentialConstraintPolicy policy;
|
||||
auto result = policy.Partition(full, dofs);
|
||||
ASSERT_TRUE(result.HasValue());
|
||||
const auto& blocks = result.Value();
|
||||
|
||||
EXPECT_EQ(blocks.kff.RowOffsets(),
|
||||
EXPECT_EQ(blocks.k_ff.RowOffsets(),
|
||||
(std::vector<std::size_t>{0U, 4U, 8U, 12U, 16U}));
|
||||
EXPECT_EQ(blocks.kff.ColumnIndices(),
|
||||
EXPECT_EQ(blocks.k_ff.ColumnIndices(),
|
||||
(std::vector<std::size_t>{0U, 1U, 2U, 3U, 0U, 1U, 2U, 3U, 0U, 1U,
|
||||
2U, 3U, 0U, 1U, 2U, 3U}));
|
||||
EXPECT_EQ(
|
||||
blocks.kff.Values(),
|
||||
blocks.k_ff.Values(),
|
||||
(std::vector<double>{1.0, 3.0, 4.0, 6.0, 21.0, 23.0, 24.0, 26.0, 31.0,
|
||||
33.0, 34.0, 36.0, 51.0, 53.0, 54.0, 56.0}));
|
||||
EXPECT_EQ(blocks.kfc.RowOffsets(),
|
||||
EXPECT_EQ(blocks.k_fc.RowOffsets(),
|
||||
(std::vector<std::size_t>{0U, 2U, 4U, 6U, 8U}));
|
||||
EXPECT_EQ(blocks.kfc.ColumnIndices(),
|
||||
EXPECT_EQ(blocks.k_fc.ColumnIndices(),
|
||||
(std::vector<std::size_t>{0U, 1U, 0U, 1U, 0U, 1U, 0U, 1U}));
|
||||
EXPECT_EQ(blocks.kfc.Values(),
|
||||
EXPECT_EQ(blocks.k_fc.Values(),
|
||||
(std::vector<double>{2.0, 5.0, 22.0, 0.0, 32.0, 35.0, 52.0, 55.0}));
|
||||
EXPECT_EQ(blocks.kcf.RowOffsets(), (std::vector<std::size_t>{0U, 4U, 8U}));
|
||||
EXPECT_EQ(blocks.kcf.ColumnIndices(),
|
||||
EXPECT_EQ(blocks.k_cf.RowOffsets(), (std::vector<std::size_t>{0U, 4U, 8U}));
|
||||
EXPECT_EQ(blocks.k_cf.ColumnIndices(),
|
||||
(std::vector<std::size_t>{0U, 1U, 2U, 3U, 0U, 1U, 2U, 3U}));
|
||||
EXPECT_EQ(blocks.kcf.Values(), (std::vector<double>{11.0, 13.0, 14.0, 16.0,
|
||||
41.0, 43.0, 44.0, 46.0}));
|
||||
EXPECT_EQ(blocks.kcc.RowOffsets(), (std::vector<std::size_t>{0U, 2U, 4U}));
|
||||
EXPECT_EQ(blocks.kcc.ColumnIndices(),
|
||||
EXPECT_EQ(
|
||||
blocks.k_cf.Values(),
|
||||
(std::vector<double>{11.0, 13.0, 14.0, 16.0, 41.0, 43.0, 44.0, 46.0}));
|
||||
EXPECT_EQ(blocks.k_cc.RowOffsets(), (std::vector<std::size_t>{0U, 2U, 4U}));
|
||||
EXPECT_EQ(blocks.k_cc.ColumnIndices(),
|
||||
(std::vector<std::size_t>{0U, 1U, 0U, 1U}));
|
||||
EXPECT_EQ(blocks.kcc.Values(), (std::vector<double>{12.0, 15.0, 42.0, 45.0}));
|
||||
EXPECT_EQ(blocks.k_cc.Values(),
|
||||
(std::vector<double>{12.0, 15.0, 42.0, 45.0}));
|
||||
|
||||
EXPECT_EQ(blocks.kfc.Values()[3U], 0.0);
|
||||
EXPECT_TRUE(blocks.kff.Validate().IsOk());
|
||||
EXPECT_TRUE(blocks.kfc.Validate().IsOk());
|
||||
EXPECT_TRUE(blocks.kcf.Validate().IsOk());
|
||||
EXPECT_TRUE(blocks.kcc.Validate().IsOk());
|
||||
EXPECT_EQ(blocks.k_fc.Values()[3U], 0.0);
|
||||
EXPECT_TRUE(blocks.k_ff.Validate().IsOk());
|
||||
EXPECT_TRUE(blocks.k_fc.Validate().IsOk());
|
||||
EXPECT_TRUE(blocks.k_cf.Validate().IsOk());
|
||||
EXPECT_TRUE(blocks.k_cc.Validate().IsOk());
|
||||
}
|
||||
|
||||
TEST(EssentialConstraints, HandlesNoAllAndMixedConstraints) {
|
||||
const auto full = MakeMatrix(6U, 6U, SequentialDense(6U));
|
||||
const fesa::EssentialConstraintPolicy policy;
|
||||
|
||||
const auto no_constraints = MakeDofs({});
|
||||
auto none = fesa::EssentialConstraints::Partition(full, no_constraints);
|
||||
auto none = policy.Partition(full, no_constraints);
|
||||
ASSERT_TRUE(none.HasValue());
|
||||
ExpectShape(none.Value().kff, 6U, 6U);
|
||||
ExpectShape(none.Value().kfc, 6U, 0U);
|
||||
ExpectShape(none.Value().kcf, 0U, 6U);
|
||||
ExpectShape(none.Value().kcc, 0U, 0U);
|
||||
EXPECT_EQ(none.Value().kff.Values(), full.Values());
|
||||
ExpectShape(none.Value().k_ff, 6U, 6U);
|
||||
ExpectShape(none.Value().k_fc, 6U, 0U);
|
||||
ExpectShape(none.Value().k_cf, 0U, 6U);
|
||||
ExpectShape(none.Value().k_cc, 0U, 0U);
|
||||
EXPECT_EQ(none.Value().k_ff.Values(), full.Values());
|
||||
|
||||
const auto all_constraints = MakeDofs({{"1", 1, 6, 1.0, {{}, 12U}}});
|
||||
auto all = fesa::EssentialConstraints::Partition(full, all_constraints);
|
||||
auto all = policy.Partition(full, all_constraints);
|
||||
ASSERT_TRUE(all.HasValue());
|
||||
ExpectShape(all.Value().kff, 0U, 0U);
|
||||
ExpectShape(all.Value().kfc, 0U, 6U);
|
||||
ExpectShape(all.Value().kcf, 6U, 0U);
|
||||
ExpectShape(all.Value().kcc, 6U, 6U);
|
||||
EXPECT_EQ(all.Value().kcc.Values(), full.Values());
|
||||
ExpectShape(all.Value().k_ff, 0U, 0U);
|
||||
ExpectShape(all.Value().k_fc, 0U, 6U);
|
||||
ExpectShape(all.Value().k_cf, 6U, 0U);
|
||||
ExpectShape(all.Value().k_cc, 6U, 6U);
|
||||
EXPECT_EQ(all.Value().k_cc.Values(), full.Values());
|
||||
|
||||
const auto mixed_constraints = MakeDofs({{"1", 3, 4, 0.0, {{}, 12U}}});
|
||||
auto mixed = fesa::EssentialConstraints::Partition(full, mixed_constraints);
|
||||
auto mixed = policy.Partition(full, mixed_constraints);
|
||||
ASSERT_TRUE(mixed.HasValue());
|
||||
ExpectShape(mixed.Value().kff, 4U, 4U);
|
||||
ExpectShape(mixed.Value().kfc, 4U, 2U);
|
||||
ExpectShape(mixed.Value().kcf, 2U, 4U);
|
||||
ExpectShape(mixed.Value().kcc, 2U, 2U);
|
||||
ExpectShape(mixed.Value().k_ff, 4U, 4U);
|
||||
ExpectShape(mixed.Value().k_fc, 4U, 2U);
|
||||
ExpectShape(mixed.Value().k_cf, 2U, 4U);
|
||||
ExpectShape(mixed.Value().k_cc, 2U, 2U);
|
||||
}
|
||||
|
||||
// MITC4-DOF-003
|
||||
TEST(EssentialConstraints,
|
||||
PreservesShellSizedNoMixedAndAllConstraintRoundTrips) {
|
||||
const auto full = MakeMatrix(24U, 24U, SequentialDense(24U));
|
||||
const fesa::EssentialConstraintPolicy policy;
|
||||
|
||||
const auto no_constraints = MakeShellSizedDofs({});
|
||||
auto none = fesa::EssentialConstraints::Partition(full, no_constraints);
|
||||
auto none = policy.Partition(full, no_constraints);
|
||||
ASSERT_TRUE(none.HasValue());
|
||||
ExpectShape(none.Value().kff, 24U, 24U);
|
||||
ExpectShape(none.Value().kfc, 24U, 0U);
|
||||
ExpectShape(none.Value().kcf, 0U, 24U);
|
||||
ExpectShape(none.Value().kcc, 0U, 0U);
|
||||
ExpectShape(none.Value().k_ff, 24U, 24U);
|
||||
ExpectShape(none.Value().k_fc, 24U, 0U);
|
||||
ExpectShape(none.Value().k_cf, 0U, 24U);
|
||||
ExpectShape(none.Value().k_cc, 0U, 0U);
|
||||
|
||||
const auto mixed_constraints = MakeShellSizedDofs(
|
||||
{{"1", 1, 6, 0.0, {{}, 12U}}, {"4", 2, 2, 2.5, {{}, 13U}}});
|
||||
auto mixed = fesa::EssentialConstraints::Partition(full, mixed_constraints);
|
||||
auto mixed = policy.Partition(full, mixed_constraints);
|
||||
ASSERT_TRUE(mixed.HasValue());
|
||||
ExpectShape(mixed.Value().kff, 17U, 17U);
|
||||
ExpectShape(mixed.Value().kfc, 17U, 7U);
|
||||
ExpectShape(mixed.Value().kcf, 7U, 17U);
|
||||
ExpectShape(mixed.Value().kcc, 7U, 7U);
|
||||
ExpectShape(mixed.Value().k_ff, 17U, 17U);
|
||||
ExpectShape(mixed.Value().k_fc, 17U, 7U);
|
||||
ExpectShape(mixed.Value().k_cf, 7U, 17U);
|
||||
ExpectShape(mixed.Value().k_cc, 7U, 7U);
|
||||
|
||||
fesa::Vector mixed_full{24U};
|
||||
for (std::size_t index = 0U; index < mixed_full.Size(); ++index) {
|
||||
@@ -221,9 +226,8 @@ TEST(EssentialConstraints,
|
||||
mixed_full[mixed_constraints.ConstrainedDofs()[index]] =
|
||||
mixed_constraints.PrescribedValues()[index];
|
||||
}
|
||||
const auto mixed_free =
|
||||
fesa::EssentialConstraints::GatherFree(mixed_full, mixed_constraints);
|
||||
const auto mixed_reconstructed = fesa::EssentialConstraints::ReconstructFull(
|
||||
const auto mixed_free = policy.GatherFree(mixed_full, mixed_constraints);
|
||||
const auto mixed_reconstructed = policy.ReconstructFull(
|
||||
mixed_free, mixed_constraints.PrescribedValues(), mixed_constraints);
|
||||
ASSERT_EQ(mixed_reconstructed.Size(), mixed_full.Size());
|
||||
for (std::size_t index = 0U; index < mixed_full.Size(); ++index) {
|
||||
@@ -235,14 +239,14 @@ TEST(EssentialConstraints,
|
||||
{"2", 1, 6, 2.0, {{}, 15U}},
|
||||
{"3", 1, 6, 3.0, {{}, 16U}},
|
||||
{"4", 1, 6, 4.0, {{}, 17U}}});
|
||||
auto all = fesa::EssentialConstraints::Partition(full, all_constraints);
|
||||
auto all = policy.Partition(full, all_constraints);
|
||||
ASSERT_TRUE(all.HasValue());
|
||||
ExpectShape(all.Value().kff, 0U, 0U);
|
||||
ExpectShape(all.Value().kfc, 0U, 24U);
|
||||
ExpectShape(all.Value().kcf, 24U, 0U);
|
||||
ExpectShape(all.Value().kcc, 24U, 24U);
|
||||
ExpectShape(all.Value().k_ff, 0U, 0U);
|
||||
ExpectShape(all.Value().k_fc, 0U, 24U);
|
||||
ExpectShape(all.Value().k_cf, 24U, 0U);
|
||||
ExpectShape(all.Value().k_cc, 24U, 24U);
|
||||
|
||||
const auto all_reconstructed = fesa::EssentialConstraints::ReconstructFull(
|
||||
const auto all_reconstructed = policy.ReconstructFull(
|
||||
fesa::Vector{0U}, all_constraints.PrescribedValues(), all_constraints);
|
||||
ASSERT_EQ(all_reconstructed.Size(), 24U);
|
||||
for (std::size_t node = 0U; node < 4U; ++node) {
|
||||
@@ -253,6 +257,7 @@ TEST(EssentialConstraints,
|
||||
}
|
||||
|
||||
TEST(EssentialConstraints, ReconstructsNonzeroPrescribedValues) {
|
||||
const fesa::EssentialConstraintPolicy policy;
|
||||
const auto dofs =
|
||||
MakeDofs({{"1", 2, 2, 2.5, {{}, 12U}}, {"1", 5, 5, -3.25, {{}, 13U}}});
|
||||
fesa::Vector full{6U};
|
||||
@@ -263,9 +268,8 @@ TEST(EssentialConstraints, ReconstructsNonzeroPrescribedValues) {
|
||||
full[4U] = -3.25;
|
||||
full[5U] = 40.0;
|
||||
|
||||
const auto free = fesa::EssentialConstraints::GatherFree(full, dofs);
|
||||
const auto constrained =
|
||||
fesa::EssentialConstraints::GatherConstrained(full, dofs);
|
||||
const auto free = policy.GatherFree(full, dofs);
|
||||
const auto constrained = policy.GatherConstrained(full, dofs);
|
||||
EXPECT_EQ(free.Size(), 4U);
|
||||
EXPECT_DOUBLE_EQ(free[0U], 10.0);
|
||||
EXPECT_DOUBLE_EQ(free[1U], 20.0);
|
||||
@@ -277,8 +281,8 @@ TEST(EssentialConstraints, ReconstructsNonzeroPrescribedValues) {
|
||||
EXPECT_EQ(constrained[0U], dofs.PrescribedValues()[0U]);
|
||||
EXPECT_EQ(constrained[1U], dofs.PrescribedValues()[1U]);
|
||||
|
||||
const auto reconstructed = fesa::EssentialConstraints::ReconstructFull(
|
||||
free, dofs.PrescribedValues(), dofs);
|
||||
const auto reconstructed =
|
||||
policy.ReconstructFull(free, dofs.PrescribedValues(), dofs);
|
||||
ASSERT_EQ(reconstructed.Size(), full.Size());
|
||||
for (std::size_t index = 0U; index < full.Size(); ++index) {
|
||||
EXPECT_DOUBLE_EQ(reconstructed[index], full[index]);
|
||||
@@ -286,10 +290,10 @@ TEST(EssentialConstraints, ReconstructsNonzeroPrescribedValues) {
|
||||
}
|
||||
|
||||
TEST(EssentialConstraints, RejectsDimensionOrOrderMismatch) {
|
||||
const fesa::EssentialConstraintPolicy policy;
|
||||
const auto dofs = MakeDofs({{"1", 2, 2, 1.0, {{}, 12U}}});
|
||||
const auto wrong_square = MakeMatrix(5U, 5U, SequentialDense(5U));
|
||||
auto wrong_dimension =
|
||||
fesa::EssentialConstraints::Partition(wrong_square, dofs);
|
||||
auto wrong_dimension = policy.Partition(wrong_square, dofs);
|
||||
ASSERT_FALSE(wrong_dimension.HasValue());
|
||||
EXPECT_EQ(wrong_dimension.GetStatus().Category(),
|
||||
fesa::FailureCategory::kModel);
|
||||
@@ -298,18 +302,17 @@ TEST(EssentialConstraints, RejectsDimensionOrOrderMismatch) {
|
||||
"invalid-constraint-dimensions");
|
||||
|
||||
const auto rectangular = MakeMatrix(6U, 5U, std::vector<double>(30U, 0.0));
|
||||
auto wrong_order = fesa::EssentialConstraints::Partition(rectangular, dofs);
|
||||
auto wrong_order = policy.Partition(rectangular, dofs);
|
||||
ASSERT_FALSE(wrong_order.HasValue());
|
||||
EXPECT_EQ(wrong_order.GetStatus().Diagnostics()[0U].code,
|
||||
"invalid-constraint-dimensions");
|
||||
|
||||
EXPECT_THROW(static_cast<void>(fesa::EssentialConstraints::GatherFree(
|
||||
fesa::Vector{5U}, dofs)),
|
||||
EXPECT_THROW(static_cast<void>(policy.GatherFree(fesa::Vector{5U}, dofs)),
|
||||
std::invalid_argument);
|
||||
EXPECT_THROW(static_cast<void>(fesa::EssentialConstraints::GatherConstrained(
|
||||
fesa::Vector{7U}, dofs)),
|
||||
std::invalid_argument);
|
||||
EXPECT_THROW(static_cast<void>(fesa::EssentialConstraints::ReconstructFull(
|
||||
EXPECT_THROW(
|
||||
static_cast<void>(policy.GatherConstrained(fesa::Vector{7U}, dofs)),
|
||||
std::invalid_argument);
|
||||
EXPECT_THROW(static_cast<void>(policy.ReconstructFull(
|
||||
fesa::Vector{4U}, fesa::Vector{2U}, dofs)),
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "fesa/elements/element.h"
|
||||
#include "fesa/elements/element_definition.h"
|
||||
#include "fesa/io/abaqus/input_reader.h"
|
||||
#include "fesa/math/vector3.h"
|
||||
@@ -292,12 +293,15 @@ TEST(InpDomainMapping, MapsEverySupportedKeywordAndLegacyDeck) {
|
||||
EXPECT_DOUBLE_EQ(step.TimePeriod(), 1.5);
|
||||
EXPECT_DOUBLE_EQ(step.MinimumIncrement(), 0.01);
|
||||
EXPECT_DOUBLE_EQ(step.MaximumIncrement(), 1.5);
|
||||
ASSERT_EQ(step.Boundaries().size(), 2U);
|
||||
EXPECT_EQ(step.Boundaries()[0].target, "rootassembly");
|
||||
EXPECT_EQ(step.Boundaries()[0].first_dof, 1);
|
||||
EXPECT_DOUBLE_EQ(step.Boundaries()[0].value, 0.125);
|
||||
EXPECT_EQ(step.Boundaries()[1].last_dof, 2);
|
||||
EXPECT_DOUBLE_EQ(step.Boundaries()[1].value, 0.0);
|
||||
ASSERT_EQ(step.PrescribedDisplacements().Size(), 2U);
|
||||
EXPECT_EQ(step.PrescribedDisplacements()[0U].Target().target_name_or_label,
|
||||
"rootassembly");
|
||||
EXPECT_EQ(step.PrescribedDisplacements()[0U].Component(),
|
||||
fesa::DofComponent::kUx);
|
||||
EXPECT_DOUBLE_EQ(step.PrescribedDisplacements()[0U].PrescribedValue(), 0.125);
|
||||
EXPECT_EQ(step.PrescribedDisplacements()[1U].Component(),
|
||||
fesa::DofComponent::kUy);
|
||||
EXPECT_DOUBLE_EQ(step.PrescribedDisplacements()[1U].PrescribedValue(), 0.0);
|
||||
ASSERT_EQ(step.ConcentratedLoads().Size(), 1U);
|
||||
EXPECT_EQ(step.ConcentratedLoads()[0U].Target().target_name_or_label,
|
||||
"RootAssembly");
|
||||
@@ -411,7 +415,11 @@ OnlySecond, 2, 5.
|
||||
(std::vector<fesa::EntityIndex>{1U}));
|
||||
|
||||
ASSERT_EQ(domain.Steps().Size(), 1U);
|
||||
EXPECT_EQ(domain.Steps()[0].Boundaries()[0].target, "OnlySecond");
|
||||
EXPECT_EQ(domain.Steps()[0]
|
||||
.PrescribedDisplacements()[0U]
|
||||
.Target()
|
||||
.target_name_or_label,
|
||||
"OnlySecond");
|
||||
EXPECT_EQ(
|
||||
domain.Steps()[0].ConcentratedLoads()[0U].Target().target_name_or_label,
|
||||
"OnlySecond");
|
||||
@@ -421,7 +429,12 @@ OnlySecond, 2, 5.
|
||||
ReplaceOnce(ReplaceOnce(MinimalDeck(), "Root, 1, 6", "1, 1, 6"),
|
||||
"Tip, 2, -1.", "2, 2, -1."));
|
||||
ASSERT_TRUE(direct.HasValue());
|
||||
EXPECT_EQ(direct.Value().Steps()[0].Boundaries()[0].target, "1");
|
||||
EXPECT_EQ(direct.Value()
|
||||
.Steps()[0]
|
||||
.PrescribedDisplacements()[0U]
|
||||
.Target()
|
||||
.target_name_or_label,
|
||||
"1");
|
||||
EXPECT_EQ(direct.Value()
|
||||
.Steps()[0]
|
||||
.ConcentratedLoads()[0U]
|
||||
@@ -551,8 +564,8 @@ TEST(InpDomainMapping, NoOpAllowlistWarnsWithoutSemanticEffect) {
|
||||
EXPECT_EQ(with_no_ops.Value().ElementSets().size(),
|
||||
plain.Value().ElementSets().size());
|
||||
EXPECT_EQ(with_no_ops.Value().Steps().Size(), plain.Value().Steps().Size());
|
||||
EXPECT_EQ(with_no_ops.Value().Steps()[0].Boundaries().size(),
|
||||
plain.Value().Steps()[0].Boundaries().size());
|
||||
EXPECT_EQ(with_no_ops.Value().Steps()[0].BoundaryConditions().size(),
|
||||
plain.Value().Steps()[0].BoundaryConditions().size());
|
||||
EXPECT_EQ(with_no_ops.Value().Steps()[0].Loads().size(),
|
||||
plain.Value().Steps()[0].Loads().size());
|
||||
}
|
||||
@@ -613,8 +626,9 @@ TEST(InpDomainMapping, MapsS4AndS4rThroughOneMitc4Identity) {
|
||||
(std::array<double, 3>{0.0, 1.0, 0.0}));
|
||||
|
||||
ASSERT_EQ(domain.Steps().Size(), 1U);
|
||||
ASSERT_EQ(domain.Steps()[0].Boundaries().size(), 1U);
|
||||
EXPECT_EQ(domain.Steps()[0].Boundaries()[0].last_dof, 6);
|
||||
ASSERT_EQ(domain.Steps()[0].PrescribedDisplacements().Size(), 6U);
|
||||
EXPECT_EQ(domain.Steps()[0].PrescribedDisplacements()[5U].Component(),
|
||||
fesa::DofComponent::kUrz);
|
||||
ASSERT_EQ(domain.Steps()[0].ConcentratedLoads().Size(), 1U);
|
||||
EXPECT_DOUBLE_EQ(
|
||||
domain.Steps()[0].ConcentratedLoads()[0U].GlobalComponents()[5U], 1.0);
|
||||
|
||||
@@ -197,7 +197,7 @@ TEST(DomainModel, ImmutableOwnershipPreservesStableOrder) {
|
||||
EXPECT_DOUBLE_EQ(domain.Steps()[0].TimePeriod(), 1.0);
|
||||
EXPECT_DOUBLE_EQ(domain.Steps()[0].MinimumIncrement(), 1.0e-5);
|
||||
EXPECT_DOUBLE_EQ(domain.Steps()[0].MaximumIncrement(), 1.0);
|
||||
ASSERT_EQ(domain.Steps()[0].Boundaries().size(), 1U);
|
||||
ASSERT_EQ(domain.Steps()[0].BoundaryConditions().size(), 6U);
|
||||
ASSERT_EQ(domain.Steps()[0].Loads().size(), 1U);
|
||||
ASSERT_EQ(domain.Steps()[0].ConcentratedLoads().Size(), 1U);
|
||||
EXPECT_DOUBLE_EQ(
|
||||
|
||||
@@ -66,7 +66,7 @@ TEST(DomainModel, SourceAndInternalIdentityRemainDistinct) {
|
||||
fesa::EntityIndex{2},
|
||||
fesa::EntityIndex{4},
|
||||
{"models/beam.inp", 20U}};
|
||||
const fesa::BoundaryCondition boundary{
|
||||
const fesa::PrescribedDisplacementDefinition boundary{
|
||||
"Root", 1, 6, 0.0, {"models/beam.inp", 60U}};
|
||||
const fesa::NodalLoad load{"Tip", 3, -1000.0, {"models/beam.inp", 70U}};
|
||||
const fesa::StaticStepDefinition step{
|
||||
|
||||
@@ -332,12 +332,14 @@ fesa::ModelDefinition MakeShellDefinition(
|
||||
}
|
||||
definition.steps = {
|
||||
{"Step-1",
|
||||
constrain_all ? std::vector<fesa::BoundaryCondition>{{"All",
|
||||
1,
|
||||
6,
|
||||
0.0,
|
||||
{source, 60U}}}
|
||||
: std::vector<fesa::BoundaryCondition>{},
|
||||
constrain_all
|
||||
? std::vector<fesa::PrescribedDisplacementDefinition>{{"All",
|
||||
1,
|
||||
6,
|
||||
0.0,
|
||||
{source,
|
||||
60U}}}
|
||||
: std::vector<fesa::PrescribedDisplacementDefinition>{},
|
||||
std::move(loads),
|
||||
0.1,
|
||||
1.0,
|
||||
|
||||
Reference in New Issue
Block a user