From 90a2f64ba427033817ee693de9891d2f94c7e7e0 Mon Sep 17 00:00:00 2001 From: "KOKO\\Mimi" Date: Sun, 9 Aug 2026 17:32:06 +0900 Subject: [PATCH] feat(linear-static-3d-euler-beam): step 15 - analysis-state --- ...tic-3d-euler-beam-implementation-report.md | 54 ++++++ include/fesa/analysis/analysis_state.hpp | 52 ++++++ include/fesa/results/result_records.hpp | 41 +++++ src/fesa/CMakeLists.txt | 1 + src/fesa/analysis/analysis_state.cpp | 89 +++++++++ tests/CMakeLists.txt | 2 + tests/unit/analysis/analysis_state_test.cpp | 173 ++++++++++++++++++ tests/unit/results/result_records_test.cpp | 102 +++++++++++ 8 files changed, 514 insertions(+) create mode 100644 include/fesa/analysis/analysis_state.hpp create mode 100644 include/fesa/results/result_records.hpp create mode 100644 src/fesa/analysis/analysis_state.cpp create mode 100644 tests/unit/analysis/analysis_state_test.cpp create mode 100644 tests/unit/results/result_records_test.cpp diff --git a/docs/implementation-plans/linear-static-3d-euler-beam-implementation-report.md b/docs/implementation-plans/linear-static-3d-euler-beam-implementation-report.md index 33b7864..be0b0ec 100644 --- a/docs/implementation-plans/linear-static-3d-euler-beam-implementation-report.md +++ b/docs/implementation-plans/linear-static-3d-euler-beam-implementation-report.md @@ -459,3 +459,57 @@ `DofManager` ledger API provide deterministic full/free/constrained maps, prescribed `dc`, active-element scatter, and full structural CSR pattern to Step 15 and later assembly/constraint tasks. + +## Step 15 — analysis-state + +- task_id: `TASK-15` +- status: `completed` +- changed_files: `include/fesa/results/result_records.hpp`, + `include/fesa/analysis/analysis_state.hpp`, + `src/fesa/analysis/analysis_state.cpp`, + `tests/unit/results/result_records_test.cpp`, + `tests/unit/analysis/analysis_state_test.cpp`, `src/fesa/CMakeLists.txt`, + `tests/CMakeLists.txt`, + `docs/implementation-plans/linear-static-3d-euler-beam-implementation-report.md`, + `phases/linear-static-3d-euler-beam/index.json`, + `.superpowers/sdd/linear-static-3d-euler-beam/task-15-report.md` +- requirement_ids: `FESA-REQ-LS3DEB-003`, `FESA-REQ-LS3DEB-023`, + `FESA-REQ-LS3DEB-031`, `FESA-REQ-LS3DEB-034` +- test_ids: `T15-STATE-001`, `T15-STATE-002`, `T15-STATE-003` + +| stage | exact command | exit_code | expected_or_observed_result | evidence_tail | +| --- | --- | ---: | --- | --- | +| RED-build | `cmake --build .harness/build --config Debug --target fesa_tests` | 1 | Exactly three planned tests were registered before production and the result-record/state API was absent | MSVC C1083 reported missing `fesa/analysis/analysis_state.hpp` from both new test translation units after successful CMake regeneration | +| RED-test | `ctest --test-dir .harness/build -C Debug -R AnalysisState --output-on-failure` | 0 | No focused test was runnable because the unit test executable could not rebuild | CTest reported `No tests were found` after the implementation-owned compile RED | +| GREEN-build | `cmake --build .harness/build --config Debug --target fesa_tests` | 0 | Minimal records/state implementation and all three tests compile and link | `analysis_state.cpp`, both test files, `fesa_solver.lib`, and `fesa_unit_tests.exe` built without a FESA warning under `/W4 /WX` | +| GREEN-test | `ctest --test-dir .harness/build -C Debug -R AnalysisState --output-on-failure` | 0 | Allocation, identity/order, and copy/move ownership tests pass | 3/3 exact `AnalysisState` tests passed | +| VERIFY-configure | `cmake -S . -B .harness/build -A x64 -DFESA_GTEST_SOURCE_DIR=C:/git/googletest "-DMKL_DIR=C:/Program Files (x86)/Intel/oneAPI/mkl/2026.1/lib/cmake/mkl" "-DTBB_DIR=C:/Program Files (x86)/Intel/oneAPI/tbb/2023.1/lib/cmake/tbb" "-DHDF5_DIR=C:/Program Files/HDF_Group/HDF5/2.1.1/cmake"` | 0 | Approved explicit-dependency MSVC x64 build tree generates | Windows SDK and oneMKL 2026.1 resolved; configure and generate completed | +| VERIFY-build | `cmake --build .harness/build --config Debug` | 0 | Full Debug build passes without a new FESA warning | `fesa_solver.lib` and `fesa_unit_tests.exe` built under `/W4 /WX` | +| VERIFY-targeted | `ctest --test-dir .harness/build -C Debug -R AnalysisState --output-on-failure` | 0 | Focused Step 15 suite remains green | 3/3 exact `AnalysisState` tests passed | +| VERIFY-discovery | `ctest --test-dir .harness/build -C Debug --show-only=json-v1` | 0 | CTest discovers the accumulated suite and all three exact AnalysisState names | 29 tests discovered with feature/unit labels | +| VERIFY-full | `ctest --test-dir .harness/build -C Debug --output-on-failure` | 0 | Full accumulated C++ suite has zero failures | 29/29 tests passed | +| VERIFY-contract-scans | Backend public-header, forbidden V0 state/API, Domain/AnalysisModel ownership, result-record upward dependency, exact-test-count, and CMake-registration scans using fail-on-match `rg` wrappers | 0 | State and row records implement only the exact backend-free V0 ledger | backend leaks 0; forbidden state/API 0; Domain/AnalysisModel ownership 0; result upward dependencies 0; tests 3; registrations 1/1/1 | +| VERIFY-diff | `git diff --check` plus trailing-whitespace scan over the five new files | 0 | Tracked and untracked Step 15 files have no whitespace errors | Diff check exit 0; new-file trailing whitespace matches 0 | +| VERIFY-reference | `git diff --exit-code -- reference/`; `git status --short -- reference/` | 0 | Approved legacy reference artifacts remain read-only and unchanged | Reference diff exit 0 and reference status empty | + +- contract_checks: displacement, external force, internal force, residual, and + reaction are five independently owned `Vector` values sized to + `DofManager::fullDofCount()` and zero-initialized. Reaction remains in full + DOF index space even when only a subset is constrained, leaving free + components available for residual sanity checks. Mutable and const accessors + return the same owned vector storage. +- contract_checks: `StepFrameIdentity`, `EndpointResultRow`, `GaussResultRow`, + and `StressS11Row` match the public ledger field order. Owned endpoint, + Gauss, and stress vectors preserve append order and expose stable mutable and + const collections. Default copy/move operations follow the ledger: copies + deep-own vector/row storage, and moved-to state retains identity and values + without aliasing another live state. +- contract_checks: no velocity, acceleration, temperature, iteration history, + nonlinear element state, Domain copy/reference, equation numbering, HDF5, + MKL, or oneTBB API/storage was added. +- generated_evidence: `.harness/build/src/fesa/Debug/fesa_solver.lib`, + `.harness/build/tests/Debug/fesa_unit_tests.exe` +- reference_diff: unchanged; `git diff --exit-code -- reference/` exit 0 +- handoff: the exact backend-free result records and `AnalysisState` ledger API + provide full-space mutable solution vectors, step/frame identity, and stable + recovery-row collections to Step 16 and later result recovery/output tasks. diff --git a/include/fesa/analysis/analysis_state.hpp b/include/fesa/analysis/analysis_state.hpp new file mode 100644 index 0000000..3fc9393 --- /dev/null +++ b/include/fesa/analysis/analysis_state.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include "fesa/fem/dof_manager.hpp" +#include "fesa/math/vector.hpp" +#include "fesa/results/result_records.hpp" + +#include +#include + +namespace fesa { + +// Owns only the mutable quantities required by the V0 linear-static frame. +class AnalysisState { +public: + static AnalysisState create( + const DofManager& dofs, StepFrameIdentity identity); + + Vector& displacement() noexcept; + const Vector& displacement() const noexcept; + Vector& externalForce() noexcept; + const Vector& externalForce() const noexcept; + Vector& internalForce() noexcept; + const Vector& internalForce() const noexcept; + Vector& residual() noexcept; + const Vector& residual() const noexcept; + Vector& reaction() noexcept; + const Vector& reaction() const noexcept; + const StepFrameIdentity& identity() const noexcept; + std::vector& endpointResults() noexcept; + const std::vector& endpointResults() const noexcept; + std::vector& gaussResults() noexcept; + const std::vector& gaussResults() const noexcept; + std::vector& stressResults() noexcept; + const std::vector& stressResults() const noexcept; + +private: + AnalysisState(std::size_t fullDofCount, StepFrameIdentity identity); + + StepFrameIdentity identity_; + Vector displacement_; + Vector externalForce_; + Vector internalForce_; + Vector residual_; + // Reactions retain full-index space so free residual components remain visible. + Vector reaction_; + // Recovery appends rows in stable element/location order. + std::vector endpointResults_; + std::vector gaussResults_; + std::vector stressResults_; +}; + +} // namespace fesa diff --git a/include/fesa/results/result_records.hpp b/include/fesa/results/result_records.hpp new file mode 100644 index 0000000..8f6e872 --- /dev/null +++ b/include/fesa/results/result_records.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include "fesa/model/model_types.hpp" + +#include +#include +#include + +namespace fesa { + +struct StepFrameIdentity { + std::string stepName; + std::size_t frameIndex; +}; + +struct EndpointResultRow { + EntityIndex element; + int endpoint; + SourceEntityId node; + std::array endAction; + std::array sectionResultant; +}; + +struct GaussResultRow { + EntityIndex element; + int gaussPoint; + std::array generalizedStrain; + std::array generalizedResultant; +}; + +struct StressS11Row { + EntityIndex element; + int gaussPoint; + std::size_t sectionPoint; + double x1; + double x2; + double s11; + std::string source; +}; + +} // namespace fesa diff --git a/src/fesa/CMakeLists.txt b/src/fesa/CMakeLists.txt index da8d684..be98364 100644 --- a/src/fesa/CMakeLists.txt +++ b/src/fesa/CMakeLists.txt @@ -2,6 +2,7 @@ add_library( fesa_solver STATIC analysis/analysis_model.cpp + analysis/analysis_state.cpp build_info.cpp core/diagnostic.cpp core/status.cpp diff --git a/src/fesa/analysis/analysis_state.cpp b/src/fesa/analysis/analysis_state.cpp new file mode 100644 index 0000000..6df7d62 --- /dev/null +++ b/src/fesa/analysis/analysis_state.cpp @@ -0,0 +1,89 @@ +#include "fesa/analysis/analysis_state.hpp" + +#include + +namespace fesa { + +AnalysisState AnalysisState::create( + const DofManager& dofs, StepFrameIdentity identity) { + return AnalysisState{dofs.fullDofCount(), std::move(identity)}; +} + +Vector& AnalysisState::displacement() noexcept { + return displacement_; +} + +const Vector& AnalysisState::displacement() const noexcept { + return displacement_; +} + +Vector& AnalysisState::externalForce() noexcept { + return externalForce_; +} + +const Vector& AnalysisState::externalForce() const noexcept { + return externalForce_; +} + +Vector& AnalysisState::internalForce() noexcept { + return internalForce_; +} + +const Vector& AnalysisState::internalForce() const noexcept { + return internalForce_; +} + +Vector& AnalysisState::residual() noexcept { + return residual_; +} + +const Vector& AnalysisState::residual() const noexcept { + return residual_; +} + +Vector& AnalysisState::reaction() noexcept { + return reaction_; +} + +const Vector& AnalysisState::reaction() const noexcept { + return reaction_; +} + +const StepFrameIdentity& AnalysisState::identity() const noexcept { + return identity_; +} + +std::vector& AnalysisState::endpointResults() noexcept { + return endpointResults_; +} + +const std::vector& AnalysisState::endpointResults() const noexcept { + return endpointResults_; +} + +std::vector& AnalysisState::gaussResults() noexcept { + return gaussResults_; +} + +const std::vector& AnalysisState::gaussResults() const noexcept { + return gaussResults_; +} + +std::vector& AnalysisState::stressResults() noexcept { + return stressResults_; +} + +const std::vector& AnalysisState::stressResults() const noexcept { + return stressResults_; +} + +AnalysisState::AnalysisState( + std::size_t fullDofCount, StepFrameIdentity identity) + : identity_{std::move(identity)}, + displacement_{fullDofCount}, + externalForce_{fullDofCount}, + internalForce_{fullDofCount}, + residual_{fullDofCount}, + reaction_{fullDofCount} {} + +} // namespace fesa diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 20360da..c5a6c21 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,6 +4,7 @@ add_executable( fesa_unit_tests unit/build_info_test.cpp unit/analysis/analysis_model_test.cpp + unit/analysis/analysis_state_test.cpp unit/core/diagnostic_test.cpp unit/core/source_identity_test.cpp unit/core/status_test.cpp @@ -15,6 +16,7 @@ add_executable( unit/io/abaqus/input_syntax_test.cpp unit/model/domain_test.cpp unit/model/model_types_test.cpp + unit/results/result_records_test.cpp ) target_link_libraries( diff --git a/tests/unit/analysis/analysis_state_test.cpp b/tests/unit/analysis/analysis_state_test.cpp new file mode 100644 index 0000000..2bfcc7b --- /dev/null +++ b/tests/unit/analysis/analysis_state_test.cpp @@ -0,0 +1,173 @@ +#include "fesa/analysis/analysis_state.hpp" + +#include + +#include +#include +#include +#include + +namespace { + +template +struct HasVelocity : std::false_type {}; + +template +struct HasVelocity().velocity())>> + : std::true_type {}; + +template +struct HasAcceleration : std::false_type {}; + +template +struct HasAcceleration< + T, std::void_t().acceleration())>> + : std::true_type {}; + +template +struct HasTemperature : std::false_type {}; + +template +struct HasTemperature().temperature())>> + : std::true_type {}; + +template +struct HasIterationHistory : std::false_type {}; + +template +struct HasIterationHistory< + T, std::void_t().iterationHistory())>> + : std::true_type {}; + +template +struct HasNonlinearState : std::false_type {}; + +template +struct HasNonlinearState< + T, std::void_t().nonlinearState())>> + : std::true_type {}; + +fesa::DofManager makeDofs() { + fesa::ModelDefinition definition{}; + definition.sourcePath = "models/analysis-state.inp"; + definition.sourceContentIdentity = "fnv1a64:0123456789abcdef"; + definition.nodes = { + {{"Beam-1", 1, "1"}, {0.0, 0.0, 0.0}, {definition.sourcePath, 10U}}, + {{"Beam-1", 2, "2"}, {1.0, 0.0, 0.0}, {definition.sourcePath, 11U}}}; + definition.steps = {{ + "Step-1", + {{"1", 1, 6, 0.0, {definition.sourcePath, 20U}}}, + {}, + 0.1, + 1.0, + 0.01, + 1.0, + {definition.sourcePath, 19U}}}; + + auto domain = fesa::Domain::create(std::move(definition)); + EXPECT_TRUE(domain.hasValue()); + auto model = fesa::AnalysisModel::create(domain.value()); + EXPECT_TRUE(model.hasValue()); + auto dofs = fesa::DofManager::create(model.value()); + EXPECT_TRUE(dofs.hasValue()); + return std::move(dofs.value()); +} + +void expectAllZero(const fesa::Vector& vector) { + for (std::size_t index = 0U; index < vector.size(); ++index) { + EXPECT_DOUBLE_EQ(vector[index], 0.0); + } +} + +} // namespace + +TEST(AnalysisState, AllocatesOnlyV0FullVectors) { + const auto dofs = makeDofs(); + ASSERT_EQ(dofs.fullDofCount(), 12U); + ASSERT_EQ(dofs.constrainedDofCount(), 6U); + + auto state = fesa::AnalysisState::create(dofs, {"Step-1", 0U}); + const fesa::AnalysisState& constState = state; + const fesa::Vector* const vectors[] = { + &constState.displacement(), + &constState.externalForce(), + &constState.internalForce(), + &constState.residual(), + &constState.reaction()}; + + for (const auto* vector : vectors) { + EXPECT_EQ(vector->size(), dofs.fullDofCount()); + expectAllZero(*vector); + } + for (std::size_t left = 0U; left < std::size(vectors); ++left) { + for (std::size_t right = left + 1U; right < std::size(vectors); ++right) { + EXPECT_NE(vectors[left]->data(), vectors[right]->data()); + } + } + + state.displacement()[0] = 4.0; + state.reaction()[11] = -9.0; + EXPECT_DOUBLE_EQ(state.displacement()[0], 4.0); + EXPECT_DOUBLE_EQ(state.reaction()[11], -9.0); + EXPECT_DOUBLE_EQ(state.externalForce()[0], 0.0); + EXPECT_DOUBLE_EQ(state.internalForce()[0], 0.0); + EXPECT_DOUBLE_EQ(state.residual()[0], 0.0); + + EXPECT_FALSE(HasVelocity::value); + EXPECT_FALSE(HasAcceleration::value); + EXPECT_FALSE(HasTemperature::value); + EXPECT_FALSE(HasIterationHistory::value); + EXPECT_FALSE(HasNonlinearState::value); +} + +TEST(AnalysisState, CopiesOrMovesWithoutAliasing) { + static_assert(std::is_copy_constructible_v); + static_assert(std::is_copy_assignable_v); + static_assert(std::is_move_constructible_v); + static_assert(std::is_move_assignable_v); + + const auto dofs = makeDofs(); + auto original = fesa::AnalysisState::create(dofs, {"Step-1", 0U}); + original.displacement()[0] = 3.0; + original.reaction()[11] = -2.0; + original.endpointResults().push_back({ + 0U, + -1, + {"Beam-1", 1, "1"}, + {1.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {2.0, 0.0, 0.0, 0.0}}); + original.gaussResults().push_back( + {0U, 1, {0.1, 0.0, 0.0, 0.0}, {10.0, 0.0, 0.0, 0.0}}); + original.stressResults().push_back( + {0U, 1, 0U, 0.0, 0.0, 10.0, "fesa-default"}); + + auto copied = original; + EXPECT_NE(copied.displacement().data(), original.displacement().data()); + EXPECT_NE(copied.reaction().data(), original.reaction().data()); + EXPECT_NE(copied.endpointResults().data(), original.endpointResults().data()); + EXPECT_NE(copied.gaussResults().data(), original.gaussResults().data()); + EXPECT_NE(copied.stressResults().data(), original.stressResults().data()); + copied.displacement()[0] = 30.0; + copied.endpointResults()[0].endAction[0] = 20.0; + EXPECT_DOUBLE_EQ(original.displacement()[0], 3.0); + EXPECT_DOUBLE_EQ(original.endpointResults()[0].endAction[0], 1.0); + + auto moved = std::move(copied); + EXPECT_EQ(moved.identity().stepName, "Step-1"); + EXPECT_DOUBLE_EQ(moved.displacement()[0], 30.0); + EXPECT_DOUBLE_EQ(moved.endpointResults()[0].endAction[0], 20.0); + EXPECT_NE(moved.displacement().data(), original.displacement().data()); + EXPECT_NE(moved.endpointResults().data(), original.endpointResults().data()); + + auto copyAssigned = fesa::AnalysisState::create(dofs, {"Other", 3U}); + copyAssigned = original; + copyAssigned.reaction()[11] = -20.0; + EXPECT_DOUBLE_EQ(original.reaction()[11], -2.0); + EXPECT_EQ(copyAssigned.identity().stepName, "Step-1"); + + auto moveAssigned = fesa::AnalysisState::create(dofs, {"Other", 4U}); + moveAssigned = std::move(copyAssigned); + EXPECT_EQ(moveAssigned.identity().stepName, "Step-1"); + EXPECT_DOUBLE_EQ(moveAssigned.reaction()[11], -20.0); + EXPECT_NE(moveAssigned.reaction().data(), original.reaction().data()); +} diff --git a/tests/unit/results/result_records_test.cpp b/tests/unit/results/result_records_test.cpp new file mode 100644 index 0000000..5cd5ac8 --- /dev/null +++ b/tests/unit/results/result_records_test.cpp @@ -0,0 +1,102 @@ +#include "fesa/analysis/analysis_state.hpp" + +#include + +#include +#include +#include + +namespace { + +fesa::DofManager makeEmptyDofs() { + fesa::ModelDefinition definition{}; + definition.sourcePath = "models/result-records.inp"; + definition.sourceContentIdentity = "fnv1a64:0123456789abcdef"; + definition.steps = {{ + "Step-1", {}, {}, 0.1, 1.0, 0.01, 1.0, + {definition.sourcePath, 10U}}}; + + auto domain = fesa::Domain::create(std::move(definition)); + EXPECT_TRUE(domain.hasValue()); + auto model = fesa::AnalysisModel::create(domain.value()); + EXPECT_TRUE(model.hasValue()); + auto dofs = fesa::DofManager::create(model.value()); + EXPECT_TRUE(dofs.hasValue()); + return std::move(dofs.value()); +} + +} // namespace + +TEST(AnalysisState, PreservesStepFrameAndStableRowOrder) { + const auto dofs = makeEmptyDofs(); + auto state = fesa::AnalysisState::create(dofs, {"Step-1", 0U}); + + const fesa::EndpointResultRow firstEndpoint{ + 2U, + -1, + {"Beam-1", 10, "010"}, + {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}, + {7.0, 8.0, 9.0, 10.0}}; + const fesa::EndpointResultRow secondEndpoint{ + 2U, + 1, + {"Beam-1", 20, "020"}, + {11.0, 12.0, 13.0, 14.0, 15.0, 16.0}, + {17.0, 18.0, 19.0, 20.0}}; + const fesa::GaussResultRow firstGauss{ + 2U, 1, {0.1, 0.2, 0.3, 0.4}, {1.1, 1.2, 1.3, 1.4}}; + const fesa::GaussResultRow secondGauss{ + 2U, 2, {0.5, 0.6, 0.7, 0.8}, {1.5, 1.6, 1.7, 1.8}}; + const fesa::StressS11Row firstStress{ + 2U, 1, 1U, -0.25, 0.5, 12.5, "input"}; + const fesa::StressS11Row secondStress{ + 2U, 1, 2U, 0.25, -0.5, -7.5, "input"}; + + state.endpointResults().push_back(firstEndpoint); + state.endpointResults().push_back(secondEndpoint); + state.gaussResults().push_back(firstGauss); + state.gaussResults().push_back(secondGauss); + state.stressResults().push_back(firstStress); + state.stressResults().push_back(secondStress); + + const auto* const endpointStorage = state.endpointResults().data(); + const auto* const gaussStorage = state.gaussResults().data(); + const auto* const stressStorage = state.stressResults().data(); + const fesa::AnalysisState& constState = state; + + EXPECT_EQ(constState.identity().stepName, "Step-1"); + EXPECT_EQ(constState.identity().frameIndex, 0U); + ASSERT_EQ(constState.endpointResults().size(), 2U); + EXPECT_EQ(constState.endpointResults().data(), endpointStorage); + EXPECT_EQ(constState.endpointResults()[0].element, 2U); + EXPECT_EQ(constState.endpointResults()[0].endpoint, -1); + EXPECT_EQ(constState.endpointResults()[0].node.instanceName, "Beam-1"); + EXPECT_EQ(constState.endpointResults()[0].node.sourceLabel, 10); + EXPECT_EQ(constState.endpointResults()[0].node.sourceLabelText, "010"); + EXPECT_EQ( + constState.endpointResults()[0].endAction, + (std::array{1.0, 2.0, 3.0, 4.0, 5.0, 6.0})); + EXPECT_EQ( + constState.endpointResults()[1].sectionResultant, + (std::array{17.0, 18.0, 19.0, 20.0})); + + ASSERT_EQ(constState.gaussResults().size(), 2U); + EXPECT_EQ(constState.gaussResults().data(), gaussStorage); + EXPECT_EQ(constState.gaussResults()[0].gaussPoint, 1); + EXPECT_EQ(constState.gaussResults()[1].gaussPoint, 2); + EXPECT_EQ( + constState.gaussResults()[0].generalizedStrain, + (std::array{0.1, 0.2, 0.3, 0.4})); + EXPECT_EQ( + constState.gaussResults()[1].generalizedResultant, + (std::array{1.5, 1.6, 1.7, 1.8})); + + ASSERT_EQ(constState.stressResults().size(), 2U); + EXPECT_EQ(constState.stressResults().data(), stressStorage); + EXPECT_EQ(constState.stressResults()[0].sectionPoint, 1U); + EXPECT_DOUBLE_EQ(constState.stressResults()[0].x1, -0.25); + EXPECT_DOUBLE_EQ(constState.stressResults()[0].x2, 0.5); + EXPECT_DOUBLE_EQ(constState.stressResults()[0].s11, 12.5); + EXPECT_EQ(constState.stressResults()[0].source, "input"); + EXPECT_EQ(constState.stressResults()[1].sectionPoint, 2U); +}