From 042edadffb42472794b466f3cdcb2f6d31676589 Mon Sep 17 00:00:00 2001 From: "KOKO\\Mimi" Date: Sun, 16 Aug 2026 04:26:14 +0900 Subject: [PATCH] feat(cpp-object-oriented-modular-refactoring): step 3 - foundation-google-style --- include/fesa/analysis/analysis_state.hpp | 4 +- .../fesa/analysis/linear_static_analysis.hpp | 6 +- include/fesa/assembly/load_assembler.hpp | 4 +- include/fesa/assembly/sparse_assembler.hpp | 4 +- include/fesa/build_info.h | 13 + include/fesa/build_info.hpp | 10 - .../constraints/essential_constraints.hpp | 6 +- include/fesa/core/diagnostic.h | 31 + include/fesa/core/diagnostic.hpp | 30 - include/fesa/core/source_identity.h | 26 + include/fesa/core/source_identity.hpp | 23 - include/fesa/core/status.h | 114 ++++ include/fesa/core/status.hpp | 94 --- include/fesa/elements/euler_beam_3d.hpp | 6 +- include/fesa/elements/mitc4_shell.hpp | 6 +- include/fesa/fem/dof_manager.hpp | 2 +- include/fesa/io/abaqus/domain_mapper.hpp | 2 +- include/fesa/io/abaqus/input_reader.hpp | 2 +- include/fesa/io/abaqus/input_syntax.hpp | 2 +- include/fesa/math/matrix.h | 59 ++ include/fesa/math/matrix.hpp | 32 - include/fesa/math/sparse_matrix.h | 73 +++ include/fesa/math/sparse_matrix.hpp | 53 -- include/fesa/math/vector.h | 64 ++ include/fesa/math/vector.hpp | 31 - include/fesa/model/domain.hpp | 2 +- include/fesa/model/model_types.hpp | 4 +- include/fesa/model/shell_geometry.hpp | 2 +- include/fesa/results/result_recovery.hpp | 4 +- include/fesa/results/results_writer.hpp | 4 +- include/fesa/solvers/linear/linear_solver.h | 30 + include/fesa/solvers/linear/linear_solver.hpp | 18 - .../fesa/solvers/linear/mkl_pardiso_solver.h | 32 + .../solvers/linear/mkl_pardiso_solver.hpp | 23 - src/fesa/analysis/analysis_model.cpp | 14 +- src/fesa/analysis/analysis_state.cpp | 8 +- src/fesa/analysis/linear_static_analysis.cpp | 86 +-- src/fesa/app/fesa_application.cpp | 26 +- src/fesa/assembly/load_assembler.cpp | 124 ++-- src/fesa/assembly/sparse_assembler.cpp | 46 +- src/fesa/build_info.cpp | 11 +- .../constraints/essential_constraints.cpp | 80 +-- src/fesa/core/diagnostic.cpp | 35 +- src/fesa/core/status.cpp | 46 +- src/fesa/elements/euler_beam_3d.cpp | 28 +- src/fesa/elements/mitc4_shell.cpp | 66 +- src/fesa/fem/dof_manager.cpp | 10 +- src/fesa/io/abaqus/domain_mapper.cpp | 24 +- src/fesa/io/abaqus/input_reader.cpp | 8 +- src/fesa/io/hdf5/hdf5_results_writer.cpp | 94 +-- src/fesa/math/matrix.cpp | 209 +++--- src/fesa/math/sparse_matrix.cpp | 369 +++++------ src/fesa/math/vector.cpp | 135 ++-- src/fesa/model/domain.cpp | 2 +- src/fesa/model/shell_geometry.cpp | 52 +- src/fesa/results/result_recovery.cpp | 136 ++-- .../solvers/linear/mkl_pardiso_solver.cpp | 615 +++++++++--------- .../analysis/linear_static_analysis_test.cpp | 80 +-- .../b33_reference_comparison_test.cpp | 6 +- .../reference/mitc4_reference_cases_test.cpp | 10 +- .../reference/mitc4_reference_comparison.cpp | 14 +- .../reference/mitc4_reference_comparison.hpp | 2 +- .../mitc4_reference_comparison_test.cpp | 30 +- tests/reference/reference_comparison.cpp | 80 +-- tests/reference/reference_comparison.hpp | 2 +- tests/reference/reference_comparison_test.cpp | 42 +- tests/unit/analysis/analysis_model_test.cpp | 54 +- tests/unit/analysis/analysis_state_test.cpp | 26 +- tests/unit/assembly/load_assembler_test.cpp | 124 ++-- tests/unit/assembly/sparse_assembler_test.cpp | 168 ++--- tests/unit/build_info_test.cpp | 24 +- .../essential_constraints_test.cpp | 168 ++--- tests/unit/core/diagnostic_test.cpp | 78 ++- tests/unit/core/source_identity_test.cpp | 20 +- tests/unit/core/status_test.cpp | 86 ++- tests/unit/elements/euler_beam_3d_test.cpp | 88 +-- tests/unit/elements/mitc4_shell_test.cpp | 226 +++---- tests/unit/fem/dof_manager_test.cpp | 42 +- tests/unit/io/abaqus/domain_mapper_test.cpp | 322 ++++----- tests/unit/io/abaqus/input_reader_test.cpp | 54 +- tests/unit/io/abaqus/input_syntax_test.cpp | 20 +- .../unit/io/hdf5/hdf5_results_writer_test.cpp | 66 +- tests/unit/math/matrix_test.cpp | 170 ++--- tests/unit/math/sparse_matrix_test.cpp | 205 +++--- tests/unit/math/vector_test.cpp | 127 ++-- tests/unit/model/domain_test.cpp | 54 +- tests/unit/model/model_types_test.cpp | 34 +- tests/unit/model/shell_geometry_test.cpp | 38 +- tests/unit/results/result_records_test.cpp | 30 +- tests/unit/results/result_recovery_test.cpp | 146 ++--- tests/unit/results/results_writer_test.cpp | 4 +- .../solvers/linear/linear_solver_test.cpp | 215 +++--- .../linear/mkl_pardiso_solver_test.cpp | 424 ++++++------ 93 files changed, 3144 insertions(+), 3175 deletions(-) create mode 100644 include/fesa/build_info.h delete mode 100644 include/fesa/build_info.hpp create mode 100644 include/fesa/core/diagnostic.h delete mode 100644 include/fesa/core/diagnostic.hpp create mode 100644 include/fesa/core/source_identity.h delete mode 100644 include/fesa/core/source_identity.hpp create mode 100644 include/fesa/core/status.h delete mode 100644 include/fesa/core/status.hpp create mode 100644 include/fesa/math/matrix.h delete mode 100644 include/fesa/math/matrix.hpp create mode 100644 include/fesa/math/sparse_matrix.h delete mode 100644 include/fesa/math/sparse_matrix.hpp create mode 100644 include/fesa/math/vector.h delete mode 100644 include/fesa/math/vector.hpp create mode 100644 include/fesa/solvers/linear/linear_solver.h delete mode 100644 include/fesa/solvers/linear/linear_solver.hpp create mode 100644 include/fesa/solvers/linear/mkl_pardiso_solver.h delete mode 100644 include/fesa/solvers/linear/mkl_pardiso_solver.hpp diff --git a/include/fesa/analysis/analysis_state.hpp b/include/fesa/analysis/analysis_state.hpp index a85e497..1dd1286 100644 --- a/include/fesa/analysis/analysis_state.hpp +++ b/include/fesa/analysis/analysis_state.hpp @@ -1,8 +1,8 @@ #pragma once -#include "fesa/core/status.hpp" +#include "fesa/core/status.h" #include "fesa/fem/dof_manager.hpp" -#include "fesa/math/vector.hpp" +#include "fesa/math/vector.h" #include "fesa/results/result_records.hpp" #include diff --git a/include/fesa/analysis/linear_static_analysis.hpp b/include/fesa/analysis/linear_static_analysis.hpp index fcbeef8..4e32f9e 100644 --- a/include/fesa/analysis/linear_static_analysis.hpp +++ b/include/fesa/analysis/linear_static_analysis.hpp @@ -3,10 +3,10 @@ #include "fesa/analysis/analysis_model.hpp" #include "fesa/analysis/analysis_state.hpp" #include "fesa/constraints/essential_constraints.hpp" -#include "fesa/core/status.hpp" +#include "fesa/core/status.h" #include "fesa/fem/dof_manager.hpp" -#include "fesa/math/sparse_matrix.hpp" -#include "fesa/math/vector.hpp" +#include "fesa/math/sparse_matrix.h" +#include "fesa/math/vector.h" #include "fesa/model/domain.hpp" #include diff --git a/include/fesa/assembly/load_assembler.hpp b/include/fesa/assembly/load_assembler.hpp index 3c14857..a09afda 100644 --- a/include/fesa/assembly/load_assembler.hpp +++ b/include/fesa/assembly/load_assembler.hpp @@ -2,8 +2,8 @@ #include "fesa/analysis/analysis_model.hpp" #include "fesa/fem/dof_manager.hpp" -#include "fesa/math/sparse_matrix.hpp" -#include "fesa/math/vector.hpp" +#include "fesa/math/sparse_matrix.h" +#include "fesa/math/vector.h" namespace fesa { diff --git a/include/fesa/assembly/sparse_assembler.hpp b/include/fesa/assembly/sparse_assembler.hpp index fa29b4c..92bceda 100644 --- a/include/fesa/assembly/sparse_assembler.hpp +++ b/include/fesa/assembly/sparse_assembler.hpp @@ -1,7 +1,7 @@ #pragma once -#include "fesa/core/status.hpp" -#include "fesa/math/sparse_matrix.hpp" +#include "fesa/core/status.h" +#include "fesa/math/sparse_matrix.h" namespace fesa { diff --git a/include/fesa/build_info.h b/include/fesa/build_info.h new file mode 100644 index 0000000..9eb8a97 --- /dev/null +++ b/include/fesa/build_info.h @@ -0,0 +1,13 @@ +#ifndef FESA_BUILD_INFO_H_ +#define FESA_BUILD_INFO_H_ + +#include + +namespace fesa { + +/// @brief Returns the stable solver version written to result metadata. +std::string_view SolverVersion() noexcept; + +} // namespace fesa + +#endif // FESA_BUILD_INFO_H_ diff --git a/include/fesa/build_info.hpp b/include/fesa/build_info.hpp deleted file mode 100644 index 545b906..0000000 --- a/include/fesa/build_info.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include - -namespace fesa { - -// Returns the stable solver version written to externally visible result metadata. -std::string_view solverVersion() noexcept; - -} // namespace fesa diff --git a/include/fesa/constraints/essential_constraints.hpp b/include/fesa/constraints/essential_constraints.hpp index 5a5d4c2..f8072d2 100644 --- a/include/fesa/constraints/essential_constraints.hpp +++ b/include/fesa/constraints/essential_constraints.hpp @@ -1,8 +1,8 @@ #pragma once -#include "fesa/core/status.hpp" -#include "fesa/math/sparse_matrix.hpp" -#include "fesa/math/vector.hpp" +#include "fesa/core/status.h" +#include "fesa/math/sparse_matrix.h" +#include "fesa/math/vector.h" namespace fesa { diff --git a/include/fesa/core/diagnostic.h b/include/fesa/core/diagnostic.h new file mode 100644 index 0000000..cd685f4 --- /dev/null +++ b/include/fesa/core/diagnostic.h @@ -0,0 +1,31 @@ +#ifndef FESA_CORE_DIAGNOSTIC_H_ +#define FESA_CORE_DIAGNOSTIC_H_ + +#include +#include + +#include "fesa/core/source_identity.h" + +namespace fesa { + +/// @brief Distinguishes recoverable warnings from operation-stopping errors. +enum class Severity { kWarning, kError }; + +/// @brief Carries a structured, backend-independent diagnostic record. +struct Diagnostic { + Severity severity; + std::string code; + SourceLocation location; + std::string keyword; + std::string entity_identity; + std::string message; +}; + +/// @brief Orders diagnostics by their externally visible source tuple. +/// @param diagnostics Records to reorder in place. +/// @note Records with identical keys retain their discovery order. +void SortDiagnostics(std::vector& diagnostics); + +} // namespace fesa + +#endif // FESA_CORE_DIAGNOSTIC_H_ diff --git a/include/fesa/core/diagnostic.hpp b/include/fesa/core/diagnostic.hpp deleted file mode 100644 index df68cc7..0000000 --- a/include/fesa/core/diagnostic.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "fesa/core/source_identity.hpp" - -#include -#include - -namespace fesa { - -// Distinguishes recoverable warnings from errors that stop the current operation. -enum class Severity { - warning, - error -}; - -// Carries a structured, backend-independent diagnostic record. -struct Diagnostic { - Severity severity; - std::string code; - SourceLocation location; - std::string keyword; - std::string entityIdentity; - std::string message; -}; - -// Orders diagnostics by their externally visible source tuple while retaining -// discovery order for records with identical keys. -void sortDiagnostics(std::vector& diagnostics); - -} // namespace fesa diff --git a/include/fesa/core/source_identity.h b/include/fesa/core/source_identity.h new file mode 100644 index 0000000..c592dfc --- /dev/null +++ b/include/fesa/core/source_identity.h @@ -0,0 +1,26 @@ +#ifndef FESA_CORE_SOURCE_IDENTITY_H_ +#define FESA_CORE_SOURCE_IDENTITY_H_ + +#include +#include +#include +#include + +namespace fesa { + +/// @brief Identifies the input location that produced an item or diagnostic. +struct SourceLocation { + std::filesystem::path file; + std::size_t line; +}; + +/// @brief Preserves semantic and raw-text forms of a source entity identity. +struct SourceEntityId { + std::string instance_name; + std::int64_t source_label; + std::string source_label_text; +}; + +} // namespace fesa + +#endif // FESA_CORE_SOURCE_IDENTITY_H_ diff --git a/include/fesa/core/source_identity.hpp b/include/fesa/core/source_identity.hpp deleted file mode 100644 index 61732ac..0000000 --- a/include/fesa/core/source_identity.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace fesa { - -// Identifies the physical input location that produced a model item or diagnostic. -struct SourceLocation { - std::filesystem::path file; - std::size_t line; -}; - -// Preserves both semantic and raw-text forms of an input entity identity. -struct SourceEntityId { - std::string instanceName; - std::int64_t sourceLabel; - std::string sourceLabelText; -}; - -} // namespace fesa diff --git a/include/fesa/core/status.h b/include/fesa/core/status.h new file mode 100644 index 0000000..8476cc5 --- /dev/null +++ b/include/fesa/core/status.h @@ -0,0 +1,114 @@ +#ifndef FESA_CORE_STATUS_H_ +#define FESA_CORE_STATUS_H_ + +#include +#include +#include +#include + +#include "fesa/core/diagnostic.h" + +namespace fesa { + +/// @brief Maps failures to the stable command-line exit-code categories. +enum class FailureCategory { kInput, kModel, kSolver, kOutput }; + +/// @brief Transports success or structured failure diagnostics. +class Status { + public: + /// @brief Creates a successful status. + /// @return A status with no failure category or diagnostics. + static Status Ok(); + + /// @brief Creates an uncategorized failed status. + /// @param diagnostics Structured diagnostics owned by the returned status. + /// @return A failed status with diagnostics in deterministic source order. + static Status Failure(std::vector diagnostics); + + /// @brief Creates a categorized failed status. + /// @param category Stable external failure category. + /// @param diagnostics Structured diagnostics owned by the returned status. + /// @return A failed status with diagnostics in deterministic source order. + static Status Failure(FailureCategory category, + std::vector diagnostics); + + /// @brief Reports whether the operation succeeded. + bool IsOk() const noexcept; + + /// @brief Returns the optional stable failure category. + std::optional Category() const noexcept; + + /// @brief Returns the deterministically ordered diagnostic records. + const std::vector& Diagnostics() const noexcept; + + private: + /// @brief Constructs a status from its validated invariant fields. + Status(bool is_ok, std::optional category, + std::vector diagnostics); + + bool is_ok_; + std::optional category_; + std::vector diagnostics_; +}; + +/// @brief Owns exactly one successful value or one failed Status. +template +class Result { + public: + /// @brief Creates a successful result that owns the supplied value. + static Result Success(T value) { + return Result{SuccessTag{}, std::move(value)}; + } + + /// @brief Creates a failed result that owns a failed status. + /// @throws std::invalid_argument if status represents success. + static Result Failure(Status status) { + if (status.IsOk()) { + throw std::invalid_argument{"A failed Result requires a failed Status."}; + } + return Result{FailureTag{}, std::move(status)}; + } + + /// @brief Reports whether this result owns a successful value. + bool HasValue() const noexcept { return value_.has_value(); } + + /// @brief Returns the owned successful value. + /// @throws std::logic_error if this result represents failure. + T& Value() { + if (!value_) { + throw std::logic_error{"Result has no value."}; + } + return *value_; + } + + /// @brief Returns the owned successful value. + /// @throws std::logic_error if this result represents failure. + const T& Value() const { + if (!value_) { + throw std::logic_error{"Result has no value."}; + } + return *value_; + } + + /// @brief Returns the success or failure status. + const Status& GetStatus() const noexcept { return status_; } + + private: + struct SuccessTag {}; + struct FailureTag {}; + + /// @brief Constructs the successful value alternative. + Result(SuccessTag, T value) + : value_{std::move(value)}, status_{Status::Ok()} {} + + /// @brief Constructs the failed status alternative. + Result(FailureTag, Status status) + : value_{std::nullopt}, status_{std::move(status)} {} + + std::optional value_; + Status status_; +}; + +} // namespace fesa + +#endif // FESA_CORE_STATUS_H_ diff --git a/include/fesa/core/status.hpp b/include/fesa/core/status.hpp deleted file mode 100644 index ee8313b..0000000 --- a/include/fesa/core/status.hpp +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once - -#include "fesa/core/diagnostic.hpp" - -#include -#include -#include -#include - -namespace fesa { - -// Maps a failure to the stable command-line exit-code classes defined by V0. -enum class FailureCategory { - input, - model, - solver, - output -}; - -// Transports success or structured diagnostics without exposing backend errors. -class Status { -public: - static Status ok(); - static Status failure(std::vector diagnostics); - static Status failure( - FailureCategory category, std::vector diagnostics); - - bool isOk() const noexcept; - std::optional failureCategory() const noexcept; - const std::vector& diagnostics() const noexcept; - -private: - Status( - bool isOk, - std::optional category, - std::vector diagnostics); - - bool isOk_; - std::optional category_; - std::vector diagnostics_; -}; - -// Owns exactly one successful value or one failed Status. -template -class Result { -public: - static Result success(T value) { - return Result{SuccessTag{}, std::move(value)}; - } - - static Result failure(Status status) { - if (status.isOk()) { - throw std::invalid_argument{"A failed Result requires a failed Status."}; - } - return Result{FailureTag{}, std::move(status)}; - } - - bool hasValue() const noexcept { - return value_.has_value(); - } - - T& value() { - if (!value_) { - throw std::logic_error{"Result has no value."}; - } - return *value_; - } - - const T& value() const { - if (!value_) { - throw std::logic_error{"Result has no value."}; - } - return *value_; - } - - const Status& status() const noexcept { - return status_; - } - -private: - struct SuccessTag {}; - struct FailureTag {}; - - Result(SuccessTag, T value) - : value_{std::move(value)}, status_{Status::ok()} {} - - Result(FailureTag, Status status) - : value_{std::nullopt}, status_{std::move(status)} {} - - std::optional value_; - Status status_; -}; - -} // namespace fesa diff --git a/include/fesa/elements/euler_beam_3d.hpp b/include/fesa/elements/euler_beam_3d.hpp index 4867c55..8cc5264 100644 --- a/include/fesa/elements/euler_beam_3d.hpp +++ b/include/fesa/elements/euler_beam_3d.hpp @@ -1,8 +1,8 @@ #pragma once -#include "fesa/core/status.hpp" -#include "fesa/math/matrix.hpp" -#include "fesa/math/vector.hpp" +#include "fesa/core/status.h" +#include "fesa/math/matrix.h" +#include "fesa/math/vector.h" #include "fesa/model/model_types.hpp" #include diff --git a/include/fesa/elements/mitc4_shell.hpp b/include/fesa/elements/mitc4_shell.hpp index 4d970ed..ac14971 100644 --- a/include/fesa/elements/mitc4_shell.hpp +++ b/include/fesa/elements/mitc4_shell.hpp @@ -1,8 +1,8 @@ #pragma once -#include "fesa/core/status.hpp" -#include "fesa/math/matrix.hpp" -#include "fesa/math/vector.hpp" +#include "fesa/core/status.h" +#include "fesa/math/matrix.h" +#include "fesa/math/vector.h" #include "fesa/model/model_types.hpp" #include diff --git a/include/fesa/fem/dof_manager.hpp b/include/fesa/fem/dof_manager.hpp index 189f790..b6bcd65 100644 --- a/include/fesa/fem/dof_manager.hpp +++ b/include/fesa/fem/dof_manager.hpp @@ -1,7 +1,7 @@ #pragma once #include "fesa/analysis/analysis_model.hpp" -#include "fesa/math/vector.hpp" +#include "fesa/math/vector.h" #include #include diff --git a/include/fesa/io/abaqus/domain_mapper.hpp b/include/fesa/io/abaqus/domain_mapper.hpp index 8d2a6e4..dc66a71 100644 --- a/include/fesa/io/abaqus/domain_mapper.hpp +++ b/include/fesa/io/abaqus/domain_mapper.hpp @@ -1,6 +1,6 @@ #pragma once -#include "fesa/core/status.hpp" +#include "fesa/core/status.h" #include "fesa/io/abaqus/input_syntax.hpp" #include "fesa/model/domain.hpp" diff --git a/include/fesa/io/abaqus/input_reader.hpp b/include/fesa/io/abaqus/input_reader.hpp index 04fe67b..4d305df 100644 --- a/include/fesa/io/abaqus/input_reader.hpp +++ b/include/fesa/io/abaqus/input_reader.hpp @@ -1,6 +1,6 @@ #pragma once -#include "fesa/core/status.hpp" +#include "fesa/core/status.h" #include "fesa/io/abaqus/input_syntax.hpp" #include diff --git a/include/fesa/io/abaqus/input_syntax.hpp b/include/fesa/io/abaqus/input_syntax.hpp index 163be25..aa91d8e 100644 --- a/include/fesa/io/abaqus/input_syntax.hpp +++ b/include/fesa/io/abaqus/input_syntax.hpp @@ -1,6 +1,6 @@ #pragma once -#include "fesa/core/source_identity.hpp" +#include "fesa/core/source_identity.h" #include #include diff --git a/include/fesa/math/matrix.h b/include/fesa/math/matrix.h new file mode 100644 index 0000000..eb700a8 --- /dev/null +++ b/include/fesa/math/matrix.h @@ -0,0 +1,59 @@ +#ifndef FESA_MATH_MATRIX_H_ +#define FESA_MATH_MATRIX_H_ + +#include +#include + +#include "fesa/math/vector.h" + +namespace fesa { + +/// @brief Owns row-major contiguous storage independently of sparse matrices. +class Matrix { + public: + /// @brief Constructs a row-major matrix initialized to one value. + Matrix(std::size_t rows, std::size_t columns, double value = 0.0); + + /// @brief Copies matrix values into independent contiguous storage. + Matrix(const Matrix& other); + + /// @brief Moves matrix storage and resets other to a zero-by-zero shape. + Matrix(Matrix&& other) noexcept; + + /// @brief Copies matrix values into independent contiguous storage. + Matrix& operator=(const Matrix& other); + + /// @brief Moves matrix storage and resets other to a zero-by-zero shape. + Matrix& operator=(Matrix&& other) noexcept; + + /// @brief Returns the row count. + std::size_t Rows() const noexcept; + + /// @brief Returns the column count. + std::size_t Columns() const noexcept; + + /// @brief Returns a bounds-checked mutable entry. + /// @throws std::out_of_range if the index is outside the matrix. + double& operator()(std::size_t row, std::size_t column); + + /// @brief Returns a bounds-checked immutable entry. + /// @throws std::out_of_range if the index is outside the matrix. + const double& operator()(std::size_t row, std::size_t column) const; + + /// @brief Multiplies this row-major matrix by a dense vector. + /// @throws std::invalid_argument if the dimensions are incompatible. + Vector Multiply(const Vector& rhs) const; + + /// @brief Multiplies this row-major matrix by another dense matrix. + /// @throws std::invalid_argument if the dimensions are incompatible. + Matrix Multiply(const Matrix& rhs) const; + + private: + std::size_t rows_; + std::size_t columns_; + std::vector values_; +}; + +} // namespace fesa + +#endif // FESA_MATH_MATRIX_H_ diff --git a/include/fesa/math/matrix.hpp b/include/fesa/math/matrix.hpp deleted file mode 100644 index 1051295..0000000 --- a/include/fesa/math/matrix.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "fesa/math/vector.hpp" - -#include -#include - -namespace fesa { - -// Owns row-major contiguous dense storage independently of sparse matrices. -class Matrix { -public: - Matrix(std::size_t rows, std::size_t columns, double value = 0.0); - Matrix(const Matrix& other); - Matrix(Matrix&& other) noexcept; - Matrix& operator=(const Matrix& other); - Matrix& operator=(Matrix&& other) noexcept; - - std::size_t rows() const noexcept; - std::size_t columns() const noexcept; - double& operator()(std::size_t row, std::size_t column); - const double& operator()(std::size_t row, std::size_t column) const; - Vector multiply(const Vector& rhs) const; - Matrix multiply(const Matrix& rhs) const; - -private: - std::size_t rows_; - std::size_t columns_; - std::vector values_; -}; - -} // namespace fesa diff --git a/include/fesa/math/sparse_matrix.h b/include/fesa/math/sparse_matrix.h new file mode 100644 index 0000000..dedf73e --- /dev/null +++ b/include/fesa/math/sparse_matrix.h @@ -0,0 +1,73 @@ +#ifndef FESA_MATH_SPARSE_MATRIX_H_ +#define FESA_MATH_SPARSE_MATRIX_H_ + +#include +#include + +#include "fesa/core/status.h" +#include "fesa/math/vector.h" + +namespace fesa { + +struct SparsePattern; + +/// @brief Carries one deterministic element-local COO contribution. +struct CooContribution { + std::size_t row; + std::size_t column; + double value; + std::size_t element_order; + std::size_t local_order; +}; + +/// @brief Owns canonical 0-based CSR independently of the dense Matrix type. +class SparseMatrix { + public: + /// @brief Reduces ordered COO contributions into an expected CSR pattern. + /// @return A validated matrix or a structured model failure. + /// @note Duplicate sums use stable element and local contribution order. + static Result FromCoo( + std::size_t rows, std::size_t columns, + std::vector contributions, + const SparsePattern& expected_pattern); + + /// @brief Returns the row count. + std::size_t Rows() const noexcept; + + /// @brief Returns the column count. + std::size_t Columns() const noexcept; + + /// @brief Returns the canonical 0-based CSR row offsets. + const std::vector& RowOffsets() const noexcept; + + /// @brief Returns sorted unique 0-based CSR column indices. + const std::vector& ColumnIndices() const noexcept; + + /// @brief Returns CSR values including preserved structural zeros. + const std::vector& Values() const noexcept; + + /// @brief Multiplies this matrix by a dense vector in stable CSR order. + /// @throws std::invalid_argument if the dimensions are incompatible. + Vector Multiply(const Vector& rhs) const; + + /// @brief Validates shape, indices, ordering, and finite CSR values. + /// @return Success or a structured model failure. + Status Validate() const; + + private: + /// @brief Constructs CSR storage after boundary validation. + SparseMatrix(std::size_t rows, std::size_t columns, + std::vector row_offsets, + std::vector column_indices, + std::vector values); + + std::size_t rows_; + std::size_t columns_; + std::vector row_offsets_; + std::vector column_indices_; + std::vector values_; +}; + +} // namespace fesa + +#endif // FESA_MATH_SPARSE_MATRIX_H_ diff --git a/include/fesa/math/sparse_matrix.hpp b/include/fesa/math/sparse_matrix.hpp deleted file mode 100644 index 30e9ff1..0000000 --- a/include/fesa/math/sparse_matrix.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "fesa/core/status.hpp" -#include "fesa/math/vector.hpp" - -#include -#include - -namespace fesa { - -struct SparsePattern; - -struct CooContribution { - std::size_t row; - std::size_t column; - double value; - std::size_t elementOrder; - std::size_t localOrder; -}; - -// Owns canonical 0-based CSR data independently of the dense Matrix adapter. -class SparseMatrix { -public: - static Result fromCoo( - std::size_t rows, - std::size_t columns, - std::vector contributions, - const SparsePattern& expectedPattern); - - std::size_t rows() const noexcept; - std::size_t columns() const noexcept; - const std::vector& rowOffsets() const noexcept; - const std::vector& columnIndices() const noexcept; - const std::vector& values() const noexcept; - Vector multiply(const Vector& rhs) const; - Status validate() const; - -private: - SparseMatrix( - std::size_t rows, - std::size_t columns, - std::vector rowOffsets, - std::vector columnIndices, - std::vector values); - - std::size_t rows_; - std::size_t columns_; - std::vector rowOffsets_; - std::vector columnIndices_; - std::vector values_; -}; - -} // namespace fesa diff --git a/include/fesa/math/vector.h b/include/fesa/math/vector.h new file mode 100644 index 0000000..61715d4 --- /dev/null +++ b/include/fesa/math/vector.h @@ -0,0 +1,64 @@ +#ifndef FESA_MATH_VECTOR_H_ +#define FESA_MATH_VECTOR_H_ + +#include +#include + +namespace fesa { + +/// @brief Owns a contiguous dense vector while keeping MKL private. +class Vector { + public: + /// @brief Constructs a vector with all entries initialized to one value. + explicit Vector(std::size_t size, double value = 0.0); + + /// @brief Copies vector values into independent contiguous storage. + Vector(const Vector& other); + + /// @brief Moves vector storage and leaves other empty. + Vector(Vector&& other) noexcept; + + /// @brief Copies vector values into independent contiguous storage. + Vector& operator=(const Vector& other); + + /// @brief Moves vector storage and leaves other empty. + Vector& operator=(Vector&& other) noexcept; + + /// @brief Returns the number of entries. + std::size_t Size() const noexcept; + + /// @brief Returns mutable contiguous storage. + double* Data() noexcept; + + /// @brief Returns immutable contiguous storage. + const double* Data() const noexcept; + + /// @brief Returns a bounds-checked mutable entry. + /// @throws std::out_of_range if index is outside the vector. + double& operator[](std::size_t index); + + /// @brief Returns a bounds-checked immutable entry. + /// @throws std::out_of_range if index is outside the vector. + const double& operator[](std::size_t index) const; + + /// @brief Computes the Euclidean dot product with rhs. + /// @throws std::invalid_argument if the vector sizes differ. + double Dot(const Vector& rhs) const; + + /// @brief Computes the Euclidean norm. + double Norm() const; + + /// @brief Scales each entry by alpha through the dense backend. + void Scale(double alpha); + + /// @brief Accumulates alpha times x into this vector. + /// @throws std::invalid_argument if the vector sizes differ. + void Axpy(double alpha, const Vector& x); + + private: + std::vector values_; +}; + +} // namespace fesa + +#endif // FESA_MATH_VECTOR_H_ diff --git a/include/fesa/math/vector.hpp b/include/fesa/math/vector.hpp deleted file mode 100644 index 0842bcd..0000000 --- a/include/fesa/math/vector.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include - -namespace fesa { - -// Owns a contiguous dense vector while keeping the MKL backend private. -class Vector { -public: - explicit Vector(std::size_t size, double value = 0.0); - Vector(const Vector& other); - Vector(Vector&& other) noexcept; - Vector& operator=(const Vector& other); - Vector& operator=(Vector&& other) noexcept; - - std::size_t size() const noexcept; - double* data() noexcept; - const double* data() const noexcept; - double& operator[](std::size_t index); - const double& operator[](std::size_t index) const; - double dot(const Vector& rhs) const; - double norm() const; - void scale(double alpha); - void axpy(double alpha, const Vector& x); - -private: - std::vector values_; -}; - -} // namespace fesa diff --git a/include/fesa/model/domain.hpp b/include/fesa/model/domain.hpp index c7f9dad..19d5cf8 100644 --- a/include/fesa/model/domain.hpp +++ b/include/fesa/model/domain.hpp @@ -1,6 +1,6 @@ #pragma once -#include "fesa/core/status.hpp" +#include "fesa/core/status.h" #include "fesa/model/model_types.hpp" #include diff --git a/include/fesa/model/model_types.hpp b/include/fesa/model/model_types.hpp index 4fff7c3..c72db64 100644 --- a/include/fesa/model/model_types.hpp +++ b/include/fesa/model/model_types.hpp @@ -1,7 +1,7 @@ #pragma once -#include "fesa/core/diagnostic.hpp" -#include "fesa/core/source_identity.hpp" +#include "fesa/core/diagnostic.h" +#include "fesa/core/source_identity.h" #include #include diff --git a/include/fesa/model/shell_geometry.hpp b/include/fesa/model/shell_geometry.hpp index 43729e5..24b0c3b 100644 --- a/include/fesa/model/shell_geometry.hpp +++ b/include/fesa/model/shell_geometry.hpp @@ -1,6 +1,6 @@ #pragma once -#include "fesa/core/status.hpp" +#include "fesa/core/status.h" #include "fesa/model/model_types.hpp" #include diff --git a/include/fesa/results/result_recovery.hpp b/include/fesa/results/result_recovery.hpp index f85b7f6..370a031 100644 --- a/include/fesa/results/result_recovery.hpp +++ b/include/fesa/results/result_recovery.hpp @@ -2,9 +2,9 @@ #include "fesa/analysis/analysis_model.hpp" #include "fesa/analysis/analysis_state.hpp" -#include "fesa/core/status.hpp" +#include "fesa/core/status.h" #include "fesa/fem/dof_manager.hpp" -#include "fesa/math/sparse_matrix.hpp" +#include "fesa/math/sparse_matrix.h" #include #include diff --git a/include/fesa/results/results_writer.hpp b/include/fesa/results/results_writer.hpp index 3352976..7d212e6 100644 --- a/include/fesa/results/results_writer.hpp +++ b/include/fesa/results/results_writer.hpp @@ -1,8 +1,8 @@ #pragma once #include "fesa/analysis/analysis_state.hpp" -#include "fesa/core/diagnostic.hpp" -#include "fesa/core/status.hpp" +#include "fesa/core/diagnostic.h" +#include "fesa/core/status.h" #include "fesa/model/domain.hpp" #include diff --git a/include/fesa/solvers/linear/linear_solver.h b/include/fesa/solvers/linear/linear_solver.h new file mode 100644 index 0000000..bd8210d --- /dev/null +++ b/include/fesa/solvers/linear/linear_solver.h @@ -0,0 +1,30 @@ +#ifndef FESA_SOLVERS_LINEAR_LINEAR_SOLVER_H_ +#define FESA_SOLVERS_LINEAR_LINEAR_SOLVER_H_ + +#include "fesa/core/status.h" + +namespace fesa { + +class SparseMatrix; +class Vector; + +/// @brief Separates reusable factorization from RHS substitution. +class LinearSolver { + public: + /// @brief Destroys a backend-neutral linear solver. + virtual ~LinearSolver() = default; + + /// @brief Factorizes a validated free-equation matrix for reuse. + /// @return Success or a structured solver failure. + virtual Status Factorize(const SparseMatrix& matrix) = 0; + + /// @brief Substitutes one right-hand side using retained factorization. + /// @param rhs Immutable right-hand side in free-equation order. + /// @param solution Updated only after successful finite substitution. + /// @return Success or a structured solver failure. + virtual Status Solve(const Vector& rhs, Vector& solution) const = 0; +}; + +} // namespace fesa + +#endif // FESA_SOLVERS_LINEAR_LINEAR_SOLVER_H_ diff --git a/include/fesa/solvers/linear/linear_solver.hpp b/include/fesa/solvers/linear/linear_solver.hpp deleted file mode 100644 index cbf7e50..0000000 --- a/include/fesa/solvers/linear/linear_solver.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "fesa/core/status.hpp" - -namespace fesa { - -class SparseMatrix; -class Vector; - -// Separates reusable matrix factorization from right-hand-side substitution. -class LinearSolver { -public: - virtual ~LinearSolver() = default; - virtual Status factorize(const SparseMatrix& matrix) = 0; - virtual Status solve(const Vector& rhs, Vector& solution) const = 0; -}; - -} // namespace fesa diff --git a/include/fesa/solvers/linear/mkl_pardiso_solver.h b/include/fesa/solvers/linear/mkl_pardiso_solver.h new file mode 100644 index 0000000..f4cab79 --- /dev/null +++ b/include/fesa/solvers/linear/mkl_pardiso_solver.h @@ -0,0 +1,32 @@ +#ifndef FESA_SOLVERS_LINEAR_MKL_PARDISO_SOLVER_H_ +#define FESA_SOLVERS_LINEAR_MKL_PARDISO_SOLVER_H_ + +#include + +#include "fesa/solvers/linear/linear_solver.h" + +namespace fesa { + +/// @brief Adapts retained oneMKL PARDISO state behind LinearSolver. +class MklPardisoSolver final : public LinearSolver { + public: + /// @brief Constructs an empty, unfactorized PARDISO adapter. + MklPardisoSolver(); + + /// @brief Releases retained PARDISO backend state. + ~MklPardisoSolver() override; + + /// @brief Validates and factorizes a symmetric free-equation matrix. + Status Factorize(const SparseMatrix& matrix) override; + + /// @brief Substitutes one right-hand side without refactorization. + Status Solve(const Vector& rhs, Vector& solution) const override; + + private: + class Impl; + std::unique_ptr impl_; +}; + +} // namespace fesa + +#endif // FESA_SOLVERS_LINEAR_MKL_PARDISO_SOLVER_H_ diff --git a/include/fesa/solvers/linear/mkl_pardiso_solver.hpp b/include/fesa/solvers/linear/mkl_pardiso_solver.hpp deleted file mode 100644 index f0d1eee..0000000 --- a/include/fesa/solvers/linear/mkl_pardiso_solver.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "fesa/solvers/linear/linear_solver.hpp" - -#include - -namespace fesa { - -// Keeps every oneMKL type and the retained factorization in the private Impl. -class MklPardisoSolver final : public LinearSolver { -public: - MklPardisoSolver(); - ~MklPardisoSolver() override; - - Status factorize(const SparseMatrix& matrix) override; - Status solve(const Vector& rhs, Vector& solution) const override; - -private: - class Impl; - std::unique_ptr impl_; -}; - -} // namespace fesa diff --git a/src/fesa/analysis/analysis_model.cpp b/src/fesa/analysis/analysis_model.cpp index b4a22f3..ca8bbb2 100644 --- a/src/fesa/analysis/analysis_model.cpp +++ b/src/fesa/analysis/analysis_model.cpp @@ -7,9 +7,9 @@ namespace fesa { Result AnalysisModel::create(const Domain& domain) { if (domain.steps().empty()) { - return Result::failure(Status::failure( - FailureCategory::input, - {{Severity::error, + return Result::Failure(Status::Failure( + FailureCategory::kInput, + {{Severity::kError, "invalid-model-cardinality", {domain.sourcePath(), 0U}, "STEP", @@ -18,16 +18,16 @@ Result AnalysisModel::create(const Domain& domain) { } if (domain.steps().size() > 1U) { const auto& secondStep = domain.steps()[1]; - return Result::failure(Status::failure( - FailureCategory::input, - {{Severity::error, + return Result::Failure(Status::Failure( + FailureCategory::kInput, + {{Severity::kError, "unsupported-multiple-step", secondStep.location, "STEP", secondStep.name, "AnalysisModel does not support multiple steps."}})); } - return Result::success(AnalysisModel{domain}); + return Result::Success(AnalysisModel{domain}); } const Domain& AnalysisModel::domain() const noexcept { diff --git a/src/fesa/analysis/analysis_state.cpp b/src/fesa/analysis/analysis_state.cpp index f1e5a7b..c3f5f5c 100644 --- a/src/fesa/analysis/analysis_state.cpp +++ b/src/fesa/analysis/analysis_state.cpp @@ -16,9 +16,9 @@ Status shellCandidateFailure( const std::string& code, const std::string& identity, const std::string& message) { - return Status::failure( - FailureCategory::model, - {{Severity::error, + return Status::Failure( + FailureCategory::kModel, + {{Severity::kError, code, {}, "ANALYSIS_STATE", @@ -224,7 +224,7 @@ Status AnalysisState::commitShellResults( physicalStrainEnergy_ = candidate.physicalStrainEnergy; equilibrium_ = candidate.equilibrium; verificationMetrics_ = candidate.verificationMetrics; - return Status::ok(); + return Status::Ok(); } const std::vector& AnalysisState::shellResults() const noexcept { diff --git a/src/fesa/analysis/linear_static_analysis.cpp b/src/fesa/analysis/linear_static_analysis.cpp index 4c877e9..8b9158d 100644 --- a/src/fesa/analysis/linear_static_analysis.cpp +++ b/src/fesa/analysis/linear_static_analysis.cpp @@ -7,7 +7,7 @@ #include "fesa/io/abaqus/input_reader.hpp" #include "fesa/results/result_recovery.hpp" #include "fesa/results/results_writer.hpp" -#include "fesa/solvers/linear/linear_solver.hpp" +#include "fesa/solvers/linear/linear_solver.h" #include @@ -15,31 +15,31 @@ namespace fesa { Status Analysis::run(const AnalysisRequest& request) { Status status = initialize(request); - if (!status.isOk()) { + if (!status.IsOk()) { return status; } status = buildAnalysisModel(); - if (!status.isOk()) { + if (!status.IsOk()) { return status; } status = buildDofMapAndSparsePattern(); - if (!status.isOk()) { + if (!status.IsOk()) { return status; } status = assembleAndPartitionStiffness(); - if (!status.isOk()) { + if (!status.IsOk()) { return status; } status = factorize(); - if (!status.isOk()) { + if (!status.IsOk()) { return status; } status = assembleLoadsAndEffectiveRhs(); - if (!status.isOk()) { + if (!status.IsOk()) { return status; } status = substituteAndReconstruct(); - if (!status.isOk()) { + if (!status.IsOk()) { return status; } return recoverAndWriteResults(); @@ -67,100 +67,100 @@ Status LinearStaticAnalysis::initialize(const AnalysisRequest& request) { request_ = request; const auto parsed = AbaqusInputReader{}.read(request_.inputPath); - if (!parsed.hasValue()) { - return parsed.status(); + if (!parsed.HasValue()) { + return parsed.GetStatus(); } - auto domain = AbaqusDomainMapper{}.map(parsed.value()); - if (!domain.hasValue()) { - return domain.status(); + auto domain = AbaqusDomainMapper{}.map(parsed.Value()); + if (!domain.HasValue()) { + return domain.GetStatus(); } - domain_ = std::make_unique(std::move(domain.value())); + domain_ = std::make_unique(std::move(domain.Value())); diagnostics_ = domain_->warnings(); - sortDiagnostics(diagnostics_); - return Status::ok(); + SortDiagnostics(diagnostics_); + return Status::Ok(); } Status LinearStaticAnalysis::buildAnalysisModel() { auto model = AnalysisModel::create(*domain_); - if (!model.hasValue()) { - return model.status(); + if (!model.HasValue()) { + return model.GetStatus(); } - model_ = std::make_unique(std::move(model.value())); - return Status::ok(); + model_ = std::make_unique(std::move(model.Value())); + return Status::Ok(); } Status LinearStaticAnalysis::buildDofMapAndSparsePattern() { auto dofs = DofManager::create(*model_); - if (!dofs.hasValue()) { - return dofs.status(); + if (!dofs.HasValue()) { + return dofs.GetStatus(); } - dofs_ = std::make_unique(std::move(dofs.value())); + dofs_ = std::make_unique(std::move(dofs.Value())); state_ = std::make_unique( AnalysisState::create(*dofs_, {"Step-1", 0U})); - return Status::ok(); + return Status::Ok(); } Status LinearStaticAnalysis::assembleAndPartitionStiffness() { auto stiffness = SparseAssembler::assembleStiffness( *model_, *dofs_, parallelFor_); - if (!stiffness.hasValue()) { - return stiffness.status(); + if (!stiffness.HasValue()) { + return stiffness.GetStatus(); } fullStiffness_ = - std::make_unique(std::move(stiffness.value())); + std::make_unique(std::move(stiffness.Value())); auto partitioned = EssentialConstraints::partition( *fullStiffness_, *dofs_); - if (!partitioned.hasValue()) { - return partitioned.status(); + if (!partitioned.HasValue()) { + return partitioned.GetStatus(); } partitionedStiffness_ = std::make_unique( - std::move(partitioned.value())); - return Status::ok(); + std::move(partitioned.Value())); + return Status::Ok(); } Status LinearStaticAnalysis::factorize() { // This call intentionally precedes all load assembly in Analysis::run. - return linearSolver_.factorize(partitionedStiffness_->kff); + return linearSolver_.Factorize(partitionedStiffness_->kff); } Status LinearStaticAnalysis::assembleLoadsAndEffectiveRhs() { auto fullLoad = LoadAssembler::assembleFullNodalLoad(*model_, *dofs_); - if (!fullLoad.hasValue()) { - return fullLoad.status(); + if (!fullLoad.HasValue()) { + return fullLoad.GetStatus(); } - state_->externalForce() = std::move(fullLoad.value()); + state_->externalForce() = std::move(fullLoad.Value()); auto rhs = LoadAssembler::effectiveFreeRhs( state_->externalForce(), partitionedStiffness_->kfc, dofs_->prescribedValues(), *dofs_); - if (!rhs.hasValue()) { - return rhs.status(); + if (!rhs.HasValue()) { + return rhs.GetStatus(); } - effectiveRhs_ = std::make_unique(std::move(rhs.value())); - return Status::ok(); + effectiveRhs_ = std::make_unique(std::move(rhs.Value())); + return Status::Ok(); } Status LinearStaticAnalysis::substituteAndReconstruct() { Vector freeDisplacement{dofs_->freeDofCount()}; const Status solveStatus = - linearSolver_.solve(*effectiveRhs_, freeDisplacement); - if (!solveStatus.isOk()) { + linearSolver_.Solve(*effectiveRhs_, freeDisplacement); + if (!solveStatus.IsOk()) { return solveStatus; } state_->displacement() = EssentialConstraints::reconstructFull( freeDisplacement, dofs_->prescribedValues(), *dofs_); - return Status::ok(); + return Status::Ok(); } Status LinearStaticAnalysis::recoverAndWriteResults() { const Status recoveryStatus = ResultRecovery::recover( *model_, *dofs_, *fullStiffness_, *state_); - if (!recoveryStatus.isOk()) { + if (!recoveryStatus.IsOk()) { return recoveryStatus; } return resultsWriter_.write( diff --git a/src/fesa/app/fesa_application.cpp b/src/fesa/app/fesa_application.cpp index 17a970a..6e0e240 100644 --- a/src/fesa/app/fesa_application.cpp +++ b/src/fesa/app/fesa_application.cpp @@ -2,9 +2,9 @@ #include "fesa/analysis/linear_static_analysis.hpp" #include "fesa/assembly/parallel_for.hpp" -#include "fesa/core/diagnostic.hpp" +#include "fesa/core/diagnostic.h" #include "fesa/io/hdf5/hdf5_results_writer.hpp" -#include "fesa/solvers/linear/mkl_pardiso_solver.hpp" +#include "fesa/solvers/linear/mkl_pardiso_solver.h" #include #include @@ -27,7 +27,7 @@ bool startsWithOption(const std::string& argument) { Diagnostic usageDiagnostic() { return { - Severity::error, + Severity::kError, "cli-usage", {{}, 0U}, "", @@ -36,11 +36,11 @@ Diagnostic usageDiagnostic() { } const char* severityName(const Severity severity) { - return severity == Severity::warning ? "warning" : "error"; + return severity == Severity::kWarning ? "warning" : "error"; } void writeDiagnostics(std::vector diagnostics) { - sortDiagnostics(diagnostics); + SortDiagnostics(diagnostics); for (const auto& diagnostic : diagnostics) { // Stable field labels and tab separators keep empty source fields // explicit without depending on locale-specific formatting. @@ -51,21 +51,21 @@ void writeDiagnostics(std::vector diagnostics) { << diagnostic.location.file.generic_u8string() << '\t' << "line=" << diagnostic.location.line << '\t' << "keyword=" << diagnostic.keyword - << '\t' << "entity_identity=" << diagnostic.entityIdentity + << '\t' << "entity_identity=" << diagnostic.entity_identity << '\t' << "message=" << diagnostic.message << '\n'; } } int exitCodeFor(const Status& status) { - switch (status.failureCategory().value_or(FailureCategory::input)) { - case FailureCategory::input: + switch (status.Category().value_or(FailureCategory::kInput)) { + case FailureCategory::kInput: return kInputExitCode; - case FailureCategory::model: + case FailureCategory::kModel: return kModelExitCode; - case FailureCategory::solver: + case FailureCategory::kSolver: return kSolverExitCode; - case FailureCategory::output: + case FailureCategory::kOutput: return kOutputExitCode; } return kInputExitCode; @@ -102,11 +102,11 @@ int FesaApplication::run(const std::vector& arguments) { LinearStaticAnalysis analysis{ parallelFor, linearSolver, resultsWriter}; const Status status = analysis.run(request); - if (status.isOk()) { + if (status.IsOk()) { return kSuccessExitCode; } - writeDiagnostics(status.diagnostics()); + writeDiagnostics(status.Diagnostics()); return exitCodeFor(status); } diff --git a/src/fesa/assembly/load_assembler.cpp b/src/fesa/assembly/load_assembler.cpp index 3f0bb83..1b37034 100644 --- a/src/fesa/assembly/load_assembler.cpp +++ b/src/fesa/assembly/load_assembler.cpp @@ -25,9 +25,9 @@ Status loadFailure( const std::string& keyword, const std::string& identity, const std::string& message) { - return Status::failure( - FailureCategory::model, - {{Severity::error, code, location, keyword, identity, message}}); + return Status::Failure( + FailureCategory::kModel, + {{Severity::kError, code, location, keyword, identity, message}}); } char asciiLower(const char value) { @@ -74,7 +74,7 @@ Status validateDofOrder( if (fullCount != expectedFullCount || freeDofs.size() != dofs.freeDofCount() || constrainedDofs.size() != dofs.constrainedDofCount() || - dofs.prescribedValues().size() != constrainedDofs.size() || + dofs.prescribedValues().Size() != constrainedDofs.size() || constrainedDofs.size() > fullCount || freeDofs.size() != fullCount - constrainedDofs.size()) { return loadFailure( @@ -139,7 +139,7 @@ Status validateDofOrder( std::to_string(fullCount), "Free and constrained DOFs must partition the full range."); } - return Status::ok(); + return Status::Ok(); } Result> resolveTarget( @@ -156,7 +156,7 @@ Result> resolveTarget( std::int64_t label = 0; if (tryPositiveInteger(load.target, label)) { for (std::size_t index = 0U; index < domain.nodes().size(); ++index) { - if (domain.nodes()[index].sourceId.sourceLabel == label) { + if (domain.nodes()[index].sourceId.source_label == label) { matchingNodes.push_back(static_cast(index)); } } @@ -164,7 +164,7 @@ Result> resolveTarget( if (matchingSets.size() > 1U || matchingNodes.size() > 1U || (!matchingSets.empty() && !matchingNodes.empty())) { - return Result>::failure(loadFailure( + return Result>::Failure(loadFailure( "invalid-load-target", load.location, "CLOAD", @@ -176,7 +176,7 @@ Result> resolveTarget( std::vector seen(domain.nodes().size(), 0U); for (const EntityIndex node : nodes) { if (node >= domain.nodes().size() || seen[node] != 0U) { - return Result>::failure(loadFailure( + return Result>::Failure(loadFailure( "invalid-load-target", load.location, "CLOAD", @@ -185,13 +185,13 @@ Result> resolveTarget( } seen[node] = 1U; } - return Result>::success(nodes); + return Result>::Success(nodes); } if (!matchingNodes.empty()) { - return Result>::success( + return Result>::Success( std::move(matchingNodes)); } - return Result>::failure(loadFailure( + return Result>::Failure(loadFailure( "invalid-load-target", load.location, "CLOAD", @@ -203,7 +203,7 @@ Status validateFiniteVector( const Vector& values, const SourceLocation& location, const std::string& identity) { - for (std::size_t index = 0U; index < values.size(); ++index) { + for (std::size_t index = 0U; index < values.Size(); ++index) { if (!std::isfinite(values[index])) { return loadFailure( "nonfinite-load-value", @@ -213,14 +213,14 @@ Status validateFiniteVector( "Load and prescribed displacement vectors must contain finite values."); } } - return Status::ok(); + return Status::Ok(); } Status validateShellMoments( const Domain& domain, const Vector& fullLoad) { if (domain.shellElements().empty()) { - return Status::ok(); + return Status::Ok(); } std::vector frameByNode( @@ -252,7 +252,7 @@ Status validateShellMoments( "invalid-shell-director", domain.nodes()[node].location, "NODE", - domain.nodes()[node].sourceId.sourceLabelText, + domain.nodes()[node].sourceId.source_label_text, "A loaded shell node must have an approved initial director."); } @@ -273,11 +273,11 @@ Status validateShellMoments( "unsupported-drilling-load", domain.nodes()[node].location, "CLOAD", - domain.nodes()[node].sourceId.sourceLabelText, + domain.nodes()[node].sourceId.source_label_text, "The aggregate nodal moment has an unsupported director-parallel component."); } } - return Status::ok(); + return Status::Ok(); } } // namespace @@ -288,7 +288,7 @@ Result LoadAssembler::assembleFullNodalLoad( const Domain& domain = model.domain(); if (domain.nodes().size() > (std::numeric_limits::max)() / dofsPerNode) { - return Result::failure(loadFailure( + return Result::Failure(loadFailure( "invalid-load-dimensions", {domain.sourcePath(), 0U}, "LOAD_ASSEMBLER", @@ -299,8 +299,8 @@ Result LoadAssembler::assembleFullNodalLoad( domain.nodes().size() * dofsPerNode; const Status dofStatus = validateDofOrder( dofs, expectedFullCount, {domain.sourcePath(), 0U}); - if (!dofStatus.isOk()) { - return Result::failure(dofStatus); + if (!dofStatus.IsOk()) { + return Result::Failure(dofStatus); } for (std::size_t node = 0U; node < domain.nodes().size(); ++node) { for (std::size_t component = 0U; @@ -311,19 +311,19 @@ Result LoadAssembler::assembleFullNodalLoad( static_cast(node), static_cast(component)) != node * dofsPerNode + component) { - return Result::failure(loadFailure( + return Result::Failure(loadFailure( "invalid-load-order", domain.nodes()[node].location, "LOAD_ASSEMBLER", - domain.nodes()[node].sourceId.sourceLabelText, + domain.nodes()[node].sourceId.source_label_text, "DofManager node/component identity must match full-DOF order.")); } } catch (const std::out_of_range&) { - return Result::failure(loadFailure( + return Result::Failure(loadFailure( "invalid-load-dimensions", domain.nodes()[node].location, "LOAD_ASSEMBLER", - domain.nodes()[node].sourceId.sourceLabelText, + domain.nodes()[node].sourceId.source_label_text, "DofManager must provide all six DOFs for every semantic node.")); } } @@ -332,7 +332,7 @@ Result LoadAssembler::assembleFullNodalLoad( const auto& activeLoads = model.activeLoads(); const auto& loads = model.step().loads; if (activeLoads.size() != loads.size()) { - return Result::failure(loadFailure( + return Result::Failure(loadFailure( "invalid-load-order", model.step().location, "CLOAD", @@ -349,7 +349,7 @@ Result LoadAssembler::assembleFullNodalLoad( const EntityIndex loadIndex = activeLoads[sourceOrder]; if (static_cast(loadIndex) != sourceOrder || loadIndex >= loads.size()) { - return Result::failure(loadFailure( + return Result::Failure(loadFailure( "invalid-load-order", model.step().location, "CLOAD", @@ -358,7 +358,7 @@ Result LoadAssembler::assembleFullNodalLoad( } const auto& load = loads[loadIndex]; if (load.dof < 1 || load.dof > static_cast(dofsPerNode)) { - return Result::failure(loadFailure( + return Result::Failure(loadFailure( "invalid-load-dof", load.location, "CLOAD", @@ -366,7 +366,7 @@ Result LoadAssembler::assembleFullNodalLoad( "A nodal load component must be in the range 1 through 6.")); } if (!std::isfinite(load.magnitude)) { - return Result::failure(loadFailure( + return Result::Failure(loadFailure( "nonfinite-load-value", load.location, "CLOAD", @@ -375,15 +375,15 @@ Result LoadAssembler::assembleFullNodalLoad( } auto target = resolveTarget(domain, load); - if (!target.hasValue()) { - return Result::failure(target.status()); + if (!target.HasValue()) { + return Result::Failure(target.GetStatus()); } const auto component = static_cast(load.dof - 1); - for (const EntityIndex node : target.value()) { + for (const EntityIndex node : target.Value()) { const std::size_t fullDof = dofs.fullDof(node, component); const double accumulated = fullLoad[fullDof] + load.magnitude; if (!std::isfinite(accumulated)) { - return Result::failure(loadFailure( + return Result::Failure(loadFailure( "nonfinite-load-accumulation", load.location, "CLOAD", @@ -394,10 +394,10 @@ Result LoadAssembler::assembleFullNodalLoad( } } const Status shellMomentStatus = validateShellMoments(domain, fullLoad); - if (!shellMomentStatus.isOk()) { - return Result::failure(shellMomentStatus); + if (!shellMomentStatus.IsOk()) { + return Result::Failure(shellMomentStatus); } - return Result::success(std::move(fullLoad)); + return Result::Success(std::move(fullLoad)); } Result LoadAssembler::effectiveFreeRhs( @@ -407,46 +407,46 @@ Result LoadAssembler::effectiveFreeRhs( const DofManager& dofs) { const SourceLocation location{{}, 0U}; const Status dofStatus = - validateDofOrder(dofs, fullLoad.size(), location); - if (!dofStatus.isOk()) { - return Result::failure(dofStatus); + validateDofOrder(dofs, fullLoad.Size(), location); + if (!dofStatus.IsOk()) { + return Result::Failure(dofStatus); } - if (kfc.rows() != dofs.freeDofCount() || - kfc.columns() != dofs.constrainedDofCount() || - prescribedValues.size() != dofs.constrainedDofCount()) { - return Result::failure(loadFailure( + if (kfc.Rows() != dofs.freeDofCount() || + kfc.Columns() != dofs.constrainedDofCount() || + prescribedValues.Size() != dofs.constrainedDofCount()) { + return Result::Failure(loadFailure( "invalid-load-dimensions", location, "LOAD_ASSEMBLER", - std::to_string(kfc.rows()) + "x" + - std::to_string(kfc.columns()), + std::to_string(kfc.Rows()) + "x" + + std::to_string(kfc.Columns()), "Kfc rows/columns and prescribed values must match free/constrained order.")); } - const Status matrixStatus = kfc.validate(); - if (!matrixStatus.isOk()) { - return Result::failure(matrixStatus); + const Status matrixStatus = kfc.Validate(); + if (!matrixStatus.IsOk()) { + return Result::Failure(matrixStatus); } const Status loadStatus = validateFiniteVector(fullLoad, location, "full-load"); - if (!loadStatus.isOk()) { - return Result::failure(loadStatus); + if (!loadStatus.IsOk()) { + return Result::Failure(loadStatus); } const Status prescribedStatus = validateFiniteVector( prescribedValues, location, "prescribed-values"); - if (!prescribedStatus.isOk()) { - return Result::failure(prescribedStatus); + if (!prescribedStatus.IsOk()) { + return Result::Failure(prescribedStatus); } - Vector correction{kfc.rows()}; - for (std::size_t row = 0U; row < kfc.rows(); ++row) { + Vector correction{kfc.Rows()}; + for (std::size_t row = 0U; row < kfc.Rows(); ++row) { double sum = 0.0; - for (std::size_t position = kfc.rowOffsets()[row]; - position < kfc.rowOffsets()[row + 1U]; + for (std::size_t position = kfc.RowOffsets()[row]; + position < kfc.RowOffsets()[row + 1U]; ++position) { - const double product = kfc.values()[position] * - prescribedValues[kfc.columnIndices()[position]]; + const double product = kfc.Values()[position] * + prescribedValues[kfc.ColumnIndices()[position]]; if (!std::isfinite(product)) { - return Result::failure(loadFailure( + return Result::Failure(loadFailure( "nonfinite-load-accumulation", location, "LOAD_ASSEMBLER", @@ -455,7 +455,7 @@ Result LoadAssembler::effectiveFreeRhs( } sum += product; if (!std::isfinite(sum)) { - return Result::failure(loadFailure( + return Result::Failure(loadFailure( "nonfinite-load-accumulation", location, "LOAD_ASSEMBLER", @@ -469,10 +469,10 @@ Result LoadAssembler::effectiveFreeRhs( Vector rhs = EssentialConstraints::gatherFree(fullLoad, dofs); // The constrained vector is already in DofManager order, so this is the // approved elimination equation rhs = Ff - Kfc*dc without reordering dc. - for (std::size_t row = 0U; row < rhs.size(); ++row) { + for (std::size_t row = 0U; row < rhs.Size(); ++row) { const double value = rhs[row] - correction[row]; if (!std::isfinite(value)) { - return Result::failure(loadFailure( + return Result::Failure(loadFailure( "nonfinite-load-accumulation", location, "LOAD_ASSEMBLER", @@ -481,7 +481,7 @@ Result LoadAssembler::effectiveFreeRhs( } rhs[row] = value; } - return Result::success(std::move(rhs)); + return Result::Success(std::move(rhs)); } } // namespace fesa diff --git a/src/fesa/assembly/sparse_assembler.cpp b/src/fesa/assembly/sparse_assembler.cpp index 13aa8df..46a43cd 100644 --- a/src/fesa/assembly/sparse_assembler.cpp +++ b/src/fesa/assembly/sparse_assembler.cpp @@ -35,9 +35,9 @@ Result assemblyFailure( const SourceLocation& location, const std::string& identity, const std::string& message) { - return Result::failure(Status::failure( - FailureCategory::model, - {{Severity::error, + return Result::Failure(Status::Failure( + FailureCategory::kModel, + {{Severity::kError, code, location, "*ELEMENT", @@ -112,7 +112,7 @@ Result SparseAssembler::assembleStiffness( return assemblyFailure( "invalid-assembly-element", element.location, - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "Shell element references an entity outside the Domain."); } @@ -126,7 +126,7 @@ Result SparseAssembler::assembleStiffness( return assemblyFailure( "invalid-assembly-scatter", element.location, - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "DofManager does not contain the active shell scatter."); } for (std::size_t nodePosition = 0U; @@ -138,7 +138,7 @@ Result SparseAssembler::assembleStiffness( return assemblyFailure( "invalid-assembly-element", element.location, - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "Shell element requires a valid node and initial director."); } input.nodes[nodePosition] = &domain.nodes()[nodeIndex]; @@ -156,7 +156,7 @@ Result SparseAssembler::assembleStiffness( return assemblyFailure( "invalid-assembly-scatter", element.location, - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "Shell scatter does not match the active model topology."); } } @@ -175,13 +175,13 @@ Result SparseAssembler::assembleStiffness( input.directors, *input.section, *input.material); - if (!shell.hasValue()) { - localFailures[elementOrder] = shell.status(); + if (!shell.HasValue()) { + localFailures[elementOrder] = shell.GetStatus(); return; } - const auto stiffness = shell.value().stiffness(); - if (!stiffness.hasValue()) { - localFailures[elementOrder] = stiffness.status(); + const auto stiffness = shell.Value().stiffness(); + if (!stiffness.HasValue()) { + localFailures[elementOrder] = stiffness.GetStatus(); return; } @@ -197,7 +197,7 @@ Result SparseAssembler::assembleStiffness( buffer[localOrder] = { input.scatter[localRow], input.scatter[localColumn], - stiffness.value().stabilizedGlobal24( + stiffness.Value().stabilizedGlobal24( localRow, localColumn), elementOrder, localOrder}; @@ -209,7 +209,7 @@ Result SparseAssembler::assembleStiffness( elementOrder < localFailures.size(); ++elementOrder) { if (localFailures[elementOrder]) { - return Result::failure( + return Result::Failure( *localFailures[elementOrder]); } } @@ -223,7 +223,7 @@ Result SparseAssembler::assembleStiffness( contributions.insert( contributions.end(), buffer.begin(), buffer.end()); } - return SparseMatrix::fromCoo( + return SparseMatrix::FromCoo( dofs.fullDofCount(), dofs.fullDofCount(), std::move(contributions), @@ -258,7 +258,7 @@ Result SparseAssembler::assembleStiffness( return assemblyFailure( "invalid-assembly-element", element.location, - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "Element references an entity outside the Domain."); } @@ -269,7 +269,7 @@ Result SparseAssembler::assembleStiffness( return assemblyFailure( "invalid-assembly-scatter", element.location, - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "DofManager does not contain the active element scatter."); } for (std::size_t endpoint = 0U; endpoint < 2U; ++endpoint) { @@ -286,7 +286,7 @@ Result SparseAssembler::assembleStiffness( return assemblyFailure( "invalid-assembly-scatter", element.location, - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "Element scatter does not match the active model topology."); } } @@ -307,12 +307,12 @@ Result SparseAssembler::assembleStiffness( domain.nodes()[definition.nodeIndices[1U]], domain.sections()[definition.sectionIndex], domain.materials()[definition.materialIndex]); - if (!beam.hasValue()) { - localFailures[elementOrder] = beam.status(); + if (!beam.HasValue()) { + localFailures[elementOrder] = beam.GetStatus(); return; } - const Matrix stiffness = beam.value().globalStiffness(); + const Matrix stiffness = beam.Value().globalStiffness(); auto& buffer = localBuffers[elementOrder]; const auto& scatter = scatters[elementOrder]; for (std::size_t localRow = 0U; @@ -337,7 +337,7 @@ Result SparseAssembler::assembleStiffness( elementOrder < localFailures.size(); ++elementOrder) { if (localFailures[elementOrder]) { - return Result::failure( + return Result::Failure( *localFailures[elementOrder]); } } @@ -350,7 +350,7 @@ Result SparseAssembler::assembleStiffness( contributions.insert( contributions.end(), buffer.begin(), buffer.end()); } - return SparseMatrix::fromCoo( + return SparseMatrix::FromCoo( dofs.fullDofCount(), dofs.fullDofCount(), std::move(contributions), diff --git a/src/fesa/build_info.cpp b/src/fesa/build_info.cpp index 2cac255..1ff7986 100644 --- a/src/fesa/build_info.cpp +++ b/src/fesa/build_info.cpp @@ -1,10 +1,11 @@ -#include "fesa/build_info.hpp" +#include "fesa/build_info.h" namespace fesa { -std::string_view solverVersion() noexcept { - // Keep this value stable until a reviewed solver release changes the metadata contract. - return "0.1.0"; +std::string_view SolverVersion() noexcept { + // Keep this value stable until a reviewed solver release changes the metadata + // contract. + return "0.1.0"; } -} // namespace fesa +} // namespace fesa diff --git a/src/fesa/constraints/essential_constraints.cpp b/src/fesa/constraints/essential_constraints.cpp index 79bc934..a562ad9 100644 --- a/src/fesa/constraints/essential_constraints.cpp +++ b/src/fesa/constraints/essential_constraints.cpp @@ -16,9 +16,9 @@ Status constraintFailure( const std::string& code, const std::string& identity, const std::string& message) { - return Status::failure( - FailureCategory::model, - {{Severity::error, + return Status::Failure( + FailureCategory::kModel, + {{Severity::kError, code, {{}, 0U}, "ESSENTIAL_CONSTRAINTS", @@ -41,7 +41,7 @@ Status validateDofOrder(const DofManager& dofs) { const auto& constrainedDofs = dofs.constrainedDofs(); if (freeDofs.size() != dofs.freeDofCount() || constrainedDofs.size() != dofs.constrainedDofCount() || - dofs.prescribedValues().size() != constrainedDofs.size() || + dofs.prescribedValues().Size() != constrainedDofs.size() || constrainedDofs.size() > fullCount || freeDofs.size() != fullCount - constrainedDofs.size()) { return constraintFailure( @@ -94,7 +94,7 @@ Status validateDofOrder(const DofManager& dofs) { std::to_string(fullCount), "Free and constrained DOFs must partition the complete full-DOF range."); } - return Status::ok(); + return Status::Ok(); } Result extractBlock( @@ -102,7 +102,7 @@ Result extractBlock( const std::vector& rowDofs, const std::vector& columnDofs) { const std::size_t absent = (std::numeric_limits::max)(); - std::vector localColumn(full.columns(), absent); + std::vector localColumn(full.Columns(), absent); for (std::size_t column = 0U; column < columnDofs.size(); ++column) { localColumn[columnDofs[column]] = column; } @@ -111,16 +111,16 @@ Result extractBlock( pattern.rowOffsets.reserve(rowDofs.size() + 1U); pattern.rowOffsets.push_back(0U); std::vector contributions; - contributions.reserve(full.values().size()); + contributions.reserve(full.Values().size()); for (std::size_t localRow = 0U; localRow < rowDofs.size(); ++localRow) { const std::size_t fullRow = rowDofs[localRow]; - for (std::size_t position = full.rowOffsets()[fullRow]; - position < full.rowOffsets()[fullRow + 1U]; + for (std::size_t position = full.RowOffsets()[fullRow]; + position < full.RowOffsets()[fullRow + 1U]; ++position) { const std::size_t column = - localColumn[full.columnIndices()[position]]; + localColumn[full.ColumnIndices()[position]]; if (column == absent) { continue; } @@ -130,13 +130,13 @@ Result extractBlock( contributions.push_back({ localRow, column, - full.values()[position], + full.Values()[position], localRow, position}); } pattern.rowOffsets.push_back(pattern.columnIndices.size()); } - return SparseMatrix::fromCoo( + return SparseMatrix::FromCoo( rowDofs.size(), columnDofs.size(), std::move(contributions), @@ -144,7 +144,7 @@ Result extractBlock( } void requireDofOrder(const DofManager& dofs) { - if (!validateDofOrder(dofs).isOk()) { + if (!validateDofOrder(dofs).IsOk()) { throw std::invalid_argument{ "DofManager constraint dimensions or order are invalid."}; } @@ -155,53 +155,53 @@ void requireDofOrder(const DofManager& dofs) { Result EssentialConstraints::partition( const SparseMatrix& full, const DofManager& dofs) { - const Status matrixStatus = full.validate(); - if (!matrixStatus.isOk()) { - return Result::failure(matrixStatus); + const Status matrixStatus = full.Validate(); + if (!matrixStatus.IsOk()) { + return Result::Failure(matrixStatus); } - if (full.rows() != full.columns() || - full.rows() != dofs.fullDofCount()) { - return Result::failure(constraintFailure( + if (full.Rows() != full.Columns() || + full.Rows() != dofs.fullDofCount()) { + return Result::Failure(constraintFailure( "invalid-constraint-dimensions", - std::to_string(full.rows()) + "x" + - std::to_string(full.columns()), + std::to_string(full.Rows()) + "x" + + std::to_string(full.Columns()), "Full stiffness must be square and match the DofManager full dimension.")); } const Status dofStatus = validateDofOrder(dofs); - if (!dofStatus.isOk()) { - return Result::failure(dofStatus); + if (!dofStatus.IsOk()) { + return Result::Failure(dofStatus); } auto kff = extractBlock(full, dofs.freeDofs(), dofs.freeDofs()); - if (!kff.hasValue()) { - return Result::failure(kff.status()); + if (!kff.HasValue()) { + return Result::Failure(kff.GetStatus()); } auto kfc = extractBlock(full, dofs.freeDofs(), dofs.constrainedDofs()); - if (!kfc.hasValue()) { - return Result::failure(kfc.status()); + if (!kfc.HasValue()) { + return Result::Failure(kfc.GetStatus()); } auto kcf = extractBlock(full, dofs.constrainedDofs(), dofs.freeDofs()); - if (!kcf.hasValue()) { - return Result::failure(kcf.status()); + if (!kcf.HasValue()) { + return Result::Failure(kcf.GetStatus()); } auto kcc = extractBlock( full, dofs.constrainedDofs(), dofs.constrainedDofs()); - if (!kcc.hasValue()) { - return Result::failure(kcc.status()); + if (!kcc.HasValue()) { + return Result::Failure(kcc.GetStatus()); } - return Result::success({ - std::move(kff.value()), - std::move(kfc.value()), - std::move(kcf.value()), - std::move(kcc.value())}); + return Result::Success({ + std::move(kff.Value()), + std::move(kfc.Value()), + std::move(kcf.Value()), + std::move(kcc.Value())}); } Vector EssentialConstraints::gatherFree( const Vector& full, const DofManager& dofs) { requireDofOrder(dofs); - if (full.size() != dofs.fullDofCount()) { + if (full.Size() != dofs.fullDofCount()) { throw std::invalid_argument{ "Full vector size must match the DofManager full dimension."}; } @@ -218,7 +218,7 @@ Vector EssentialConstraints::gatherConstrained( const Vector& full, const DofManager& dofs) { requireDofOrder(dofs); - if (full.size() != dofs.fullDofCount()) { + if (full.Size() != dofs.fullDofCount()) { throw std::invalid_argument{ "Full vector size must match the DofManager full dimension."}; } @@ -236,8 +236,8 @@ Vector EssentialConstraints::reconstructFull( const Vector& constrainedValues, const DofManager& dofs) { requireDofOrder(dofs); - if (freeValues.size() != dofs.freeDofCount() || - constrainedValues.size() != dofs.constrainedDofCount()) { + if (freeValues.Size() != dofs.freeDofCount() || + constrainedValues.Size() != dofs.constrainedDofCount()) { throw std::invalid_argument{ "Reduced vector sizes must match the DofManager order."}; } diff --git a/src/fesa/core/diagnostic.cpp b/src/fesa/core/diagnostic.cpp index 06e355d..e94c8fb 100644 --- a/src/fesa/core/diagnostic.cpp +++ b/src/fesa/core/diagnostic.cpp @@ -1,30 +1,21 @@ -#include "fesa/core/diagnostic.hpp" +#include "fesa/core/diagnostic.h" #include #include namespace fesa { -void sortDiagnostics(std::vector& diagnostics) { - // stable_sort makes discovery order the final tie-breaker without storing it - // in the externally visible Diagnostic record. - std::stable_sort( - diagnostics.begin(), - diagnostics.end(), - [](const Diagnostic& left, const Diagnostic& right) { - return std::tie( - left.location.file, - left.location.line, - left.keyword, - left.entityIdentity, - left.code) < - std::tie( - right.location.file, - right.location.line, - right.keyword, - right.entityIdentity, - right.code); - }); +void SortDiagnostics(std::vector& diagnostics) { + // stable_sort makes discovery order the final tie-breaker without storing it + // in the externally visible Diagnostic record. + std::stable_sort( + diagnostics.begin(), diagnostics.end(), + [](const Diagnostic& left, const Diagnostic& right) { + return std::tie(left.location.file, left.location.line, left.keyword, + left.entity_identity, left.code) < + std::tie(right.location.file, right.location.line, right.keyword, + right.entity_identity, right.code); + }); } -} // namespace fesa +} // namespace fesa diff --git a/src/fesa/core/status.cpp b/src/fesa/core/status.cpp index 6563352..cc1aff0 100644 --- a/src/fesa/core/status.cpp +++ b/src/fesa/core/status.cpp @@ -1,42 +1,36 @@ -#include "fesa/core/status.hpp" +#include "fesa/core/status.h" #include namespace fesa { -Status Status::ok() { - return Status{true, std::nullopt, {}}; +Status Status::Ok() { return Status{true, std::nullopt, {}}; } + +Status Status::Failure(std::vector diagnostics) { + SortDiagnostics(diagnostics); + return Status{false, std::nullopt, std::move(diagnostics)}; } -Status Status::failure(std::vector diagnostics) { - sortDiagnostics(diagnostics); - return Status{false, std::nullopt, std::move(diagnostics)}; +Status Status::Failure(FailureCategory category, + std::vector diagnostics) { + SortDiagnostics(diagnostics); + return Status{false, category, std::move(diagnostics)}; } -Status Status::failure( - FailureCategory category, std::vector diagnostics) { - sortDiagnostics(diagnostics); - return Status{false, category, std::move(diagnostics)}; +bool Status::IsOk() const noexcept { return is_ok_; } + +std::optional Status::Category() const noexcept { + return category_; } -bool Status::isOk() const noexcept { - return isOk_; +const std::vector& Status::Diagnostics() const noexcept { + return diagnostics_; } -std::optional Status::failureCategory() const noexcept { - return category_; -} - -const std::vector& Status::diagnostics() const noexcept { - return diagnostics_; -} - -Status::Status( - bool isOk, - std::optional category, - std::vector diagnostics) - : isOk_{isOk}, +Status::Status(bool is_ok, std::optional category, + std::vector diagnostics) + : is_ok_{is_ok}, category_{category}, diagnostics_{std::move(diagnostics)} {} -} // namespace fesa +} // namespace fesa diff --git a/src/fesa/elements/euler_beam_3d.cpp b/src/fesa/elements/euler_beam_3d.cpp index be9d216..db2294f 100644 --- a/src/fesa/elements/euler_beam_3d.cpp +++ b/src/fesa/elements/euler_beam_3d.cpp @@ -40,18 +40,18 @@ bool isFinite(const Vector3& value) { } std::string elementIdentity(const Node& firstNode, const Node& secondNode) { - return firstNode.sourceId.instanceName + ":" + - firstNode.sourceId.sourceLabelText + "-" + - secondNode.sourceId.sourceLabelText; + return firstNode.sourceId.instance_name + ":" + + firstNode.sourceId.source_label_text + "-" + + secondNode.sourceId.source_label_text; } Result modelFailure(const std::string& code, const SourceLocation& location, const std::string& identity, const std::string& message) { - return Result::failure(Status::failure( - FailureCategory::model, - {{Severity::error, code, location, "*ELEMENT", identity, message}})); + return Result::Failure(Status::Failure( + FailureCategory::kModel, + {{Severity::kError, code, location, "*ELEMENT", identity, message}})); } Matrix transformation(const std::array& rotation) { @@ -148,8 +148,8 @@ Matrix closedStiffness(double length, double normalizedMatrixError(const Matrix& lhs, const Matrix& rhs) { double maximumDifference = 0.0; double scale = 1.0; - for (std::size_t row = 0; row < lhs.rows(); ++row) { - for (std::size_t column = 0; column < lhs.columns(); ++column) { + for (std::size_t row = 0; row < lhs.Rows(); ++row) { + for (std::size_t column = 0; column < lhs.Columns(); ++column) { const double lhsValue = lhs(row, column); const double rhsValue = rhs(row, column); if (!std::isfinite(lhsValue) || !std::isfinite(rhsValue)) { @@ -181,7 +181,7 @@ std::array generalizedStrain( const Vector& localDisplacement) { std::array strain{}; for (std::size_t component = 0; component < strain.size(); ++component) { - for (std::size_t dof = 0; dof < localDisplacement.size(); ++dof) { + for (std::size_t dof = 0; dof < localDisplacement.Size(); ++dof) { strain[component] += b(component, dof) * localDisplacement[dof]; } } @@ -356,7 +356,7 @@ Result EulerBeam3D::create( ey[0], ey[1], ey[2], ez[0], ez[1], ez[2]}; - return Result::success(EulerBeam3D{ + return Result::Success(EulerBeam3D{ length, material.youngsModulus, shearModulus, @@ -401,7 +401,7 @@ Matrix EulerBeam3D::localStiffness() const { Matrix EulerBeam3D::globalStiffness() const { const Matrix local = localStiffness(); const Matrix transform = transformation(rotation_); - const Matrix localTimesTransform = local.multiply(transform); + const Matrix localTimesTransform = local.Multiply(transform); Matrix global{kElementDofCount, kElementDofCount}; // Kg=T^T*Kl*T while dl=T*dg. for (std::size_t row = 0; row < kElementDofCount; ++row) { @@ -423,7 +423,7 @@ Vector EulerBeam3D::localEquivalentLoad(const ConstantLocalLineLoad& load) const const double jacobian = 0.5 * length_; for (const double xi : gaussPoints) { const Matrix interpolation = kinematicInterpolation(xi, length_); - for (std::size_t dof = 0; dof < equivalent.size(); ++dof) { + for (std::size_t dof = 0; dof < equivalent.Size(); ++dof) { for (std::size_t component = 0; component < components.size(); ++component) { equivalent[dof] += interpolation(component, dof) * components[component] * jacobian; @@ -435,13 +435,13 @@ Vector EulerBeam3D::localEquivalentLoad(const ConstantLocalLineLoad& load) const BeamRecovery EulerBeam3D::recover(const Vector& globalElementDisplacement) const { const Matrix transform = transformation(rotation_); - const Vector localDisplacement = transform.multiply(globalElementDisplacement); + const Vector localDisplacement = transform.Multiply(globalElementDisplacement); const auto diagonal = constitutiveDiagonal( youngsModulus_, shearModulus_, area_, iy_, iz_, torsionalConstant_); BeamRecovery recovery{}; // With parser/CLI distributed loading excluded, Kl*dl is the local outward end action. - const Vector endAction = localStiffness().multiply(localDisplacement); + const Vector endAction = localStiffness().Multiply(localDisplacement); for (std::size_t endpoint = 0; endpoint < 2U; ++endpoint) { for (std::size_t component = 0; component < 6U; ++component) { recovery.equilibriumEndActions[endpoint][component] = diff --git a/src/fesa/elements/mitc4_shell.cpp b/src/fesa/elements/mitc4_shell.cpp index da2f5ed..98d751f 100644 --- a/src/fesa/elements/mitc4_shell.cpp +++ b/src/fesa/elements/mitc4_shell.cpp @@ -126,7 +126,7 @@ std::string elementIdentity(const std::array& nodes) { if (!identity.empty()) { identity += "-"; } - identity += node->sourceId.sourceLabelText; + identity += node->sourceId.source_label_text; } return identity; } @@ -136,9 +136,9 @@ Result modelFailure( const SourceLocation& location, const std::string& identity, std::string message) { - return Result::failure(Status::failure( - FailureCategory::model, - {{Severity::error, + return Result::Failure(Status::Failure( + FailureCategory::kModel, + {{Severity::kError, std::move(code), location, "*ELEMENT", @@ -205,9 +205,9 @@ std::array localEngineeringComponents( } Matrix scaledMatrix(const Matrix& source, double factor) { - Matrix result{source.rows(), source.columns()}; - for (std::size_t row = 0U; row < source.rows(); ++row) { - for (std::size_t column = 0U; column < source.columns(); ++column) { + Matrix result{source.Rows(), source.Columns()}; + for (std::size_t row = 0U; row < source.Rows(); ++row) { + for (std::size_t column = 0U; column < source.Columns(); ++column) { result(row, column) = factor * source(row, column); } } @@ -215,17 +215,17 @@ Matrix scaledMatrix(const Matrix& source, double factor) { } Matrix congruence(const Matrix& local, const Matrix& transformation) { - if (local.rows() != local.columns() || - local.rows() != transformation.rows()) { + if (local.Rows() != local.Columns() || + local.Rows() != transformation.Rows()) { throw std::invalid_argument{"MITC4 congruence dimensions are incompatible."}; } - Matrix result{transformation.columns(), transformation.columns()}; - for (std::size_t row = 0U; row < result.rows(); ++row) { - for (std::size_t column = row; column < result.columns(); ++column) { + Matrix result{transformation.Columns(), transformation.Columns()}; + for (std::size_t row = 0U; row < result.Rows(); ++row) { + for (std::size_t column = row; column < result.Columns(); ++column) { double value = 0.0; - for (std::size_t localRow = 0U; localRow < local.rows(); ++localRow) { + for (std::size_t localRow = 0U; localRow < local.Rows(); ++localRow) { for (std::size_t localColumn = 0U; - localColumn < local.columns(); ++localColumn) { + localColumn < local.Columns(); ++localColumn) { value += transformation(localRow, row) * local(localRow, localColumn) * transformation(localColumn, column); @@ -239,8 +239,8 @@ Matrix congruence(const Matrix& local, const Matrix& transformation) { } bool isFinite(const Matrix& matrix) { - for (std::size_t row = 0U; row < matrix.rows(); ++row) { - for (std::size_t column = 0U; column < matrix.columns(); ++column) { + for (std::size_t row = 0U; row < matrix.Rows(); ++row) { + for (std::size_t column = 0U; column < matrix.Columns(); ++column) { if (!std::isfinite(matrix(row, column))) { return false; } @@ -250,7 +250,7 @@ bool isFinite(const Matrix& matrix) { } bool isFinite(const Vector& vector) { - for (std::size_t index = 0U; index < vector.size(); ++index) { + for (std::size_t index = 0U; index < vector.Size(); ++index) { if (!std::isfinite(vector[index])) { return false; } @@ -262,9 +262,9 @@ Result stiffnessFailure( const SourceLocation& location, const std::string& identity, std::string message) { - return Result::failure(Status::failure( - FailureCategory::model, - {{Severity::error, + return Result::Failure(Status::Failure( + FailureCategory::kModel, + {{Severity::kError, "invalid-shell-stiffness", location, "*ELEMENT", @@ -276,9 +276,9 @@ Result recoveryFailure( const SourceLocation& location, const std::string& identity, std::string message) { - return Result::failure(Status::failure( - FailureCategory::model, - {{Severity::error, + return Result::Failure(Status::Failure( + FailureCategory::kModel, + {{Severity::kError, "invalid-shell-recovery", location, "*ELEMENT", @@ -430,7 +430,7 @@ Result Mitc4Shell::create( } } - return Result::success(std::move(shell)); + return Result::Success(std::move(shell)); } Mitc4ShapeFunctions Mitc4Shell::shapeFunctions( @@ -688,7 +688,7 @@ Result Mitc4Shell::stiffness() const { "MITC4 transformed stiffness must contain only finite values."); } - return Result::success(Mitc4Stiffness{ + return Result::Success(Mitc4Stiffness{ std::move(physicalLocal), std::move(physicalGlobal), std::move(drillingGlobal), @@ -698,7 +698,7 @@ Result Mitc4Shell::stiffness() const { Result Mitc4Shell::recoverPhysical( const Vector& globalElementDisplacement24) const { - if (globalElementDisplacement24.size() != kGlobalDofCount) { + if (globalElementDisplacement24.Size() != kGlobalDofCount) { return recoveryFailure( sourceLocation_, identity_, "MITC4 physical recovery requires exactly 24 global element DOFs."); @@ -710,7 +710,7 @@ Result Mitc4Shell::recoverPhysical( } const Vector physicalDisplacement = - physicalTransformation20().multiply(globalElementDisplacement24); + physicalTransformation20().Multiply(globalElementDisplacement24); const Matrix tyingSamples = covariantTyingShearSamples20(); const Matrix constitutive = materialConstitutive5(); const Matrix planeStress = planeStressConstitutive(); @@ -742,8 +742,8 @@ Result Mitc4Shell::recoverPhysical( const Matrix strainMatrix = strainDisplacement( point.naturalCoordinates[0], point.naturalCoordinates[1], zeta, &tyingSamples); - const Vector strain = strainMatrix.multiply(physicalDisplacement); - const Vector stress = constitutive.multiply(strain); + const Vector strain = strainMatrix.Multiply(physicalDisplacement); + const Vector stress = constitutive.Multiply(strain); GeometryData geometry{}; if (!evaluateGeometry( point.naturalCoordinates[0], point.naturalCoordinates[1], @@ -769,7 +769,7 @@ Result Mitc4Shell::recoverPhysical( 0.5 * thickness_ * stress[3U + component]; } recovery.strainEnergy += - 0.5 * strain.dot(stress) * geometry.jacobian; + 0.5 * strain.Dot(stress) * geometry.jacobian; } for (std::size_t position = 0U; @@ -785,12 +785,12 @@ Result Mitc4Shell::recoverPhysical( const Vector strain = strainDisplacement( point.naturalCoordinates[0], point.naturalCoordinates[1], sectionPositions[position], &tyingSamples) - .multiply(physicalDisplacement); + .Multiply(physicalDisplacement); Vector inPlaneStrain{3U}; for (std::size_t component = 0U; component < 3U; ++component) { inPlaneStrain[component] = strain[component]; } - const Vector stress = planeStress.multiply(inPlaneStrain); + const Vector stress = planeStress.Multiply(inPlaneStrain); for (std::size_t component = 0U; component < 3U; ++component) { point.inPlaneStress[position][component] = stress[component]; } @@ -819,7 +819,7 @@ Result Mitc4Shell::recoverPhysical( } } - return Result::success(std::move(recovery)); + return Result::Success(std::move(recovery)); } Mitc4Shell::Mitc4Shell( diff --git a/src/fesa/fem/dof_manager.cpp b/src/fesa/fem/dof_manager.cpp index 1e6a555..4a54a28 100644 --- a/src/fesa/fem/dof_manager.cpp +++ b/src/fesa/fem/dof_manager.cpp @@ -46,7 +46,7 @@ std::vector expandBoundaryTarget( std::int64_t sourceLabel = 0; if (tryPositiveInteger(boundary.target, sourceLabel)) { for (std::size_t node = 0U; node < domain.nodes().size(); ++node) { - if (domain.nodes()[node].sourceId.sourceLabel == sourceLabel) { + if (domain.nodes()[node].sourceId.source_label == sourceLabel) { return {static_cast(node)}; } } @@ -111,9 +111,9 @@ Result DofManager::create(const AnalysisModel& model) { static_cast(component - 1); auto& prescribed = prescribedByFullDof[fullDof]; if (prescribed && *prescribed != boundary.value) { - return Result::failure(Status::failure( - FailureCategory::input, - {{Severity::error, + return Result::Failure(Status::Failure( + FailureCategory::kInput, + {{Severity::kError, "conflicting-boundary-condition", boundary.location, "BOUNDARY", @@ -188,7 +188,7 @@ Result DofManager::create(const AnalysisModel& model) { model.activeElements(), elementScatters, shellElementScatters); - return Result::success(DofManager{ + return Result::Success(DofManager{ fullCount, std::move(freeEquations), std::move(elementScatters), diff --git a/src/fesa/io/abaqus/domain_mapper.cpp b/src/fesa/io/abaqus/domain_mapper.cpp index c6aa07d..54ed37b 100644 --- a/src/fesa/io/abaqus/domain_mapper.cpp +++ b/src/fesa/io/abaqus/domain_mapper.cpp @@ -147,10 +147,10 @@ public: finalizeModel(); } if (failure_) { - return Result::failure(Status::failure( + return Result::Failure(Status::Failure( failure_->category, {std::move(failure_->diagnostic)})); } - sortDiagnostics(definition_.warnings); + SortDiagnostics(definition_.warnings); return Domain::create(std::move(definition_)); } @@ -177,7 +177,7 @@ private: if (!failure_) { failure_ = MappingFailure{ category, - {Severity::error, + {Severity::kError, std::move(code), location, std::move(keyword), @@ -194,7 +194,7 @@ private: std::string entityIdentity, std::string message) { return fail( - FailureCategory::input, + FailureCategory::kInput, std::move(code), location, std::move(keyword), @@ -209,7 +209,7 @@ private: std::string entityIdentity, std::string message) { return fail( - FailureCategory::model, + FailureCategory::kModel, std::move(code), location, std::move(keyword), @@ -1677,7 +1677,7 @@ private: // One warning per allowlisted keyword keeps no-op provenance stable; // subordinate variable rows remain attached to that keyword record. definition_.warnings.push_back({ - Severity::warning, + Severity::kWarning, "ignored-input-keyword", block.location, block.canonicalName, @@ -1781,15 +1781,15 @@ private: definition_.nodes, definition_.shellElements, definition_.shellSections); - if (!geometry.hasValue()) { - const auto& status = geometry.status(); + if (!geometry.HasValue()) { + const auto& status = geometry.GetStatus(); failure_ = MappingFailure{ - status.failureCategory().value_or(FailureCategory::model), - status.diagnostics().front()}; + status.Category().value_or(FailureCategory::kModel), + status.Diagnostics().front()}; return; } definition_.shellNodeInitialFrames = - std::move(geometry.value().nodalFrames); + std::move(geometry.Value().nodalFrames); } expandAssemblySets(); if (failure_) { @@ -2357,7 +2357,7 @@ private: for (std::size_t index = 0U; index < definition_.nodes.size(); ++index) { - if (definition_.nodes[index].sourceId.sourceLabel == label) { + if (definition_.nodes[index].sourceId.source_label == label) { matchingNodes.push_back(static_cast(index)); } } diff --git a/src/fesa/io/abaqus/input_reader.cpp b/src/fesa/io/abaqus/input_reader.cpp index c932a5e..d3568f6 100644 --- a/src/fesa/io/abaqus/input_reader.cpp +++ b/src/fesa/io/abaqus/input_reader.cpp @@ -86,14 +86,14 @@ Result failure( std::string keyword, std::string message) { Diagnostic diagnostic{ - Severity::error, + Severity::kError, std::move(code), {sourcePath, line}, std::move(keyword), "", std::move(message)}; - return Result::failure(Status::failure( - FailureCategory::input, {std::move(diagnostic)})); + return Result::Failure(Status::Failure( + FailureCategory::kInput, {std::move(diagnostic)})); } } // namespace @@ -210,7 +210,7 @@ Result AbaqusInputReader::read( ++lineNumber; } - return Result::success(std::move(parsed)); + return Result::Success(std::move(parsed)); } } // namespace fesa diff --git a/src/fesa/io/hdf5/hdf5_results_writer.cpp b/src/fesa/io/hdf5/hdf5_results_writer.cpp index 54aea71..47b0c3d 100644 --- a/src/fesa/io/hdf5/hdf5_results_writer.cpp +++ b/src/fesa/io/hdf5/hdf5_results_writer.cpp @@ -4,7 +4,7 @@ #include "fesa/io/hdf5/hdf5_results_writer.hpp" #include "fesa/analysis/analysis_model.hpp" -#include "fesa/build_info.hpp" +#include "fesa/build_info.h" #include "fesa/fem/dof_manager.hpp" #include @@ -153,9 +153,9 @@ hid_t requireHdf5Id(const hid_t result, const char* message) { } Status outputFailure(const std::string& code, const std::string& message) { - return Status::failure( - FailureCategory::output, - {{Severity::error, code, {}, "", "", message}}); + return Status::Failure( + FailureCategory::kOutput, + {{Severity::kError, code, {}, "", "", message}}); } bool isFinite(const std::array& values) { @@ -217,9 +217,9 @@ bool isValidUtf8(const std::string& value) { } bool sameIdentity(const SourceEntityId& left, const SourceEntityId& right) { - return left.instanceName == right.instanceName && - left.sourceLabel == right.sourceLabel && - left.sourceLabelText == right.sourceLabelText; + return left.instance_name == right.instance_name && + left.source_label == right.source_label && + left.source_label_text == right.source_label_text; } using AxisSet = std::array; @@ -346,10 +346,10 @@ Status validateShellWriterInput( for (const auto& element : domain.shellElements()) { if ((element.sourceType != ShellSourceElementType::s4 && element.sourceType != ShellSourceElementType::s4r) || - element.sourceId.sourceLabel <= 0 || - element.sourceId.sourceLabelText.empty() || - !isValidUtf8(element.sourceId.instanceName) || - !isValidUtf8(element.sourceId.sourceLabelText) || + element.sourceId.source_label <= 0 || + element.sourceId.source_label_text.empty() || + !isValidUtf8(element.sourceId.instance_name) || + !isValidUtf8(element.sourceId.source_label_text) || element.materialIndex >= domain.materials().size() || element.sectionIndex >= domain.shellSections().size() || domain.shellSections()[element.sectionIndex].materialIndex != @@ -444,7 +444,7 @@ Status validateShellWriterInput( "invalid-result-rows", "Shell energy, equilibrium, and verification metrics must be finite."); } - return Status::ok(); + return Status::Ok(); } Status validateWriterInput( @@ -467,7 +467,7 @@ Status validateWriterInput( const bool shell = isShellDomain(domain); if (shell) { const Status shellValidation = validateShellWriterInput(domain, state); - if (!shellValidation.isOk()) { + if (!shellValidation.IsOk()) { return shellValidation; } } else if (!state.shellResults().empty()) { @@ -488,12 +488,12 @@ Status validateWriterInput( &state.residual(), &state.reaction()}; for (const Vector* vector : vectors) { - if (vector->size() != fullDofCount) { + if (vector->Size() != fullDofCount) { return outputFailure( "invalid-result-state", "Every V0 analysis vector must have node_count*6 values."); } - for (std::size_t index = 0U; index < vector->size(); ++index) { + for (std::size_t index = 0U; index < vector->Size(); ++index) { if (!std::isfinite((*vector)[index])) { return outputFailure( "invalid-result-state", @@ -509,10 +509,10 @@ Status validateWriterInput( "Source path and UTF-8 content identity are required."); } for (const Node& node : domain.nodes()) { - if (node.sourceId.sourceLabel <= 0 || - node.sourceId.sourceLabelText.empty() || - !isValidUtf8(node.sourceId.instanceName) || - !isValidUtf8(node.sourceId.sourceLabelText) || + if (node.sourceId.source_label <= 0 || + node.sourceId.source_label_text.empty() || + !isValidUtf8(node.sourceId.instance_name) || + !isValidUtf8(node.sourceId.source_label_text) || !isFinite(node.coordinates)) { return outputFailure( "invalid-result-identity", @@ -524,10 +524,10 @@ Status validateWriterInput( modelData.beamLocalAxes.reserve(domain.elements().size()); for (const EulerBeam3DDefinition& element : domain.elements()) { AxisSet axes{}; - if (element.sourceId.sourceLabel <= 0 || - element.sourceId.sourceLabelText.empty() || - !isValidUtf8(element.sourceId.instanceName) || - !isValidUtf8(element.sourceId.sourceLabelText) || + if (element.sourceId.source_label <= 0 || + element.sourceId.source_label_text.empty() || + !isValidUtf8(element.sourceId.instance_name) || + !isValidUtf8(element.sourceId.source_label_text) || element.nodeIndices[0U] == element.nodeIndices[1U] || element.materialIndex >= domain.materials().size() || !computeLocalAxes(domain, element, axes)) { @@ -626,7 +626,7 @@ Status validateWriterInput( for (const Diagnostic& diagnostic : diagnostics) { if (!isValidUtf8(diagnostic.code) || !isValidUtf8(diagnostic.keyword) || - !isValidUtf8(diagnostic.entityIdentity) || + !isValidUtf8(diagnostic.entity_identity) || !isValidUtf8(diagnostic.message)) { return outputFailure( "invalid-result-diagnostic", @@ -636,23 +636,23 @@ Status validateWriterInput( auto analysisModelResult = AnalysisModel::create(domain); - if (!analysisModelResult.hasValue()) { + if (!analysisModelResult.HasValue()) { return outputFailure( "invalid-result-state", "The HDF5 writer could not reconstruct the active model view."); } const AnalysisModel analysisModel = - std::move(analysisModelResult.value()); + std::move(analysisModelResult.Value()); auto dofResult = DofManager::create(analysisModel); - if (!dofResult.hasValue()) { + if (!dofResult.HasValue()) { return outputFailure( "invalid-result-state", "The HDF5 writer could not reconstruct stable constraint identity."); } - const DofManager dofs = std::move(dofResult.value()); + const DofManager dofs = std::move(dofResult.Value()); modelData.constraintMask.assign(fullDofCount, 0U); modelData.prescribedDisplacement.assign(fullDofCount, 0.0); - if (dofs.constrainedDofs().size() != dofs.prescribedValues().size()) { + if (dofs.constrainedDofs().size() != dofs.prescribedValues().Size()) { return outputFailure( "invalid-result-state", "Constraint identities and prescribed values have inconsistent sizes."); @@ -670,7 +670,7 @@ Status validateWriterInput( modelData.constraintMask[fullDof] = 1U; modelData.prescribedDisplacement[fullDof] = prescribed; } - return Status::ok(); + return Status::Ok(); } std::string normalizedPathString(const std::filesystem::path& path) { @@ -948,7 +948,7 @@ void writeMetadata(const hid_t file, const Domain& domain) { auto metadata = createGroup(file, "/metadata"); writeUint64Attribute(metadata.get(), "schema_version", 0U); writeStringAttribute( - metadata.get(), "solver_version", std::string{solverVersion()}); + metadata.get(), "solver_version", std::string{SolverVersion()}); writeStringAttribute( metadata.get(), "source_input_identity", sourceInputIdentity(domain)); writeStringAttribute( @@ -987,8 +987,8 @@ void writeNodes(const hid_t file, const Domain& domain) { const Node& node = domain.nodes()[index]; rows.push_back({ static_cast(index), - node.sourceId.instanceName.c_str(), - node.sourceId.sourceLabelText.c_str(), + node.sourceId.instance_name.c_str(), + node.sourceId.source_label_text.c_str(), {node.coordinates[0U], node.coordinates[1U], node.coordinates[2U]}}); } @@ -1062,8 +1062,8 @@ void writeBeamElements( const auto& element = domain.elements()[index]; ElementWriteRow row{ static_cast(index), - element.sourceId.instanceName.c_str(), - element.sourceId.sourceLabelText.c_str(), + element.sourceId.instance_name.c_str(), + element.sourceId.source_label_text.c_str(), {static_cast(element.nodeIndices[0U]), static_cast(element.nodeIndices[1U])}, {}}; @@ -1146,8 +1146,8 @@ void writeShellElements(const hid_t file, const Domain& domain) { const auto& element = domain.shellElements()[index]; rows.push_back({ static_cast(index), - element.sourceId.instanceName.c_str(), - element.sourceId.sourceLabelText.c_str(), + element.sourceId.instance_name.c_str(), + element.sourceId.source_label_text.c_str(), shellSourceTypeName(element.sourceType), kMitc4InternalFormulation.data(), {static_cast(element.nodeIndices[0U]), @@ -1660,7 +1660,7 @@ void writeShellResultDatasets( void writeDiagnostics( const hid_t file, const std::vector& inputDiagnostics) { std::vector diagnostics = inputDiagnostics; - sortDiagnostics(diagnostics); + SortDiagnostics(diagnostics); std::vector files; files.reserve(diagnostics.size()); for (const auto& diagnostic : diagnostics) { @@ -1671,12 +1671,12 @@ void writeDiagnostics( for (std::size_t index = 0U; index < diagnostics.size(); ++index) { const auto& diagnostic = diagnostics[index]; rows.push_back({ - diagnostic.severity == Severity::warning ? "warning" : "error", + diagnostic.severity == Severity::kWarning ? "warning" : "error", diagnostic.code.c_str(), files[index].c_str(), static_cast(diagnostic.location.line), diagnostic.keyword.c_str(), - diagnostic.entityIdentity.c_str(), + diagnostic.entity_identity.c_str(), diagnostic.message.c_str()}); } @@ -1735,8 +1735,8 @@ void writeResultDatasets( file, std::string{kStepRoot} + "/nodal/displacement", nodalDimensions, - state.displacement().data(), - state.displacement().size(), + state.displacement().Data(), + state.displacement().Size(), "UX,UY,UZ,URX,URY,URZ", "length,length,length,radian,radian,radian", "global-cartesian", @@ -1745,8 +1745,8 @@ void writeResultDatasets( file, std::string{kStepRoot} + "/nodal/reaction", nodalDimensions, - state.reaction().data(), - state.reaction().size(), + state.reaction().Data(), + state.reaction().Size(), "RF1,RF2,RF3,RM1,RM2,RM3", "force,force,force,force*length,force*length,force*length", "global-cartesian", @@ -2253,7 +2253,7 @@ void selfCheckFile( H5Gclose}; requireUint64Attribute(metadata.get(), "schema_version", 0U); requireStringAttribute( - metadata.get(), "solver_version", std::string{solverVersion()}); + metadata.get(), "solver_version", std::string{SolverVersion()}); requireStringAttribute( metadata.get(), "source_input_identity", sourceInputIdentity(domain)); requireStringAttribute( @@ -2572,7 +2572,7 @@ Status Hdf5ResultsWriter::write( try { const Status validation = validateWriterInput( outputPath, domain, state, diagnostics, modelData); - if (!validation.isOk()) { + if (!validation.IsOk()) { return validation; } @@ -2590,7 +2590,7 @@ Status Hdf5ResultsWriter::write( "The checked temporary HDF5 file could not replace the final output."); } cleanup.release(); - return Status::ok(); + return Status::Ok(); } catch (const Hdf5Failure& failure) { return outputFailure("hdf5-write-failure", failure.what()); } catch (const std::exception& failure) { diff --git a/src/fesa/math/matrix.cpp b/src/fesa/math/matrix.cpp index f4d7b2e..82b5849 100644 --- a/src/fesa/math/matrix.cpp +++ b/src/fesa/math/matrix.cpp @@ -1,4 +1,4 @@ -#include "fesa/math/matrix.hpp" +#include "fesa/math/matrix.h" #include @@ -9,151 +9,140 @@ namespace fesa { namespace { -std::size_t checkedStorageSize(const std::size_t rows, const std::size_t columns) { - // Reject shape multiplication overflow before logical dimensions and storage diverge. - if (columns != 0 && - rows > (std::numeric_limits::max)() / columns) { - throw std::length_error{"Dense matrix dimensions exceed the storage size range."}; - } - return rows * columns; +/// @brief Rejects shape overflow before logical dimensions diverge from +/// storage. +std::size_t CheckedStorageSize(const std::size_t rows, + const std::size_t columns) { + if (columns != 0 && + rows > (std::numeric_limits::max)() / columns) { + throw std::length_error{ + "Dense matrix dimensions exceed the storage size range."}; + } + return rows * columns; } -MKL_INT toMklSize(const std::size_t size) { - if (size > static_cast((std::numeric_limits::max)())) { - throw std::length_error{"Dense matrix dimension exceeds the MKL integer range."}; - } - return static_cast(size); +/// @brief Converts a dense matrix dimension to the private MKL integer +/// contract. +MKL_INT ToMklSize(const std::size_t size) { + if (size > static_cast((std::numeric_limits::max)())) { + throw std::length_error{ + "Dense matrix dimension exceeds the MKL integer range."}; + } + return static_cast(size); } -void copyValues(const std::vector& source, std::vector& destination) { - if (source.empty()) { - return; - } +/// @brief Copies owned values without exposing the dense backend publicly. +void CopyValues(const std::vector& source, + std::vector& destination) { + if (source.empty()) { + return; + } - cblas_dcopy(toMklSize(source.size()), source.data(), 1, destination.data(), 1); + cblas_dcopy(ToMklSize(source.size()), source.data(), 1, destination.data(), + 1); } -} // namespace +} // namespace -Matrix::Matrix( - const std::size_t rows, - const std::size_t columns, - const double value) - : rows_(rows), columns_(columns), values_(checkedStorageSize(rows, columns), value) {} +Matrix::Matrix(const std::size_t rows, const std::size_t columns, + const double value) + : rows_(rows), + columns_(columns), + values_(CheckedStorageSize(rows, columns), value) {} Matrix::Matrix(const Matrix& other) - : rows_(other.rows_), columns_(other.columns_), values_(other.values_.size()) { - copyValues(other.values_, values_); + : rows_(other.rows_), + columns_(other.columns_), + values_(other.values_.size()) { + CopyValues(other.values_, values_); } Matrix::Matrix(Matrix&& other) noexcept : rows_(other.rows_), columns_(other.columns_), values_(std::move(other.values_)) { - other.rows_ = 0; - other.columns_ = 0; - other.values_.clear(); + other.rows_ = 0; + other.columns_ = 0; + other.values_.clear(); } Matrix& Matrix::operator=(const Matrix& other) { - if (this != &other) { - std::vector copied(other.values_.size()); - copyValues(other.values_, copied); - rows_ = other.rows_; - columns_ = other.columns_; - values_.swap(copied); - } - return *this; + if (this != &other) { + std::vector copied(other.values_.size()); + CopyValues(other.values_, copied); + rows_ = other.rows_; + columns_ = other.columns_; + values_.swap(copied); + } + return *this; } Matrix& Matrix::operator=(Matrix&& other) noexcept { - if (this != &other) { - rows_ = other.rows_; - columns_ = other.columns_; - values_ = std::move(other.values_); - other.rows_ = 0; - other.columns_ = 0; - other.values_.clear(); - } - return *this; + if (this != &other) { + rows_ = other.rows_; + columns_ = other.columns_; + values_ = std::move(other.values_); + other.rows_ = 0; + other.columns_ = 0; + other.values_.clear(); + } + return *this; } -std::size_t Matrix::rows() const noexcept { - return rows_; -} +std::size_t Matrix::Rows() const noexcept { return rows_; } -std::size_t Matrix::columns() const noexcept { - return columns_; -} +std::size_t Matrix::Columns() const noexcept { return columns_; } double& Matrix::operator()(const std::size_t row, const std::size_t column) { - if (row >= rows_ || column >= columns_) { - throw std::out_of_range{"Matrix index is outside its dimensions."}; - } - return values_[row * columns_ + column]; + if (row >= rows_ || column >= columns_) { + throw std::out_of_range{"Matrix index is outside its dimensions."}; + } + return values_[row * columns_ + column]; } -const double& Matrix::operator()(const std::size_t row, const std::size_t column) const { - if (row >= rows_ || column >= columns_) { - throw std::out_of_range{"Matrix index is outside its dimensions."}; - } - return values_[row * columns_ + column]; +const double& Matrix::operator()(const std::size_t row, + const std::size_t column) const { + if (row >= rows_ || column >= columns_) { + throw std::out_of_range{"Matrix index is outside its dimensions."}; + } + return values_[row * columns_ + column]; } -Vector Matrix::multiply(const Vector& rhs) const { - if (columns_ != rhs.size()) { - throw std::invalid_argument{"Matrix-vector multiplication has incompatible dimensions."}; - } +Vector Matrix::Multiply(const Vector& rhs) const { + if (columns_ != rhs.Size()) { + throw std::invalid_argument{ + "Matrix-vector multiplication has incompatible dimensions."}; + } - Vector result{rows_}; - if (rows_ == 0 || columns_ == 0) { - return result; - } - - // The owned layout is row-major, so the leading dimension is the column - // count for the adapter call and remains invisible to public consumers. - cblas_dgemv( - CblasRowMajor, - CblasNoTrans, - toMklSize(rows_), - toMklSize(columns_), - 1.0, - values_.data(), - toMklSize(columns_), - rhs.data(), - 1, - 0.0, - result.data(), - 1); + Vector result{rows_}; + if (rows_ == 0 || columns_ == 0) { return result; + } + + // The owned layout is row-major, so the leading dimension is the column + // count for the adapter call and remains invisible to public consumers. + cblas_dgemv(CblasRowMajor, CblasNoTrans, ToMklSize(rows_), + ToMklSize(columns_), 1.0, values_.data(), ToMklSize(columns_), + rhs.Data(), 1, 0.0, result.Data(), 1); + return result; } -Matrix Matrix::multiply(const Matrix& rhs) const { - if (columns_ != rhs.rows_) { - throw std::invalid_argument{"Matrix multiplication has incompatible dimensions."}; - } +Matrix Matrix::Multiply(const Matrix& rhs) const { + if (columns_ != rhs.rows_) { + throw std::invalid_argument{ + "Matrix multiplication has incompatible dimensions."}; + } - Matrix result{rows_, rhs.columns_}; - if (rows_ == 0 || columns_ == 0 || rhs.columns_ == 0) { - return result; - } - - cblas_dgemm( - CblasRowMajor, - CblasNoTrans, - CblasNoTrans, - toMklSize(rows_), - toMklSize(rhs.columns_), - toMklSize(columns_), - 1.0, - values_.data(), - toMklSize(columns_), - rhs.values_.data(), - toMklSize(rhs.columns_), - 0.0, - result.values_.data(), - toMklSize(rhs.columns_)); + Matrix result{rows_, rhs.columns_}; + if (rows_ == 0 || columns_ == 0 || rhs.columns_ == 0) { return result; + } + + cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, ToMklSize(rows_), + ToMklSize(rhs.columns_), ToMklSize(columns_), 1.0, values_.data(), + ToMklSize(columns_), rhs.values_.data(), ToMklSize(rhs.columns_), + 0.0, result.values_.data(), ToMklSize(rhs.columns_)); + return result; } -} // namespace fesa +} // namespace fesa diff --git a/src/fesa/math/sparse_matrix.cpp b/src/fesa/math/sparse_matrix.cpp index d3fae40..971c379 100644 --- a/src/fesa/math/sparse_matrix.cpp +++ b/src/fesa/math/sparse_matrix.cpp @@ -1,6 +1,4 @@ -#include "fesa/math/sparse_matrix.hpp" - -#include "fesa/fem/dof_manager.hpp" +#include "fesa/math/sparse_matrix.h" #include #include @@ -10,230 +8,193 @@ #include #include +#include "fesa/fem/dof_manager.hpp" + namespace fesa { namespace { -Status sparseFailure( - const std::string& code, - const std::string& identity, - const std::string& message) { - return Status::failure( - FailureCategory::model, - {{Severity::error, - code, - {{}, 0U}, - "SPARSE_MATRIX", - identity, - message}}); +/// @brief Builds a structured sparse-matrix model failure. +Status SparseFailure(const std::string& code, const std::string& identity, + const std::string& message) { + return Status::Failure( + FailureCategory::kModel, + {{Severity::kError, code, {{}, 0U}, "SPARSE_MATRIX", identity, message}}); } -Status validateCsr( - const std::size_t rows, - const std::size_t columns, - const std::vector& rowOffsets, - const std::vector& columnIndices, - const std::vector* const values) { - if (rows == (std::numeric_limits::max)() || - rowOffsets.size() != rows + 1U) { - return sparseFailure( - "invalid-sparse-shape", - "row-offset-count", - "CSR row offsets must contain exactly rows plus one entries."); - } - if (rowOffsets.empty() || rowOffsets.front() != 0U || - rowOffsets.back() != columnIndices.size()) { - return sparseFailure( - "invalid-sparse-pattern", - "row-offset-range", - "CSR row offsets must start at zero and end at the column count."); - } - if (values != nullptr && values->size() != columnIndices.size()) { - return sparseFailure( - "invalid-sparse-shape", - "value-count", - "CSR column and value arrays must have equal sizes."); - } +/// @brief Validates canonical CSR shape, order, index, and finite-value rules. +Status ValidateCsr(const std::size_t rows, const std::size_t columns, + const std::vector& row_offsets, + const std::vector& column_indices, + const std::vector* const values) { + if (rows == (std::numeric_limits::max)() || + row_offsets.size() != rows + 1U) { + return SparseFailure( + "invalid-sparse-shape", "row-offset-count", + "CSR row offsets must contain exactly rows plus one entries."); + } + if (row_offsets.empty() || row_offsets.front() != 0U || + row_offsets.back() != column_indices.size()) { + return SparseFailure( + "invalid-sparse-pattern", "row-offset-range", + "CSR row offsets must start at zero and end at the column count."); + } + if (values != nullptr && values->size() != column_indices.size()) { + return SparseFailure("invalid-sparse-shape", "value-count", + "CSR column and value arrays must have equal sizes."); + } - for (std::size_t row = 0U; row < rows; ++row) { - const std::size_t begin = rowOffsets[row]; - const std::size_t end = rowOffsets[row + 1U]; - if (begin > end || end > columnIndices.size()) { - return sparseFailure( - "invalid-sparse-pattern", - std::to_string(row), - "CSR row offsets must be nondecreasing and remain in range."); - } - for (std::size_t position = begin; position < end; ++position) { - if (columnIndices[position] >= columns) { - return sparseFailure( - "invalid-sparse-index", - std::to_string(position), - "CSR column index is outside the matrix dimensions."); - } - if (position > begin && - columnIndices[position - 1U] >= columnIndices[position]) { - return sparseFailure( - "invalid-sparse-pattern", - std::to_string(row), - "CSR columns must be sorted and unique within each row."); - } - if (values != nullptr && !std::isfinite((*values)[position])) { - return sparseFailure( - "nonfinite-sparse-value", - std::to_string(position), - "CSR values must be finite."); - } - } + for (std::size_t row = 0U; row < rows; ++row) { + const std::size_t begin = row_offsets[row]; + const std::size_t end = row_offsets[row + 1U]; + if (begin > end || end > column_indices.size()) { + return SparseFailure( + "invalid-sparse-pattern", std::to_string(row), + "CSR row offsets must be nondecreasing and remain in range."); } - return Status::ok(); + for (std::size_t position = begin; position < end; ++position) { + if (column_indices[position] >= columns) { + return SparseFailure( + "invalid-sparse-index", std::to_string(position), + "CSR column index is outside the matrix dimensions."); + } + if (position > begin && + column_indices[position - 1U] >= column_indices[position]) { + return SparseFailure( + "invalid-sparse-pattern", std::to_string(row), + "CSR columns must be sorted and unique within each row."); + } + if (values != nullptr && !std::isfinite((*values)[position])) { + return SparseFailure("nonfinite-sparse-value", std::to_string(position), + "CSR values must be finite."); + } + } + } + return Status::Ok(); } -} // namespace +} // namespace -Result SparseMatrix::fromCoo( - const std::size_t rows, - const std::size_t columns, +Result SparseMatrix::FromCoo( + const std::size_t rows, const std::size_t columns, std::vector contributions, - const SparsePattern& expectedPattern) { - const Status patternStatus = validateCsr( - rows, - columns, - expectedPattern.rowOffsets, - expectedPattern.columnIndices, - nullptr); - if (!patternStatus.isOk()) { - return Result::failure(patternStatus); + const SparsePattern& expected_pattern) { + const Status pattern_status = + ValidateCsr(rows, columns, expected_pattern.rowOffsets, + expected_pattern.columnIndices, nullptr); + if (!pattern_status.IsOk()) { + return Result::Failure(pattern_status); + } + + for (const auto& contribution : contributions) { + if (contribution.row >= rows || contribution.column >= columns) { + return Result::Failure(SparseFailure( + "invalid-sparse-index", + std::to_string(contribution.row) + ":" + + std::to_string(contribution.column), + "COO contribution index is outside the matrix dimensions.")); + } + if (!std::isfinite(contribution.value)) { + return Result::Failure( + SparseFailure("nonfinite-sparse-value", + std::to_string(contribution.element_order) + ":" + + std::to_string(contribution.local_order), + "COO contribution values must be finite.")); + } + } + + // The complete tuple fixes duplicate summation order independently of + // worker completion order. stable_sort also preserves exact tuple ties. + std::stable_sort( + contributions.begin(), contributions.end(), + [](const CooContribution& left, const CooContribution& right) { + return std::tie(left.row, left.column, left.element_order, + left.local_order) < std::tie(right.row, right.column, + right.element_order, + right.local_order); + }); + + std::vector values(expected_pattern.columnIndices.size(), 0.0); + for (const auto& contribution : contributions) { + const std::size_t begin = expected_pattern.rowOffsets[contribution.row]; + const std::size_t end = expected_pattern.rowOffsets[contribution.row + 1U]; + const auto first = expected_pattern.columnIndices.begin() + begin; + const auto last = expected_pattern.columnIndices.begin() + end; + const auto found = std::lower_bound(first, last, contribution.column); + if (found == last || *found != contribution.column) { + return Result::Failure(SparseFailure( + "sparse-pattern-mismatch", + std::to_string(contribution.row) + ":" + + std::to_string(contribution.column), + "COO contribution is absent from the expected sparse pattern.")); } - for (const auto& contribution : contributions) { - if (contribution.row >= rows || contribution.column >= columns) { - return Result::failure(sparseFailure( - "invalid-sparse-index", - std::to_string(contribution.row) + ":" + - std::to_string(contribution.column), - "COO contribution index is outside the matrix dimensions.")); - } - if (!std::isfinite(contribution.value)) { - return Result::failure(sparseFailure( - "nonfinite-sparse-value", - std::to_string(contribution.elementOrder) + ":" + - std::to_string(contribution.localOrder), - "COO contribution values must be finite.")); - } + const std::size_t position = static_cast( + std::distance(expected_pattern.columnIndices.begin(), found)); + values[position] += contribution.value; + if (!std::isfinite(values[position])) { + return Result::Failure(SparseFailure( + "nonfinite-sparse-value", + std::to_string(contribution.row) + ":" + + std::to_string(contribution.column), + "Ordered COO duplicate summation produced a nonfinite value.")); } + } - // The complete tuple fixes duplicate summation order independently of - // worker completion order. stable_sort also preserves exact tuple ties. - std::stable_sort( - contributions.begin(), - contributions.end(), - [](const CooContribution& left, const CooContribution& right) { - return std::tie( - left.row, - left.column, - left.elementOrder, - left.localOrder) < - std::tie( - right.row, - right.column, - right.elementOrder, - right.localOrder); - }); + SparseMatrix matrix{rows, columns, expected_pattern.rowOffsets, + expected_pattern.columnIndices, std::move(values)}; + const Status status = matrix.Validate(); + if (!status.IsOk()) { + return Result::Failure(status); + } + return Result::Success(std::move(matrix)); +} - std::vector values(expectedPattern.columnIndices.size(), 0.0); - for (const auto& contribution : contributions) { - const std::size_t begin = expectedPattern.rowOffsets[contribution.row]; - const std::size_t end = expectedPattern.rowOffsets[contribution.row + 1U]; - const auto first = expectedPattern.columnIndices.begin() + begin; - const auto last = expectedPattern.columnIndices.begin() + end; - const auto found = std::lower_bound(first, last, contribution.column); - if (found == last || *found != contribution.column) { - return Result::failure(sparseFailure( - "sparse-pattern-mismatch", - std::to_string(contribution.row) + ":" + - std::to_string(contribution.column), - "COO contribution is absent from the expected sparse pattern.")); - } +std::size_t SparseMatrix::Rows() const noexcept { return rows_; } - const std::size_t position = static_cast( - std::distance(expectedPattern.columnIndices.begin(), found)); - values[position] += contribution.value; - if (!std::isfinite(values[position])) { - return Result::failure(sparseFailure( - "nonfinite-sparse-value", - std::to_string(contribution.row) + ":" + - std::to_string(contribution.column), - "Ordered COO duplicate summation produced a nonfinite value.")); - } +std::size_t SparseMatrix::Columns() const noexcept { return columns_; } + +const std::vector& SparseMatrix::RowOffsets() const noexcept { + return row_offsets_; +} + +const std::vector& SparseMatrix::ColumnIndices() const noexcept { + return column_indices_; +} + +const std::vector& SparseMatrix::Values() const noexcept { + return values_; +} + +Vector SparseMatrix::Multiply(const Vector& rhs) const { + if (columns_ != rhs.Size()) { + throw std::invalid_argument{ + "Sparse matrix-vector multiplication has incompatible dimensions."}; + } + + Vector result{rows_}; + for (std::size_t row = 0U; row < rows_; ++row) { + double value = 0.0; + for (std::size_t position = row_offsets_[row]; + position < row_offsets_[row + 1U]; ++position) { + value += values_[position] * rhs[column_indices_[position]]; } - - SparseMatrix matrix{ - rows, - columns, - expectedPattern.rowOffsets, - expectedPattern.columnIndices, - std::move(values)}; - const Status status = matrix.validate(); - if (!status.isOk()) { - return Result::failure(status); - } - return Result::success(std::move(matrix)); + result[row] = value; + } + return result; } -std::size_t SparseMatrix::rows() const noexcept { - return rows_; +Status SparseMatrix::Validate() const { + return ValidateCsr(rows_, columns_, row_offsets_, column_indices_, &values_); } -std::size_t SparseMatrix::columns() const noexcept { - return columns_; -} - -const std::vector& SparseMatrix::rowOffsets() const noexcept { - return rowOffsets_; -} - -const std::vector& SparseMatrix::columnIndices() const noexcept { - return columnIndices_; -} - -const std::vector& SparseMatrix::values() const noexcept { - return values_; -} - -Vector SparseMatrix::multiply(const Vector& rhs) const { - if (columns_ != rhs.size()) { - throw std::invalid_argument{ - "Sparse matrix-vector multiplication has incompatible dimensions."}; - } - - Vector result{rows_}; - for (std::size_t row = 0U; row < rows_; ++row) { - double value = 0.0; - for (std::size_t position = rowOffsets_[row]; - position < rowOffsets_[row + 1U]; - ++position) { - value += values_[position] * rhs[columnIndices_[position]]; - } - result[row] = value; - } - return result; -} - -Status SparseMatrix::validate() const { - return validateCsr( - rows_, columns_, rowOffsets_, columnIndices_, &values_); -} - -SparseMatrix::SparseMatrix( - const std::size_t rows, - const std::size_t columns, - std::vector rowOffsets, - std::vector columnIndices, - std::vector values) +SparseMatrix::SparseMatrix(const std::size_t rows, const std::size_t columns, + std::vector row_offsets, + std::vector column_indices, + std::vector values) : rows_{rows}, columns_{columns}, - rowOffsets_{std::move(rowOffsets)}, - columnIndices_{std::move(columnIndices)}, + row_offsets_{std::move(row_offsets)}, + column_indices_{std::move(column_indices)}, values_{std::move(values)} {} -} // namespace fesa +} // namespace fesa diff --git a/src/fesa/math/vector.cpp b/src/fesa/math/vector.cpp index 1c9c255..a7e5f5f 100644 --- a/src/fesa/math/vector.cpp +++ b/src/fesa/math/vector.cpp @@ -1,4 +1,4 @@ -#include "fesa/math/vector.hpp" +#include "fesa/math/vector.h" #include @@ -9,111 +9,108 @@ namespace fesa { namespace { -MKL_INT toMklSize(const std::size_t size) { - if (size > static_cast((std::numeric_limits::max)())) { - throw std::length_error{"Dense vector size exceeds the MKL integer range."}; - } - return static_cast(size); +/// @brief Converts a dense vector size to the private MKL integer contract. +MKL_INT ToMklSize(const std::size_t size) { + if (size > static_cast((std::numeric_limits::max)())) { + throw std::length_error{"Dense vector size exceeds the MKL integer range."}; + } + return static_cast(size); } -void copyValues(const std::vector& source, std::vector& destination) { - if (source.empty()) { - return; - } +/// @brief Copies owned values without exposing the dense backend publicly. +void CopyValues(const std::vector& source, + std::vector& destination) { + if (source.empty()) { + return; + } - // Keep the backend operation in this translation unit so public ownership - // remains independent of MKL headers and integer types. - cblas_dcopy(toMklSize(source.size()), source.data(), 1, destination.data(), 1); + // Keep the backend operation in this translation unit so public ownership + // remains independent of MKL headers and integer types. + cblas_dcopy(ToMklSize(source.size()), source.data(), 1, destination.data(), + 1); } -} // namespace +} // namespace Vector::Vector(const std::size_t size, const double value) : values_(size, value) {} -Vector::Vector(const Vector& other) - : values_(other.size()) { - copyValues(other.values_, values_); +Vector::Vector(const Vector& other) : values_(other.Size()) { + CopyValues(other.values_, values_); } -Vector::Vector(Vector&& other) noexcept - : values_(std::move(other.values_)) { - other.values_.clear(); +Vector::Vector(Vector&& other) noexcept : values_(std::move(other.values_)) { + other.values_.clear(); } Vector& Vector::operator=(const Vector& other) { - if (this != &other) { - std::vector copied(other.size()); - copyValues(other.values_, copied); - values_.swap(copied); - } - return *this; + if (this != &other) { + std::vector copied(other.Size()); + CopyValues(other.values_, copied); + values_.swap(copied); + } + return *this; } Vector& Vector::operator=(Vector&& other) noexcept { - if (this != &other) { - values_ = std::move(other.values_); - other.values_.clear(); - } - return *this; + if (this != &other) { + values_ = std::move(other.values_); + other.values_.clear(); + } + return *this; } -std::size_t Vector::size() const noexcept { - return values_.size(); -} +std::size_t Vector::Size() const noexcept { return values_.size(); } -double* Vector::data() noexcept { - return values_.data(); -} +double* Vector::Data() noexcept { return values_.data(); } -const double* Vector::data() const noexcept { - return values_.data(); -} +const double* Vector::Data() const noexcept { return values_.data(); } double& Vector::operator[](const std::size_t index) { - return values_.at(index); + return values_.at(index); } const double& Vector::operator[](const std::size_t index) const { - return values_.at(index); + return values_.at(index); } -double Vector::dot(const Vector& rhs) const { - if (size() != rhs.size()) { - throw std::invalid_argument{"Vector dot product requires equal dimensions."}; - } - if (values_.empty()) { - return 0.0; - } +double Vector::Dot(const Vector& rhs) const { + if (Size() != rhs.Size()) { + throw std::invalid_argument{ + "Vector dot product requires equal dimensions."}; + } + if (values_.empty()) { + return 0.0; + } - return cblas_ddot(toMklSize(size()), data(), 1, rhs.data(), 1); + return cblas_ddot(ToMklSize(Size()), Data(), 1, rhs.Data(), 1); } -double Vector::norm() const { - if (values_.empty()) { - return 0.0; - } +double Vector::Norm() const { + if (values_.empty()) { + return 0.0; + } - return cblas_dnrm2(toMklSize(size()), data(), 1); + return cblas_dnrm2(ToMklSize(Size()), Data(), 1); } -void Vector::scale(const double alpha) { - if (values_.empty()) { - return; - } +void Vector::Scale(const double alpha) { + if (values_.empty()) { + return; + } - cblas_dscal(toMklSize(size()), alpha, data(), 1); + cblas_dscal(ToMklSize(Size()), alpha, Data(), 1); } -void Vector::axpy(const double alpha, const Vector& x) { - if (size() != x.size()) { - throw std::invalid_argument{"Vector axpy requires equal dimensions."}; - } - if (values_.empty()) { - return; - } +void Vector::Axpy(const double alpha, const Vector& x) { + if (Size() != x.Size()) { + throw std::invalid_argument{"Vector axpy requires equal dimensions."}; + } + if (values_.empty()) { + return; + } - cblas_daxpy(toMklSize(size()), alpha, x.data(), 1, data(), 1); + cblas_daxpy(ToMklSize(Size()), alpha, x.Data(), 1, Data(), 1); } -} // namespace fesa +} // namespace fesa diff --git a/src/fesa/model/domain.cpp b/src/fesa/model/domain.cpp index 81dc7ea..d1a2ae1 100644 --- a/src/fesa/model/domain.cpp +++ b/src/fesa/model/domain.cpp @@ -5,7 +5,7 @@ namespace fesa { Result Domain::create(ModelDefinition definition) { - return Result::success(Domain{std::move(definition)}); + return Result::Success(Domain{std::move(definition)}); } const std::vector& Domain::nodes() const noexcept { diff --git a/src/fesa/model/shell_geometry.cpp b/src/fesa/model/shell_geometry.cpp index 11f2163..b70b357 100644 --- a/src/fesa/model/shell_geometry.cpp +++ b/src/fesa/model/shell_geometry.cpp @@ -102,9 +102,9 @@ Result geometryFailure( std::string keyword, std::string identity, std::string message) { - return Result::failure(Status::failure( - FailureCategory::model, - {{Severity::error, + return Result::Failure(Status::Failure( + FailureCategory::kModel, + {{Severity::kError, std::move(code), location, std::move(keyword), @@ -153,14 +153,14 @@ bool sourceIdentityLess( const Mitc4ShellDefinition& right, std::size_t rightIndex) { return std::tie( - left.sourceId.instanceName, - left.sourceId.sourceLabel, - left.sourceId.sourceLabelText, + left.sourceId.instance_name, + left.sourceId.source_label, + left.sourceId.source_label_text, leftIndex) < std::tie( - right.sourceId.instanceName, - right.sourceId.sourceLabel, - right.sourceId.sourceLabelText, + right.sourceId.instance_name, + right.sourceId.source_label, + right.sourceId.source_label_text, rightIndex); } @@ -218,7 +218,7 @@ Result preprocessShellGeometry( if (element.nodeIndices[localNode] >= nodes.size()) { return geometryFailure( "invalid-shell-geometry", element.location, "ELEMENT", - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "Shell geometry references an unavailable internal node."); } current.coordinates[localNode] = @@ -226,7 +226,7 @@ Result preprocessShellGeometry( if (!isFinite(current.coordinates[localNode])) { return geometryFailure( "invalid-shell-geometry", element.location, "ELEMENT", - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "Shell geometry contains a nonfinite source coordinate."); } } @@ -237,7 +237,7 @@ Result preprocessShellGeometry( current.coordinates[first], current.coordinates[second])) { return geometryFailure( "invalid-shell-geometry", element.location, "ELEMENT", - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "Shell geometry contains duplicate nodes."); } } @@ -255,7 +255,7 @@ Result preprocessShellGeometry( !(centerMeasure > 0.0)) { return geometryFailure( "invalid-shell-geometry", element.location, "ELEMENT", - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "Shell center has no finite nonzero normal candidate."); } current.normal = scale(1.0 / centerMeasure, centerCross); @@ -268,7 +268,7 @@ Result preprocessShellGeometry( current.coordinates[3], current.coordinates[0], current.normal)) { return geometryFailure( "invalid-shell-geometry", element.location, "ELEMENT", - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "Shell boundary is self-intersecting in the center-normal projection."); } @@ -286,7 +286,7 @@ Result preprocessShellGeometry( !(measure > 0.0) || !(dot(areaVector, current.normal) > 0.0)) { return geometryFailure( "invalid-shell-geometry", element.location, "ELEMENT", - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "Shell surface is zero-area or locally reversed at a required point."); } areaWeight += measure; @@ -294,7 +294,7 @@ Result preprocessShellGeometry( if (!std::isfinite(areaWeight) || !(areaWeight > 0.0)) { return geometryFailure( "invalid-shell-geometry", element.location, "ELEMENT", - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "Shell surface-area weight is nonfinite or zero."); } current.areaWeight = areaWeight; @@ -335,7 +335,7 @@ Result preprocessShellGeometry( if (!std::isfinite(pairDot) || !(pairDot > 0.0)) { return geometryFailure( "opposed-incident-normal", nodes[nodeIndex].location, - "NODE", nodes[nodeIndex].sourceId.sourceLabelText, + "NODE", nodes[nodeIndex].sourceId.source_label_text, "Incident shell normal candidates do not share a positive orientation hemisphere."); } } @@ -358,7 +358,7 @@ Result preprocessShellGeometry( !(directorNorm > 0.0)) { return geometryFailure( "invalid-shell-director", nodes[nodeIndex].location, "NODE", - nodes[nodeIndex].sourceId.sourceLabelText, + nodes[nodeIndex].sourceId.source_label_text, "Area-weighted shell director is nonfinite or zero."); } const Vector3 director = scale(1.0 / directorNorm, directorSum); @@ -384,7 +384,7 @@ Result preprocessShellGeometry( !(tangentNorm > 0.0)) { return geometryFailure( "invalid-shell-director", nodes[nodeIndex].location, "NODE", - nodes[nodeIndex].sourceId.sourceLabelText, + nodes[nodeIndex].sourceId.source_label_text, "Least-aligned-axis tangent frame construction failed."); } const Vector3 tangentA = scale(1.0 / tangentNorm, tangentCandidate); @@ -392,7 +392,7 @@ Result preprocessShellGeometry( if (!isFinite(tangentB) || !(norm(tangentB) > 0.0)) { return geometryFailure( "invalid-shell-director", nodes[nodeIndex].location, "NODE", - nodes[nodeIndex].sourceId.sourceLabelText, + nodes[nodeIndex].sourceId.source_label_text, "Right-handed shell tangent frame construction failed."); } geometry.nodalFrames.push_back({ @@ -412,7 +412,7 @@ Result preprocessShellGeometry( if (element.sectionIndex >= sections.size()) { return geometryFailure( "invalid-shell-jacobian", element.location, "ELEMENT", - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "Shell geometry cannot resolve its thickness for Jacobian validation."); } const double thickness = sections[element.sectionIndex].thickness; @@ -422,7 +422,7 @@ Result preprocessShellGeometry( if (frame == nullptr) { return geometryFailure( "invalid-shell-director", element.location, "ELEMENT", - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "Shell element is missing a nodal director."); } directors[localNode] = frame->director; @@ -445,7 +445,7 @@ Result preprocessShellGeometry( !(dot(areaVector, work[elementIndex].normal) > 0.0)) { return geometryFailure( "invalid-shell-geometry", element.location, "ELEMENT", - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "Shell surface basis is nonfinite, zero, or reversed at a required point."); } @@ -466,7 +466,7 @@ Result preprocessShellGeometry( !(jacobian > 0.0)) { return geometryFailure( "invalid-shell-jacobian", element.location, "ELEMENT", - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "Shell Jacobian is nonfinite or nonpositive at a required point."); } const Vector3 reciprocalXi = @@ -479,13 +479,13 @@ Result preprocessShellGeometry( !isFinite(reciprocalZeta)) { return geometryFailure( "invalid-shell-jacobian", element.location, "ELEMENT", - element.sourceId.sourceLabelText, + element.sourceId.source_label_text, "Shell reciprocal basis is nonfinite at a required point."); } } } - return Result::success(std::move(geometry)); + return Result::Success(std::move(geometry)); } } // namespace fesa diff --git a/src/fesa/results/result_recovery.cpp b/src/fesa/results/result_recovery.cpp index 722cd4a..424340e 100644 --- a/src/fesa/results/result_recovery.cpp +++ b/src/fesa/results/result_recovery.cpp @@ -34,9 +34,9 @@ Status recoveryFailure(const std::string& code, const SourceLocation& location, const std::string& identity, const std::string& message) { - return Status::failure( - FailureCategory::model, - {{Severity::error, + return Status::Failure( + FailureCategory::kModel, + {{Severity::kError, code, location, "RESULT_RECOVERY", @@ -49,15 +49,15 @@ Result recoveryResultFailure(const std::string& code, const SourceLocation& location, const std::string& identity, const std::string& message) { - return Result::failure( + return Result::Failure( recoveryFailure(code, location, identity, message)); } bool sameSourceIdentity(const SourceEntityId& left, const SourceEntityId& right) { - return left.instanceName == right.instanceName && - left.sourceLabel == right.sourceLabel && - left.sourceLabelText == right.sourceLabelText; + return left.instance_name == right.instance_name && + left.source_label == right.source_label && + left.source_label_text == right.source_label_text; } template @@ -68,7 +68,7 @@ bool finite(const std::array& values) { } bool finite(const Vector& values) { - for (std::size_t index = 0U; index < values.size(); ++index) { + for (std::size_t index = 0U; index < values.Size(); ++index) { if (!std::isfinite(values[index])) { return false; } @@ -99,12 +99,12 @@ std::array freeEquationInternalTermNorms( for (const std::size_t row : dofs.freeDofs()) { double freeTerm = 0.0; double constrainedTerm = 0.0; - for (std::size_t position = stiffness.rowOffsets()[row]; - position < stiffness.rowOffsets()[row + 1U]; + for (std::size_t position = stiffness.RowOffsets()[row]; + position < stiffness.RowOffsets()[row + 1U]; ++position) { - const std::size_t column = stiffness.columnIndices()[position]; + const std::size_t column = stiffness.ColumnIndices()[position]; const double contribution = - stiffness.values()[position] * displacement[column]; + stiffness.Values()[position] * displacement[column]; if (freeColumn[column] != 0U) { freeTerm += contribution; } else { @@ -141,21 +141,21 @@ Status validateRecoveryInputs(const AnalysisModel& model, } const std::size_t fullCount = domain.nodes().size() * kDofsPerNode; if (dofs.fullDofCount() != fullCount || - fullStiffness.rows() != fullCount || - fullStiffness.columns() != fullCount || - state.displacement().size() != fullCount || - state.externalForce().size() != fullCount || - state.internalForce().size() != fullCount || - state.residual().size() != fullCount || - state.reaction().size() != fullCount) { + fullStiffness.Rows() != fullCount || + fullStiffness.Columns() != fullCount || + state.displacement().Size() != fullCount || + state.externalForce().Size() != fullCount || + state.internalForce().Size() != fullCount || + state.residual().Size() != fullCount || + state.reaction().Size() != fullCount) { return recoveryFailure( "invalid-recovery-dimensions", {domain.sourcePath(), 0U}, domain.sourceContentIdentity(), "Model, DOF, stiffness, and AnalysisState full-space dimensions must agree."); } - const Status matrixStatus = fullStiffness.validate(); - if (!matrixStatus.isOk()) { + const Status matrixStatus = fullStiffness.Validate(); + if (!matrixStatus.IsOk()) { return matrixStatus; } if (!finite(state.displacement()) || !finite(state.externalForce())) { @@ -170,7 +170,7 @@ Status validateRecoveryInputs(const AnalysisModel& model, const auto& constrainedDofs = dofs.constrainedDofs(); if (freeDofs.size() != dofs.freeDofCount() || constrainedDofs.size() != dofs.constrainedDofCount() || - dofs.prescribedValues().size() != constrainedDofs.size() || + dofs.prescribedValues().Size() != constrainedDofs.size() || freeDofs.size() + constrainedDofs.size() != fullCount || !strictlyIncreasing(freeDofs) || !strictlyIncreasing(constrainedDofs)) { @@ -253,7 +253,7 @@ Status validateRecoveryInputs(const AnalysisModel& model, return recoveryFailure( "invalid-recovery-entity", definition.location, - definition.sourceId.sourceLabelText, + definition.sourceId.source_label_text, "Active beam references must resolve before recovery."); } try { @@ -271,7 +271,7 @@ Status validateRecoveryInputs(const AnalysisModel& model, return recoveryFailure( "invalid-recovery-order", definition.location, - definition.sourceId.sourceLabelText, + definition.sourceId.source_label_text, "Element scatter must preserve endpoint/component full-DOF order."); } } @@ -280,7 +280,7 @@ Status validateRecoveryInputs(const AnalysisModel& model, return recoveryFailure( "invalid-recovery-entity", definition.location, - definition.sourceId.sourceLabelText, + definition.sourceId.source_label_text, "Every active element requires one twelve-DOF scatter map."); } } @@ -310,7 +310,7 @@ Status validateRecoveryInputs(const AnalysisModel& model, return recoveryFailure( "invalid-recovery-entity", definition.location, - definition.sourceId.sourceLabelText, + definition.sourceId.source_label_text, "Active shell material and section references must resolve before recovery."); } try { @@ -324,7 +324,7 @@ Status validateRecoveryInputs(const AnalysisModel& model, return recoveryFailure( "invalid-recovery-entity", definition.location, - definition.sourceId.sourceLabelText, + definition.sourceId.source_label_text, "Active shell node references must resolve before recovery."); } for (std::size_t component = 0U; @@ -339,7 +339,7 @@ Status validateRecoveryInputs(const AnalysisModel& model, return recoveryFailure( "invalid-recovery-order", definition.location, - definition.sourceId.sourceLabelText, + definition.sourceId.source_label_text, "Shell scatter must preserve node/component full-DOF order."); } } @@ -348,11 +348,11 @@ Status validateRecoveryInputs(const AnalysisModel& model, return recoveryFailure( "invalid-recovery-entity", definition.location, - definition.sourceId.sourceLabelText, + definition.sourceId.source_label_text, "Every active shell requires one twenty-four-DOF scatter map."); } } - return Status::ok(); + return Status::Ok(); } char asciiLower(const char value) { @@ -390,7 +390,7 @@ Result> resolveLoadTarget(const Domain& domain, std::int64_t sourceLabel = 0; if (tryPositiveInteger(load.target, sourceLabel)) { for (std::size_t node = 0U; node < domain.nodes().size(); ++node) { - if (domain.nodes()[node].sourceId.sourceLabel == sourceLabel) { + if (domain.nodes()[node].sourceId.source_label == sourceLabel) { nodes.push_back(static_cast(node)); } } @@ -415,11 +415,11 @@ Result> resolveLoadTarget(const Domain& domain, } seen[node] = 1U; } - return Result>::success( + return Result>::Success( sets.front()->nodeIndices); } if (!nodes.empty()) { - return Result>::success(std::move(nodes)); + return Result>::Success(std::move(nodes)); } return recoveryResultFailure>( "invalid-node-station-entity", @@ -542,7 +542,7 @@ Status populateShellGlobalEvidence( return recoveryFailure( "nonfinite-recovery-value", domain.nodes()[node].location, - domain.nodes()[node].sourceId.sourceLabelText, + domain.nodes()[node].sourceId.source_label_text, "Global force and moment evidence must remain finite in source-node order."); } } @@ -581,7 +581,7 @@ Status populateShellGlobalEvidence( "global-equilibrium", "Normalized global force or moment balance exceeds 1e-10."); } - return Status::ok(); + return Status::Ok(); } std::optional localAxes(const Domain& domain, @@ -646,11 +646,11 @@ Status ResultRecovery::recover(const AnalysisModel& model, AnalysisState& state) { const Status inputStatus = validateRecoveryInputs(model, dofs, fullStiffness, state); - if (!inputStatus.isOk()) { + if (!inputStatus.IsOk()) { return inputStatus; } - Vector internalForce = fullStiffness.multiply(state.displacement()); + Vector internalForce = fullStiffness.Multiply(state.displacement()); if (!finite(internalForce)) { return recoveryFailure( "nonfinite-recovery-value", @@ -659,7 +659,7 @@ Status ResultRecovery::recover(const AnalysisModel& model, "Full stiffness multiplication must produce finite internal force."); } Vector residual{dofs.fullDofCount()}; - for (std::size_t fullDof = 0U; fullDof < residual.size(); ++fullDof) { + for (std::size_t fullDof = 0U; fullDof < residual.Size(); ++fullDof) { residual[fullDof] = internalForce[fullDof] - state.externalForce()[fullDof]; if (!std::isfinite(residual[fullDof])) { @@ -722,8 +722,8 @@ Status ResultRecovery::recover(const AnalysisModel& model, domain.nodes()[definition.nodeIndices[1U]], domain.sections()[definition.sectionIndex], domain.materials()[definition.materialIndex]); - if (!beam.hasValue()) { - return beam.status(); + if (!beam.HasValue()) { + return beam.GetStatus(); } Vector elementDisplacement{kElementDofCount}; @@ -735,14 +735,14 @@ Status ResultRecovery::recover(const AnalysisModel& model, state.displacement()[scatter[localDof]]; } const BeamRecovery recovered = - beam.value().recover(elementDisplacement); + beam.Value().recover(elementDisplacement); for (std::size_t endpoint = 0U; endpoint < 2U; ++endpoint) { if (!finite(recovered.equilibriumEndActions[endpoint]) || !finite(recovered.endpointSectionResultants[endpoint])) { return recoveryFailure( "nonfinite-recovery-value", definition.location, - definition.sourceId.sourceLabelText, + definition.sourceId.source_label_text, "Endpoint recovery values must be finite."); } endpointRows.push_back({ @@ -758,7 +758,7 @@ Status ResultRecovery::recover(const AnalysisModel& model, return recoveryFailure( "nonfinite-recovery-value", definition.location, - definition.sourceId.sourceLabelText, + definition.sourceId.source_label_text, "Gauss recovery values must be finite."); } gaussRows.push_back({ @@ -774,7 +774,7 @@ Status ResultRecovery::recover(const AnalysisModel& model, return recoveryFailure( "nonfinite-recovery-value", definition.location, - definition.sourceId.sourceLabelText, + definition.sourceId.source_label_text, "Stress recovery identity and values must be finite and ordered."); } stressRows.push_back({ @@ -844,7 +844,7 @@ Status ResultRecovery::recover(const AnalysisModel& model, return recoveryFailure( "invalid-recovery-entity", definition.location, - definition.sourceId.sourceLabelText, + definition.sourceId.source_label_text, "Shell recovery requires one initial director per element node."); } nodes[nodePosition] = &domain.nodes()[node]; @@ -856,8 +856,8 @@ Status ResultRecovery::recover(const AnalysisModel& model, directors, domain.shellSections()[definition.sectionIndex], domain.materials()[definition.materialIndex]); - if (!shell.hasValue()) { - return shell.status(); + if (!shell.HasValue()) { + return shell.GetStatus(); } Vector elementDisplacement{kShellElementDofCount}; const auto& scatter = dofs.shellElementScatter(elementIndex); @@ -867,28 +867,28 @@ Status ResultRecovery::recover(const AnalysisModel& model, elementDisplacement[localDof] = state.displacement()[scatter[localDof]]; } - auto recovered = shell.value().recoverPhysical( + auto recovered = shell.Value().recoverPhysical( elementDisplacement); - if (!recovered.hasValue()) { - return recovered.status(); + if (!recovered.HasValue()) { + return recovered.GetStatus(); } const double accumulatedEnergy = shellCandidate.physicalStrainEnergy + - recovered.value().strainEnergy; + recovered.Value().strainEnergy; if (!std::isfinite(accumulatedEnergy)) { return recoveryFailure( "nonfinite-recovery-value", definition.location, - definition.sourceId.sourceLabelText, + definition.sourceId.source_label_text, "Source-order physical shell energy reduction must remain finite."); } shellCandidate.physicalStrainEnergy = accumulatedEnergy; expectedShellElements.push_back(elementIndex); for (std::size_t point = 0U; - point < recovered.value().points.size(); + point < recovered.Value().points.size(); ++point) { - const auto& physicalPoint = recovered.value().points[point]; + const auto& physicalPoint = recovered.Value().points[point]; ShellResultRow row{}; row.element = elementIndex; row.location = locations[point]; @@ -917,7 +917,7 @@ Status ResultRecovery::recover(const AnalysisModel& model, residual, normalizedResidual, shellCandidate); - if (!evidenceStatus.isOk()) { + if (!evidenceStatus.IsOk()) { return evidenceStatus; } } @@ -934,11 +934,11 @@ Status ResultRecovery::recover(const AnalysisModel& model, candidateState.stressResults() = std::move(stressRows); const Status shellCommitStatus = candidateState.commitShellResults( expectedShellElements, std::move(shellCandidate)); - if (!shellCommitStatus.isOk()) { + if (!shellCommitStatus.IsOk()) { return shellCommitStatus; } state = std::move(candidateState); - return Status::ok(); + return Status::Ok(); } Result> @@ -990,14 +990,14 @@ ResultRecovery::normalizeSectionResultantsToNodeStations( return recoveryResultFailure>( "invalid-node-station-entity", definition.location, - definition.sourceId.sourceLabelText, + definition.sourceId.source_label_text, "Endpoint rows must preserve active element, endpoint, and source-node order."); } if (!finite(row.sectionResultant)) { return recoveryResultFailure>( "nonfinite-node-station-value", definition.location, - definition.sourceId.sourceLabelText, + definition.sourceId.source_label_text, "Node-station section resultants must be finite."); } rowsByNode[nodeIndex].push_back(&row); @@ -1030,12 +1030,12 @@ ResultRecovery::normalizeSectionResultantsToNodeStations( "Station eligibility requires finite concentrated loads."); } auto targets = resolveLoadTarget(domain, load); - if (!targets.hasValue()) { - return Result>::failure( - targets.status()); + if (!targets.HasValue()) { + return Result>::Failure( + targets.GetStatus()); } if (load.magnitude != 0.0) { - for (const EntityIndex node : targets.value()) { + for (const EntityIndex node : targets.Value()) { loadedNodes[node] = 1U; } } @@ -1061,7 +1061,7 @@ ResultRecovery::normalizeSectionResultantsToNodeStations( return recoveryResultFailure>( "ineligible-node-station", domain.nodes()[nodeIndex].location, - domain.nodes()[nodeIndex].sourceId.sourceLabelText, + domain.nodes()[nodeIndex].sourceId.source_label_text, "Interior station collapse requires exactly two unloaded endpoints."); } @@ -1080,7 +1080,7 @@ ResultRecovery::normalizeSectionResultantsToNodeStations( return recoveryResultFailure>( "ineligible-node-station", domain.nodes()[nodeIndex].location, - domain.nodes()[nodeIndex].sourceId.sourceLabelText, + domain.nodes()[nodeIndex].sourceId.source_label_text, "Interior station endpoints require one consistent section and local-axis chain."); } @@ -1097,14 +1097,14 @@ ResultRecovery::normalizeSectionResultantsToNodeStations( return recoveryResultFailure>( "nonfinite-node-station-value", domain.nodes()[nodeIndex].location, - domain.nodes()[nodeIndex].sourceId.sourceLabelText, + domain.nodes()[nodeIndex].sourceId.source_label_text, "Endpoint comparison must produce a finite difference."); } if (difference > componentTolerances[component]) { return recoveryResultFailure>( "node-station-tolerance-failure", domain.nodes()[nodeIndex].location, - domain.nodes()[nodeIndex].sourceId.sourceLabelText, + domain.nodes()[nodeIndex].sourceId.source_label_text, "Interior endpoint resultants disagree beyond component tolerance."); } } @@ -1118,7 +1118,7 @@ ResultRecovery::normalizeSectionResultantsToNodeStations( representative->element, representative->sectionResultant}); } - return Result>::success( + return Result>::Success( std::move(stations)); } diff --git a/src/fesa/solvers/linear/mkl_pardiso_solver.cpp b/src/fesa/solvers/linear/mkl_pardiso_solver.cpp index 8b2fafa..add8672 100644 --- a/src/fesa/solvers/linear/mkl_pardiso_solver.cpp +++ b/src/fesa/solvers/linear/mkl_pardiso_solver.cpp @@ -1,7 +1,4 @@ -#include "fesa/solvers/linear/mkl_pardiso_solver.hpp" - -#include "fesa/math/sparse_matrix.hpp" -#include "fesa/math/vector.hpp" +#include "fesa/solvers/linear/mkl_pardiso_solver.h" #include @@ -15,369 +12,337 @@ #include #include +#include "fesa/math/sparse_matrix.h" +#include "fesa/math/vector.h" + namespace fesa { namespace { -Status solverFailure( - const std::string& code, - const std::string& identity, - const std::string& message) { - return Status::failure( - FailureCategory::solver, - {{Severity::error, - code, - {{}, 0U}, - "PARDISO", - identity, - message}}); +/// @brief Builds a structured linear-solver failure. +Status SolverFailure(const std::string& code, const std::string& identity, + const std::string& message) { + return Status::Failure( + FailureCategory::kSolver, + {{Severity::kError, code, {{}, 0U}, "PARDISO", identity, message}}); } -Status pardisoFailure( - const MKL_INT phase, - const MKL_INT error) { - std::string code; - std::string reason; - switch (error) { +/// @brief Translates a PARDISO phase error into the stable solver taxonomy. +Status PardisoFailure(const MKL_INT phase, const MKL_INT error) { + std::string code; + std::string reason; + switch (error) { case -4: - code = "pardiso-zero-or-negative-pivot"; - reason = "zero or negative pivot"; - break; + code = "pardiso-zero-or-negative-pivot"; + reason = "zero or negative pivot"; + break; case -7: - code = "pardiso-singular-diagonal"; - reason = "singular diagonal"; - break; + code = "pardiso-singular-diagonal"; + reason = "singular diagonal"; + break; case -8: - code = "pardiso-integer-overflow"; - reason = "32-bit backend integer overflow"; - break; + code = "pardiso-integer-overflow"; + reason = "32-bit backend integer overflow"; + break; case 21: case 22: case 23: case 24: - code = "pardiso-invalid-csr"; - reason = "matrix checker rejected the CSR indices"; - break; + code = "pardiso-invalid-csr"; + reason = "matrix checker rejected the CSR indices"; + break; default: - code = phase == 11 ? "pardiso-analysis-failed" : - phase == 22 ? "pardiso-factorization-failed" : - phase == 33 ? "pardiso-solve-failed" : - "pardiso-release-failed"; - reason = "backend error"; - break; - } + code = phase == 11 ? "pardiso-analysis-failed" + : phase == 22 ? "pardiso-factorization-failed" + : phase == 33 ? "pardiso-solve-failed" + : "pardiso-release-failed"; + reason = "backend error"; + break; + } - const std::string phaseText = std::to_string(phase); - const std::string errorText = std::to_string(error); - return solverFailure( - code, - "phase=" + phaseText + ",error=" + errorText, - "oneMKL PARDISO phase " + phaseText + " failed with error " + - errorText + " (" + reason + ")."); + const std::string phase_text = std::to_string(phase); + const std::string error_text = std::to_string(error); + return SolverFailure(code, "phase=" + phase_text + ",error=" + error_text, + "oneMKL PARDISO phase " + phase_text + + " failed with error " + error_text + " (" + reason + + ")."); } -bool convertsToMklInt(const std::size_t value) { - return value <= - static_cast((std::numeric_limits::max)()); +/// @brief Reports whether a public size fits the private MKL integer type. +bool ConvertsToMklInt(const std::size_t value) { + return value <= + static_cast((std::numeric_limits::max)()); } -} // namespace +} // namespace class MklPardisoSolver::Impl { -public: - Impl() = default; + public: + Impl() = default; - ~Impl() { - static_cast(release()); + ~Impl() { static_cast(Release()); } + + /// @brief Validates and factorizes one public CSR matrix. + Status Factorize(const SparseMatrix& matrix) { + const MKL_INT release_error = Release(); + if (release_error != 0) { + return PardisoFailure(-1, release_error); } - Status factorize(const SparseMatrix& matrix) { - const MKL_INT releaseError = release(); - if (releaseError != 0) { - return pardisoFailure(-1, releaseError); - } - - const Status csrStatus = matrix.validate(); - if (!csrStatus.isOk()) { - return solverFailure( - "solver-invalid-csr", - "public-csr", - "The public sparse matrix failed CSR validation."); - } - if (matrix.rows() != matrix.columns()) { - return solverFailure( - "solver-matrix-not-square", - "matrix-shape", - "PARDISO factorization requires a square matrix."); - } - if (matrix.rows() == 0U) { - // A fully constrained model has no free equations. Preserve the - // observable factorize/solve lifecycle without creating backend - // state or calling PARDISO with its invalid n=0 input. - factorized_ = true; - return Status::ok(); - } - if (!convertsToMklInt(matrix.rows()) || - !convertsToMklInt(matrix.values().size())) { - return solverFailure( - "solver-dimension-overflow", - "matrix-shape", - "Sparse matrix dimensions exceed the oneMKL integer range."); - } - - const Status copyStatus = copyValidatedUpperTriangle(matrix); - if (!copyStatus.isOk()) { - clearOwnedArrays(); - return copyStatus; - } - - // PARDISO owns internal memory behind pt after phase 11. Initialize - // once per factorization and retain it until refactorization/destruction. - pt_.fill(nullptr); - iparm_.fill(0); - pardisoinit(pt_.data(), &mtype_, iparm_.data()); - iparm_[26] = 1; // Validate sorted CSR integer arrays. - iparm_[34] = 1; // Consume the project's native zero-based CSR. - permutation_.assign(static_cast(equationCount_), 0); - ownsPardisoState_ = true; - - MKL_INT phase = 11; - MKL_INT error = 0; - callPardiso(phase, nullptr, nullptr, error); - if (error != 0) { - const Status failure = pardisoFailure(phase, error); - static_cast(release()); - return failure; - } - - phase = 22; - error = 0; - callPardiso(phase, nullptr, nullptr, error); - if (error != 0) { - const Status failure = pardisoFailure(phase, error); - static_cast(release()); - return failure; - } - - factorized_ = true; - return Status::ok(); + const Status csr_status = matrix.Validate(); + if (!csr_status.IsOk()) { + return SolverFailure("solver-invalid-csr", "public-csr", + "The public sparse matrix failed CSR validation."); + } + if (matrix.Rows() != matrix.Columns()) { + return SolverFailure("solver-matrix-not-square", "matrix-shape", + "PARDISO factorization requires a square matrix."); + } + if (matrix.Rows() == 0U) { + // A fully constrained model has no free equations. Preserve the + // observable factorize/solve lifecycle without creating backend + // state or calling PARDISO with its invalid n=0 input. + factorized_ = true; + return Status::Ok(); + } + if (!ConvertsToMklInt(matrix.Rows()) || + !ConvertsToMklInt(matrix.Values().size())) { + return SolverFailure( + "solver-dimension-overflow", "matrix-shape", + "Sparse matrix dimensions exceed the oneMKL integer range."); } - Status solve(const Vector& rhs, Vector& solution) { - if (!factorized_) { - return solverFailure( - "solver-not-factorized", - "factorization-state", - "Substitution requires a successful retained factorization."); - } - const std::size_t size = static_cast(equationCount_); - if (rhs.size() != size || solution.size() != size) { - return solverFailure( - "solver-vector-dimension-mismatch", - "rhs-or-solution", - "RHS and solution dimensions must match the factorized matrix."); - } - for (std::size_t index = 0U; index < rhs.size(); ++index) { - if (!std::isfinite(rhs[index])) { - return solverFailure( - "nonfinite-solver-rhs", - std::to_string(index), - "PARDISO RHS values must be finite."); - } - } - if (size == 0U) { - return Status::ok(); - } - - std::vector rhsCopy(rhs.data(), rhs.data() + rhs.size()); - Vector candidate{size}; - MKL_INT phase = 33; - MKL_INT error = 0; - callPardiso(phase, rhsCopy.data(), candidate.data(), error); - if (error != 0) { - return pardisoFailure(phase, error); - } - for (std::size_t index = 0U; index < candidate.size(); ++index) { - if (!std::isfinite(candidate[index])) { - return solverFailure( - "nonfinite-solver-solution", - std::to_string(index), - "PARDISO substitution produced a nonfinite solution."); - } - } - - solution = std::move(candidate); - return Status::ok(); + const Status copy_status = CopyValidatedUpperTriangle(matrix); + if (!copy_status.IsOk()) { + ClearOwnedArrays(); + return copy_status; } -private: - Status copyValidatedUpperTriangle(const SparseMatrix& matrix) { - const auto& publicOffsets = matrix.rowOffsets(); - const auto& publicColumns = matrix.columnIndices(); - const auto& publicValues = matrix.values(); + // PARDISO owns internal memory behind pt after phase 11. Initialize + // once per factorization and retain it until refactorization/destruction. + pt_.fill(nullptr); + iparm_.fill(0); + pardisoinit(pt_.data(), &mtype_, iparm_.data()); + iparm_[26] = 1; // Validate sorted CSR integer arrays. + iparm_[34] = 1; // Consume the project's native zero-based CSR. + permutation_.assign(static_cast(equation_count_), 0); + owns_pardiso_state_ = true; - double matrixScale = 0.0; - for (const double value : publicValues) { - matrixScale = (std::max)(matrixScale, std::abs(value)); - } - - for (std::size_t row = 0U; row < matrix.rows(); ++row) { - for (std::size_t position = publicOffsets[row]; - position < publicOffsets[row + 1U]; - ++position) { - const std::size_t column = publicColumns[position]; - const auto reverseBegin = publicColumns.begin() + - static_cast(publicOffsets[column]); - const auto reverseEnd = publicColumns.begin() + - static_cast(publicOffsets[column + 1U]); - const auto reverse = - std::lower_bound(reverseBegin, reverseEnd, row); - if (reverse == reverseEnd || *reverse != row) { - return solverFailure( - "solver-matrix-not-symmetric", - std::to_string(row) + ":" + std::to_string(column), - "The full public CSR must contain both symmetric entries."); - } - - const std::size_t reversePosition = static_cast( - std::distance(publicColumns.begin(), reverse)); - const double left = publicValues[position]; - const double right = publicValues[reversePosition]; - const double difference = std::abs(left - right); - // The approved symmetry test is normalized by the matrix's - // actual nonzero scale, without an absolute unit-size floor. - const bool isSymmetric = matrixScale == 0.0 ? - difference == 0.0 : - difference <= 1.0e-12 * matrixScale; - if (!isSymmetric) { - return solverFailure( - "solver-matrix-not-symmetric", - std::to_string(row) + ":" + std::to_string(column), - "The full public CSR values violate the approved symmetry tolerance."); - } - } - } - - equationCount_ = static_cast(matrix.rows()); - rowOffsets_.clear(); - columnIndices_.clear(); - values_.clear(); - rowOffsets_.reserve(matrix.rows() + 1U); - rowOffsets_.push_back(0); - - for (std::size_t row = 0U; row < matrix.rows(); ++row) { - bool hasDiagonal = false; - for (std::size_t position = publicOffsets[row]; - position < publicOffsets[row + 1U]; - ++position) { - const std::size_t column = publicColumns[position]; - if (column < row) { - continue; - } - if (!convertsToMklInt(column) || - !convertsToMklInt(columnIndices_.size())) { - return solverFailure( - "solver-dimension-overflow", - std::to_string(row) + ":" + std::to_string(column), - "CSR indices exceed the oneMKL integer range."); - } - hasDiagonal = hasDiagonal || column == row; - columnIndices_.push_back(static_cast(column)); - values_.push_back(publicValues[position]); - } - if (!hasDiagonal) { - return solverFailure( - "solver-missing-diagonal", - std::to_string(row), - "Every PARDISO SPD row must retain its diagonal slot."); - } - if (!convertsToMklInt(columnIndices_.size())) { - return solverFailure( - "solver-dimension-overflow", - std::to_string(row), - "CSR row offsets exceed the oneMKL integer range."); - } - rowOffsets_.push_back( - static_cast(columnIndices_.size())); - } - return Status::ok(); + MKL_INT phase = 11; + MKL_INT error = 0; + CallPardiso(phase, nullptr, nullptr, error); + if (error != 0) { + const Status failure = PardisoFailure(phase, error); + static_cast(Release()); + return failure; } - void callPardiso( - const MKL_INT phase, - double* rhs, - double* solution, - MKL_INT& error) { - pardiso( - pt_.data(), - &maxFactorizations_, - &matrixNumber_, - &mtype_, - &phase, - &equationCount_, - values_.data(), - rowOffsets_.data(), - columnIndices_.data(), - permutation_.data(), - &rhsCount_, - iparm_.data(), - &messageLevel_, - rhs, - solution, - &error); + phase = 22; + error = 0; + CallPardiso(phase, nullptr, nullptr, error); + if (error != 0) { + const Status failure = PardisoFailure(phase, error); + static_cast(Release()); + return failure; } - MKL_INT release() noexcept { - MKL_INT error = 0; - if (ownsPardisoState_) { - const MKL_INT phase = -1; - double placeholder = 0.0; - callPardiso(phase, &placeholder, &placeholder, error); + factorized_ = true; + return Status::Ok(); + } + + /// @brief Substitutes one RHS while preserving solution on failure. + Status Solve(const Vector& rhs, Vector& solution) { + if (!factorized_) { + return SolverFailure( + "solver-not-factorized", "factorization-state", + "Substitution requires a successful retained factorization."); + } + const std::size_t size = static_cast(equation_count_); + if (rhs.Size() != size || solution.Size() != size) { + return SolverFailure( + "solver-vector-dimension-mismatch", "rhs-or-solution", + "RHS and solution dimensions must match the factorized matrix."); + } + for (std::size_t index = 0U; index < rhs.Size(); ++index) { + if (!std::isfinite(rhs[index])) { + return SolverFailure("nonfinite-solver-rhs", std::to_string(index), + "PARDISO RHS values must be finite."); + } + } + if (size == 0U) { + return Status::Ok(); + } + + std::vector rhs_copy(rhs.Data(), rhs.Data() + rhs.Size()); + Vector candidate{size}; + MKL_INT phase = 33; + MKL_INT error = 0; + CallPardiso(phase, rhs_copy.data(), candidate.Data(), error); + if (error != 0) { + return PardisoFailure(phase, error); + } + for (std::size_t index = 0U; index < candidate.Size(); ++index) { + if (!std::isfinite(candidate[index])) { + return SolverFailure( + "nonfinite-solver-solution", std::to_string(index), + "PARDISO substitution produced a nonfinite solution."); + } + } + + solution = std::move(candidate); + return Status::Ok(); + } + + private: + /// @brief Validates symmetry and copies the upper triangle for PARDISO. + Status CopyValidatedUpperTriangle(const SparseMatrix& matrix) { + const auto& public_offsets = matrix.RowOffsets(); + const auto& public_columns = matrix.ColumnIndices(); + const auto& public_values = matrix.Values(); + + double matrix_scale = 0.0; + for (const double value : public_values) { + matrix_scale = (std::max)(matrix_scale, std::abs(value)); + } + + for (std::size_t row = 0U; row < matrix.Rows(); ++row) { + for (std::size_t position = public_offsets[row]; + position < public_offsets[row + 1U]; ++position) { + const std::size_t column = public_columns[position]; + const auto reverse_begin = + public_columns.begin() + + static_cast(public_offsets[column]); + const auto reverse_end = + public_columns.begin() + + static_cast(public_offsets[column + 1U]); + const auto reverse = std::lower_bound(reverse_begin, reverse_end, row); + if (reverse == reverse_end || *reverse != row) { + return SolverFailure( + "solver-matrix-not-symmetric", + std::to_string(row) + ":" + std::to_string(column), + "The full public CSR must contain both symmetric entries."); } - ownsPardisoState_ = false; - factorized_ = false; - pt_.fill(nullptr); - iparm_.fill(0); - permutation_.clear(); - clearOwnedArrays(); - return error; + + const std::size_t reverse_position = static_cast( + std::distance(public_columns.begin(), reverse)); + const double left = public_values[position]; + const double right = public_values[reverse_position]; + const double difference = std::abs(left - right); + // The approved symmetry test is normalized by the matrix's + // actual nonzero scale, without an absolute unit-size floor. + const bool is_symmetric = matrix_scale == 0.0 + ? difference == 0.0 + : difference <= 1.0e-12 * matrix_scale; + if (!is_symmetric) { + return SolverFailure( + "solver-matrix-not-symmetric", + std::to_string(row) + ":" + std::to_string(column), + "The full public CSR values violate the approved symmetry " + "tolerance."); + } + } } - void clearOwnedArrays() noexcept { - equationCount_ = 0; - rowOffsets_.clear(); - columnIndices_.clear(); - values_.clear(); - } + equation_count_ = static_cast(matrix.Rows()); + row_offsets_.clear(); + column_indices_.clear(); + values_.clear(); + row_offsets_.reserve(matrix.Rows() + 1U); + row_offsets_.push_back(0); - std::array pt_{}; - std::array iparm_{}; - std::vector rowOffsets_; - std::vector columnIndices_; - std::vector permutation_; - std::vector values_; - MKL_INT equationCount_{0}; - MKL_INT maxFactorizations_{1}; - MKL_INT matrixNumber_{1}; - MKL_INT mtype_{2}; - MKL_INT rhsCount_{1}; - MKL_INT messageLevel_{0}; - bool ownsPardisoState_{false}; - bool factorized_{false}; + for (std::size_t row = 0U; row < matrix.Rows(); ++row) { + bool has_diagonal = false; + for (std::size_t position = public_offsets[row]; + position < public_offsets[row + 1U]; ++position) { + const std::size_t column = public_columns[position]; + if (column < row) { + continue; + } + if (!ConvertsToMklInt(column) || + !ConvertsToMklInt(column_indices_.size())) { + return SolverFailure( + "solver-dimension-overflow", + std::to_string(row) + ":" + std::to_string(column), + "CSR indices exceed the oneMKL integer range."); + } + has_diagonal = has_diagonal || column == row; + column_indices_.push_back(static_cast(column)); + values_.push_back(public_values[position]); + } + if (!has_diagonal) { + return SolverFailure( + "solver-missing-diagonal", std::to_string(row), + "Every PARDISO SPD row must retain its diagonal slot."); + } + if (!ConvertsToMklInt(column_indices_.size())) { + return SolverFailure( + "solver-dimension-overflow", std::to_string(row), + "CSR row offsets exceed the oneMKL integer range."); + } + row_offsets_.push_back(static_cast(column_indices_.size())); + } + return Status::Ok(); + } + + /// @brief Calls PARDISO with retained private arrays and phase state. + void CallPardiso(const MKL_INT phase, double* rhs, double* solution, + MKL_INT& error) { + pardiso(pt_.data(), &max_factorizations_, &matrix_number_, &mtype_, &phase, + &equation_count_, values_.data(), row_offsets_.data(), + column_indices_.data(), permutation_.data(), &rhs_count_, + iparm_.data(), &message_level_, rhs, solution, &error); + } + + /// @brief Releases backend state and resets every retained array. + MKL_INT Release() noexcept { + MKL_INT error = 0; + if (owns_pardiso_state_) { + const MKL_INT phase = -1; + double placeholder = 0.0; + CallPardiso(phase, &placeholder, &placeholder, error); + } + owns_pardiso_state_ = false; + factorized_ = false; + pt_.fill(nullptr); + iparm_.fill(0); + permutation_.clear(); + ClearOwnedArrays(); + return error; + } + + /// @brief Clears FESA-owned CSR arrays without touching backend state. + void ClearOwnedArrays() noexcept { + equation_count_ = 0; + row_offsets_.clear(); + column_indices_.clear(); + values_.clear(); + } + + std::array pt_{}; + std::array iparm_{}; + std::vector row_offsets_; + std::vector column_indices_; + std::vector permutation_; + std::vector values_; + MKL_INT equation_count_{0}; + MKL_INT max_factorizations_{1}; + MKL_INT matrix_number_{1}; + MKL_INT mtype_{2}; + MKL_INT rhs_count_{1}; + MKL_INT message_level_{0}; + bool owns_pardiso_state_{false}; + bool factorized_{false}; }; -MklPardisoSolver::MklPardisoSolver() - : impl_{std::make_unique()} {} +MklPardisoSolver::MklPardisoSolver() : impl_{std::make_unique()} {} MklPardisoSolver::~MklPardisoSolver() = default; -Status MklPardisoSolver::factorize(const SparseMatrix& matrix) { - return impl_->factorize(matrix); +Status MklPardisoSolver::Factorize(const SparseMatrix& matrix) { + return impl_->Factorize(matrix); } -Status MklPardisoSolver::solve( - const Vector& rhs, - Vector& solution) const { - return impl_->solve(rhs, solution); +Status MklPardisoSolver::Solve(const Vector& rhs, Vector& solution) const { + return impl_->Solve(rhs, solution); } -} // namespace fesa +} // namespace fesa diff --git a/tests/integration/analysis/linear_static_analysis_test.cpp b/tests/integration/analysis/linear_static_analysis_test.cpp index 5f59f01..06d9431 100644 --- a/tests/integration/analysis/linear_static_analysis_test.cpp +++ b/tests/integration/analysis/linear_static_analysis_test.cpp @@ -2,7 +2,7 @@ #include "fesa/assembly/parallel_for.hpp" #include "fesa/results/results_writer.hpp" -#include "fesa/solvers/linear/mkl_pardiso_solver.hpp" +#include "fesa/solvers/linear/mkl_pardiso_solver.h" #include @@ -183,7 +183,7 @@ protected: private: fesa::Status record(const char* event) { events_.emplace_back(event); - return fesa::Status::ok(); + return fesa::Status::Ok(); } std::vector events_; @@ -196,21 +196,21 @@ public: explicit SpyLinearSolver(std::vector& events) : events_{events} {} - fesa::Status factorize(const fesa::SparseMatrix&) override { + fesa::Status Factorize(const fesa::SparseMatrix&) override { ++factorizeCalls_; events_.emplace_back("solver-factorize"); - return fesa::Status::ok(); + return fesa::Status::Ok(); } - fesa::Status solve( + fesa::Status Solve( const fesa::Vector& rhs, fesa::Vector& solution) const override { ++solveCalls_; events_.emplace_back("solver-solve"); for (std::size_t index = 0U; - index < rhs.size() && index < solution.size(); ++index) { + index < rhs.Size() && index < solution.Size(); ++index) { solution[index] = 0.0; } - return fesa::Status::ok(); + return fesa::Status::Ok(); } int factorizeCalls() const noexcept { return factorizeCalls_; } @@ -224,25 +224,25 @@ private: class RecordingMklSolver final : public fesa::LinearSolver { public: - fesa::Status factorize(const fesa::SparseMatrix& matrix) override { + fesa::Status Factorize(const fesa::SparseMatrix& matrix) override { ++factorizeCalls_; - factorizedDimension_ = matrix.rows(); - if (matrix.rows() == 1U && matrix.columns() == 1U && - matrix.values().size() == 1U) { - scalarStiffness_ = matrix.values()[0U]; + factorizedDimension_ = matrix.Rows(); + if (matrix.Rows() == 1U && matrix.Columns() == 1U && + matrix.Values().size() == 1U) { + scalarStiffness_ = matrix.Values()[0U]; } - return backend_.factorize(matrix); + return backend_.Factorize(matrix); } - fesa::Status solve( + fesa::Status Solve( const fesa::Vector& rhs, fesa::Vector& solution) const override { ++solveCalls_; - if (rhs.size() == 0U) { + if (rhs.Size() == 0U) { rhs_.clear(); } else { - rhs_.assign(rhs.data(), rhs.data() + rhs.size()); + rhs_.assign(rhs.Data(), rhs.Data() + rhs.Size()); } - return backend_.solve(rhs, solution); + return backend_.Solve(rhs, solution); } int factorizeCalls() const noexcept { return factorizeCalls_; } @@ -264,16 +264,16 @@ private: class NonfiniteLinearSolver final : public fesa::LinearSolver { public: - fesa::Status factorize(const fesa::SparseMatrix&) override { - return fesa::Status::ok(); + fesa::Status Factorize(const fesa::SparseMatrix&) override { + return fesa::Status::Ok(); } - fesa::Status solve( + fesa::Status Solve( const fesa::Vector&, fesa::Vector& solution) const override { - for (std::size_t index = 0U; index < solution.size(); ++index) { + for (std::size_t index = 0U; index < solution.Size(); ++index) { solution[index] = (std::numeric_limits::quiet_NaN)(); } - return fesa::Status::ok(); + return fesa::Status::Ok(); } }; @@ -289,7 +289,7 @@ public: const std::vector&) override { ++writeCalls_; events_.emplace_back("writer-write"); - return fesa::Status::ok(); + return fesa::Status::Ok(); } int writeCalls() const noexcept { return writeCalls_; } @@ -311,7 +311,7 @@ public: shellElementCount_ = domain.shellElements().size(); state_ = std::make_unique(state); diagnostics_ = diagnostics; - return fesa::Status::ok(); + return fesa::Status::Ok(); } const fesa::AnalysisState& state() const { @@ -345,7 +345,7 @@ private: TEST(LinearStaticCli, FactorizesBeforeLoadAndSolvesWithoutRefactorization) { SpyAnalysis lifecycle; const fesa::AnalysisRequest emptyRequest{}; - ASSERT_TRUE(lifecycle.run(emptyRequest).isOk()); + ASSERT_TRUE(lifecycle.run(emptyRequest).IsOk()); EXPECT_EQ( lifecycle.events(), (std::vector{ @@ -369,7 +369,7 @@ TEST(LinearStaticCli, FactorizesBeforeLoadAndSolvesWithoutRefactorization) { SpyResultsWriter writer{adapterEvents}; fesa::LinearStaticAnalysis analysis{serial, solver, writer}; - ASSERT_TRUE(analysis.run({input, output}).isOk()); + ASSERT_TRUE(analysis.run({input, output}).IsOk()); EXPECT_EQ(solver.factorizeCalls(), 1); EXPECT_EQ(solver.solveCalls(), 1); EXPECT_EQ(writer.writeCalls(), 1); @@ -391,13 +391,13 @@ TEST(LinearStaticCli, RealPipelineHandlesAnalyticalAndNonzeroPrescription) { fesa::LinearStaticAnalysis analysis{serial, solver, writer}; const auto status = analysis.run({input, output}); - ASSERT_TRUE(status.isOk()); + ASSERT_TRUE(status.IsOk()); EXPECT_EQ(writer.outputPath(), output); EXPECT_EQ(writer.nodeCount(), 2U); EXPECT_TRUE(writer.diagnostics().empty()); const auto& state = writer.state(); - ASSERT_EQ(state.displacement().size(), 12U); + ASSERT_EQ(state.displacement().Size(), 12U); EXPECT_EQ(state.identity().stepName, "Step-1"); EXPECT_EQ(state.identity().frameIndex, 0U); @@ -427,7 +427,7 @@ TEST(Mitc4ShellCli, UsesExistingLifecycleAndExactlyOneFactorization) { SpyResultsWriter writer{adapterEvents}; fesa::LinearStaticAnalysis analysis{serial, solver, writer}; - ASSERT_TRUE(analysis.run({input, output}).isOk()); + ASSERT_TRUE(analysis.run({input, output}).IsOk()); EXPECT_EQ(solver.factorizeCalls(), 1); EXPECT_EQ(solver.solveCalls(), 1); EXPECT_EQ(writer.writeCalls(), 1); @@ -450,11 +450,11 @@ TEST(Mitc4ShellCli, AppliesKfcForNonzeroPrescribedDisplacement) { fesa::LinearStaticAnalysis analysis{serial, solver, writer}; const auto status = analysis.run({input, output}); - for (const auto& diagnostic : status.diagnostics()) { - EXPECT_TRUE(status.isOk()) + for (const auto& diagnostic : status.Diagnostics()) { + EXPECT_TRUE(status.IsOk()) << diagnostic.code << ": " << diagnostic.message; } - ASSERT_TRUE(status.isOk()); + ASSERT_TRUE(status.IsOk()); ASSERT_EQ(solver.factorizeCalls(), 1); ASSERT_EQ(solver.solveCalls(), 1); ASSERT_EQ(solver.factorizedDimension(), 1U); @@ -467,7 +467,7 @@ TEST(Mitc4ShellCli, AppliesKfcForNonzeroPrescribedDisplacement) { EXPECT_EQ(writer.nodeCount(), 4U); EXPECT_EQ(writer.shellElementCount(), 1U); const auto& state = writer.state(); - ASSERT_EQ(state.displacement().size(), 24U); + ASSERT_EQ(state.displacement().Size(), 24U); EXPECT_NEAR(state.displacement()[6U], 0.1, 1.0e-12); EXPECT_NEAR(state.displacement()[12U], -1.0 / 110.0, 1.0e-12); EXPECT_NEAR(state.verificationMetrics()[0U], 0.0, 1.0e-10); @@ -491,8 +491,8 @@ TEST(Mitc4ShellCli, RejectsSingularAndAcceptsZeroByZeroFreeSystem) { serial, singularSolver, singularWriter}; const auto singular = singularAnalysis.run( {singularInput, directory.path() / "singular.h5"}); - ASSERT_FALSE(singular.isOk()); - EXPECT_EQ(singular.failureCategory(), fesa::FailureCategory::solver); + ASSERT_FALSE(singular.IsOk()); + EXPECT_EQ(singular.Category(), fesa::FailureCategory::kSolver); EXPECT_EQ(singularWriter.writeCalls(), 0); RecordingMklSolver constrainedSolver; @@ -501,7 +501,7 @@ TEST(Mitc4ShellCli, RejectsSingularAndAcceptsZeroByZeroFreeSystem) { serial, constrainedSolver, constrainedWriter}; const auto constrained = constrainedAnalysis.run( {constrainedInput, directory.path() / "constrained.h5"}); - ASSERT_TRUE(constrained.isOk()); + ASSERT_TRUE(constrained.IsOk()); EXPECT_EQ(constrainedSolver.factorizeCalls(), 1); EXPECT_EQ(constrainedSolver.factorizedDimension(), 0U); EXPECT_EQ(constrainedSolver.solveCalls(), 1); @@ -523,10 +523,10 @@ TEST(Mitc4ShellCli, DoesNotWriteAnInvalidRecoveryCandidate) { const auto status = analysis.run( {input, directory.path() / "must-not-exist.h5"}); - ASSERT_FALSE(status.isOk()); - EXPECT_EQ(status.failureCategory(), fesa::FailureCategory::model); - ASSERT_FALSE(status.diagnostics().empty()); - EXPECT_EQ(status.diagnostics().front().code, "nonfinite-recovery-value"); + ASSERT_FALSE(status.IsOk()); + EXPECT_EQ(status.Category(), fesa::FailureCategory::kModel); + ASSERT_FALSE(status.Diagnostics().empty()); + EXPECT_EQ(status.Diagnostics().front().code, "nonfinite-recovery-value"); EXPECT_EQ(writer.writeCalls(), 0); EXPECT_TRUE(adapterEvents.empty()); } diff --git a/tests/reference/b33_reference_comparison_test.cpp b/tests/reference/b33_reference_comparison_test.cpp index e34fdc6..a19ac3a 100644 --- a/tests/reference/b33_reference_comparison_test.cpp +++ b/tests/reference/b33_reference_comparison_test.cpp @@ -202,8 +202,8 @@ TEST(B33ReferenceComparison, auto comparisonResult = fesa::test::ReferenceComparison::compare( results, referenceDirectory); - ASSERT_TRUE(comparisonResult.hasValue()); - const auto& report = comparisonResult.value(); + ASSERT_TRUE(comparisonResult.HasValue()); + const auto& report = comparisonResult.Value(); ASSERT_TRUE(report.passed); ASSERT_EQ(report.rows.size(), kExpectedRowCount); ASSERT_EQ(report.metrics.size(), kExpectedMetricCount); @@ -255,7 +255,7 @@ TEST(B33ReferenceComparison, ASSERT_TRUE( fesa::test::ReferenceComparison::writeDeterministicJson( report, comparison) - .isOk()); + .IsOk()); ASSERT_TRUE(std::filesystem::is_regular_file(comparison)); const std::string json = readBytes(comparison); EXPECT_NE(json.find("\"stress_comparison_applicable\":false"), diff --git a/tests/reference/mitc4_reference_cases_test.cpp b/tests/reference/mitc4_reference_cases_test.cpp index 27ad3c7..ad6db0f 100644 --- a/tests/reference/mitc4_reference_cases_test.cpp +++ b/tests/reference/mitc4_reference_cases_test.cpp @@ -86,10 +86,10 @@ CaseEvidence runCase( EXPECT_TRUE(std::filesystem::is_regular_file(results)); auto comparisonResult = fesa::test::Mitc4ReferenceComparison::compare( {caseId, sourceElementType, input, csv, results}); - if (!comparisonResult.hasValue()) { + if (!comparisonResult.HasValue()) { std::string diagnostics; for (const auto& diagnostic : - comparisonResult.status().diagnostics()) { + comparisonResult.GetStatus().Diagnostics()) { diagnostics += "\n" + diagnostic.code + ": " + diagnostic.message; } ADD_FAILURE() << "MITC4 comparison precheck failed for " << caseId @@ -98,8 +98,8 @@ CaseEvidence runCase( } EXPECT_TRUE( fesa::test::Mitc4ReferenceComparison::writeDeterministicJson( - comparisonResult.value(), comparison) - .isOk()); + comparisonResult.Value(), comparison) + .IsOk()); EXPECT_TRUE(std::filesystem::is_regular_file(comparison)); std::vector generated; @@ -114,7 +114,7 @@ CaseEvidence runCase( EXPECT_TRUE(std::filesystem::is_directory(referenceDirectory)); expectUnchanged(input, inputBefore); expectUnchanged(csv, csvBefore); - return {std::move(comparisonResult.value()), comparison}; + return {std::move(comparisonResult.Value()), comparison}; } void expectCommonMetadata( diff --git a/tests/reference/mitc4_reference_comparison.cpp b/tests/reference/mitc4_reference_comparison.cpp index 2ef807a..3be97f6 100644 --- a/tests/reference/mitc4_reference_comparison.cpp +++ b/tests/reference/mitc4_reference_comparison.cpp @@ -58,9 +58,9 @@ Status failureStatus( const std::string& caseId, const std::string& code, const std::string& message) { - return Status::failure( - FailureCategory::model, - {{Severity::error, code, {}, "", caseId, message}}); + return Status::Failure( + FailureCategory::kModel, + {{Severity::kError, code, {}, "", caseId, message}}); } std::string trim(const std::string& value) { @@ -835,12 +835,12 @@ Result Mitc4ReferenceComparison::compare( errorNorms[component], worstRows[component]}); } - return Result::success(std::move(report)); + return Result::Success(std::move(report)); } catch (const ComparisonFailure& exception) { - return Result::failure(failureStatus( + return Result::Failure(failureStatus( referenceCase.caseId, exception.code(), exception.what())); } catch (const std::exception& exception) { - return Result::failure(failureStatus( + return Result::Failure(failureStatus( referenceCase.caseId, "comparison-failure", exception.what())); } } @@ -936,7 +936,7 @@ Status Mitc4ReferenceComparison::writeDeterministicJson( "comparison-report-write-failed", "The deterministic MITC4 JSON report could not be completed."); } - return Status::ok(); + return Status::Ok(); } catch (const std::exception& exception) { return failureStatus( report.caseId, "comparison-report-write-failed", exception.what()); diff --git a/tests/reference/mitc4_reference_comparison.hpp b/tests/reference/mitc4_reference_comparison.hpp index 9a77bf3..18c1b9c 100644 --- a/tests/reference/mitc4_reference_comparison.hpp +++ b/tests/reference/mitc4_reference_comparison.hpp @@ -1,6 +1,6 @@ #pragma once -#include "fesa/core/status.hpp" +#include "fesa/core/status.h" #include #include diff --git a/tests/reference/mitc4_reference_comparison_test.cpp b/tests/reference/mitc4_reference_comparison_test.cpp index 578b7d5..d3e8f5b 100644 --- a/tests/reference/mitc4_reference_comparison_test.cpp +++ b/tests/reference/mitc4_reference_comparison_test.cpp @@ -501,9 +501,9 @@ private: void expectFailureCode( const fesa::Result& result, const std::string& code) { - ASSERT_FALSE(result.hasValue()); - ASSERT_FALSE(result.status().diagnostics().empty()); - EXPECT_EQ(result.status().diagnostics().front().code, code); + ASSERT_FALSE(result.HasValue()); + ASSERT_FALSE(result.GetStatus().Diagnostics().empty()); + EXPECT_EQ(result.GetStatus().Diagnostics().front().code, code); } const fesa::test::Mitc4RowDecision* findRow( @@ -550,8 +550,8 @@ TEST(Mitc4ReferenceComparison, MapsTrimmedHeaderAndSixComponentsBySourceIdentity auto result = fesa::test::Mitc4ReferenceComparison::compare( fixture.referenceCase()); - ASSERT_TRUE(result.hasValue()); - const auto& report = result.value(); + ASSERT_TRUE(result.HasValue()); + const auto& report = result.Value(); ASSERT_TRUE(report.passed); ASSERT_EQ(report.rows.size(), 12U); EXPECT_EQ(report.rows[0U].sourceNodeLabel, 1); @@ -676,8 +676,8 @@ TEST(Mitc4ReferenceComparison, AppliesFixedAbsoluteToleranceWithoutScaleClampOrR auto result = fesa::test::Mitc4ReferenceComparison::compare( fixture.referenceCase()); - ASSERT_TRUE(result.hasValue()); - const auto& report = result.value(); + ASSERT_TRUE(result.HasValue()); + const auto& report = result.Value(); EXPECT_FALSE(report.passed); const auto* u1Zero = findRow(report, 1, "U1"); const auto* u1Scaled = findRow(report, 2, "U1"); @@ -717,8 +717,8 @@ TEST(Mitc4ReferenceComparison, RotationExceedanceWarnsWithoutBlockingTranslation auto result = fesa::test::Mitc4ReferenceComparison::compare( fixture.referenceCase()); - ASSERT_TRUE(result.hasValue()); - const auto& report = result.value(); + ASSERT_TRUE(result.HasValue()); + const auto& report = result.Value(); EXPECT_TRUE(report.passed); ASSERT_EQ(report.warnings.size(), 1U); EXPECT_EQ(report.warnings[0U].code, "rotation-reference-exceedance"); @@ -744,8 +744,8 @@ TEST(Mitc4ReferenceComparison, ReportsMetricsVectorsWorstRowAndJsonDeterministic auto result = fesa::test::Mitc4ReferenceComparison::compare( fixture.referenceCase()); - ASSERT_TRUE(result.hasValue()); - const auto& report = result.value(); + ASSERT_TRUE(result.HasValue()); + const auto& report = result.Value(); ASSERT_TRUE(report.passed); ASSERT_EQ(report.metrics.size(), 6U); ASSERT_EQ(report.vectorMetrics.size(), 2U); @@ -771,11 +771,11 @@ TEST(Mitc4ReferenceComparison, ReportsMetricsVectorsWorstRowAndJsonDeterministic ASSERT_TRUE( fesa::test::Mitc4ReferenceComparison::writeDeterministicJson( report, jsonA) - .isOk()); + .IsOk()); ASSERT_TRUE( fesa::test::Mitc4ReferenceComparison::writeDeterministicJson( report, jsonB) - .isOk()); + .IsOk()); const std::string first = readBytes(jsonA); EXPECT_EQ(first, readBytes(jsonB)); for (const char* key : { @@ -798,8 +798,8 @@ TEST(Mitc4ReferenceComparison, RequiresOnlyDeclaredInputCsvAndHdf5) { (std::vector{"case.inp", "displacements.csv", "results.h5"})); auto result = fesa::test::Mitc4ReferenceComparison::compare( fixture.referenceCase()); - ASSERT_TRUE(result.hasValue()); - EXPECT_TRUE(result.value().passed); + ASSERT_TRUE(result.HasValue()); + EXPECT_TRUE(result.Value().passed); } } // namespace diff --git a/tests/reference/reference_comparison.cpp b/tests/reference/reference_comparison.cpp index bc8c6ec..8dc6379 100644 --- a/tests/reference/reference_comparison.cpp +++ b/tests/reference/reference_comparison.cpp @@ -71,9 +71,9 @@ private: Status comparisonFailureStatus( const std::string& code, const std::string& message) { - return Status::failure( - FailureCategory::model, - {{Severity::error, code, {}, "", kModelId, message}}); + return Status::Failure( + FailureCategory::kModel, + {{Severity::kError, code, {}, "", kModelId, message}}); } std::string trim(const std::string& value) { @@ -256,17 +256,17 @@ void requireExactArtifactInventory( Domain readApprovedDomain(const std::filesystem::path& inputPath) { AbaqusInputReader reader; auto parsed = reader.read(inputPath); - if (!parsed.hasValue()) { + if (!parsed.HasValue()) { fail("needs-reference-artifacts", "The approved reference input cannot be parsed."); } AbaqusDomainMapper mapper; - auto domain = mapper.map(parsed.value()); - if (!domain.hasValue()) { + auto domain = mapper.map(parsed.Value()); + if (!domain.HasValue()) { fail( "needs-reference-artifacts", "The approved reference input is not the required B33 model."); } - return std::move(domain.value()); + return std::move(domain.Value()); } class Hdf5Handle { @@ -878,9 +878,9 @@ HdfProjection readHdfProjection( const auto& actual = projection.nodes[node]; const auto& expected = domain.nodes()[node]; if (actual.internalNodeId != node || - actual.instanceName != expected.sourceId.instanceName || - actual.sourceNodeLabel != expected.sourceId.sourceLabel || - actual.sourceNodeLabelText != expected.sourceId.sourceLabelText || + actual.instanceName != expected.sourceId.instance_name || + actual.sourceNodeLabel != expected.sourceId.source_label || + actual.sourceNodeLabelText != expected.sourceId.source_label_text || actual.coordinates != expected.coordinates) { fail("schema-mismatch", "An HDF5 node identity does not match the input."); } @@ -889,9 +889,9 @@ HdfProjection readHdfProjection( const auto& actual = projection.elements[element]; const auto& expected = domain.elements()[element]; if (actual.internalElementId != element || - actual.instanceName != expected.sourceId.instanceName || - actual.sourceElementLabel != expected.sourceId.sourceLabel || - actual.sourceElementLabelText != expected.sourceId.sourceLabelText || + actual.instanceName != expected.sourceId.instance_name || + actual.sourceElementLabel != expected.sourceId.source_label || + actual.sourceElementLabelText != expected.sourceId.source_label_text || actual.nodeInternalIds[0U] != expected.nodeIndices[0U] || actual.nodeInternalIds[1U] != expected.nodeIndices[1U]) { fail("schema-mismatch", "An HDF5 element identity does not match the input."); @@ -963,10 +963,10 @@ std::vector normalizeStations( const HdfProjection& hdf, const ReferenceTable& sectionTable) { auto modelResult = AnalysisModel::create(domain); - if (!modelResult.hasValue()) { + if (!modelResult.HasValue()) { fail("schema-mismatch", "The approved input cannot create an analysis view."); } - const AnalysisModel model = std::move(modelResult.value()); + const AnalysisModel model = std::move(modelResult.Value()); const std::array tolerances = { kForceMomentFloor + kRelativeCoefficient * tableScale(sectionTable, 0U), kForceMomentFloor + kRelativeCoefficient * tableScale(sectionTable, 3U), @@ -993,15 +993,15 @@ std::vector normalizeStations( } auto normalized = ResultRecovery::normalizeSectionResultantsToNodeStations( model, endpoints, tolerances); - if (!normalized.hasValue()) { - const auto& diagnostics = normalized.status().diagnostics(); + if (!normalized.HasValue()) { + const auto& diagnostics = normalized.GetStatus().Diagnostics(); const std::string code = diagnostics.empty() ? std::string{} : diagnostics[0U].code; if (code == "node-station-tolerance-failure") { fail("tolerance-failure", "Interior endpoint section resultants disagree."); } fail("schema-mismatch", "A node station is not eligible for legacy projection."); } - return std::move(normalized.value()); + return std::move(normalized.Value()); } const NodeStationResultRow& findStation( @@ -1009,8 +1009,8 @@ const NodeStationResultRow& findStation( const HdfNode& node) { const auto found = std::find_if( stations.begin(), stations.end(), [&](const NodeStationResultRow& row) { - return row.node.instanceName == node.instanceName && - row.node.sourceLabel == node.sourceNodeLabel; + return row.node.instance_name == node.instanceName && + row.node.source_label == node.sourceNodeLabel; }); if (found == stations.end()) { fail("schema-mismatch", "A projected HDF5 node station is missing."); @@ -1165,21 +1165,21 @@ PhysicsEvidence makePhysicsEvidence( const Domain& domain, const HdfProjection& hdf) { auto modelResult = AnalysisModel::create(domain); - if (!modelResult.hasValue()) { + if (!modelResult.HasValue()) { fail("schema-mismatch", "The approved input cannot create physics evidence."); } - const AnalysisModel model = std::move(modelResult.value()); + const AnalysisModel model = std::move(modelResult.Value()); auto dofsResult = DofManager::create(model); - if (!dofsResult.hasValue()) { + if (!dofsResult.HasValue()) { fail("schema-mismatch", "The approved input cannot create a DOF map."); } - const DofManager dofs = std::move(dofsResult.value()); + const DofManager dofs = std::move(dofsResult.Value()); auto loadResult = LoadAssembler::assembleFullNodalLoad(model, dofs); - if (!loadResult.hasValue()) { + if (!loadResult.HasValue()) { fail("schema-mismatch", "The approved input load cannot be assembled."); } - const Vector load = std::move(loadResult.value()); - if (load.size() != hdf.reaction.size()) { + const Vector load = std::move(loadResult.Value()); + if (load.Size() != hdf.reaction.size()) { fail("schema-mismatch", "The load and reaction spaces are inconsistent."); } @@ -1407,12 +1407,12 @@ Result ReferenceComparison::compare( report.stressComparisonReason = "Abaqus beam stress comparison is N/A; analytical/unit and HDF5 " "schema tests provide stress evidence."; - return Result::success(std::move(report)); + return Result::Success(std::move(report)); } catch (const ComparisonFailure& failure) { - return Result::failure( + return Result::Failure( comparisonFailureStatus(failure.code(), failure.what())); } catch (const std::exception& failure) { - return Result::failure(comparisonFailureStatus( + return Result::Failure(comparisonFailureStatus( "schema-mismatch", failure.what())); } } @@ -1422,9 +1422,9 @@ Status ReferenceComparison::writeDeterministicJson( const std::filesystem::path& outputJson) { if (outputJson.empty() || outputJson.filename().empty() || !finiteReport(report)) { - return Status::failure( - FailureCategory::output, - {{Severity::error, + return Status::Failure( + FailureCategory::kOutput, + {{Severity::kError, "comparison-json-write-failure", {}, "", @@ -1433,9 +1433,9 @@ Status ReferenceComparison::writeDeterministicJson( } std::ofstream stream{outputJson, std::ios::binary | std::ios::trunc}; if (!stream) { - return Status::failure( - FailureCategory::output, - {{Severity::error, + return Status::Failure( + FailureCategory::kOutput, + {{Severity::kError, "comparison-json-write-failure", {}, "", @@ -1495,16 +1495,16 @@ Status ReferenceComparison::writeDeterministicJson( writeJsonString(stream, report.stressComparisonReason); stream << ",\"passed\":" << (report.passed ? "true" : "false") << "}\n"; if (!stream) { - return Status::failure( - FailureCategory::output, - {{Severity::error, + return Status::Failure( + FailureCategory::kOutput, + {{Severity::kError, "comparison-json-write-failure", {}, "", kModelId, "The deterministic comparison JSON write failed."}}); } - return Status::ok(); + return Status::Ok(); } } // namespace fesa::test diff --git a/tests/reference/reference_comparison.hpp b/tests/reference/reference_comparison.hpp index 28114e2..e7c056b 100644 --- a/tests/reference/reference_comparison.hpp +++ b/tests/reference/reference_comparison.hpp @@ -1,6 +1,6 @@ #pragma once -#include "fesa/core/status.hpp" +#include "fesa/core/status.h" #include #include diff --git a/tests/reference/reference_comparison_test.cpp b/tests/reference/reference_comparison_test.cpp index 3d430bf..9361eb1 100644 --- a/tests/reference/reference_comparison_test.cpp +++ b/tests/reference/reference_comparison_test.cpp @@ -214,25 +214,25 @@ void writeResultsFixture( const std::filesystem::path& input, const ComparisonValues& values) { auto parsedInput = fesa::AbaqusInputReader{}.read(input); - if (!parsedInput.hasValue()) { + if (!parsedInput.HasValue()) { throw std::runtime_error{"Reference fixture input identity read failed."}; } auto domainResult = fesa::Domain::create( - makeDefinition(input, parsedInput.value().sourceContentIdentity)); - if (!domainResult.hasValue()) { + makeDefinition(input, parsedInput.Value().sourceContentIdentity)); + if (!domainResult.HasValue()) { throw std::runtime_error{"Reference fixture Domain construction failed."}; } - fesa::Domain domain = std::move(domainResult.value()); + fesa::Domain domain = std::move(domainResult.Value()); auto modelResult = fesa::AnalysisModel::create(domain); - if (!modelResult.hasValue()) { + if (!modelResult.HasValue()) { throw std::runtime_error{"Reference fixture AnalysisModel construction failed."}; } - fesa::AnalysisModel model = std::move(modelResult.value()); + fesa::AnalysisModel model = std::move(modelResult.Value()); auto dofsResult = fesa::DofManager::create(model); - if (!dofsResult.hasValue()) { + if (!dofsResult.HasValue()) { throw std::runtime_error{"Reference fixture DofManager construction failed."}; } - fesa::DofManager dofs = std::move(dofsResult.value()); + fesa::DofManager dofs = std::move(dofsResult.Value()); fesa::AnalysisState state = fesa::AnalysisState::create(dofs, {"Step-1", 0U}); @@ -278,7 +278,7 @@ void writeResultsFixture( fesa::Hdf5ResultsWriter writer; const fesa::Status status = writer.write(output, domain, state, {}); - if (!status.isOk()) { + if (!status.IsOk()) { throw std::runtime_error{"Reference fixture HDF5 write failed."}; } } @@ -330,10 +330,10 @@ private: void expectFailureCode( const fesa::Result& result, const std::string& expectedCode) { - ASSERT_FALSE(result.hasValue()); - ASSERT_FALSE(result.status().isOk()); - ASSERT_FALSE(result.status().diagnostics().empty()); - EXPECT_EQ(result.status().diagnostics().front().code, expectedCode); + ASSERT_FALSE(result.HasValue()); + ASSERT_FALSE(result.GetStatus().IsOk()); + ASSERT_FALSE(result.GetStatus().Diagnostics().empty()); + EXPECT_EQ(result.GetStatus().Diagnostics().front().code, expectedCode); } const fesa::test::RowDecision* findRow( @@ -557,8 +557,8 @@ TEST(ReferenceComparisonContract, auto result = fesa::test::ReferenceComparison::compare( fixture.results(), fixture.legacy()); - ASSERT_TRUE(result.hasValue()); - const auto& report = result.value(); + ASSERT_TRUE(result.HasValue()); + const auto& report = result.Value(); EXPECT_FALSE(report.passed); EXPECT_EQ(report.rows.size(), kExpectedRowCount); EXPECT_EQ(report.metrics.size(), kExpectedMetricCount); @@ -608,8 +608,8 @@ TEST(ReferenceComparisonContract, auto result = fesa::test::ReferenceComparison::compare( fixture.results(), fixture.legacy()); - ASSERT_TRUE(result.hasValue()); - const auto& report = result.value(); + ASSERT_TRUE(result.HasValue()); + const auto& report = result.Value(); ASSERT_TRUE(report.passed); expectExactRowInventory(report); ASSERT_EQ(report.metrics.size(), kExpectedMetricCount); @@ -658,10 +658,10 @@ TEST(ReferenceComparisonContract, const auto jsonB = fixture.root() / "comparison-b.json"; ASSERT_TRUE( fesa::test::ReferenceComparison::writeDeterministicJson(report, jsonA) - .isOk()); + .IsOk()); ASSERT_TRUE( fesa::test::ReferenceComparison::writeDeterministicJson(report, jsonB) - .isOk()); + .IsOk()); const std::string first = readBytes(jsonA); EXPECT_EQ(first, readBytes(jsonB)); for (const char* required : { @@ -690,8 +690,8 @@ TEST(ReferenceComparisonContract, auto result = fesa::test::ReferenceComparison::compare( fixture.results(), fixture.legacy()); - ASSERT_TRUE(result.hasValue()); - const auto& report = result.value(); + ASSERT_TRUE(result.HasValue()); + const auto& report = result.Value(); ASSERT_TRUE(report.passed); EXPECT_TRUE(report.physicsEvidence.endpointConsistencyPassed); diff --git a/tests/unit/analysis/analysis_model_test.cpp b/tests/unit/analysis/analysis_model_test.cpp index dedd0ed..8145cde 100644 --- a/tests/unit/analysis/analysis_model_test.cpp +++ b/tests/unit/analysis/analysis_model_test.cpp @@ -58,11 +58,11 @@ fesa::ModelDefinition makeDefinition() { TEST(AnalysisModel, ClassifiesActiveEntitiesInStableOrder) { auto domainResult = fesa::Domain::create(makeDefinition()); - ASSERT_TRUE(domainResult.hasValue()); + ASSERT_TRUE(domainResult.HasValue()); - auto modelResult = fesa::AnalysisModel::create(domainResult.value()); - ASSERT_TRUE(modelResult.hasValue()); - const auto& model = modelResult.value(); + auto modelResult = fesa::AnalysisModel::create(domainResult.Value()); + ASSERT_TRUE(modelResult.HasValue()); + const auto& model = modelResult.Value(); EXPECT_EQ( model.activeElements(), @@ -83,8 +83,8 @@ TEST(AnalysisModel, ClassifiesActiveEntitiesInStableOrder) { TEST(AnalysisModel, ReferencesWithoutCopyingOrMutatingDomain) { auto domainResult = fesa::Domain::create(makeDefinition()); - ASSERT_TRUE(domainResult.hasValue()); - const fesa::Domain& domain = domainResult.value(); + ASSERT_TRUE(domainResult.HasValue()); + const fesa::Domain& domain = domainResult.Value(); const auto* const elementAddress = domain.elements().data(); const auto* const materialAddress = domain.materials().data(); const auto* const sectionAddress = domain.sections().data(); @@ -92,8 +92,8 @@ TEST(AnalysisModel, ReferencesWithoutCopyingOrMutatingDomain) { const double firstLoadMagnitude = domain.steps()[0].loads[0].magnitude; auto modelResult = fesa::AnalysisModel::create(domain); - ASSERT_TRUE(modelResult.hasValue()); - const auto& model = modelResult.value(); + ASSERT_TRUE(modelResult.HasValue()); + const auto& model = modelResult.Value(); EXPECT_EQ(&model.domain(), &domain); EXPECT_EQ(&model.step(), &domain.steps()[0]); @@ -117,19 +117,19 @@ TEST(AnalysisModel, RejectsMissingOrMultipleStep) { auto missingDefinition = makeDefinition(); missingDefinition.steps.clear(); auto missingDomain = fesa::Domain::create(std::move(missingDefinition)); - ASSERT_TRUE(missingDomain.hasValue()); + ASSERT_TRUE(missingDomain.HasValue()); - auto missing = fesa::AnalysisModel::create(missingDomain.value()); - ASSERT_FALSE(missing.hasValue()); + auto missing = fesa::AnalysisModel::create(missingDomain.Value()); + ASSERT_FALSE(missing.HasValue()); EXPECT_EQ( - missing.status().failureCategory(), - fesa::FailureCategory::input); - ASSERT_EQ(missing.status().diagnostics().size(), 1U); + missing.GetStatus().Category(), + fesa::FailureCategory::kInput); + ASSERT_EQ(missing.GetStatus().Diagnostics().size(), 1U); EXPECT_EQ( - missing.status().diagnostics()[0].code, + missing.GetStatus().Diagnostics()[0].code, "invalid-model-cardinality"); - EXPECT_EQ(missing.status().diagnostics()[0].keyword, "STEP"); - EXPECT_EQ(missing.status().diagnostics()[0].entityIdentity, "0"); + EXPECT_EQ(missing.GetStatus().Diagnostics()[0].keyword, "STEP"); + EXPECT_EQ(missing.GetStatus().Diagnostics()[0].entity_identity, "0"); auto multipleDefinition = makeDefinition(); auto secondStep = multipleDefinition.steps.front(); @@ -137,18 +137,18 @@ TEST(AnalysisModel, RejectsMissingOrMultipleStep) { secondStep.location.line = 60U; multipleDefinition.steps.push_back(std::move(secondStep)); auto multipleDomain = fesa::Domain::create(std::move(multipleDefinition)); - ASSERT_TRUE(multipleDomain.hasValue()); + ASSERT_TRUE(multipleDomain.HasValue()); - auto multiple = fesa::AnalysisModel::create(multipleDomain.value()); - ASSERT_FALSE(multiple.hasValue()); + auto multiple = fesa::AnalysisModel::create(multipleDomain.Value()); + ASSERT_FALSE(multiple.HasValue()); EXPECT_EQ( - multiple.status().failureCategory(), - fesa::FailureCategory::input); - ASSERT_EQ(multiple.status().diagnostics().size(), 1U); + multiple.GetStatus().Category(), + fesa::FailureCategory::kInput); + ASSERT_EQ(multiple.GetStatus().Diagnostics().size(), 1U); EXPECT_EQ( - multiple.status().diagnostics()[0].code, + multiple.GetStatus().Diagnostics()[0].code, "unsupported-multiple-step"); - EXPECT_EQ(multiple.status().diagnostics()[0].keyword, "STEP"); - EXPECT_EQ(multiple.status().diagnostics()[0].entityIdentity, "Step-2"); - EXPECT_EQ(multiple.status().diagnostics()[0].location.line, 60U); + EXPECT_EQ(multiple.GetStatus().Diagnostics()[0].keyword, "STEP"); + EXPECT_EQ(multiple.GetStatus().Diagnostics()[0].entity_identity, "Step-2"); + EXPECT_EQ(multiple.GetStatus().Diagnostics()[0].location.line, 60U); } diff --git a/tests/unit/analysis/analysis_state_test.cpp b/tests/unit/analysis/analysis_state_test.cpp index 2bfcc7b..3cf1d40 100644 --- a/tests/unit/analysis/analysis_state_test.cpp +++ b/tests/unit/analysis/analysis_state_test.cpp @@ -65,16 +65,16 @@ fesa::DofManager makeDofs() { {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()); + 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) { + for (std::size_t index = 0U; index < vector.Size(); ++index) { EXPECT_DOUBLE_EQ(vector[index], 0.0); } } @@ -96,12 +96,12 @@ TEST(AnalysisState, AllocatesOnlyV0FullVectors) { &constState.reaction()}; for (const auto* vector : vectors) { - EXPECT_EQ(vector->size(), dofs.fullDofCount()); + 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()); + EXPECT_NE(vectors[left]->Data(), vectors[right]->Data()); } } @@ -142,8 +142,8 @@ TEST(AnalysisState, CopiesOrMovesWithoutAliasing) { {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.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()); @@ -156,7 +156,7 @@ TEST(AnalysisState, CopiesOrMovesWithoutAliasing) { 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.displacement().Data(), original.displacement().Data()); EXPECT_NE(moved.endpointResults().data(), original.endpointResults().data()); auto copyAssigned = fesa::AnalysisState::create(dofs, {"Other", 3U}); @@ -169,5 +169,5 @@ TEST(AnalysisState, CopiesOrMovesWithoutAliasing) { 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()); + EXPECT_NE(moveAssigned.reaction().Data(), original.reaction().Data()); } diff --git a/tests/unit/assembly/load_assembler_test.cpp b/tests/unit/assembly/load_assembler_test.cpp index be87014..f88da39 100644 --- a/tests/unit/assembly/load_assembler_test.cpp +++ b/tests/unit/assembly/load_assembler_test.cpp @@ -54,25 +54,25 @@ LoadFixture makeFixture( {source, 20U}}}; auto domainResult = fesa::Domain::create(std::move(definition)); - if (!domainResult.hasValue()) { + if (!domainResult.HasValue()) { throw std::runtime_error{"Load fixture Domain construction failed."}; } auto domain = std::make_unique( - std::move(domainResult.value())); + std::move(domainResult.Value())); auto modelResult = fesa::AnalysisModel::create(*domain); - if (!modelResult.hasValue()) { + if (!modelResult.HasValue()) { throw std::runtime_error{"Load fixture AnalysisModel construction failed."}; } auto model = std::make_unique( - std::move(modelResult.value())); + std::move(modelResult.Value())); auto dofResult = fesa::DofManager::create(*model); - if (!dofResult.hasValue()) { + if (!dofResult.HasValue()) { throw std::runtime_error{"Load fixture DofManager construction failed."}; } auto dofs = std::make_unique( - std::move(dofResult.value())); + std::move(dofResult.Value())); return {std::move(domain), std::move(model), std::move(dofs)}; } @@ -117,25 +117,25 @@ LoadFixture makeShellFixture( {source, 20U}}}; auto domainResult = fesa::Domain::create(std::move(definition)); - if (!domainResult.hasValue()) { + if (!domainResult.HasValue()) { throw std::runtime_error{"Shell load fixture Domain construction failed."}; } auto domain = std::make_unique( - std::move(domainResult.value())); + std::move(domainResult.Value())); auto modelResult = fesa::AnalysisModel::create(*domain); - if (!modelResult.hasValue()) { + if (!modelResult.HasValue()) { throw std::runtime_error{"Shell load fixture AnalysisModel construction failed."}; } auto model = std::make_unique( - std::move(modelResult.value())); + std::move(modelResult.Value())); auto dofResult = fesa::DofManager::create(*model); - if (!dofResult.hasValue()) { + if (!dofResult.HasValue()) { throw std::runtime_error{"Shell load fixture DofManager construction failed."}; } auto dofs = std::make_unique( - std::move(dofResult.value())); + std::move(dofResult.Value())); return {std::move(domain), std::move(model), std::move(dofs)}; } @@ -164,21 +164,21 @@ fesa::SparseMatrix makeDenseSparse( pattern.rowOffsets.push_back(pattern.columnIndices.size()); } - auto result = fesa::SparseMatrix::fromCoo( + auto result = fesa::SparseMatrix::FromCoo( rows, columns, std::move(contributions), pattern); - if (!result.hasValue()) { + if (!result.HasValue()) { throw std::runtime_error{"Sparse fixture construction failed."}; } - return std::move(result.value()); + return std::move(result.Value()); } void expectFailureCode( const fesa::Result& result, const std::string& code) { - ASSERT_FALSE(result.hasValue()); - EXPECT_EQ(result.status().failureCategory(), fesa::FailureCategory::model); - ASSERT_EQ(result.status().diagnostics().size(), 1U); - EXPECT_EQ(result.status().diagnostics()[0U].code, code); + ASSERT_FALSE(result.HasValue()); + EXPECT_EQ(result.GetStatus().Category(), fesa::FailureCategory::kModel); + ASSERT_EQ(result.GetStatus().Diagnostics().size(), 1U); + EXPECT_EQ(result.GetStatus().Diagnostics()[0U].code, code); } } // namespace @@ -198,16 +198,16 @@ TEST(LoadAssembly, AssemblesNodeSetAndSixComponentLoads) { auto result = fesa::LoadAssembler::assembleFullNodalLoad( *fixture.model, *fixture.dofs); - ASSERT_TRUE(result.hasValue()); - ASSERT_EQ(result.value().size(), 12U); + ASSERT_TRUE(result.HasValue()); + ASSERT_EQ(result.Value().Size(), 12U); EXPECT_EQ( - std::vector(result.value().data(), result.value().data() + 12U), + std::vector(result.Value().Data(), result.Value().Data() + 12U), (std::vector{ 1.0, 2.0, 0.0, -4.0, 5.0, 0.0, 1.0, 0.0, 3.0, 0.0, 5.0, 6.0})); EXPECT_EQ(fixture.dofs->constrainedDofs(), (std::vector{0U})); - EXPECT_DOUBLE_EQ(result.value()[0U], 1.0); + EXPECT_DOUBLE_EQ(result.Value()[0U], 1.0); } TEST(LoadAssembly, AccumulatesSignedLoadsInSourceOrder) { @@ -231,10 +231,10 @@ TEST(LoadAssembly, AccumulatesSignedLoadsInSourceOrder) { *firstOrder.model, *firstOrder.dofs); auto second = fesa::LoadAssembler::assembleFullNodalLoad( *secondOrder.model, *secondOrder.dofs); - ASSERT_TRUE(first.hasValue()); - ASSERT_TRUE(second.hasValue()); - EXPECT_DOUBLE_EQ(first.value()[0U], 1.0); - EXPECT_DOUBLE_EQ(second.value()[0U], 0.0); + ASSERT_TRUE(first.HasValue()); + ASSERT_TRUE(second.HasValue()); + EXPECT_DOUBLE_EQ(first.Value()[0U], 1.0); + EXPECT_DOUBLE_EQ(second.Value()[0U], 0.0); } // MITC4-LOAD-001 @@ -257,10 +257,10 @@ TEST(LoadAssembly, AggregatesAllSixGlobalShellLoadComponentsInSourceOrder) { const auto result = fesa::LoadAssembler::assembleFullNodalLoad( *fixture.model, *fixture.dofs); - ASSERT_TRUE(result.hasValue()); - ASSERT_EQ(result.value().size(), 24U); + ASSERT_TRUE(result.HasValue()); + ASSERT_EQ(result.Value().Size(), 24U); EXPECT_EQ( - std::vector(result.value().data(), result.value().data() + 6U), + std::vector(result.Value().Data(), result.Value().Data() + 6U), (std::vector{1.0, 2.0, 3.0, 4.0, 5.0, 0.0})); } @@ -279,10 +279,10 @@ TEST(LoadAssembly, AcceptsExactlyZeroAggregateShellMoment) { const auto result = fesa::LoadAssembler::assembleFullNodalLoad( *fixture.model, *fixture.dofs); - ASSERT_TRUE(result.hasValue()); - EXPECT_DOUBLE_EQ(result.value()[3U], 0.0); - EXPECT_DOUBLE_EQ(result.value()[4U], 0.0); - EXPECT_DOUBLE_EQ(result.value()[5U], 0.0); + ASSERT_TRUE(result.HasValue()); + EXPECT_DOUBLE_EQ(result.Value()[3U], 0.0); + EXPECT_DOUBLE_EQ(result.Value()[4U], 0.0); + EXPECT_DOUBLE_EQ(result.Value()[5U], 0.0); } // MITC4-LOAD-003 @@ -302,15 +302,15 @@ TEST(LoadAssembly, EnforcesAggregateShellMomentDirectorProjectionThreshold) { const auto rejected = fesa::LoadAssembler::assembleFullNodalLoad( *rejectedFixture.model, *rejectedFixture.dofs); - ASSERT_TRUE(accepted.hasValue()); - ASSERT_FALSE(rejected.hasValue()); - EXPECT_EQ(rejected.status().failureCategory(), fesa::FailureCategory::model); - ASSERT_EQ(rejected.status().diagnostics().size(), 1U); + ASSERT_TRUE(accepted.HasValue()); + ASSERT_FALSE(rejected.HasValue()); + EXPECT_EQ(rejected.GetStatus().Category(), fesa::FailureCategory::kModel); + ASSERT_EQ(rejected.GetStatus().Diagnostics().size(), 1U); EXPECT_EQ( - rejected.status().diagnostics()[0U].code, + rejected.GetStatus().Diagnostics()[0U].code, "unsupported-drilling-load"); - EXPECT_EQ(rejected.status().diagnostics()[0U].keyword, "CLOAD"); - EXPECT_EQ(rejected.status().diagnostics()[0U].entityIdentity, "10"); + EXPECT_EQ(rejected.GetStatus().Diagnostics()[0U].keyword, "CLOAD"); + EXPECT_EQ(rejected.GetStatus().Diagnostics()[0U].entity_identity, "10"); } // MITC4-LOAD-004 @@ -323,11 +323,11 @@ TEST(LoadAssembly, RejectsDrillingMomentBeforeEffectiveRhsCanBeFormed) { const auto rejected = fesa::LoadAssembler::assembleFullNodalLoad( *fixture.model, *fixture.dofs); - ASSERT_FALSE(rejected.hasValue()); - EXPECT_EQ(rejected.status().failureCategory(), fesa::FailureCategory::model); - ASSERT_EQ(rejected.status().diagnostics().size(), 1U); + ASSERT_FALSE(rejected.HasValue()); + EXPECT_EQ(rejected.GetStatus().Category(), fesa::FailureCategory::kModel); + ASSERT_EQ(rejected.GetStatus().Diagnostics().size(), 1U); EXPECT_EQ( - rejected.status().diagnostics()[0U].code, + rejected.GetStatus().Diagnostics()[0U].code, "unsupported-drilling-load"); } @@ -346,7 +346,7 @@ TEST(LoadAssembly, FormsNonzeroPrescribedEffectiveRhs) { {"10", 6, 40.0, {source, 35U}}}); auto full = fesa::LoadAssembler::assembleFullNodalLoad( *fixture.model, *fixture.dofs); - ASSERT_TRUE(full.hasValue()); + ASSERT_TRUE(full.HasValue()); const auto kfc = makeDenseSparse( 4U, 2U, @@ -356,11 +356,11 @@ TEST(LoadAssembly, FormsNonzeroPrescribedEffectiveRhs) { 0.5, -1.0}); auto rhs = fesa::LoadAssembler::effectiveFreeRhs( - full.value(), kfc, fixture.dofs->prescribedValues(), *fixture.dofs); - ASSERT_TRUE(rhs.hasValue()); - ASSERT_EQ(rhs.value().size(), 4U); + full.Value(), kfc, fixture.dofs->prescribedValues(), *fixture.dofs); + ASSERT_TRUE(rhs.HasValue()); + ASSERT_EQ(rhs.Value().Size(), 4U); EXPECT_EQ( - std::vector(rhs.value().data(), rhs.value().data() + 4U), + std::vector(rhs.Value().Data(), rhs.Value().Data() + 4U), (std::vector{10.0, 18.0, 39.0, 38.0})); } @@ -469,22 +469,22 @@ TEST(LoadAssembly, ZeroLoadsRemainZero) { {{"10", 3, 0.0, {source, 30U}}}); auto full = fesa::LoadAssembler::assembleFullNodalLoad( *freeFixture.model, *freeFixture.dofs); - ASSERT_TRUE(full.hasValue()); + ASSERT_TRUE(full.HasValue()); EXPECT_TRUE(std::all_of( - full.value().data(), - full.value().data() + full.value().size(), + full.Value().Data(), + full.Value().Data() + full.Value().Size(), [](const double value) { return value == 0.0; })); const auto noConstrainedColumns = makeDenseSparse(6U, 0U, {}); auto freeRhs = fesa::LoadAssembler::effectiveFreeRhs( - full.value(), + full.Value(), noConstrainedColumns, freeFixture.dofs->prescribedValues(), *freeFixture.dofs); - ASSERT_TRUE(freeRhs.hasValue()); - EXPECT_EQ(freeRhs.value().size(), 6U); + ASSERT_TRUE(freeRhs.HasValue()); + EXPECT_EQ(freeRhs.Value().Size(), 6U); EXPECT_TRUE(std::all_of( - freeRhs.value().data(), - freeRhs.value().data() + freeRhs.value().size(), + freeRhs.Value().Data(), + freeRhs.Value().Data() + freeRhs.Value().Size(), [](const double value) { return value == 0.0; })); auto constrainedFixture = makeFixture( @@ -494,13 +494,13 @@ TEST(LoadAssembly, ZeroLoadsRemainZero) { {}); auto constrainedFull = fesa::LoadAssembler::assembleFullNodalLoad( *constrainedFixture.model, *constrainedFixture.dofs); - ASSERT_TRUE(constrainedFull.hasValue()); + ASSERT_TRUE(constrainedFull.HasValue()); const auto noFreeRows = makeDenseSparse(0U, 6U, {}); auto constrainedRhs = fesa::LoadAssembler::effectiveFreeRhs( - constrainedFull.value(), + constrainedFull.Value(), noFreeRows, constrainedFixture.dofs->prescribedValues(), *constrainedFixture.dofs); - ASSERT_TRUE(constrainedRhs.hasValue()); - EXPECT_EQ(constrainedRhs.value().size(), 0U); + ASSERT_TRUE(constrainedRhs.HasValue()); + EXPECT_EQ(constrainedRhs.Value().Size(), 0U); } diff --git a/tests/unit/assembly/sparse_assembler_test.cpp b/tests/unit/assembly/sparse_assembler_test.cpp index 5a3cadd..ad79de4 100644 --- a/tests/unit/assembly/sparse_assembler_test.cpp +++ b/tests/unit/assembly/sparse_assembler_test.cpp @@ -115,10 +115,10 @@ fesa::Result directShellStiffness( directors, domain.shellSections().at(definition.sectionIndex), domain.materials().at(definition.materialIndex)); - if (!shell.hasValue()) { - return fesa::Result::failure(shell.status()); + if (!shell.HasValue()) { + return fesa::Result::Failure(shell.GetStatus()); } - return shell.value().stiffness(); + return shell.Value().stiffness(); } fesa::Result assembleShell( @@ -127,19 +127,19 @@ fesa::Result assembleShell( const bool twoElements = false) { auto domain = fesa::Domain::create( makeShellDefinition(sourceType, twoElements)); - if (!domain.hasValue()) { - return fesa::Result::failure(domain.status()); + if (!domain.HasValue()) { + return fesa::Result::Failure(domain.GetStatus()); } - auto model = fesa::AnalysisModel::create(domain.value()); - if (!model.hasValue()) { - return fesa::Result::failure(model.status()); + auto model = fesa::AnalysisModel::create(domain.Value()); + if (!model.HasValue()) { + return fesa::Result::Failure(model.GetStatus()); } - auto dofs = fesa::DofManager::create(model.value()); - if (!dofs.hasValue()) { - return fesa::Result::failure(dofs.status()); + auto dofs = fesa::DofManager::create(model.Value()); + if (!dofs.HasValue()) { + return fesa::Result::Failure(dofs.GetStatus()); } return fesa::SparseAssembler::assembleStiffness( - model.value(), dofs.value(), parallelFor); + model.Value(), dofs.Value(), parallelFor); } template @@ -154,14 +154,14 @@ double entry( const fesa::SparseMatrix& matrix, const std::size_t row, const std::size_t column) { - const auto begin = matrix.columnIndices().begin() + matrix.rowOffsets()[row]; - const auto end = matrix.columnIndices().begin() + matrix.rowOffsets()[row + 1U]; + const auto begin = matrix.ColumnIndices().begin() + matrix.RowOffsets()[row]; + const auto end = matrix.ColumnIndices().begin() + matrix.RowOffsets()[row + 1U]; const auto found = std::lower_bound(begin, end, column); if (found == end || *found != column) { return 0.0; } - return matrix.values()[static_cast( - std::distance(matrix.columnIndices().begin(), found))]; + return matrix.Values()[static_cast( + std::distance(matrix.ColumnIndices().begin(), found))]; } class ReverseParallelFor final : public fesa::ParallelFor { @@ -192,66 +192,66 @@ private: void expectByteIdentical( const fesa::SparseMatrix& actual, const fesa::SparseMatrix& expected) { - EXPECT_TRUE(byteIdentical(actual.rowOffsets(), expected.rowOffsets())); - EXPECT_TRUE(byteIdentical(actual.columnIndices(), expected.columnIndices())); - EXPECT_TRUE(byteIdentical(actual.values(), expected.values())); + EXPECT_TRUE(byteIdentical(actual.RowOffsets(), expected.RowOffsets())); + EXPECT_TRUE(byteIdentical(actual.ColumnIndices(), expected.ColumnIndices())); + EXPECT_TRUE(byteIdentical(actual.Values(), expected.Values())); } TEST(SparseAssembly, SerialTbbAndRepeatedRunsAreByteIdentical) { auto domainResult = fesa::Domain::create(makeDefinition()); - ASSERT_TRUE(domainResult.hasValue()); - auto modelResult = fesa::AnalysisModel::create(domainResult.value()); - ASSERT_TRUE(modelResult.hasValue()); - auto dofsResult = fesa::DofManager::create(modelResult.value()); - ASSERT_TRUE(dofsResult.hasValue()); + ASSERT_TRUE(domainResult.HasValue()); + auto modelResult = fesa::AnalysisModel::create(domainResult.Value()); + ASSERT_TRUE(modelResult.HasValue()); + auto dofsResult = fesa::DofManager::create(modelResult.Value()); + ASSERT_TRUE(dofsResult.HasValue()); fesa::SerialParallelFor serialExecutor; fesa::TbbParallelFor tbbExecutor; ReverseParallelFor reverseExecutor; auto serial = fesa::SparseAssembler::assembleStiffness( - modelResult.value(), dofsResult.value(), serialExecutor); + modelResult.Value(), dofsResult.Value(), serialExecutor); auto tbb = fesa::SparseAssembler::assembleStiffness( - modelResult.value(), dofsResult.value(), tbbExecutor); + modelResult.Value(), dofsResult.Value(), tbbExecutor); auto reversed = fesa::SparseAssembler::assembleStiffness( - modelResult.value(), dofsResult.value(), reverseExecutor); - ASSERT_TRUE(serial.hasValue()); - ASSERT_TRUE(tbb.hasValue()); - ASSERT_TRUE(reversed.hasValue()); + modelResult.Value(), dofsResult.Value(), reverseExecutor); + ASSERT_TRUE(serial.HasValue()); + ASSERT_TRUE(tbb.HasValue()); + ASSERT_TRUE(reversed.HasValue()); EXPECT_EQ(reverseExecutor.calls(), 1U); EXPECT_EQ(reverseExecutor.observedCount(), 2U); - EXPECT_EQ(serial.value().rows(), 18U); - EXPECT_EQ(serial.value().columns(), 18U); - EXPECT_EQ(serial.value().rowOffsets(), dofsResult.value().sparsePattern().rowOffsets); + EXPECT_EQ(serial.Value().Rows(), 18U); + EXPECT_EQ(serial.Value().Columns(), 18U); + EXPECT_EQ(serial.Value().RowOffsets(), dofsResult.Value().sparsePattern().rowOffsets); EXPECT_EQ( - serial.value().columnIndices(), - dofsResult.value().sparsePattern().columnIndices); - EXPECT_TRUE(serial.value().validate().isOk()); - expectByteIdentical(tbb.value(), serial.value()); - expectByteIdentical(reversed.value(), serial.value()); + serial.Value().ColumnIndices(), + dofsResult.Value().sparsePattern().columnIndices); + EXPECT_TRUE(serial.Value().Validate().IsOk()); + expectByteIdentical(tbb.Value(), serial.Value()); + expectByteIdentical(reversed.Value(), serial.Value()); for (std::size_t repetition = 0U; repetition < 8U; ++repetition) { auto repeated = fesa::SparseAssembler::assembleStiffness( - modelResult.value(), dofsResult.value(), tbbExecutor); - ASSERT_TRUE(repeated.hasValue()); - expectByteIdentical(repeated.value(), serial.value()); + modelResult.Value(), dofsResult.Value(), tbbExecutor); + ASSERT_TRUE(repeated.HasValue()); + expectByteIdentical(repeated.Value(), serial.Value()); } - for (std::size_t row = 0U; row < serial.value().rows(); ++row) { + for (std::size_t row = 0U; row < serial.Value().Rows(); ++row) { for (std::size_t column = 0U; - column < serial.value().columns(); + column < serial.Value().Columns(); ++column) { EXPECT_DOUBLE_EQ( - entry(serial.value(), row, column), - entry(serial.value(), column, row)); + entry(serial.Value(), row, column), + entry(serial.Value(), column, row)); } } - EXPECT_NEAR(entry(serial.value(), 0U, 0U), 120.0, 1.0e-12); - EXPECT_NEAR(entry(serial.value(), 0U, 6U), -120.0, 1.0e-12); - EXPECT_NEAR(entry(serial.value(), 6U, 6U), 200.0, 1.0e-12); - EXPECT_NEAR(entry(serial.value(), 6U, 12U), -80.0, 1.0e-12); - EXPECT_NEAR(entry(serial.value(), 12U, 12U), 80.0, 1.0e-12); + EXPECT_NEAR(entry(serial.Value(), 0U, 0U), 120.0, 1.0e-12); + EXPECT_NEAR(entry(serial.Value(), 0U, 6U), -120.0, 1.0e-12); + EXPECT_NEAR(entry(serial.Value(), 6U, 6U), 200.0, 1.0e-12); + EXPECT_NEAR(entry(serial.Value(), 6U, 12U), -80.0, 1.0e-12); + EXPECT_NEAR(entry(serial.Value(), 12U, 12U), 80.0, 1.0e-12); } TEST( @@ -259,38 +259,38 @@ TEST( AssemblesFourNodeTwentyFourDofKernelAndPreservesDiagonalSlots) { auto domain = fesa::Domain::create( makeShellDefinition(fesa::ShellSourceElementType::s4)); - ASSERT_TRUE(domain.hasValue()); - auto model = fesa::AnalysisModel::create(domain.value()); - ASSERT_TRUE(model.hasValue()); - auto dofs = fesa::DofManager::create(model.value()); - ASSERT_TRUE(dofs.hasValue()); + ASSERT_TRUE(domain.HasValue()); + auto model = fesa::AnalysisModel::create(domain.Value()); + ASSERT_TRUE(model.HasValue()); + auto dofs = fesa::DofManager::create(model.Value()); + ASSERT_TRUE(dofs.HasValue()); fesa::SerialParallelFor serialExecutor; auto assembled = fesa::SparseAssembler::assembleStiffness( - model.value(), dofs.value(), serialExecutor); - auto expected = directShellStiffness(domain.value(), 0U); - ASSERT_TRUE(assembled.hasValue()); - ASSERT_TRUE(expected.hasValue()); + model.Value(), dofs.Value(), serialExecutor); + auto expected = directShellStiffness(domain.Value(), 0U); + ASSERT_TRUE(assembled.HasValue()); + ASSERT_TRUE(expected.HasValue()); - EXPECT_EQ(assembled.value().rows(), 24U); - EXPECT_EQ(assembled.value().columns(), 24U); - EXPECT_EQ(assembled.value().values().size(), 24U * 24U); + EXPECT_EQ(assembled.Value().Rows(), 24U); + EXPECT_EQ(assembled.Value().Columns(), 24U); + EXPECT_EQ(assembled.Value().Values().size(), 24U * 24U); EXPECT_EQ( - assembled.value().rowOffsets(), - dofs.value().sparsePattern().rowOffsets); + assembled.Value().RowOffsets(), + dofs.Value().sparsePattern().rowOffsets); EXPECT_EQ( - assembled.value().columnIndices(), - dofs.value().sparsePattern().columnIndices); + assembled.Value().ColumnIndices(), + dofs.Value().sparsePattern().columnIndices); for (std::size_t row = 0U; row < 24U; ++row) { - const auto begin = assembled.value().columnIndices().begin() + - assembled.value().rowOffsets()[row]; - const auto end = assembled.value().columnIndices().begin() + - assembled.value().rowOffsets()[row + 1U]; + const auto begin = assembled.Value().ColumnIndices().begin() + + assembled.Value().RowOffsets()[row]; + const auto end = assembled.Value().ColumnIndices().begin() + + assembled.Value().RowOffsets()[row + 1U]; EXPECT_NE(std::lower_bound(begin, end, row), end); for (std::size_t column = 0U; column < 24U; ++column) { EXPECT_DOUBLE_EQ( - entry(assembled.value(), row, column), - expected.value().stabilizedGlobal24(row, column)); + entry(assembled.Value(), row, column), + expected.Value().stabilizedGlobal24(row, column)); } } } @@ -305,19 +305,19 @@ TEST(SparseAssembly, ShellSerialTbbReverseAndRepeatedRunsAreByteIdentical) { fesa::ShellSourceElementType::s4, tbbExecutor, true); auto reversed = assembleShell( fesa::ShellSourceElementType::s4, reverseExecutor, true); - ASSERT_TRUE(serial.hasValue()); - ASSERT_TRUE(tbb.hasValue()); - ASSERT_TRUE(reversed.hasValue()); + ASSERT_TRUE(serial.HasValue()); + ASSERT_TRUE(tbb.HasValue()); + ASSERT_TRUE(reversed.HasValue()); EXPECT_EQ(reverseExecutor.calls(), 1U); EXPECT_EQ(reverseExecutor.observedCount(), 2U); - expectByteIdentical(tbb.value(), serial.value()); - expectByteIdentical(reversed.value(), serial.value()); + expectByteIdentical(tbb.Value(), serial.Value()); + expectByteIdentical(reversed.Value(), serial.Value()); for (std::size_t repetition = 0U; repetition < 8U; ++repetition) { auto repeated = assembleShell( fesa::ShellSourceElementType::s4, tbbExecutor, true); - ASSERT_TRUE(repeated.hasValue()); - expectByteIdentical(repeated.value(), serial.value()); + ASSERT_TRUE(repeated.HasValue()); + expectByteIdentical(repeated.Value(), serial.Value()); } } @@ -327,13 +327,13 @@ TEST(SparseAssembly, S4AndS4rSemanticFixturesAssembleIdenticalStiffness) { fesa::ShellSourceElementType::s4, serialExecutor); auto s4r = assembleShell( fesa::ShellSourceElementType::s4r, serialExecutor); - ASSERT_TRUE(s4.hasValue()); - ASSERT_TRUE(s4r.hasValue()); + ASSERT_TRUE(s4.HasValue()); + ASSERT_TRUE(s4r.HasValue()); EXPECT_TRUE(std::any_of( - s4.value().values().begin(), - s4.value().values().end(), + s4.Value().Values().begin(), + s4.Value().Values().end(), [](const double value) { return value != 0.0; })); - expectByteIdentical(s4r.value(), s4.value()); + expectByteIdentical(s4r.Value(), s4.Value()); } } // namespace diff --git a/tests/unit/build_info_test.cpp b/tests/unit/build_info_test.cpp index d3579d4..aac736e 100644 --- a/tests/unit/build_info_test.cpp +++ b/tests/unit/build_info_test.cpp @@ -1,4 +1,4 @@ -#include "fesa/build_info.hpp" +#include "fesa/build_info.h" #include @@ -8,20 +8,20 @@ #include TEST(BuildInfo, VersionIsStableAndNonEmpty) { - const std::string_view first = fesa::solverVersion(); - const std::string_view second = fesa::solverVersion(); + const std::string_view first = fesa::SolverVersion(); + const std::string_view second = fesa::SolverVersion(); - EXPECT_FALSE(first.empty()); - EXPECT_EQ(first, second); - EXPECT_TRUE(std::regex_match( - std::string(first), std::regex{R"(^[0-9]+\.[0-9]+\.[0-9]+$)"})); + EXPECT_FALSE(first.empty()); + EXPECT_EQ(first, second); + EXPECT_TRUE(std::regex_match(std::string(first), + std::regex{R"(^[0-9]+\.[0-9]+\.[0-9]+$)"})); } TEST(BuildInfo, PublicHeaderHasNoBackendDependency) { - static_assert( - std::is_same_v, - "The public BuildInfo API must use only a standard-library value type."); - static_assert(noexcept(fesa::solverVersion())); + static_assert( + std::is_same_v, + "The public BuildInfo API must use only a standard-library value type."); + static_assert(noexcept(fesa::SolverVersion())); - SUCCEED(); + SUCCEED(); } diff --git a/tests/unit/constraints/essential_constraints_test.cpp b/tests/unit/constraints/essential_constraints_test.cpp index 81844cc..4730aa6 100644 --- a/tests/unit/constraints/essential_constraints_test.cpp +++ b/tests/unit/constraints/essential_constraints_test.cpp @@ -34,12 +34,12 @@ fesa::DofManager makeDofs( {source, 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()); + 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()); } fesa::DofManager makeShellSizedDofs( @@ -75,12 +75,12 @@ fesa::DofManager makeShellSizedDofs( {source, 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()); + 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()); } fesa::SparseMatrix makeMatrix( @@ -105,10 +105,10 @@ fesa::SparseMatrix makeMatrix( pattern.rowOffsets.push_back(pattern.columnIndices.size()); } - auto matrix = fesa::SparseMatrix::fromCoo( + auto matrix = fesa::SparseMatrix::FromCoo( rows, columns, std::move(contributions), pattern); - EXPECT_TRUE(matrix.hasValue()); - return std::move(matrix.value()); + EXPECT_TRUE(matrix.HasValue()); + return std::move(matrix.Value()); } std::vector sequentialDense(const std::size_t size) { @@ -126,9 +126,9 @@ void expectShape( const fesa::SparseMatrix& matrix, const std::size_t rows, const std::size_t columns) { - EXPECT_EQ(matrix.rows(), rows); - EXPECT_EQ(matrix.columns(), columns); - EXPECT_TRUE(matrix.validate().isOk()); + EXPECT_EQ(matrix.Rows(), rows); + EXPECT_EQ(matrix.Columns(), columns); + EXPECT_TRUE(matrix.Validate().IsOk()); } } // namespace @@ -142,45 +142,45 @@ TEST(EssentialConstraints, ExtractsHandComputedBlocksInStableOrder) { const auto full = makeMatrix(6U, 6U, fullValues); auto result = fesa::EssentialConstraints::partition(full, dofs); - ASSERT_TRUE(result.hasValue()); - const auto& blocks = result.value(); + ASSERT_TRUE(result.HasValue()); + const auto& blocks = result.Value(); - EXPECT_EQ(blocks.kff.rowOffsets(), (std::vector{0U, 4U, 8U, 12U, 16U})); + EXPECT_EQ(blocks.kff.RowOffsets(), (std::vector{0U, 4U, 8U, 12U, 16U})); EXPECT_EQ( - blocks.kff.columnIndices(), + blocks.kff.ColumnIndices(), (std::vector{ 0U, 1U, 2U, 3U, 0U, 1U, 2U, 3U, 0U, 1U, 2U, 3U, 0U, 1U, 2U, 3U})); EXPECT_EQ( - blocks.kff.values(), + blocks.kff.Values(), (std::vector{ 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(), (std::vector{0U, 2U, 4U, 6U, 8U})); + EXPECT_EQ(blocks.kfc.RowOffsets(), (std::vector{0U, 2U, 4U, 6U, 8U})); EXPECT_EQ( - blocks.kfc.columnIndices(), + blocks.kfc.ColumnIndices(), (std::vector{0U, 1U, 0U, 1U, 0U, 1U, 0U, 1U})); EXPECT_EQ( - blocks.kfc.values(), + blocks.kfc.Values(), (std::vector{2.0, 5.0, 22.0, 0.0, 32.0, 35.0, 52.0, 55.0})); - EXPECT_EQ(blocks.kcf.rowOffsets(), (std::vector{0U, 4U, 8U})); + EXPECT_EQ(blocks.kcf.RowOffsets(), (std::vector{0U, 4U, 8U})); EXPECT_EQ( - blocks.kcf.columnIndices(), + blocks.kcf.ColumnIndices(), (std::vector{0U, 1U, 2U, 3U, 0U, 1U, 2U, 3U})); EXPECT_EQ( - blocks.kcf.values(), + blocks.kcf.Values(), (std::vector{11.0, 13.0, 14.0, 16.0, 41.0, 43.0, 44.0, 46.0})); - EXPECT_EQ(blocks.kcc.rowOffsets(), (std::vector{0U, 2U, 4U})); - EXPECT_EQ(blocks.kcc.columnIndices(), (std::vector{0U, 1U, 0U, 1U})); - EXPECT_EQ(blocks.kcc.values(), (std::vector{12.0, 15.0, 42.0, 45.0})); + EXPECT_EQ(blocks.kcc.RowOffsets(), (std::vector{0U, 2U, 4U})); + EXPECT_EQ(blocks.kcc.ColumnIndices(), (std::vector{0U, 1U, 0U, 1U})); + EXPECT_EQ(blocks.kcc.Values(), (std::vector{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.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()); } TEST(EssentialConstraints, HandlesNoAllAndMixedConstraints) { @@ -188,29 +188,29 @@ TEST(EssentialConstraints, HandlesNoAllAndMixedConstraints) { const auto noConstraints = makeDofs({}); auto none = fesa::EssentialConstraints::partition(full, noConstraints); - 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()); + 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()); const auto allConstraints = makeDofs({{"1", 1, 6, 1.0, {{}, 12U}}}); auto all = fesa::EssentialConstraints::partition(full, allConstraints); - 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()); + 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()); const auto mixedConstraints = makeDofs({{"1", 3, 4, 0.0, {{}, 12U}}}); auto mixed = fesa::EssentialConstraints::partition(full, mixedConstraints); - 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); + 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); } // MITC4-DOF-003 @@ -219,24 +219,24 @@ TEST(EssentialConstraints, PreservesShellSizedNoMixedAndAllConstraintRoundTrips) const auto noConstraints = makeShellSizedDofs({}); auto none = fesa::EssentialConstraints::partition(full, noConstraints); - 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); + 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); const auto mixedConstraints = makeShellSizedDofs({ {"1", 1, 6, 0.0, {{}, 12U}}, {"4", 2, 2, 2.5, {{}, 13U}}}); auto mixed = fesa::EssentialConstraints::partition(full, mixedConstraints); - 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); + 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); fesa::Vector mixedFull{24U}; - for (std::size_t index = 0U; index < mixedFull.size(); ++index) { + for (std::size_t index = 0U; index < mixedFull.Size(); ++index) { mixedFull[index] = static_cast(index) + 0.5; } for (std::size_t index = 0U; @@ -250,8 +250,8 @@ TEST(EssentialConstraints, PreservesShellSizedNoMixedAndAllConstraintRoundTrips) const auto mixedReconstructed = fesa::EssentialConstraints::reconstructFull( mixedFree, mixedConstraints.prescribedValues(), mixedConstraints); - ASSERT_EQ(mixedReconstructed.size(), mixedFull.size()); - for (std::size_t index = 0U; index < mixedFull.size(); ++index) { + ASSERT_EQ(mixedReconstructed.Size(), mixedFull.Size()); + for (std::size_t index = 0U; index < mixedFull.Size(); ++index) { EXPECT_DOUBLE_EQ(mixedReconstructed[index], mixedFull[index]); } @@ -261,18 +261,18 @@ TEST(EssentialConstraints, PreservesShellSizedNoMixedAndAllConstraintRoundTrips) {"3", 1, 6, 3.0, {{}, 16U}}, {"4", 1, 6, 4.0, {{}, 17U}}}); auto all = fesa::EssentialConstraints::partition(full, allConstraints); - 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); + 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); const auto allReconstructed = fesa::EssentialConstraints::reconstructFull( fesa::Vector{0U}, allConstraints.prescribedValues(), allConstraints); - ASSERT_EQ(allReconstructed.size(), 24U); + ASSERT_EQ(allReconstructed.Size(), 24U); for (std::size_t node = 0U; node < 4U; ++node) { for (std::size_t component = 0U; component < 6U; ++component) { EXPECT_DOUBLE_EQ(allReconstructed[node * 6U + component], node + 1.0); @@ -295,12 +295,12 @@ TEST(EssentialConstraints, ReconstructsNonzeroPrescribedValues) { const auto free = fesa::EssentialConstraints::gatherFree(full, dofs); const auto constrained = fesa::EssentialConstraints::gatherConstrained(full, dofs); - EXPECT_EQ(free.size(), 4U); + EXPECT_EQ(free.Size(), 4U); EXPECT_DOUBLE_EQ(free[0U], 10.0); EXPECT_DOUBLE_EQ(free[1U], 20.0); EXPECT_DOUBLE_EQ(free[2U], 30.0); EXPECT_DOUBLE_EQ(free[3U], 40.0); - EXPECT_EQ(constrained.size(), 2U); + EXPECT_EQ(constrained.Size(), 2U); EXPECT_DOUBLE_EQ(constrained[0U], 2.5); EXPECT_DOUBLE_EQ(constrained[1U], -3.25); EXPECT_EQ(constrained[0U], dofs.prescribedValues()[0U]); @@ -308,8 +308,8 @@ TEST(EssentialConstraints, ReconstructsNonzeroPrescribedValues) { const auto reconstructed = fesa::EssentialConstraints::reconstructFull( free, dofs.prescribedValues(), dofs); - ASSERT_EQ(reconstructed.size(), full.size()); - for (std::size_t index = 0U; index < full.size(); ++index) { + ASSERT_EQ(reconstructed.Size(), full.Size()); + for (std::size_t index = 0U; index < full.Size(); ++index) { EXPECT_DOUBLE_EQ(reconstructed[index], full[index]); } } @@ -319,21 +319,21 @@ TEST(EssentialConstraints, RejectsDimensionOrOrderMismatch) { const auto wrongSquare = makeMatrix(5U, 5U, sequentialDense(5U)); auto wrongDimension = fesa::EssentialConstraints::partition(wrongSquare, dofs); - ASSERT_FALSE(wrongDimension.hasValue()); + ASSERT_FALSE(wrongDimension.HasValue()); EXPECT_EQ( - wrongDimension.status().failureCategory(), - fesa::FailureCategory::model); - ASSERT_EQ(wrongDimension.status().diagnostics().size(), 1U); + wrongDimension.GetStatus().Category(), + fesa::FailureCategory::kModel); + ASSERT_EQ(wrongDimension.GetStatus().Diagnostics().size(), 1U); EXPECT_EQ( - wrongDimension.status().diagnostics()[0U].code, + wrongDimension.GetStatus().Diagnostics()[0U].code, "invalid-constraint-dimensions"); const auto rectangular = makeMatrix( 6U, 5U, std::vector(30U, 0.0)); auto wrongOrder = fesa::EssentialConstraints::partition(rectangular, dofs); - ASSERT_FALSE(wrongOrder.hasValue()); + ASSERT_FALSE(wrongOrder.HasValue()); EXPECT_EQ( - wrongOrder.status().diagnostics()[0U].code, + wrongOrder.GetStatus().Diagnostics()[0U].code, "invalid-constraint-dimensions"); EXPECT_THROW( diff --git a/tests/unit/core/diagnostic_test.cpp b/tests/unit/core/diagnostic_test.cpp index 840e2c2..15dc7ce 100644 --- a/tests/unit/core/diagnostic_test.cpp +++ b/tests/unit/core/diagnostic_test.cpp @@ -1,4 +1,4 @@ -#include "fesa/core/diagnostic.hpp" +#include "fesa/core/diagnostic.h" #include @@ -9,51 +9,47 @@ namespace { -fesa::Diagnostic makeDiagnostic( - std::string file, - std::size_t line, - std::string keyword, - std::string entityIdentity, - std::string code, - std::string message) { - return fesa::Diagnostic{ - fesa::Severity::error, - std::move(code), - {std::filesystem::path{std::move(file)}, line}, - std::move(keyword), - std::move(entityIdentity), - std::move(message)}; +fesa::Diagnostic MakeDiagnostic(std::string file, std::size_t line, + std::string keyword, + std::string entity_identity, std::string code, + std::string message) { + return fesa::Diagnostic{fesa::Severity::kError, + std::move(code), + {std::filesystem::path{std::move(file)}, line}, + std::move(keyword), + std::move(entity_identity), + std::move(message)}; } -} // namespace +} // namespace TEST(CoreDiagnostics, DiagnosticsSortDeterministically) { - std::vector diagnostics{ - makeDiagnostic("b.inp", 1U, "*NODE", "I.1", "a", "file-b"), - makeDiagnostic("a.inp", 3U, "*NODE", "I.1", "z", "code-z"), - makeDiagnostic("a.inp", 3U, "*NODE", "I.1", "a", "first-equal"), - makeDiagnostic("a.inp", 3U, "*NODE", "I.1", "a", "second-equal"), - makeDiagnostic("a.inp", 3U, "*NODE", "I.2", "a", "entity-2"), - makeDiagnostic("a.inp", 3U, "*BOUNDARY", "I.1", "a", "keyword"), - makeDiagnostic("a.inp", 2U, "*NODE", "I.1", "a", "line")}; + std::vector diagnostics{ + MakeDiagnostic("b.inp", 1U, "*NODE", "I.1", "a", "file-b"), + MakeDiagnostic("a.inp", 3U, "*NODE", "I.1", "z", "code-z"), + MakeDiagnostic("a.inp", 3U, "*NODE", "I.1", "a", "first-equal"), + MakeDiagnostic("a.inp", 3U, "*NODE", "I.1", "a", "second-equal"), + MakeDiagnostic("a.inp", 3U, "*NODE", "I.2", "a", "entity-2"), + MakeDiagnostic("a.inp", 3U, "*BOUNDARY", "I.1", "a", "keyword"), + MakeDiagnostic("a.inp", 2U, "*NODE", "I.1", "a", "line")}; - fesa::sortDiagnostics(diagnostics); + fesa::SortDiagnostics(diagnostics); - ASSERT_EQ(diagnostics.size(), 7U); - EXPECT_EQ(diagnostics[0].message, "line"); - EXPECT_EQ(diagnostics[1].message, "keyword"); - EXPECT_EQ(diagnostics[2].message, "first-equal"); - EXPECT_EQ(diagnostics[3].message, "second-equal"); - EXPECT_EQ(diagnostics[4].message, "code-z"); - EXPECT_EQ(diagnostics[5].message, "entity-2"); - EXPECT_EQ(diagnostics[6].message, "file-b"); + ASSERT_EQ(diagnostics.size(), 7U); + EXPECT_EQ(diagnostics[0].message, "line"); + EXPECT_EQ(diagnostics[1].message, "keyword"); + EXPECT_EQ(diagnostics[2].message, "first-equal"); + EXPECT_EQ(diagnostics[3].message, "second-equal"); + EXPECT_EQ(diagnostics[4].message, "code-z"); + EXPECT_EQ(diagnostics[5].message, "entity-2"); + EXPECT_EQ(diagnostics[6].message, "file-b"); - const fesa::Diagnostic& exact = diagnostics[2]; - EXPECT_EQ(exact.severity, fesa::Severity::error); - EXPECT_EQ(exact.code, "a"); - EXPECT_EQ(exact.location.file, std::filesystem::path{"a.inp"}); - EXPECT_EQ(exact.location.line, 3U); - EXPECT_EQ(exact.keyword, "*NODE"); - EXPECT_EQ(exact.entityIdentity, "I.1"); - EXPECT_EQ(exact.message, "first-equal"); + const fesa::Diagnostic& exact = diagnostics[2]; + EXPECT_EQ(exact.severity, fesa::Severity::kError); + EXPECT_EQ(exact.code, "a"); + EXPECT_EQ(exact.location.file, std::filesystem::path{"a.inp"}); + EXPECT_EQ(exact.location.line, 3U); + EXPECT_EQ(exact.keyword, "*NODE"); + EXPECT_EQ(exact.entity_identity, "I.1"); + EXPECT_EQ(exact.message, "first-equal"); } diff --git a/tests/unit/core/source_identity_test.cpp b/tests/unit/core/source_identity_test.cpp index 236a91f..0201bf7 100644 --- a/tests/unit/core/source_identity_test.cpp +++ b/tests/unit/core/source_identity_test.cpp @@ -1,4 +1,4 @@ -#include "fesa/core/source_identity.hpp" +#include "fesa/core/source_identity.h" #include @@ -7,14 +7,14 @@ #include TEST(CoreDiagnostics, SourceIdentityPreservesRawIdentity) { - const fesa::SourceLocation location{ - std::filesystem::path{"models/My Beam.inp"}, 27U}; - const fesa::SourceEntityId identity{ - "Beam-Instance_A", std::int64_t{42}, "00042"}; + const fesa::SourceLocation location{ + std::filesystem::path{"models/My Beam.inp"}, 27U}; + const fesa::SourceEntityId identity{"Beam-Instance_A", std::int64_t{42}, + "00042"}; - EXPECT_EQ(location.file, std::filesystem::path{"models/My Beam.inp"}); - EXPECT_EQ(location.line, 27U); - EXPECT_EQ(identity.instanceName, "Beam-Instance_A"); - EXPECT_EQ(identity.sourceLabel, 42); - EXPECT_EQ(identity.sourceLabelText, "00042"); + EXPECT_EQ(location.file, std::filesystem::path{"models/My Beam.inp"}); + EXPECT_EQ(location.line, 27U); + EXPECT_EQ(identity.instance_name, "Beam-Instance_A"); + EXPECT_EQ(identity.source_label, 42); + EXPECT_EQ(identity.source_label_text, "00042"); } diff --git a/tests/unit/core/status_test.cpp b/tests/unit/core/status_test.cpp index 8798a05..90ace79 100644 --- a/tests/unit/core/status_test.cpp +++ b/tests/unit/core/status_test.cpp @@ -1,4 +1,4 @@ -#include "fesa/core/status.hpp" +#include "fesa/core/status.h" #include @@ -10,57 +10,55 @@ namespace { -fesa::Diagnostic modelDiagnostic() { - return fesa::Diagnostic{ - fesa::Severity::error, - "invalid-beam-length", - {std::filesystem::path{"beam.inp"}, 12U}, - "*ELEMENT", - "Beam-1.10", - "Beam length must be positive."}; +fesa::Diagnostic ModelDiagnostic() { + return fesa::Diagnostic{fesa::Severity::kError, + "invalid-beam-length", + {std::filesystem::path{"beam.inp"}, 12U}, + "*ELEMENT", + "Beam-1.10", + "Beam length must be positive."}; } -} // namespace +} // namespace TEST(CoreDiagnostics, ResultEnforcesValueErrorExclusivity) { - const fesa::Status ok = fesa::Status::ok(); - EXPECT_TRUE(ok.isOk()); - EXPECT_FALSE(ok.failureCategory().has_value()); - EXPECT_TRUE(ok.diagnostics().empty()); + const fesa::Status ok = fesa::Status::Ok(); + EXPECT_TRUE(ok.IsOk()); + EXPECT_FALSE(ok.Category().has_value()); + EXPECT_TRUE(ok.Diagnostics().empty()); - const fesa::Status uncategorized = - fesa::Status::failure(std::vector{modelDiagnostic()}); - EXPECT_FALSE(uncategorized.isOk()); - EXPECT_FALSE(uncategorized.failureCategory().has_value()); - ASSERT_EQ(uncategorized.diagnostics().size(), 1U); - EXPECT_EQ(uncategorized.diagnostics()[0].code, "invalid-beam-length"); + const fesa::Status uncategorized = + fesa::Status::Failure(std::vector{ModelDiagnostic()}); + EXPECT_FALSE(uncategorized.IsOk()); + EXPECT_FALSE(uncategorized.Category().has_value()); + ASSERT_EQ(uncategorized.Diagnostics().size(), 1U); + EXPECT_EQ(uncategorized.Diagnostics()[0].code, "invalid-beam-length"); - const fesa::Status categorized = fesa::Status::failure( - fesa::FailureCategory::model, - std::vector{modelDiagnostic()}); - EXPECT_FALSE(categorized.isOk()); - ASSERT_TRUE(categorized.failureCategory().has_value()); - EXPECT_EQ(*categorized.failureCategory(), fesa::FailureCategory::model); + const fesa::Status categorized = + fesa::Status::Failure(fesa::FailureCategory::kModel, + std::vector{ModelDiagnostic()}); + EXPECT_FALSE(categorized.IsOk()); + ASSERT_TRUE(categorized.Category().has_value()); + EXPECT_EQ(*categorized.Category(), fesa::FailureCategory::kModel); - const auto success = fesa::Result::success("solved"); - EXPECT_TRUE(success.hasValue()); - EXPECT_TRUE(success.status().isOk()); - EXPECT_EQ(success.value(), "solved"); + const auto success = fesa::Result::Success("solved"); + EXPECT_TRUE(success.HasValue()); + EXPECT_TRUE(success.GetStatus().IsOk()); + EXPECT_EQ(success.Value(), "solved"); - auto copied = success; - EXPECT_EQ(copied.value(), "solved"); - auto moved = std::move(copied); - EXPECT_EQ(moved.value(), "solved"); + auto copied = success; + EXPECT_EQ(copied.Value(), "solved"); + auto moved = std::move(copied); + EXPECT_EQ(moved.Value(), "solved"); - auto failure = fesa::Result::failure(categorized); - EXPECT_FALSE(failure.hasValue()); - EXPECT_FALSE(failure.status().isOk()); - EXPECT_EQ(failure.status().failureCategory(), fesa::FailureCategory::model); - EXPECT_THROW(failure.value(), std::logic_error); + auto failure = fesa::Result::Failure(categorized); + EXPECT_FALSE(failure.HasValue()); + EXPECT_FALSE(failure.GetStatus().IsOk()); + EXPECT_EQ(failure.GetStatus().Category(), fesa::FailureCategory::kModel); + EXPECT_THROW(failure.Value(), std::logic_error); - const auto& constFailure = failure; - EXPECT_THROW(constFailure.value(), std::logic_error); - EXPECT_THROW( - (void)fesa::Result::failure(fesa::Status::ok()), - std::invalid_argument); + const auto& const_failure = failure; + EXPECT_THROW(const_failure.Value(), std::logic_error); + EXPECT_THROW((void)fesa::Result::Failure(fesa::Status::Ok()), + std::invalid_argument); } diff --git a/tests/unit/elements/euler_beam_3d_test.cpp b/tests/unit/elements/euler_beam_3d_test.cpp index 3f9e4d4..d827a6a 100644 --- a/tests/unit/elements/euler_beam_3d_test.cpp +++ b/tests/unit/elements/euler_beam_3d_test.cpp @@ -50,10 +50,10 @@ EulerBeam3D requireBeam(const Node& firstNode, const GeneralBeamSection& section, const LinearElasticMaterial& material) { auto result = EulerBeam3D::create(firstNode, secondNode, section, material); - if (!result.hasValue()) { + if (!result.HasValue()) { throw std::runtime_error{"Expected a valid EulerBeam3D fixture."}; } - return std::move(result.value()); + return std::move(result.Value()); } EulerBeam3D alignedBeam(double length, @@ -68,8 +68,8 @@ EulerBeam3D alignedBeam(double length, double maximumAbsoluteEntry(const Matrix& matrix) { double maximum = 0.0; - for (std::size_t row = 0; row < matrix.rows(); ++row) { - for (std::size_t column = 0; column < matrix.columns(); ++column) { + for (std::size_t row = 0; row < matrix.Rows(); ++row) { + for (std::size_t column = 0; column < matrix.Columns(); ++column) { maximum = (std::max)(maximum, std::abs(matrix(row, column))); } } @@ -77,8 +77,8 @@ double maximumAbsoluteEntry(const Matrix& matrix) { } bool matrixIsFinite(const Matrix& matrix) { - for (std::size_t row = 0; row < matrix.rows(); ++row) { - for (std::size_t column = 0; column < matrix.columns(); ++column) { + for (std::size_t row = 0; row < matrix.Rows(); ++row) { + for (std::size_t column = 0; column < matrix.Columns(); ++column) { if (!std::isfinite(matrix(row, column))) { return false; } @@ -88,13 +88,13 @@ bool matrixIsFinite(const Matrix& matrix) { } double normalizedMatrixError(const Matrix& actual, const Matrix& expected) { - if (actual.rows() != expected.rows() || actual.columns() != expected.columns()) { + if (actual.Rows() != expected.Rows() || actual.Columns() != expected.Columns()) { throw std::invalid_argument{"Matrix comparison requires equal shapes."}; } double maximumDifference = 0.0; - for (std::size_t row = 0; row < actual.rows(); ++row) { - for (std::size_t column = 0; column < actual.columns(); ++column) { + for (std::size_t row = 0; row < actual.Rows(); ++row) { + for (std::size_t column = 0; column < actual.Columns(); ++column) { maximumDifference = (std::max)( maximumDifference, std::abs(actual(row, column) - expected(row, column))); @@ -109,16 +109,16 @@ double normalizedMatrixError(const Matrix& actual, const Matrix& expected) { double vectorNorm(const Vector& vector) { double sum = 0.0; - for (std::size_t index = 0; index < vector.size(); ++index) { + for (std::size_t index = 0; index < vector.Size(); ++index) { sum += vector[index] * vector[index]; } return std::sqrt(sum); } double quadraticEnergy(const Matrix& matrix, const Vector& vector) { - const Vector product = matrix.multiply(vector); + const Vector product = matrix.Multiply(vector); double value = 0.0; - for (std::size_t index = 0; index < vector.size(); ++index) { + for (std::size_t index = 0; index < vector.Size(); ++index) { value += vector[index] * product[index]; } return value; @@ -328,14 +328,14 @@ Vector solveFixedFirstNode(const Matrix& stiffness, } Vector solveDenseSystem(Matrix matrix, Vector rightHandSide) { - if (matrix.rows() != matrix.columns() || - matrix.rows() != rightHandSide.size()) { + if (matrix.Rows() != matrix.Columns() || + matrix.Rows() != rightHandSide.Size()) { throw std::invalid_argument{"Dense test solve requires a square system."}; } - for (std::size_t pivot = 0; pivot < matrix.rows(); ++pivot) { + for (std::size_t pivot = 0; pivot < matrix.Rows(); ++pivot) { std::size_t pivotRow = pivot; - for (std::size_t row = pivot + 1U; row < matrix.rows(); ++row) { + for (std::size_t row = pivot + 1U; row < matrix.Rows(); ++row) { if (std::abs(matrix(row, pivot)) > std::abs(matrix(pivotRow, pivot))) { pivotRow = row; @@ -345,22 +345,22 @@ Vector solveDenseSystem(Matrix matrix, Vector rightHandSide) { !std::isfinite(matrix(pivotRow, pivot))) { throw std::runtime_error{"Uniform-load test fixture is singular."}; } - for (std::size_t column = pivot; column < matrix.columns(); ++column) { + for (std::size_t column = pivot; column < matrix.Columns(); ++column) { std::swap(matrix(pivot, column), matrix(pivotRow, column)); } std::swap(rightHandSide[pivot], rightHandSide[pivotRow]); const double pivotValue = matrix(pivot, pivot); - for (std::size_t column = pivot; column < matrix.columns(); ++column) { + for (std::size_t column = pivot; column < matrix.Columns(); ++column) { matrix(pivot, column) /= pivotValue; } rightHandSide[pivot] /= pivotValue; - for (std::size_t row = 0; row < matrix.rows(); ++row) { + for (std::size_t row = 0; row < matrix.Rows(); ++row) { if (row == pivot) { continue; } const double factor = matrix(row, pivot); - for (std::size_t column = pivot; column < matrix.columns(); ++column) { + for (std::size_t column = pivot; column < matrix.Columns(); ++column) { matrix(row, column) -= factor * matrix(pivot, column); } rightHandSide[row] -= factor * rightHandSide[pivot]; @@ -489,12 +489,12 @@ Matrix transformationFromKnownRows( } Vector transposeMultiply(const Matrix& matrix, const Vector& vector) { - if (matrix.rows() != vector.size()) { + if (matrix.Rows() != vector.Size()) { throw std::invalid_argument{"Transpose multiply dimension mismatch."}; } - Vector result{matrix.columns()}; - for (std::size_t column = 0; column < matrix.columns(); ++column) { - for (std::size_t row = 0; row < matrix.rows(); ++row) { + Vector result{matrix.Columns()}; + for (std::size_t column = 0; column < matrix.Columns(); ++column) { + for (std::size_t row = 0; row < matrix.Rows(); ++row) { result[column] += matrix(row, column) * vector[row]; } } @@ -595,9 +595,9 @@ TEST(EulerBeam3D, TwoPointGaussMatchesClosedStiffness) { const Matrix closed = expectedClosedStiffness(length, section, material); EXPECT_LE(normalizedMatrixError(actual, closed), kMatrixTolerance); - Matrix transpose{actual.rows(), actual.columns()}; - for (std::size_t row = 0; row < actual.rows(); ++row) { - for (std::size_t column = 0; column < actual.columns(); ++column) { + Matrix transpose{actual.Rows(), actual.Columns()}; + for (std::size_t row = 0; row < actual.Rows(); ++row) { + for (std::size_t column = 0; column < actual.Columns(); ++column) { transpose(row, column) = actual(column, row); } } @@ -629,7 +629,7 @@ TEST(EulerBeam3D, HasSixRigidModesRankSixAndPositiveDeformationEnergy) { const double stiffnessScale = (std::max)(1.0, maximumAbsoluteEntry(stiffness)); for (const Vector& mode : rigidModes) { const double normalizedResidual = - vectorNorm(stiffness.multiply(mode)) / + vectorNorm(stiffness.Multiply(mode)) / (stiffnessScale * (std::max)(1.0, vectorNorm(mode))); EXPECT_LE(normalizedResidual, kRigidTolerance); } @@ -695,7 +695,7 @@ TEST(EulerBeam3D, RotatedTransformPreservesWorkAndEnergy) { const Matrix global = beam.globalStiffness(); Matrix expectedGlobal{kElementDofCount, kElementDofCount}; - const Matrix localTimesTransform = local.multiply(transformation); + const Matrix localTimesTransform = local.Multiply(transformation); for (std::size_t row = 0; row < kElementDofCount; ++row) { for (std::size_t column = 0; column < kElementDofCount; ++column) { for (std::size_t inner = 0; inner < kElementDofCount; ++inner) { @@ -707,12 +707,12 @@ TEST(EulerBeam3D, RotatedTransformPreservesWorkAndEnergy) { EXPECT_LE(normalizedMatrixError(global, expectedGlobal), kMatrixTolerance); Vector localDisplacement{kElementDofCount}; - for (std::size_t index = 0; index < localDisplacement.size(); ++index) { + for (std::size_t index = 0; index < localDisplacement.Size(); ++index) { localDisplacement[index] = 0.01 * static_cast(index + 1U) - 0.04; } const Vector globalDisplacement = transposeMultiply(transformation, localDisplacement); - const Vector localForce = local.multiply(localDisplacement); - const Vector globalForce = global.multiply(globalDisplacement); + const Vector localForce = local.Multiply(localDisplacement); + const Vector globalForce = global.Multiply(globalDisplacement); const Vector expectedGlobalForce = transposeMultiply(transformation, localForce); for (std::size_t index = 0; index < kElementDofCount; ++index) { expectScaledNear(globalForce[index], expectedGlobalForce[index], kMatrixTolerance); @@ -723,13 +723,13 @@ TEST(EulerBeam3D, RotatedTransformPreservesWorkAndEnergy) { kMatrixTolerance); Vector globalVariation{kElementDofCount}; - for (std::size_t index = 0; index < globalVariation.size(); ++index) { + for (std::size_t index = 0; index < globalVariation.Size(); ++index) { globalVariation[index] = 0.03 - 0.002 * static_cast(index); } - const Vector localVariation = transformation.multiply(globalVariation); + const Vector localVariation = transformation.Multiply(globalVariation); expectScaledNear( - globalVariation.dot(globalForce), - localVariation.dot(localForce), + globalVariation.Dot(globalForce), + localVariation.Dot(localForce), kMatrixTolerance); const BeamRecovery recovery = beam.recover(globalDisplacement); @@ -746,7 +746,7 @@ TEST(EulerBeam3D, ConstantLineLoadMatchesAllSignedComponents) { const std::array expected = { 5.0, -6.0, 11.0, -14.0, -22.0 / 3.0, -4.0, 5.0, -6.0, 11.0, -14.0, 22.0 / 3.0, 4.0}; - ASSERT_EQ(equivalent.size(), expected.size()); + ASSERT_EQ(equivalent.Size(), expected.size()); for (std::size_t index = 0; index < expected.size(); ++index) { expectScaledNear(equivalent[index], expected[index], kMatrixTolerance); } @@ -931,17 +931,17 @@ TEST(EulerBeam3D, RejectsInvalidGeometryAndProperties) { const auto expectFailure = [](const Result& result, const std::string& code) { - if (result.hasValue()) { - const Matrix stiffness = result.value().localStiffness(); + if (result.HasValue()) { + const Matrix stiffness = result.Value().localStiffness(); ADD_FAILURE() << "Invalid fixture was accepted; local stiffness finite=" << matrixIsFinite(stiffness) << ", maximum absolute entry=" << maximumAbsoluteEntry(stiffness); return; } - EXPECT_EQ(result.status().failureCategory(), FailureCategory::model); - ASSERT_EQ(result.status().diagnostics().size(), 1U); - EXPECT_EQ(result.status().diagnostics()[0U].code, code); + EXPECT_EQ(result.GetStatus().Category(), FailureCategory::kModel); + ASSERT_EQ(result.GetStatus().Diagnostics().size(), 1U); + EXPECT_EQ(result.GetStatus().Diagnostics()[0U].code, code); }; expectFailure( @@ -964,7 +964,7 @@ TEST(EulerBeam3D, RejectsInvalidGeometryAndProperties) { "invalid-beam-length"); EXPECT_TRUE(EulerBeam3D::create( scaledFirst, aboveThreshold, validSection, validMaterial) - .hasValue()); + .HasValue()); auto parallelGuide = validSection; parallelGuide.firstAxis = {1.0, 0.0, 0.0}; @@ -978,7 +978,7 @@ TEST(EulerBeam3D, RejectsInvalidGeometryAndProperties) { "invalid-beam-guide-vector"); auto guideAboveThreshold = validSection; guideAboveThreshold.firstAxis = {1.0, 2.0e-12, 0.0}; - EXPECT_TRUE(EulerBeam3D::create(origin, unitX, guideAboveThreshold, validMaterial).hasValue()); + EXPECT_TRUE(EulerBeam3D::create(origin, unitX, guideAboveThreshold, validMaterial).HasValue()); auto invalidMaterial = validMaterial; invalidMaterial.youngsModulus = 0.0; diff --git a/tests/unit/elements/mitc4_shell_test.cpp b/tests/unit/elements/mitc4_shell_test.cpp index 229af97..d9f543f 100644 --- a/tests/unit/elements/mitc4_shell_test.cpp +++ b/tests/unit/elements/mitc4_shell_test.cpp @@ -84,10 +84,10 @@ void expectMatrixNear( const fesa::Matrix& actual, const fesa::Matrix& expected, double tolerance = 1.0e-12) { - ASSERT_EQ(actual.rows(), expected.rows()); - ASSERT_EQ(actual.columns(), expected.columns()); - for (std::size_t row = 0U; row < actual.rows(); ++row) { - for (std::size_t column = 0U; column < actual.columns(); ++column) { + ASSERT_EQ(actual.Rows(), expected.Rows()); + ASSERT_EQ(actual.Columns(), expected.Columns()); + for (std::size_t row = 0U; row < actual.Rows(); ++row) { + for (std::size_t column = 0U; column < actual.Columns(); ++column) { EXPECT_NEAR(actual(row, column), expected(row, column), tolerance) << "at (" << row << ", " << column << ")"; } @@ -95,20 +95,20 @@ void expectMatrixNear( } void expectSymmetric(const fesa::Matrix& matrix) { - ASSERT_EQ(matrix.rows(), matrix.columns()); - for (std::size_t row = 0U; row < matrix.rows(); ++row) { - for (std::size_t column = 0U; column < matrix.columns(); ++column) { + ASSERT_EQ(matrix.Rows(), matrix.Columns()); + for (std::size_t row = 0U; row < matrix.Rows(); ++row) { + for (std::size_t column = 0U; column < matrix.Columns(); ++column) { EXPECT_NEAR(matrix(row, column), matrix(column, row), 1.0e-12); } } } bool hasPositiveCholeskyPivots(const fesa::Matrix& matrix) { - if (matrix.rows() != matrix.columns()) { + if (matrix.Rows() != matrix.Columns()) { return false; } - fesa::Matrix lower{matrix.rows(), matrix.columns()}; - for (std::size_t row = 0U; row < matrix.rows(); ++row) { + fesa::Matrix lower{matrix.Rows(), matrix.Columns()}; + for (std::size_t row = 0U; row < matrix.Rows(); ++row) { for (std::size_t column = 0U; column <= row; ++column) { double value = matrix(row, column); for (std::size_t inner = 0U; inner < column; ++inner) { @@ -129,8 +129,8 @@ bool hasPositiveCholeskyPivots(const fesa::Matrix& matrix) { double frobeniusNorm(const fesa::Matrix& matrix) { double squaredNorm = 0.0; - for (std::size_t row = 0U; row < matrix.rows(); ++row) { - for (std::size_t column = 0U; column < matrix.columns(); ++column) { + for (std::size_t row = 0U; row < matrix.Rows(); ++row) { + for (std::size_t column = 0U; column < matrix.Columns(); ++column) { squaredNorm += matrix(row, column) * matrix(row, column); } } @@ -141,11 +141,11 @@ double scaledSymmetryError( const fesa::Matrix& matrix, std::size_t dofsPerNode, double elementLength) { - fesa::Matrix difference{matrix.rows(), matrix.columns()}; - fesa::Matrix scaled{matrix.rows(), matrix.columns()}; - for (std::size_t row = 0U; row < matrix.rows(); ++row) { + fesa::Matrix difference{matrix.Rows(), matrix.Columns()}; + fesa::Matrix scaled{matrix.Rows(), matrix.Columns()}; + for (std::size_t row = 0U; row < matrix.Rows(); ++row) { const double rowScale = row % dofsPerNode < 3U ? elementLength : 1.0; - for (std::size_t column = 0U; column < matrix.columns(); ++column) { + for (std::size_t column = 0U; column < matrix.Columns(); ++column) { const double columnScale = column % dofsPerNode < 3U ? elementLength : 1.0; scaled(row, column) = @@ -161,10 +161,10 @@ fesa::Matrix scaledStiffness( const fesa::Matrix& matrix, std::size_t dofsPerNode, double elementLength) { - fesa::Matrix scaled{matrix.rows(), matrix.columns()}; - for (std::size_t row = 0U; row < matrix.rows(); ++row) { + fesa::Matrix scaled{matrix.Rows(), matrix.Columns()}; + for (std::size_t row = 0U; row < matrix.Rows(); ++row) { const double rowScale = row % dofsPerNode < 3U ? elementLength : 1.0; - for (std::size_t column = 0U; column < matrix.columns(); ++column) { + for (std::size_t column = 0U; column < matrix.Columns(); ++column) { const double columnScale = column % dofsPerNode < 3U ? elementLength : 1.0; scaled(row, column) = @@ -175,10 +175,10 @@ fesa::Matrix scaledStiffness( } std::vector symmetricEigenvalues(fesa::Matrix matrix) { - if (matrix.rows() != matrix.columns()) { + if (matrix.Rows() != matrix.Columns()) { throw std::invalid_argument{"Symmetric eigensolve requires a square matrix."}; } - const std::size_t size = matrix.rows(); + const std::size_t size = matrix.Rows(); double matrixScale = 0.0; for (std::size_t row = 0U; row < size; ++row) { for (std::size_t column = 0U; column < size; ++column) { @@ -266,7 +266,7 @@ double symmetricOperatorNorm(const fesa::Matrix& matrix) { } double quadraticEnergy(const fesa::Matrix& stiffness, const fesa::Vector& vector) { - return 0.5 * vector.dot(stiffness.multiply(vector)); + return 0.5 * vector.Dot(stiffness.Multiply(vector)); } fesa::Vector physicalField(const std::array, 4>& values) { @@ -286,7 +286,7 @@ void expectStrain( double eta, double zeta, const std::array& expected) { - const auto actual = shell.strainDisplacement20(xi, eta, zeta).multiply(field); + const auto actual = shell.strainDisplacement20(xi, eta, zeta).Multiply(field); for (std::size_t component = 0U; component < expected.size(); ++component) { EXPECT_NEAR(actual[component], expected[component], 1.0e-12) << "component " << component; @@ -367,8 +367,8 @@ TEST(Mitc4ShellKinematics, BuildsRightHandedFramesAndSeparatePhysicalDrillingMap node(4, {0.0, -1.0, 1.0})}; const auto candidate = fesa::Mitc4Shell::create( nodePointers(nodes), directors({1.0, 0.0, 0.0}), section(), material()); - ASSERT_TRUE(candidate.hasValue()); - const auto& shell = candidate.value(); + ASSERT_TRUE(candidate.HasValue()); + const auto& shell = candidate.Value(); const auto frame = shell.localFrame(0.0, 0.0); expectVectorNear(frame.e1, {0.0, 1.0, 0.0}); @@ -378,10 +378,10 @@ TEST(Mitc4ShellKinematics, BuildsRightHandedFramesAndSeparatePhysicalDrillingMap const auto physical = shell.physicalTransformation20(); const auto drilling = shell.drillingTransformation4(); - ASSERT_EQ(physical.rows(), 20U); - ASSERT_EQ(physical.columns(), 24U); - ASSERT_EQ(drilling.rows(), 4U); - ASSERT_EQ(drilling.columns(), 24U); + ASSERT_EQ(physical.Rows(), 20U); + ASSERT_EQ(physical.Columns(), 24U); + ASSERT_EQ(drilling.Rows(), 4U); + ASSERT_EQ(drilling.Columns(), 24U); for (std::size_t nodeIndex = 0U; nodeIndex < 4U; ++nodeIndex) { const std::size_t physicalOffset = 5U * nodeIndex; const std::size_t globalOffset = 6U * nodeIndex; @@ -411,7 +411,7 @@ TEST(Mitc4ShellKinematics, BuildsRightHandedFramesAndSeparatePhysicalDrillingMap invalidDirectors[2] = {0.0, 0.0, 0.0}; EXPECT_FALSE(fesa::Mitc4Shell::create( nodePointers(nodes), invalidDirectors, section(), material()) - .hasValue()); + .HasValue()); } // MITC4-KIN-003 @@ -419,12 +419,12 @@ TEST(Mitc4ShellKinematics, FormsDirectColumnsAndAllCovariantTyingSamples) { const auto nodes = planarNodes(); const auto candidate = fesa::Mitc4Shell::create( nodePointers(nodes), directors(), section(), material()); - ASSERT_TRUE(candidate.hasValue()); - const auto& shell = candidate.value(); + ASSERT_TRUE(candidate.HasValue()); + const auto& shell = candidate.Value(); const auto direct = shell.directStrainDisplacement20(0.0, 0.0, 0.5); - ASSERT_EQ(direct.rows(), 5U); - ASSERT_EQ(direct.columns(), 20U); + ASSERT_EQ(direct.Rows(), 5U); + ASSERT_EQ(direct.Columns(), 20U); EXPECT_DOUBLE_EQ(direct(0U, 0U), -0.25); EXPECT_DOUBLE_EQ(direct(0U, 4U), -0.125); EXPECT_DOUBLE_EQ(direct(1U, 1U), -0.25); @@ -439,8 +439,8 @@ TEST(Mitc4ShellKinematics, FormsDirectColumnsAndAllCovariantTyingSamples) { EXPECT_DOUBLE_EQ(direct(4U, 3U), -0.25); const auto samples = shell.covariantTyingShearSamples20(); - ASSERT_EQ(samples.rows(), 4U); - ASSERT_EQ(samples.columns(), 20U); + ASSERT_EQ(samples.Rows(), 4U); + ASSERT_EQ(samples.Columns(), 20U); EXPECT_DOUBLE_EQ(samples(0U, 2U), -0.25); EXPECT_DOUBLE_EQ(samples(0U, 4U), 0.25); EXPECT_DOUBLE_EQ(samples(0U, 7U), 0.25); @@ -476,21 +476,21 @@ TEST(Mitc4ShellConstitutive, BuildsExactPositiveDefiniteSectionMatricesAndRescal const auto nodes = planarNodes(); const auto candidate = fesa::Mitc4Shell::create( nodePointers(nodes), directors(), section(), material()); - ASSERT_TRUE(candidate.hasValue()); - const auto& shell = candidate.value(); + ASSERT_TRUE(candidate.HasValue()); + const auto& shell = candidate.Value(); const auto cps = shell.planeStressConstitutive(); const auto c5 = shell.materialConstitutive5(); const auto a = shell.membraneSectionMatrix(); const auto d = shell.bendingSectionMatrix(); const auto as = shell.transverseShearSectionMatrix(); - EXPECT_EQ(cps.rows(), 3U); - EXPECT_EQ(cps.columns(), 3U); - EXPECT_EQ(c5.rows(), 5U); - EXPECT_EQ(c5.columns(), 5U); - EXPECT_EQ(a.rows(), 3U); - EXPECT_EQ(d.rows(), 3U); - EXPECT_EQ(as.rows(), 2U); + EXPECT_EQ(cps.Rows(), 3U); + EXPECT_EQ(cps.Columns(), 3U); + EXPECT_EQ(c5.Rows(), 5U); + EXPECT_EQ(c5.Columns(), 5U); + EXPECT_EQ(a.Rows(), 3U); + EXPECT_EQ(d.Rows(), 3U); + EXPECT_EQ(as.Rows(), 2U); EXPECT_DOUBLE_EQ(cps(0U, 0U), 128.0); EXPECT_DOUBLE_EQ(cps(0U, 1U), 32.0); EXPECT_DOUBLE_EQ(cps(2U, 2U), 48.0); @@ -514,8 +514,8 @@ TEST(Mitc4ShellConstitutive, BuildsExactPositiveDefiniteSectionMatricesAndRescal directors(), section(2.0 * lengthScale), material(120.0 * forceScale / (lengthScale * lengthScale), 0.25)); - ASSERT_TRUE(scaledCandidate.hasValue()); - const auto& scaled = scaledCandidate.value(); + ASSERT_TRUE(scaledCandidate.HasValue()); + const auto& scaled = scaledCandidate.Value(); fesa::Matrix expectedCps{3U, 3U}; fesa::Matrix expectedC5{5U, 5U}; fesa::Matrix expectedA{3U, 3U}; @@ -548,13 +548,13 @@ TEST(Mitc4ShellConstitutive, BuildsExactPositiveDefiniteSectionMatricesAndRescal EXPECT_FALSE(fesa::Mitc4Shell::create( nodePointers(nodes), directors(), section(0.0), material()) - .hasValue()); + .HasValue()); EXPECT_FALSE(fesa::Mitc4Shell::create( nodePointers(nodes), directors(), section(), material(0.0, 0.25)) - .hasValue()); + .HasValue()); EXPECT_FALSE(fesa::Mitc4Shell::create( nodePointers(nodes), directors(), section(), material(120.0, 0.5)) - .hasValue()); + .HasValue()); } // MITC4-KIN-005 @@ -578,23 +578,23 @@ TEST(Mitc4ShellKernel, FormsFiniteScaledSymmetricPhysicalAndStabilizedStiffness) const auto nodes = planarNodes(); const auto shellCandidate = fesa::Mitc4Shell::create( nodePointers(nodes), directors(), section(), material()); - ASSERT_TRUE(shellCandidate.hasValue()); + ASSERT_TRUE(shellCandidate.HasValue()); - const auto stiffnessCandidate = shellCandidate.value().stiffness(); - ASSERT_TRUE(stiffnessCandidate.hasValue()); - const auto& stiffness = stiffnessCandidate.value(); - EXPECT_EQ(stiffness.physicalLocal20.rows(), 20U); - EXPECT_EQ(stiffness.physicalLocal20.columns(), 20U); - EXPECT_EQ(stiffness.physicalGlobal24.rows(), 24U); - EXPECT_EQ(stiffness.drillingGlobal24.rows(), 24U); - EXPECT_EQ(stiffness.stabilizedGlobal24.rows(), 24U); + const auto stiffnessCandidate = shellCandidate.Value().stiffness(); + ASSERT_TRUE(stiffnessCandidate.HasValue()); + const auto& stiffness = stiffnessCandidate.Value(); + EXPECT_EQ(stiffness.physicalLocal20.Rows(), 20U); + EXPECT_EQ(stiffness.physicalLocal20.Columns(), 20U); + EXPECT_EQ(stiffness.physicalGlobal24.Rows(), 24U); + EXPECT_EQ(stiffness.drillingGlobal24.Rows(), 24U); + EXPECT_EQ(stiffness.stabilizedGlobal24.Rows(), 24U); for (const fesa::Matrix* matrix : { &stiffness.physicalLocal20, &stiffness.physicalGlobal24, &stiffness.drillingGlobal24, &stiffness.stabilizedGlobal24}) { - for (std::size_t row = 0U; row < matrix->rows(); ++row) { - for (std::size_t column = 0U; column < matrix->columns(); ++column) { + for (std::size_t row = 0U; row < matrix->Rows(); ++row) { + for (std::size_t column = 0U; column < matrix->Columns(); ++column) { EXPECT_TRUE(std::isfinite((*matrix)(row, column))); } } @@ -604,9 +604,9 @@ TEST(Mitc4ShellKernel, FormsFiniteScaledSymmetricPhysicalAndStabilizedStiffness) EXPECT_LE(scaledSymmetryError(stiffness.drillingGlobal24, 6U, 2.0), 1.0e-12); EXPECT_LE(scaledSymmetryError(stiffness.stabilizedGlobal24, 6U, 2.0), 1.0e-12); - const auto repeatedCandidate = shellCandidate.value().stiffness(); - ASSERT_TRUE(repeatedCandidate.hasValue()); - const auto& repeated = repeatedCandidate.value(); + const auto repeatedCandidate = shellCandidate.Value().stiffness(); + ASSERT_TRUE(repeatedCandidate.HasValue()); + const auto& repeated = repeatedCandidate.Value(); expectMatrixNear(repeated.physicalLocal20, stiffness.physicalLocal20, 0.0); expectMatrixNear(repeated.physicalGlobal24, stiffness.physicalGlobal24, 0.0); expectMatrixNear(repeated.drillingGlobal24, stiffness.drillingGlobal24, 0.0); @@ -619,18 +619,18 @@ TEST(Mitc4ShellKernel, PreservesPhysicalEnergyUnderTwentyToTwentyFourCongruence) const auto nodes = planarNodes(); const auto shellCandidate = fesa::Mitc4Shell::create( nodePointers(nodes), directors(), section(), material()); - ASSERT_TRUE(shellCandidate.hasValue()); - const auto stiffnessCandidate = shellCandidate.value().stiffness(); - ASSERT_TRUE(stiffnessCandidate.hasValue()); - const auto& stiffness = stiffnessCandidate.value(); + ASSERT_TRUE(shellCandidate.HasValue()); + const auto stiffnessCandidate = shellCandidate.Value().stiffness(); + ASSERT_TRUE(stiffnessCandidate.HasValue()); + const auto& stiffness = stiffnessCandidate.Value(); fesa::Vector globalField{24U}; - for (std::size_t index = 0U; index < globalField.size(); ++index) { + for (std::size_t index = 0U; index < globalField.Size(); ++index) { globalField[index] = 0.125 * static_cast( static_cast(index % 7U) - 3); } const auto physicalField20 = - shellCandidate.value().physicalTransformation20().multiply(globalField); + shellCandidate.Value().physicalTransformation20().Multiply(globalField); const double localEnergy = quadraticEnergy( stiffness.physicalLocal20, physicalField20); const double globalEnergy = quadraticEnergy( @@ -648,10 +648,10 @@ TEST(Mitc4ShellKernel, RetainsSixRigidModesAndHasExpectedPhysicalAndStabilizedRa const auto nodes = planarNodes(); const auto shellCandidate = fesa::Mitc4Shell::create( nodePointers(nodes), directors(), section(), material()); - ASSERT_TRUE(shellCandidate.hasValue()); - const auto stiffnessCandidate = shellCandidate.value().stiffness(); - ASSERT_TRUE(stiffnessCandidate.hasValue()); - const auto& stiffness = stiffnessCandidate.value(); + ASSERT_TRUE(shellCandidate.HasValue()); + const auto stiffnessCandidate = shellCandidate.Value().stiffness(); + ASSERT_TRUE(stiffnessCandidate.HasValue()); + const auto& stiffness = stiffnessCandidate.Value(); const auto scaledPhysical20 = scaledStiffness(stiffness.physicalLocal20, 5U, 2.0); @@ -680,14 +680,14 @@ TEST(Mitc4ShellKernel, RetainsSixRigidModesAndHasExpectedPhysicalAndStabilizedRa scaledMode[6U * nodeIndex + component] /= 2.0; } } - const double modeNorm = scaledMode.norm(); + const double modeNorm = scaledMode.Norm(); ASSERT_GT(modeNorm, 0.0); EXPECT_LE( - scaledPhysical24.multiply(scaledMode).norm() / + scaledPhysical24.Multiply(scaledMode).Norm() / (physicalNorm * modeNorm), 1.0e-10); EXPECT_LE( - scaledStabilized24.multiply(scaledMode).norm() / + scaledStabilized24.Multiply(scaledMode).Norm() / (stabilizedNorm * modeNorm), 1.0e-10); } @@ -698,11 +698,11 @@ TEST(Mitc4ShellPatch, ReproducesIndependentMembraneBendingShearAndTwistFields) { const auto nodes = planarNodes(); const auto shellCandidate = fesa::Mitc4Shell::create( nodePointers(nodes), directors(), section(), material()); - ASSERT_TRUE(shellCandidate.hasValue()); - const auto& shell = shellCandidate.value(); + ASSERT_TRUE(shellCandidate.HasValue()); + const auto& shell = shellCandidate.Value(); const auto stiffnessCandidate = shell.stiffness(); - ASSERT_TRUE(stiffnessCandidate.hasValue()); - const auto& stiffness = stiffnessCandidate.value().physicalLocal20; + ASSERT_TRUE(stiffnessCandidate.HasValue()); + const auto& stiffness = stiffnessCandidate.Value().physicalLocal20; constexpr double magnitude = 0.2; const double gauss = 1.0 / std::sqrt(3.0); @@ -755,10 +755,10 @@ TEST(Mitc4ShellDrilling, UsesOnlyEightPositivePhysicalRotationDiagonalsAndFixedF node(3, {50.0, 50.0, 0.0}), node(4, {-50.0, 50.0, 0.0})}; const auto shellCandidate = fesa::Mitc4Shell::create( nodePointers(nodes), directors(), section(0.1), material()); - ASSERT_TRUE(shellCandidate.hasValue()); - const auto stiffnessCandidate = shellCandidate.value().stiffness(); - ASSERT_TRUE(stiffnessCandidate.hasValue()); - const auto& stiffness = stiffnessCandidate.value(); + ASSERT_TRUE(shellCandidate.HasValue()); + const auto stiffnessCandidate = shellCandidate.Value().stiffness(); + ASSERT_TRUE(stiffnessCandidate.HasValue()); + const auto& stiffness = stiffnessCandidate.Value(); double expectedReference = (std::numeric_limits::max)(); double allDiagonalMinimum = (std::numeric_limits::max)(); @@ -788,17 +788,17 @@ TEST(Mitc4ShellDrilling, FailsNonfiniteReferenceAndStabilizesEachPureDrillCoordi const auto nodes = planarNodes(); const auto shellCandidate = fesa::Mitc4Shell::create( nodePointers(nodes), directors(), section(), material()); - ASSERT_TRUE(shellCandidate.hasValue()); - const auto stiffnessCandidate = shellCandidate.value().stiffness(); - ASSERT_TRUE(stiffnessCandidate.hasValue()); - const auto& stiffness = stiffnessCandidate.value(); + ASSERT_TRUE(shellCandidate.HasValue()); + const auto stiffnessCandidate = shellCandidate.Value().stiffness(); + ASSERT_TRUE(stiffnessCandidate.HasValue()); + const auto& stiffness = stiffnessCandidate.Value(); for (std::size_t nodeIndex = 0U; nodeIndex < 4U; ++nodeIndex) { fesa::Vector pureDrill{24U}; pureDrill[6U * nodeIndex + 5U] = 1.0; EXPECT_DOUBLE_EQ( - stiffness.physicalGlobal24.multiply(pureDrill).norm(), 0.0); - const auto drillAction = stiffness.drillingGlobal24.multiply(pureDrill); + stiffness.physicalGlobal24.Multiply(pureDrill).Norm(), 0.0); + const auto drillAction = stiffness.drillingGlobal24.Multiply(pureDrill); EXPECT_DOUBLE_EQ(drillAction[6U * nodeIndex + 5U], stiffness.drillingStiffness); EXPECT_GT(quadraticEnergy(stiffness.drillingGlobal24, pureDrill), 0.0); } @@ -808,20 +808,20 @@ TEST(Mitc4ShellDrilling, FailsNonfiniteReferenceAndStabilizesEachPureDrillCoordi node(3, {5.0e9, 5.0e9, 0.0}), node(4, {-5.0e9, 5.0e9, 0.0})}; const auto extremeShell = fesa::Mitc4Shell::create( nodePointers(extremeNodes), directors(), section(1.0), material(1.0e300)); - ASSERT_TRUE(extremeShell.hasValue()); - const auto failure = extremeShell.value().stiffness(); - ASSERT_FALSE(failure.hasValue()); - ASSERT_EQ(failure.status().diagnostics().size(), 1U); - EXPECT_EQ(failure.status().diagnostics()[0].code, "invalid-shell-stiffness"); - const auto repeatedFailure = extremeShell.value().stiffness(); - ASSERT_FALSE(repeatedFailure.hasValue()); - ASSERT_EQ(repeatedFailure.status().diagnostics().size(), 1U); + ASSERT_TRUE(extremeShell.HasValue()); + const auto failure = extremeShell.Value().stiffness(); + ASSERT_FALSE(failure.HasValue()); + ASSERT_EQ(failure.GetStatus().Diagnostics().size(), 1U); + EXPECT_EQ(failure.GetStatus().Diagnostics()[0].code, "invalid-shell-stiffness"); + const auto repeatedFailure = extremeShell.Value().stiffness(); + ASSERT_FALSE(repeatedFailure.HasValue()); + ASSERT_EQ(repeatedFailure.GetStatus().Diagnostics().size(), 1U); EXPECT_EQ( - repeatedFailure.status().diagnostics()[0].code, - failure.status().diagnostics()[0].code); + repeatedFailure.GetStatus().Diagnostics()[0].code, + failure.GetStatus().Diagnostics()[0].code); EXPECT_EQ( - repeatedFailure.status().diagnostics()[0].message, - failure.status().diagnostics()[0].message); + repeatedFailure.GetStatus().Diagnostics()[0].message, + failure.GetStatus().Diagnostics()[0].message); } // MITC4-KERNEL-007 @@ -829,21 +829,21 @@ TEST(Mitc4ShellDrilling, ExcludesPureDrillFromPhysicalRecoveryAndEnergy) { const auto nodes = planarNodes(); const auto shellCandidate = fesa::Mitc4Shell::create( nodePointers(nodes), directors(), section(), material()); - ASSERT_TRUE(shellCandidate.hasValue()); - const auto& shell = shellCandidate.value(); + ASSERT_TRUE(shellCandidate.HasValue()); + const auto& shell = shellCandidate.Value(); const auto stiffnessCandidate = shell.stiffness(); - ASSERT_TRUE(stiffnessCandidate.hasValue()); + ASSERT_TRUE(stiffnessCandidate.HasValue()); for (std::size_t nodeIndex = 0U; nodeIndex < nodes.size(); ++nodeIndex) { fesa::Vector pureDrill{24U}; pureDrill[6U * nodeIndex + 5U] = 1.0; EXPECT_GT( - stiffnessCandidate.value().stabilizedGlobal24.multiply(pureDrill).norm(), + stiffnessCandidate.Value().stabilizedGlobal24.Multiply(pureDrill).Norm(), 0.0); const auto recoveryCandidate = shell.recoverPhysical(pureDrill); - ASSERT_TRUE(recoveryCandidate.hasValue()); - const auto& recovery = recoveryCandidate.value(); + ASSERT_TRUE(recoveryCandidate.HasValue()); + const auto& recovery = recoveryCandidate.Value(); EXPECT_DOUBLE_EQ(recovery.strainEnergy, 0.0); for (const auto& point : recovery.points) { for (double value : point.generalizedStrain) { @@ -866,8 +866,8 @@ TEST(Mitc4ShellPhysicalRecovery, RecoversHandFieldAtFixedLocationsAndSectionPosi const auto nodes = planarNodes(); const auto shellCandidate = fesa::Mitc4Shell::create( nodePointers(nodes), directors(), section(), material()); - ASSERT_TRUE(shellCandidate.hasValue()); - const auto& shell = shellCandidate.value(); + ASSERT_TRUE(shellCandidate.HasValue()); + const auto& shell = shellCandidate.Value(); constexpr std::array generalized{ 0.1, -0.05, 0.2, 0.3, -0.15, 0.25, 0.4, -0.3}; @@ -889,8 +889,8 @@ TEST(Mitc4ShellPhysicalRecovery, RecoversHandFieldAtFixedLocationsAndSectionPosi } const auto recoveryCandidate = shell.recoverPhysical(globalField); - ASSERT_TRUE(recoveryCandidate.hasValue()); - const auto& recovery = recoveryCandidate.value(); + ASSERT_TRUE(recoveryCandidate.HasValue()); + const auto& recovery = recoveryCandidate.Value(); const double gauss = 1.0 / std::sqrt(3.0); const std::array, 4> expectedCoordinates{ std::array{-gauss, -gauss}, diff --git a/tests/unit/fem/dof_manager_test.cpp b/tests/unit/fem/dof_manager_test.cpp index 8de7c8c..75185ab 100644 --- a/tests/unit/fem/dof_manager_test.cpp +++ b/tests/unit/fem/dof_manager_test.cpp @@ -78,12 +78,12 @@ struct DofFixture { DofFixture makeDofFixture(fesa::ModelDefinition definition = makeDefinition()) { 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())}; + 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())}; } std::vector rowColumns( @@ -122,7 +122,7 @@ TEST(DofManager, NumbersSixDofsAndFreeEquationsStably) { TEST(DofManager, ExpandsAndValidatesPrescribedValues) { const auto fixture = makeDofFixture(); const auto& values = fixture.dofs.prescribedValues(); - ASSERT_EQ(values.size(), 5U); + ASSERT_EQ(values.Size(), 5U); EXPECT_DOUBLE_EQ(values[0], 0.0); EXPECT_DOUBLE_EQ(values[1], 0.0); EXPECT_DOUBLE_EQ(values[2], 0.25); @@ -133,18 +133,18 @@ TEST(DofManager, ExpandsAndValidatesPrescribedValues) { conflictingDefinition.steps[0].boundaries.push_back( {"root", 1, 1, 1.0, {conflictingDefinition.sourcePath, 77U}}); auto domain = fesa::Domain::create(std::move(conflictingDefinition)); - ASSERT_TRUE(domain.hasValue()); - auto model = fesa::AnalysisModel::create(domain.value()); - ASSERT_TRUE(model.hasValue()); + ASSERT_TRUE(domain.HasValue()); + auto model = fesa::AnalysisModel::create(domain.Value()); + ASSERT_TRUE(model.HasValue()); - auto conflict = fesa::DofManager::create(model.value()); - ASSERT_FALSE(conflict.hasValue()); - EXPECT_EQ(conflict.status().failureCategory(), fesa::FailureCategory::input); - ASSERT_EQ(conflict.status().diagnostics().size(), 1U); - const auto& diagnostic = conflict.status().diagnostics()[0]; + auto conflict = fesa::DofManager::create(model.Value()); + ASSERT_FALSE(conflict.HasValue()); + EXPECT_EQ(conflict.GetStatus().Category(), fesa::FailureCategory::kInput); + ASSERT_EQ(conflict.GetStatus().Diagnostics().size(), 1U); + const auto& diagnostic = conflict.GetStatus().Diagnostics()[0]; EXPECT_EQ(diagnostic.code, "conflicting-boundary-condition"); EXPECT_EQ(diagnostic.keyword, "BOUNDARY"); - EXPECT_EQ(diagnostic.entityIdentity, "root"); + EXPECT_EQ(diagnostic.entity_identity, "root"); EXPECT_EQ(diagnostic.location.file, std::filesystem::path{"models/dof-manager.inp"}); EXPECT_EQ(diagnostic.location.line, 77U); } @@ -227,7 +227,7 @@ TEST(DofManager, ReconstructsFullReducedRoundTrip) { const auto fixture = makeDofFixture(); const auto& dofs = fixture.dofs; fesa::Vector full{dofs.fullDofCount()}; - for (std::size_t index = 0U; index < full.size(); ++index) { + for (std::size_t index = 0U; index < full.Size(); ++index) { full[index] = static_cast(index) + 0.5; } for (std::size_t index = 0U; index < dofs.constrainedDofCount(); ++index) { @@ -235,19 +235,19 @@ TEST(DofManager, ReconstructsFullReducedRoundTrip) { } fesa::Vector reduced{dofs.freeDofCount()}; - for (std::size_t equation = 0U; equation < reduced.size(); ++equation) { + for (std::size_t equation = 0U; equation < reduced.Size(); ++equation) { reduced[equation] = full[dofs.freeDofs()[equation]]; } fesa::Vector reconstructed{dofs.fullDofCount()}; - for (std::size_t equation = 0U; equation < reduced.size(); ++equation) { + for (std::size_t equation = 0U; equation < reduced.Size(); ++equation) { reconstructed[dofs.freeDofs()[equation]] = reduced[equation]; } for (std::size_t index = 0U; index < dofs.constrainedDofCount(); ++index) { reconstructed[dofs.constrainedDofs()[index]] = dofs.prescribedValues()[index]; } - ASSERT_EQ(reconstructed.size(), full.size()); - for (std::size_t index = 0U; index < full.size(); ++index) { + ASSERT_EQ(reconstructed.Size(), full.Size()); + for (std::size_t index = 0U; index < full.Size(); ++index) { EXPECT_DOUBLE_EQ(reconstructed[index], full[index]); } } diff --git a/tests/unit/io/abaqus/domain_mapper_test.cpp b/tests/unit/io/abaqus/domain_mapper_test.cpp index e8e54f7..4e0f205 100644 --- a/tests/unit/io/abaqus/domain_mapper_test.cpp +++ b/tests/unit/io/abaqus/domain_mapper_test.cpp @@ -45,10 +45,10 @@ fesa::Result mapText( const std::string& content) { const TemporaryInputFile input{stem, content}; auto parsed = fesa::AbaqusInputReader{}.read(input.path()); - if (!parsed.hasValue()) { - return fesa::Result::failure(parsed.status()); + if (!parsed.HasValue()) { + return fesa::Result::Failure(parsed.GetStatus()); } - return fesa::AbaqusDomainMapper{}.map(parsed.value()); + return fesa::AbaqusDomainMapper{}.map(parsed.Value()); } std::string readExactBytes(const std::filesystem::path& path) { @@ -73,12 +73,12 @@ const fesa::Diagnostic* findDiagnostic( const fesa::Status& status, const std::string& code) { const auto found = std::find_if( - status.diagnostics().begin(), - status.diagnostics().end(), + status.Diagnostics().begin(), + status.Diagnostics().end(), [&code](const fesa::Diagnostic& diagnostic) { return diagnostic.code == code; }); - return found == status.diagnostics().end() ? nullptr : &*found; + return found == status.Diagnostics().end() ? nullptr : &*found; } std::string replaceOnce( @@ -246,18 +246,18 @@ RootAssembly, 6, -12.5 TEST(InpDomainMapping, MapsEverySupportedKeywordAndLegacyDeck) { auto result = mapText("supported-inventory", supportedInventoryDeck(true)); - ASSERT_TRUE(result.hasValue()); - const fesa::Domain& domain = result.value(); + ASSERT_TRUE(result.HasValue()); + const fesa::Domain& domain = result.Value(); ASSERT_EQ(domain.nodes().size(), 2U); - EXPECT_EQ(domain.nodes()[0].sourceId.instanceName, "Beam-1"); - EXPECT_EQ(domain.nodes()[0].sourceId.sourceLabel, 1); - EXPECT_EQ(domain.nodes()[0].sourceId.sourceLabelText, "0001"); - EXPECT_EQ(domain.nodes()[1].sourceId.sourceLabelText, "0002"); + EXPECT_EQ(domain.nodes()[0].sourceId.instance_name, "Beam-1"); + EXPECT_EQ(domain.nodes()[0].sourceId.source_label, 1); + EXPECT_EQ(domain.nodes()[0].sourceId.source_label_text, "0001"); + EXPECT_EQ(domain.nodes()[1].sourceId.source_label_text, "0002"); EXPECT_DOUBLE_EQ(domain.nodes()[1].coordinates[0], 2.0); ASSERT_EQ(domain.elements().size(), 1U); - EXPECT_EQ(domain.elements()[0].sourceId.sourceLabelText, "0007"); + EXPECT_EQ(domain.elements()[0].sourceId.source_label_text, "0007"); EXPECT_EQ(domain.elements()[0].nodeIndices[0], 0U); EXPECT_EQ(domain.elements()[0].nodeIndices[1], 1U); @@ -304,15 +304,15 @@ TEST(InpDomainMapping, MapsEverySupportedKeywordAndLegacyDeck) { const auto bytesBefore = readExactBytes(legacyPath); const auto timestampBefore = std::filesystem::last_write_time(legacyPath); auto parsedLegacy = fesa::AbaqusInputReader{}.read(legacyPath); - ASSERT_TRUE(parsedLegacy.hasValue()); - auto legacy = fesa::AbaqusDomainMapper{}.map(parsedLegacy.value()); - ASSERT_TRUE(legacy.hasValue()); - EXPECT_EQ(legacy.value().nodes().size(), 11U); - EXPECT_EQ(legacy.value().elements().size(), 10U); - EXPECT_EQ(legacy.value().materials().size(), 1U); - EXPECT_EQ(legacy.value().sections().size(), 1U); - EXPECT_EQ(legacy.value().steps().size(), 1U); - EXPECT_EQ(legacy.value().warnings().size(), 7U); + ASSERT_TRUE(parsedLegacy.HasValue()); + auto legacy = fesa::AbaqusDomainMapper{}.map(parsedLegacy.Value()); + ASSERT_TRUE(legacy.HasValue()); + EXPECT_EQ(legacy.Value().nodes().size(), 11U); + EXPECT_EQ(legacy.Value().elements().size(), 10U); + EXPECT_EQ(legacy.Value().materials().size(), 1U); + EXPECT_EQ(legacy.Value().sections().size(), 1U); + EXPECT_EQ(legacy.Value().steps().size(), 1U); + EXPECT_EQ(legacy.Value().warnings().size(), 7U); EXPECT_EQ(readExactBytes(legacyPath), bytesBefore); EXPECT_EQ(std::filesystem::last_write_time(legacyPath), timestampBefore); } @@ -356,24 +356,24 @@ OnlySecond, 2, 5. )inp"; auto result = mapText("multiple-instances", deck); - ASSERT_TRUE(result.hasValue()); - const fesa::Domain& domain = result.value(); + ASSERT_TRUE(result.HasValue()); + const fesa::Domain& domain = result.Value(); ASSERT_EQ(domain.nodes().size(), 4U); - EXPECT_EQ(domain.nodes()[0].sourceId.instanceName, "First"); - EXPECT_EQ(domain.nodes()[0].sourceId.sourceLabel, 10); - EXPECT_EQ(domain.nodes()[1].sourceId.instanceName, "First"); - EXPECT_EQ(domain.nodes()[1].sourceId.sourceLabel, 20); - EXPECT_EQ(domain.nodes()[2].sourceId.instanceName, "Second"); - EXPECT_EQ(domain.nodes()[2].sourceId.sourceLabel, 10); - EXPECT_EQ(domain.nodes()[3].sourceId.instanceName, "Second"); - EXPECT_EQ(domain.nodes()[3].sourceId.sourceLabel, 20); + EXPECT_EQ(domain.nodes()[0].sourceId.instance_name, "First"); + EXPECT_EQ(domain.nodes()[0].sourceId.source_label, 10); + EXPECT_EQ(domain.nodes()[1].sourceId.instance_name, "First"); + EXPECT_EQ(domain.nodes()[1].sourceId.source_label, 20); + EXPECT_EQ(domain.nodes()[2].sourceId.instance_name, "Second"); + EXPECT_EQ(domain.nodes()[2].sourceId.source_label, 10); + EXPECT_EQ(domain.nodes()[3].sourceId.instance_name, "Second"); + EXPECT_EQ(domain.nodes()[3].sourceId.source_label, 20); ASSERT_EQ(domain.elements().size(), 2U); - EXPECT_EQ(domain.elements()[0].sourceId.instanceName, "First"); + EXPECT_EQ(domain.elements()[0].sourceId.instance_name, "First"); EXPECT_EQ(domain.elements()[0].nodeIndices, (std::array{0U, 1U})); - EXPECT_EQ(domain.elements()[1].sourceId.instanceName, "Second"); + EXPECT_EQ(domain.elements()[1].sourceId.instance_name, "Second"); EXPECT_EQ(domain.elements()[1].nodeIndices, (std::array{2U, 3U})); @@ -412,9 +412,9 @@ OnlySecond, 2, 5. 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].loads[0].target, "2"); + ASSERT_TRUE(direct.HasValue()); + EXPECT_EQ(direct.Value().steps()[0].boundaries[0].target, "1"); + EXPECT_EQ(direct.Value().steps()[0].loads[0].target, "2"); auto aboveThresholds = mapText( "above-geometry-thresholds", @@ -422,7 +422,7 @@ OnlySecond, 2, 5. replaceOnce(minimalDeck(), "2, 1., 0., 0.", "2, 2e-12, 0., 0."), "0., 1., 0.", "1., 2e-12, 0.")); - ASSERT_TRUE(aboveThresholds.hasValue()); + ASSERT_TRUE(aboveThresholds.HasValue()); auto largeFinite = mapText( "large-finite-geometry", @@ -433,7 +433,7 @@ OnlySecond, 2, 5. "2, 1e308, 1e297, 0."), "0., 1., 0.", "1e308, 0., 0.")); - ASSERT_TRUE(largeFinite.hasValue()); + ASSERT_TRUE(largeFinite.HasValue()); } TEST(InpDomainMapping, KeepsNodeAndElementSetNamesInSeparateNamespaces) { @@ -444,8 +444,8 @@ TEST(InpDomainMapping, KeepsNodeAndElementSetNamesInSeparateNamespaces) { "*Elset, elset=ShellS4\n10", "*Nset, nset=ShellS4\n1, 2, 3, 4\n*Elset, elset=ShellS4\n10")); - ASSERT_TRUE(sharedName.hasValue()); - const auto& domain = sharedName.value(); + ASSERT_TRUE(sharedName.HasValue()); + const auto& domain = sharedName.Value(); EXPECT_TRUE(std::any_of( domain.nodeSets().begin(), domain.nodeSets().end(), [](const fesa::NodeSet& set) { return set.name == "ShellS4"; })); @@ -460,8 +460,8 @@ TEST(InpDomainMapping, KeepsNodeAndElementSetNamesInSeparateNamespaces) { "*Elset, elset=ShellS4\n10", "*Nset, nset=Shared\n1\n*Nset, nset=Shared\n2\n" "*Elset, elset=ShellS4\n10")); - ASSERT_FALSE(duplicateNodeSet.hasValue()); - EXPECT_NE(findDiagnostic(duplicateNodeSet.status(), "duplicate-entity"), nullptr); + ASSERT_FALSE(duplicateNodeSet.HasValue()); + EXPECT_NE(findDiagnostic(duplicateNodeSet.GetStatus(), "duplicate-entity"), nullptr); auto duplicateElementSet = mapText( "duplicate-part-element-set", @@ -470,8 +470,8 @@ TEST(InpDomainMapping, KeepsNodeAndElementSetNamesInSeparateNamespaces) { "*Elset, elset=ShellS4\n10", "*Elset, elset=Repeated\n10\n*Elset, elset=Repeated\n20\n" "*Elset, elset=ShellS4\n10")); - ASSERT_FALSE(duplicateElementSet.hasValue()); - EXPECT_NE(findDiagnostic(duplicateElementSet.status(), "duplicate-entity"), nullptr); + ASSERT_FALSE(duplicateElementSet.HasValue()); + EXPECT_NE(findDiagnostic(duplicateElementSet.GetStatus(), "duplicate-entity"), nullptr); auto assemblySharedName = mapText( "separate-assembly-set-namespaces", @@ -483,8 +483,8 @@ TEST(InpDomainMapping, KeepsNodeAndElementSetNamesInSeparateNamespaces) { "*Nset, nset=Root, instance=Beam-1\n1", "*Nset, nset=Root, instance=Beam-1\n1\n" "*Elset, elset=Root, instance=Beam-1\n1")); - ASSERT_TRUE(assemblySharedName.hasValue()); - const auto& assemblyDomain = assemblySharedName.value(); + ASSERT_TRUE(assemblySharedName.HasValue()); + const auto& assemblyDomain = assemblySharedName.Value(); const auto rootNodeSetCount = std::count_if( assemblyDomain.nodeSets().begin(), assemblyDomain.nodeSets().end(), [](const fesa::NodeSet& set) { return set.name == "Root"; }); @@ -502,48 +502,48 @@ TEST(InpDomainMapping, KeepsNodeAndElementSetNamesInSeparateNamespaces) { TEST(InpDomainMapping, NoOpAllowlistWarnsWithoutSemanticEffect) { auto plain = mapText("without-no-ops", supportedInventoryDeck(false)); auto withNoOps = mapText("with-no-ops", supportedInventoryDeck(true)); - ASSERT_TRUE(plain.hasValue()); - ASSERT_TRUE(withNoOps.hasValue()); + ASSERT_TRUE(plain.HasValue()); + ASSERT_TRUE(withNoOps.HasValue()); - EXPECT_TRUE(plain.value().warnings().empty()); - ASSERT_EQ(withNoOps.value().warnings().size(), 8U); - EXPECT_EQ(withNoOps.value().warnings()[0].code, "ignored-input-keyword"); - EXPECT_EQ(withNoOps.value().warnings()[0].keyword, "PREPRINT"); - EXPECT_EQ(withNoOps.value().warnings()[1].keyword, + EXPECT_TRUE(plain.Value().warnings().empty()); + ASSERT_EQ(withNoOps.Value().warnings().size(), 8U); + EXPECT_EQ(withNoOps.Value().warnings()[0].code, "ignored-input-keyword"); + EXPECT_EQ(withNoOps.Value().warnings()[0].keyword, "PREPRINT"); + EXPECT_EQ(withNoOps.Value().warnings()[1].keyword, "TRANSVERSE SHEAR STIFFNESS"); - EXPECT_EQ(withNoOps.value().warnings()[2].keyword, "RESTART"); - EXPECT_EQ(withNoOps.value().warnings()[3].keyword, "OUTPUT"); - EXPECT_EQ(withNoOps.value().warnings()[4].keyword, "NODE OUTPUT"); - EXPECT_EQ(withNoOps.value().warnings()[5].keyword, "ELEMENT OUTPUT"); - EXPECT_EQ(withNoOps.value().warnings()[6].keyword, "CONTACT OUTPUT"); - EXPECT_EQ(withNoOps.value().warnings()[7].keyword, "OUTPUT"); - for (const auto& warning : withNoOps.value().warnings()) { - EXPECT_EQ(warning.severity, fesa::Severity::warning); + EXPECT_EQ(withNoOps.Value().warnings()[2].keyword, "RESTART"); + EXPECT_EQ(withNoOps.Value().warnings()[3].keyword, "OUTPUT"); + EXPECT_EQ(withNoOps.Value().warnings()[4].keyword, "NODE OUTPUT"); + EXPECT_EQ(withNoOps.Value().warnings()[5].keyword, "ELEMENT OUTPUT"); + EXPECT_EQ(withNoOps.Value().warnings()[6].keyword, "CONTACT OUTPUT"); + EXPECT_EQ(withNoOps.Value().warnings()[7].keyword, "OUTPUT"); + for (const auto& warning : withNoOps.Value().warnings()) { + EXPECT_EQ(warning.severity, fesa::Severity::kWarning); } - EXPECT_EQ(withNoOps.value().nodes().size(), plain.value().nodes().size()); - EXPECT_EQ(withNoOps.value().elements().size(), plain.value().elements().size()); - EXPECT_EQ(withNoOps.value().materials().size(), plain.value().materials().size()); - EXPECT_EQ(withNoOps.value().sections().size(), plain.value().sections().size()); - EXPECT_EQ(withNoOps.value().nodeSets().size(), plain.value().nodeSets().size()); - EXPECT_EQ(withNoOps.value().elementSets().size(), plain.value().elementSets().size()); - EXPECT_EQ(withNoOps.value().steps().size(), plain.value().steps().size()); - EXPECT_EQ(withNoOps.value().steps()[0].boundaries.size(), - plain.value().steps()[0].boundaries.size()); - EXPECT_EQ(withNoOps.value().steps()[0].loads.size(), - plain.value().steps()[0].loads.size()); + EXPECT_EQ(withNoOps.Value().nodes().size(), plain.Value().nodes().size()); + EXPECT_EQ(withNoOps.Value().elements().size(), plain.Value().elements().size()); + EXPECT_EQ(withNoOps.Value().materials().size(), plain.Value().materials().size()); + EXPECT_EQ(withNoOps.Value().sections().size(), plain.Value().sections().size()); + EXPECT_EQ(withNoOps.Value().nodeSets().size(), plain.Value().nodeSets().size()); + EXPECT_EQ(withNoOps.Value().elementSets().size(), plain.Value().elementSets().size()); + EXPECT_EQ(withNoOps.Value().steps().size(), plain.Value().steps().size()); + EXPECT_EQ(withNoOps.Value().steps()[0].boundaries.size(), + plain.Value().steps()[0].boundaries.size()); + EXPECT_EQ(withNoOps.Value().steps()[0].loads.size(), + plain.Value().steps()[0].loads.size()); } // MITC4-MAP-001 TEST(InpDomainMapping, MapsS4AndS4rThroughOneMitc4Identity) { auto result = mapText("mitc4-map-001", shellDeck()); - ASSERT_TRUE(result.hasValue()); - const fesa::Domain& domain = result.value(); + ASSERT_TRUE(result.HasValue()); + const fesa::Domain& domain = result.Value(); EXPECT_TRUE(domain.elements().empty()); ASSERT_EQ(domain.shellElements().size(), 4U); - EXPECT_EQ(domain.shellElements()[0].sourceId.instanceName, "First"); - EXPECT_EQ(domain.shellElements()[0].sourceId.sourceLabelText, "0010"); + EXPECT_EQ(domain.shellElements()[0].sourceId.instance_name, "First"); + EXPECT_EQ(domain.shellElements()[0].sourceId.source_label_text, "0010"); EXPECT_EQ( domain.shellElements()[0].sourceType, fesa::ShellSourceElementType::s4); @@ -553,7 +553,7 @@ TEST(InpDomainMapping, MapsS4AndS4rThroughOneMitc4Identity) { EXPECT_EQ(domain.shellElements()[0].sectionIndex, 0U); EXPECT_EQ(domain.shellElements()[0].materialIndex, 0U); - EXPECT_EQ(domain.shellElements()[1].sourceId.instanceName, "First"); + EXPECT_EQ(domain.shellElements()[1].sourceId.instance_name, "First"); EXPECT_EQ( domain.shellElements()[1].sourceType, fesa::ShellSourceElementType::s4r); @@ -563,11 +563,11 @@ TEST(InpDomainMapping, MapsS4AndS4rThroughOneMitc4Identity) { EXPECT_EQ(domain.shellElements()[1].sectionIndex, 1U); EXPECT_EQ(domain.shellElements()[1].materialIndex, 1U); - EXPECT_EQ(domain.shellElements()[2].sourceId.instanceName, "Second"); + EXPECT_EQ(domain.shellElements()[2].sourceId.instance_name, "Second"); EXPECT_EQ( domain.shellElements()[2].nodeIndices, (std::array{6U, 7U, 8U, 9U})); - EXPECT_EQ(domain.shellElements()[3].sourceId.instanceName, "Second"); + EXPECT_EQ(domain.shellElements()[3].sourceId.instance_name, "Second"); EXPECT_EQ( fesa::kMitc4InternalFormulation, std::string_view{"FESA-MITC4"}); @@ -612,34 +612,34 @@ TEST(InpDomainMapping, RejectsInvalidShellAssignmentsAndProperties) { const std::vector cases{ {"unresolved-material", replaceOnce(base, "material=Steel", "material=Missing"), - "unresolved-shell-section", fesa::FailureCategory::input}, + "unresolved-shell-section", fesa::FailureCategory::kInput}, {"unresolved-elset", replaceOnce(base, "elset=ShellS4, material=Steel", "elset=Missing, material=Steel"), - "unresolved-shell-section", fesa::FailureCategory::input}, + "unresolved-shell-section", fesa::FailureCategory::kInput}, {"missing-assignment", replaceOnce( base, "*Shell Section, elset=ShellS4R, material=Aluminum\n0.2\n", ""), - "invalid-shell-section-assignment", fesa::FailureCategory::input}, + "invalid-shell-section-assignment", fesa::FailureCategory::kInput}, {"conflicting-assignment", replaceOnce(base, "elset=ShellS4R, material=Aluminum", "elset=ShellS4, material=Aluminum"), - "invalid-shell-section-assignment", fesa::FailureCategory::input}, + "invalid-shell-section-assignment", fesa::FailureCategory::kInput}, {"invalid-thickness", replaceOnce(base, "0.2\n*End Part", "0.\n*End Part"), - "invalid-shell-thickness", fesa::FailureCategory::model}, + "invalid-shell-thickness", fesa::FailureCategory::kModel}, {"invalid-material", replaceOnce(base, "70000., 0.25", "70000., 0.5"), - "invalid-shell-material", fesa::FailureCategory::model}}; + "invalid-shell-material", fesa::FailureCategory::kModel}}; for (const auto& testCase : cases) { SCOPED_TRACE(testCase.name); auto result = mapText("mitc4-map-002-" + testCase.name, testCase.deck); - ASSERT_FALSE(result.hasValue()); - EXPECT_EQ(result.status().failureCategory(), testCase.category); - ASSERT_NE(findDiagnostic(result.status(), testCase.expectedCode), nullptr); + ASSERT_FALSE(result.HasValue()); + EXPECT_EQ(result.GetStatus().Category(), testCase.category); + ASSERT_NE(findDiagnostic(result.GetStatus(), testCase.expectedCode), nullptr); } } @@ -674,11 +674,11 @@ TEST(InpDomainMapping, RejectsInvalidShellConnectivityOptionsAndMixedModels) { for (const auto& testCase : cases) { SCOPED_TRACE(testCase.name); auto result = mapText("mitc4-map-003-" + testCase.name, testCase.deck); - ASSERT_FALSE(result.hasValue()); + ASSERT_FALSE(result.HasValue()); EXPECT_EQ( - result.status().failureCategory(), - fesa::FailureCategory::input); - ASSERT_NE(findDiagnostic(result.status(), testCase.expectedCode), nullptr); + result.GetStatus().Category(), + fesa::FailureCategory::kInput); + ASSERT_NE(findDiagnostic(result.GetStatus(), testCase.expectedCode), nullptr); } } @@ -689,11 +689,11 @@ TEST(InpDomainMapping, PreservesProcedureLoadAndOutputRequestBoundariesForShells "*End Step\n", "*Output, field\n*Node Output\nU, RF\n*Element Output\nS\n*End Step\n"); auto valid = mapText("mitc4-map-004-no-ops", withNoOps); - ASSERT_TRUE(valid.hasValue()); - ASSERT_EQ(valid.value().warnings().size(), 3U); - EXPECT_EQ(valid.value().warnings()[0].keyword, "OUTPUT"); - EXPECT_EQ(valid.value().warnings()[1].keyword, "NODE OUTPUT"); - EXPECT_EQ(valid.value().warnings()[2].keyword, "ELEMENT OUTPUT"); + ASSERT_TRUE(valid.HasValue()); + ASSERT_EQ(valid.Value().warnings().size(), 3U); + EXPECT_EQ(valid.Value().warnings()[0].keyword, "OUTPUT"); + EXPECT_EQ(valid.Value().warnings()[1].keyword, "NODE OUTPUT"); + EXPECT_EQ(valid.Value().warnings()[2].keyword, "ELEMENT OUTPUT"); struct InvalidCase { std::string name; @@ -720,11 +720,11 @@ TEST(InpDomainMapping, PreservesProcedureLoadAndOutputRequestBoundariesForShells for (const auto& testCase : cases) { SCOPED_TRACE(testCase.name); auto result = mapText("mitc4-map-004-" + testCase.name, testCase.deck); - ASSERT_FALSE(result.hasValue()); + ASSERT_FALSE(result.HasValue()); EXPECT_EQ( - result.status().failureCategory(), - fesa::FailureCategory::input); - ASSERT_NE(findDiagnostic(result.status(), testCase.expectedCode), nullptr); + result.GetStatus().Category(), + fesa::FailureCategory::kInput); + ASSERT_NE(findDiagnostic(result.GetStatus(), testCase.expectedCode), nullptr); } } @@ -739,116 +739,116 @@ TEST(InpDomainMapping, RejectsUnsupportedAndInvalidPortfolio) { const std::string base = minimalDeck(); const std::vector cases{ {"b31", replaceOnce(base, "type=B33", "type=B31"), - "unsupported-element-formulation", fesa::FailureCategory::input}, + "unsupported-element-formulation", fesa::FailureCategory::kInput}, {"transform", replaceOnce(base, "*End Instance\n", "1., 0., 0.\n*End Instance\n"), - "unsupported-instance-transform", fesa::FailureCategory::input}, + "unsupported-instance-transform", fesa::FailureCategory::kInput}, {"nested-assembly", replaceOnce(base, "*End Assembly\n", "*Assembly, name=Nested\n*End Assembly\n*End Assembly\n"), - "unsupported-nested-assembly", fesa::FailureCategory::input}, + "unsupported-nested-assembly", fesa::FailureCategory::kInput}, {"multiple-step", base + "*Step\n*Static\n1., 1., 1., 1.\n*End Step\n", - "unsupported-multiple-step", fesa::FailureCategory::input}, + "unsupported-multiple-step", fesa::FailureCategory::kInput}, {"late-part", replaceOnce(base, "*End Assembly\n*Material", "*End Assembly\n*Part, name=Late\n*End Part\n*Material"), - "invalid-keyword-location", fesa::FailureCategory::input}, + "invalid-keyword-location", fesa::FailureCategory::kInput}, {"material-before-assembly", replaceOnce(base, "*Assembly, name=Assembly", "*Material, name=Early\n*Elastic\n50., 0.2\n*Assembly, name=Assembly"), - "invalid-keyword-location", fesa::FailureCategory::input}, + "invalid-keyword-location", fesa::FailureCategory::kInput}, {"material-after-model-boundary", replaceOnce(base, "*Material, name=Steel\n*Elastic\n100., 0.25\n*Boundary\nRoot, 1, 6", "*Boundary\nRoot, 1, 6\n*Material, name=Steel\n*Elastic\n100., 0.25"), - "invalid-keyword-location", fesa::FailureCategory::input}, + "invalid-keyword-location", fesa::FailureCategory::kInput}, {"keyword-after-step", base + "*Preprint, echo=NO\n", - "invalid-keyword-location", fesa::FailureCategory::input}, + "invalid-keyword-location", fesa::FailureCategory::kInput}, {"assembly-instance-after-set", replaceOnce(base, "*Instance, name=Beam-1, part=BeamPart\n*End Instance\n*Nset, nset=Root, instance=Beam-1\n1", "*Nset, nset=Root, instance=Beam-1\n1\n*Instance, name=Beam-1, part=BeamPart\n*End Instance"), - "invalid-keyword-location", fesa::FailureCategory::input}, + "invalid-keyword-location", fesa::FailureCategory::kInput}, {"element-before-node", replaceOnce(base, "*Node\n1, 0., 0., 0.\n2, 1., 0., 0.\n*Element, type=B33\n1, 1, 2", "*Element, type=B33\n1, 1, 2\n*Node\n1, 0., 0., 0.\n2, 1., 0., 0."), - "invalid-keyword-location", fesa::FailureCategory::input}, + "invalid-keyword-location", fesa::FailureCategory::kInput}, {"shear-before-section-context", replaceOnce(base, "*Beam General Section", "*Transverse Shear Stiffness\n1., 2., 3.\n*Beam General Section"), - "invalid-keyword-location", fesa::FailureCategory::input}, + "invalid-keyword-location", fesa::FailureCategory::kInput}, {"incomplete-part", replaceOnce(base, base.substr(base.find("*Part"), base.find("*End Part") + std::string{"*End Part\n"}.size() - base.find("*Part")), "*Part, name=BeamPart\n*End Part\n"), - "invalid-keyword-location", fesa::FailureCategory::input}, + "invalid-keyword-location", fesa::FailureCategory::kInput}, {"empty-assembly", replaceOnce(base, base.substr(base.find("*Assembly"), base.find("*End Assembly") + std::string{"*End Assembly\n"}.size() - base.find("*Assembly")), "*Assembly, name=Assembly\n*End Assembly\n"), - "invalid-keyword-location", fesa::FailureCategory::input}, + "invalid-keyword-location", fesa::FailureCategory::kInput}, {"cload-before-static", replaceOnce(base, "*Static\n0.1, 1., 0.01, 1.\n*Cload\nTip, 2, -1.", "*Cload\nTip, 2, -1.\n*Static\n0.1, 1., 0.01, 1."), - "invalid-keyword-location", fesa::FailureCategory::input}, + "invalid-keyword-location", fesa::FailureCategory::kInput}, {"static-after-boundary", replaceOnce( replaceOnce(base, "*Boundary\nRoot, 1, 6\n*Step", "*Step"), "*Static\n0.1, 1., 0.01, 1.", "*Boundary\nRoot, 1, 6\n*Static\n0.1, 1., 0.01, 1."), - "invalid-keyword-location", fesa::FailureCategory::input}, + "invalid-keyword-location", fesa::FailureCategory::kInput}, {"boundary-after-cload", replaceOnce( replaceOnce(base, "*Boundary\nRoot, 1, 6\n*Step", "*Step"), "Tip, 2, -1.\n*End Step", "Tip, 2, -1.\n*Boundary\nRoot, 1, 6\n*End Step"), - "invalid-keyword-location", fesa::FailureCategory::input}, + "invalid-keyword-location", fesa::FailureCategory::kInput}, {"cload-after-no-op", replaceOnce(base, "*Cload", "*Restart, write, frequency=0\n*Cload"), - "invalid-keyword-location", fesa::FailureCategory::input}, + "invalid-keyword-location", fesa::FailureCategory::kInput}, {"step-without-static", replaceOnce(base, "*Static\n0.1, 1., 0.01, 1.\n*Cload\nTip, 2, -1.\n*End Step", "*End Step"), - "invalid-keyword-location", fesa::FailureCategory::input}, + "invalid-keyword-location", fesa::FailureCategory::kInput}, {"dependent-instance", replaceOnce(base, "part=BeamPart", "part=BeamPart, dependent=YES"), - "unsupported-instance-mesh-semantics", fesa::FailureCategory::input}, + "unsupported-instance-mesh-semantics", fesa::FailureCategory::kInput}, {"coupled-section", replaceOnce(base, "1., 1., 0., 1., 1.", "1., 1., 0.5, 1., 1."), - "unsupported-coupled-section", fesa::FailureCategory::model}, + "unsupported-coupled-section", fesa::FailureCategory::kModel}, {"zero-length", replaceOnce(base, "2, 1., 0., 0.", "2, 0., 0., 0."), - "invalid-beam-length", fesa::FailureCategory::model}, + "invalid-beam-length", fesa::FailureCategory::kModel}, {"parallel-guide", replaceOnce(base, "0., 1., 0.", "1., 0., 0."), - "invalid-beam-guide-vector", fesa::FailureCategory::model}, + "invalid-beam-guide-vector", fesa::FailureCategory::kModel}, {"nonpositive-area", replaceOnce(base, "1., 1., 0., 1., 1.", "0., 1., 0., 1., 1."), - "invalid-beam-property", fesa::FailureCategory::model}, + "invalid-beam-property", fesa::FailureCategory::kModel}, {"nonpositive-derived-shear", replaceOnce(base, "100., 0.25", "100., -1.25"), - "invalid-beam-property", fesa::FailureCategory::model}, + "invalid-beam-property", fesa::FailureCategory::kModel}, {"nonfinite-elastic", replaceOnce(base, "100., 0.25", "inf, 0.25"), - "invalid-beam-property", fesa::FailureCategory::model}, + "invalid-beam-property", fesa::FailureCategory::kModel}, {"nonfinite-section-property", replaceOnce(base, "1., 1., 0., 1., 1.", "nan, 1., 0., 1., 1."), - "invalid-beam-property", fesa::FailureCategory::model}, + "invalid-beam-property", fesa::FailureCategory::kModel}, {"nonfinite-guide", replaceOnce(base, "0., 1., 0.", "0., inf, 0."), - "invalid-beam-guide-vector", fesa::FailureCategory::model}, + "invalid-beam-guide-vector", fesa::FailureCategory::kModel}, {"length-at-threshold", replaceOnce(base, "2, 1., 0., 0.", "2, 1e-12, 0., 0."), - "invalid-beam-length", fesa::FailureCategory::model}, + "invalid-beam-length", fesa::FailureCategory::kModel}, {"guide-at-threshold", replaceOnce(base, "0., 1., 0.", "1., 1e-12, 0."), - "invalid-beam-guide-vector", fesa::FailureCategory::model}, + "invalid-beam-guide-vector", fesa::FailureCategory::kModel}, {"duplicate-elastic", replaceOnce(base, "100., 0.25\n*Boundary", "100., 0.25\n*Elastic\n100., 0.25\n*Boundary"), - "duplicate-entity", fesa::FailureCategory::input}, + "duplicate-entity", fesa::FailureCategory::kInput}, {"duplicate-node-label", replaceOnce(base, "2, 1., 0., 0.", "1, 1., 0., 0."), - "duplicate-entity", fesa::FailureCategory::input}, + "duplicate-entity", fesa::FailureCategory::kInput}, {"dangling-connectivity", replaceOnce(base, "1, 1, 2", "1, 1, 9"), - "unresolved-reference", fesa::FailureCategory::input}, + "unresolved-reference", fesa::FailureCategory::kInput}, {"invalid-dof", replaceOnce(base, "Root, 1, 6", "Root, 1, 7"), - "invalid-dof", fesa::FailureCategory::input}, + "invalid-dof", fesa::FailureCategory::kInput}, {"nonfinite-coordinate", replaceOnce(base, "1., 0., 0.", "nan, 0., 0."), - "invalid-numeric-value", fesa::FailureCategory::input}, + "invalid-numeric-value", fesa::FailureCategory::kInput}, {"malformed-node-arity", replaceOnce(base, "1, 0., 0., 0.", "1, 0., 0."), - "invalid-data-arity", fesa::FailureCategory::input}, + "invalid-data-arity", fesa::FailureCategory::kInput}, {"nlgeom", replaceOnce(base, "nlgeom=NO", "nlgeom=YES"), - "unsupported-nonlinear-geometry", fesa::FailureCategory::model}, + "unsupported-nonlinear-geometry", fesa::FailureCategory::kModel}, {"unknown-keyword", replaceOnce(base, "*Assembly, name=Assembly", "*Density\n1.\n*Assembly, name=Assembly"), - "unsupported-keyword", fesa::FailureCategory::input}, + "unsupported-keyword", fesa::FailureCategory::kInput}, {"invalid-static-arity", replaceOnce(base, "0.1, 1., 0.01, 1.", "0.1, 1., 0.01"), - "invalid-static-data", fesa::FailureCategory::input}, + "invalid-static-data", fesa::FailureCategory::kInput}, {"invalid-static-range", replaceOnce(base, "0.1, 1., 0.01, 1.", "0.1, 1., 2., 1."), - "invalid-static-data", fesa::FailureCategory::input}, + "invalid-static-data", fesa::FailureCategory::kInput}, {"invalid-generate", replaceOnce(base, "*Elset, elset=BeamSet\n1", "*Elset, elset=BeamSet, generate\n1, 1, 0"), - "invalid-set-range", fesa::FailureCategory::input}, + "invalid-set-range", fesa::FailureCategory::kInput}, {"nonlanding-generate", replaceOnce(base, "*Elset, elset=BeamSet\n1", "*Elset, elset=BeamSet, generate\n1, 2, 2"), - "invalid-set-range", fesa::FailureCategory::input}, + "invalid-set-range", fesa::FailureCategory::kInput}, {"ambiguous-direct-label", replaceOnce( replaceOnce(base, "*End Instance\n*Nset, nset=Root", "*End Instance\n*Instance, name=Beam-2, part=BeamPart\n*End Instance\n*Nset, nset=Root"), "Root, 1, 6", "1, 1, 6"), - "unresolved-reference", fesa::FailureCategory::input}, + "unresolved-reference", fesa::FailureCategory::kInput}, {"ambiguous-part-set", replaceOnce( replaceOnce( replaceOnce(base, @@ -858,24 +858,24 @@ TEST(InpDomainMapping, RejectsUnsupportedAndInvalidPortfolio) { "*End Instance\n*Instance, name=Beam-2, part=BeamPart\n*End Instance\n*Nset, nset=Root"), "Root, 1, 6", "Local, 1, 6"), - "unresolved-reference", fesa::FailureCategory::input}, + "unresolved-reference", fesa::FailureCategory::kInput}, {"direct-set-conflict", replaceOnce(base, "*Step, name=Load", "*Boundary\n1, 1, 1, 2.\n*Step, name=Load"), - "conflicting-boundary-condition", fesa::FailureCategory::input}, + "conflicting-boundary-condition", fesa::FailureCategory::kInput}, {"dangling-boundary-target", replaceOnce(base, "Root, 1, 6", "Missing, 1, 6"), - "unresolved-reference", fesa::FailureCategory::input}, + "unresolved-reference", fesa::FailureCategory::kInput}, {"conflicting-boundary", replaceOnce(base, "*Step, name=Load", "*Boundary\nRoot, 1, 1, 2.\n*Step, name=Load"), - "conflicting-boundary-condition", fesa::FailureCategory::input}}; + "conflicting-boundary-condition", fesa::FailureCategory::kInput}}; for (const auto& testCase : cases) { SCOPED_TRACE(testCase.name); auto result = mapText("invalid-" + testCase.name, testCase.deck); - ASSERT_FALSE(result.hasValue()); - EXPECT_EQ(result.status().failureCategory(), testCase.category); - const auto* diagnostic = findDiagnostic(result.status(), testCase.expectedCode); + ASSERT_FALSE(result.HasValue()); + EXPECT_EQ(result.GetStatus().Category(), testCase.category); + const auto* diagnostic = findDiagnostic(result.GetStatus(), testCase.expectedCode); ASSERT_NE(diagnostic, nullptr); - EXPECT_EQ(diagnostic->severity, fesa::Severity::error); + EXPECT_EQ(diagnostic->severity, fesa::Severity::kError); EXPECT_FALSE(diagnostic->location.file.empty()); EXPECT_GT(diagnostic->location.line, 0U); } @@ -899,16 +899,16 @@ TEST(InpDomainMapping, RejectsUnsupportedAndInvalidPortfolio) { for (const auto& testCase : sameTokenCases) { SCOPED_TRACE("same-token-" + testCase.name); auto result = mapText("same-token-" + testCase.name, testCase.deck); - ASSERT_FALSE(result.hasValue()); + ASSERT_FALSE(result.HasValue()); EXPECT_EQ( - result.status().failureCategory(), - fesa::FailureCategory::input); + result.GetStatus().Category(), + fesa::FailureCategory::kInput); const auto* diagnostic = - findDiagnostic(result.status(), "unresolved-reference"); + findDiagnostic(result.GetStatus(), "unresolved-reference"); ASSERT_NE(diagnostic, nullptr); - EXPECT_EQ(diagnostic->severity, fesa::Severity::error); + EXPECT_EQ(diagnostic->severity, fesa::Severity::kError); EXPECT_EQ(diagnostic->keyword, testCase.expectedKeyword); - EXPECT_EQ(diagnostic->entityIdentity, "1"); + EXPECT_EQ(diagnostic->entity_identity, "1"); EXPECT_EQ(diagnostic->location.line, testCase.expectedLine); EXPECT_EQ( diagnostic->location.file.filename().string(), @@ -923,9 +923,9 @@ TEST(InpDomainMapping, RejectsDloadWithoutDistributedLoadObject) { "*Dload\nBeamSet, PY, -1.\n"); auto result = mapText("dload", deck); - ASSERT_FALSE(result.hasValue()); - EXPECT_EQ(result.status().failureCategory(), fesa::FailureCategory::input); - const auto* diagnostic = findDiagnostic(result.status(), "unsupported-keyword"); + ASSERT_FALSE(result.HasValue()); + EXPECT_EQ(result.GetStatus().Category(), fesa::FailureCategory::kInput); + const auto* diagnostic = findDiagnostic(result.GetStatus(), "unsupported-keyword"); ASSERT_NE(diagnostic, nullptr); EXPECT_EQ(diagnostic->keyword, "DLOAD"); } diff --git a/tests/unit/io/abaqus/input_reader_test.cpp b/tests/unit/io/abaqus/input_reader_test.cpp index 7304f99..bf66c25 100644 --- a/tests/unit/io/abaqus/input_reader_test.cpp +++ b/tests/unit/io/abaqus/input_reader_test.cpp @@ -63,37 +63,37 @@ TEST(InpSyntax, RejectsMalformedOrOrphanData) { std::filesystem::remove(missingPath, removeError); const auto unreadable = fesa::AbaqusInputReader{}.read(missingPath); - ASSERT_FALSE(unreadable.hasValue()); + ASSERT_FALSE(unreadable.HasValue()); EXPECT_EQ( - unreadable.status().failureCategory(), - fesa::FailureCategory::input); - ASSERT_EQ(unreadable.status().diagnostics().size(), 1U); - EXPECT_EQ(unreadable.status().diagnostics()[0].code, + unreadable.GetStatus().Category(), + fesa::FailureCategory::kInput); + ASSERT_EQ(unreadable.GetStatus().Diagnostics().size(), 1U); + EXPECT_EQ(unreadable.GetStatus().Diagnostics()[0].code, "input-file-unreadable"); const TemporaryInputFile malformed{"malformed-keyword", "*, name=value\n"}; const auto malformedResult = fesa::AbaqusInputReader{}.read(malformed.path()); - ASSERT_FALSE(malformedResult.hasValue()); + ASSERT_FALSE(malformedResult.HasValue()); EXPECT_EQ( - malformedResult.status().failureCategory(), - fesa::FailureCategory::input); - ASSERT_EQ(malformedResult.status().diagnostics().size(), 1U); - EXPECT_EQ(malformedResult.status().diagnostics()[0].code, + malformedResult.GetStatus().Category(), + fesa::FailureCategory::kInput); + ASSERT_EQ(malformedResult.GetStatus().Diagnostics().size(), 1U); + EXPECT_EQ(malformedResult.GetStatus().Diagnostics()[0].code, "malformed-keyword"); - EXPECT_EQ(malformedResult.status().diagnostics()[0].location.line, 1U); + EXPECT_EQ(malformedResult.GetStatus().Diagnostics()[0].location.line, 1U); const TemporaryInputFile orphan{ "orphan-data", "** comment\n\norphan, data\n"}; const auto orphanResult = fesa::AbaqusInputReader{}.read(orphan.path()); - ASSERT_FALSE(orphanResult.hasValue()); + ASSERT_FALSE(orphanResult.HasValue()); EXPECT_EQ( - orphanResult.status().failureCategory(), - fesa::FailureCategory::input); - ASSERT_EQ(orphanResult.status().diagnostics().size(), 1U); - EXPECT_EQ(orphanResult.status().diagnostics()[0].code, + orphanResult.GetStatus().Category(), + fesa::FailureCategory::kInput); + ASSERT_EQ(orphanResult.GetStatus().Diagnostics().size(), 1U); + EXPECT_EQ(orphanResult.GetStatus().Diagnostics()[0].code, "orphan-data-line"); - EXPECT_EQ(orphanResult.status().diagnostics()[0].location.line, 3U); + EXPECT_EQ(orphanResult.GetStatus().Diagnostics()[0].location.line, 3U); } TEST(InpSyntax, ReadsLegacyCantileverWithoutMutation) { @@ -105,23 +105,23 @@ TEST(InpSyntax, ReadsLegacyCantileverWithoutMutation) { const auto result = fesa::AbaqusInputReader{}.read(inputPath); - ASSERT_TRUE(result.hasValue()); - EXPECT_EQ(result.value().sourceContentIdentity, + ASSERT_TRUE(result.HasValue()); + EXPECT_EQ(result.Value().sourceContentIdentity, "fnv1a64:04543464cc970405"); - EXPECT_EQ(result.value().sourcePath, + EXPECT_EQ(result.Value().sourcePath, std::filesystem::absolute(inputPath).lexically_normal()); - ASSERT_EQ(result.value().blocks.size(), 30U); - EXPECT_EQ(result.value().blocks.front().canonicalName, "HEADING"); - EXPECT_EQ(result.value().blocks.front().location.line, 1U); - EXPECT_EQ(result.value().blocks.back().canonicalName, "END STEP"); + ASSERT_EQ(result.Value().blocks.size(), 30U); + EXPECT_EQ(result.Value().blocks.front().canonicalName, "HEADING"); + EXPECT_EQ(result.Value().blocks.front().location.line, 1U); + EXPECT_EQ(result.Value().blocks.back().canonicalName, "END STEP"); const auto element = std::find_if( - result.value().blocks.begin(), - result.value().blocks.end(), + result.Value().blocks.begin(), + result.Value().blocks.end(), [](const fesa::KeywordBlock& block) { return block.canonicalName == "ELEMENT"; }); - ASSERT_NE(element, result.value().blocks.end()); + ASSERT_NE(element, result.Value().blocks.end()); ASSERT_EQ(element->parameters.size(), 1U); EXPECT_EQ(element->parameters[0].name, "TYPE"); ASSERT_TRUE(element->parameters[0].value.has_value()); diff --git a/tests/unit/io/abaqus/input_syntax_test.cpp b/tests/unit/io/abaqus/input_syntax_test.cpp index 618750b..b01e233 100644 --- a/tests/unit/io/abaqus/input_syntax_test.cpp +++ b/tests/unit/io/abaqus/input_syntax_test.cpp @@ -45,9 +45,9 @@ TEST(InpSyntax, CanonicalizesKeywordAndParameterNamesOnly) { const auto result = fesa::AbaqusInputReader{}.read(input.path()); - ASSERT_TRUE(result.hasValue()); - ASSERT_EQ(result.value().blocks.size(), 1U); - const auto& block = result.value().blocks[0]; + ASSERT_TRUE(result.HasValue()); + ASSERT_EQ(result.Value().blocks.size(), 1U); + const auto& block = result.Value().blocks[0]; EXPECT_EQ(block.canonicalName, "ELEMENT"); EXPECT_EQ(block.originalLine, originalLine); EXPECT_EQ(block.location.line, 1U); @@ -73,23 +73,23 @@ TEST(InpSyntax, PreservesDataAndSourceLocations) { const auto result = fesa::AbaqusInputReader{}.read(input.path()); - ASSERT_TRUE(result.hasValue()); + ASSERT_TRUE(result.HasValue()); EXPECT_EQ( - result.value().sourcePath, + result.Value().sourcePath, std::filesystem::absolute(input.path()).lexically_normal()); - EXPECT_EQ(result.value().sourceContentIdentity, + EXPECT_EQ(result.Value().sourceContentIdentity, "fnv1a64:c120b6ed2445be46"); - ASSERT_EQ(result.value().blocks.size(), 1U); - const auto& block = result.value().blocks[0]; + ASSERT_EQ(result.Value().blocks.size(), 1U); + const auto& block = result.Value().blocks[0]; EXPECT_EQ(block.canonicalName, "NODE"); EXPECT_EQ(block.originalLine, "*NoDe"); - EXPECT_EQ(block.location.file, result.value().sourcePath); + EXPECT_EQ(block.location.file, result.Value().sourcePath); EXPECT_EQ(block.location.line, 3U); ASSERT_EQ(block.data.size(), 1U); EXPECT_EQ( block.data[0].fields, (std::vector{"0007", "Label_A", "", ""})); - EXPECT_EQ(block.data[0].location.file, result.value().sourcePath); + EXPECT_EQ(block.data[0].location.file, result.Value().sourcePath); EXPECT_EQ(block.data[0].location.line, 4U); } diff --git a/tests/unit/io/hdf5/hdf5_results_writer_test.cpp b/tests/unit/io/hdf5/hdf5_results_writer_test.cpp index f15f5d4..ba6e989 100644 --- a/tests/unit/io/hdf5/hdf5_results_writer_test.cpp +++ b/tests/unit/io/hdf5/hdf5_results_writer_test.cpp @@ -5,7 +5,7 @@ #include "fesa/analysis/analysis_model.hpp" #include "fesa/analysis/analysis_state.hpp" -#include "fesa/build_info.hpp" +#include "fesa/build_info.h" #include "fesa/fem/dof_manager.hpp" #include "fesa/model/domain.hpp" @@ -158,27 +158,27 @@ WriterFixture makeFixture( const bool useDefaultCentroid = false) { auto domainResult = fesa::Domain::create( makeDefinition(source, useDefaultCentroid)); - if (!domainResult.hasValue()) { + if (!domainResult.HasValue()) { throw std::runtime_error{"Writer fixture Domain construction failed."}; } auto domain = std::make_unique( - std::move(domainResult.value())); + std::move(domainResult.Value())); auto modelResult = fesa::AnalysisModel::create(*domain); - if (!modelResult.hasValue()) { + if (!modelResult.HasValue()) { throw std::runtime_error{"Writer fixture AnalysisModel construction failed."}; } - const fesa::AnalysisModel model = std::move(modelResult.value()); + const fesa::AnalysisModel model = std::move(modelResult.Value()); auto dofsResult = fesa::DofManager::create(model); - if (!dofsResult.hasValue()) { + if (!dofsResult.HasValue()) { throw std::runtime_error{"Writer fixture DofManager construction failed."}; } auto dofs = std::make_unique( - std::move(dofsResult.value())); + std::move(dofsResult.Value())); auto state = std::make_unique( fesa::AnalysisState::create(*dofs, {"Step-1", 0U})); - for (std::size_t index = 0U; index < state->displacement().size(); ++index) { + for (std::size_t index = 0U; index < state->displacement().Size(); ++index) { state->displacement()[index] = 0.25 + static_cast(index); state->externalForce()[index] = 100.0 + static_cast(index); state->internalForce()[index] = 200.0 + 2.0 * static_cast(index); @@ -259,25 +259,25 @@ fesa::ModelDefinition makeShellDefinition(const std::filesystem::path& source) { WriterFixture makeShellFixture(const std::filesystem::path& source) { auto domainResult = fesa::Domain::create(makeShellDefinition(source)); - if (!domainResult.hasValue()) { + if (!domainResult.HasValue()) { throw std::runtime_error{"Shell writer fixture Domain construction failed."}; } auto domain = std::make_unique( - std::move(domainResult.value())); + std::move(domainResult.Value())); auto modelResult = fesa::AnalysisModel::create(*domain); - if (!modelResult.hasValue()) { + if (!modelResult.HasValue()) { throw std::runtime_error{"Shell writer fixture AnalysisModel construction failed."}; } - const fesa::AnalysisModel model = std::move(modelResult.value()); + const fesa::AnalysisModel model = std::move(modelResult.Value()); auto dofsResult = fesa::DofManager::create(model); - if (!dofsResult.hasValue()) { + if (!dofsResult.HasValue()) { throw std::runtime_error{"Shell writer fixture DofManager construction failed."}; } auto dofs = std::make_unique( - std::move(dofsResult.value())); + std::move(dofsResult.Value())); auto state = std::make_unique( fesa::AnalysisState::create(*dofs, {"Step-1", 0U})); - for (std::size_t index = 0U; index < state->displacement().size(); ++index) { + for (std::size_t index = 0U; index < state->displacement().Size(); ++index) { state->displacement()[index] = 0.01 * static_cast(index + 1U); state->externalForce()[index] = 10.0 + static_cast(index); state->internalForce()[index] = 20.0 + static_cast(index); @@ -325,7 +325,7 @@ WriterFixture makeShellFixture(const std::filesystem::path& source) { candidate.verificationMetrics = {1.0e-13, 2.0e-13, 3.0e-13}; const fesa::Status commit = state->commitShellResults( {0U}, std::move(candidate)); - if (!commit.isOk()) { + if (!commit.IsOk()) { throw std::runtime_error{"Shell writer fixture state commit failed."}; } return {std::move(domain), std::move(dofs), std::move(state)}; @@ -830,11 +830,11 @@ std::size_t entryCount(const std::filesystem::path& directory) { void expectOutputFailure( const fesa::Status& status, const std::string& expectedCode) { - ASSERT_FALSE(status.isOk()); - EXPECT_EQ(status.failureCategory(), fesa::FailureCategory::output); - ASSERT_EQ(status.diagnostics().size(), 1U); - EXPECT_EQ(status.diagnostics()[0U].severity, fesa::Severity::error); - EXPECT_EQ(status.diagnostics()[0U].code, expectedCode); + ASSERT_FALSE(status.IsOk()); + EXPECT_EQ(status.Category(), fesa::FailureCategory::kOutput); + ASSERT_EQ(status.Diagnostics().size(), 1U); + EXPECT_EQ(status.Diagnostics()[0U].severity, fesa::Severity::kError); + EXPECT_EQ(status.Diagnostics()[0U].code, expectedCode); } } // namespace @@ -846,7 +846,7 @@ TEST(Hdf5ResultsWriter, WritesExactSchemaShapesAttributesAndIdentity) { const auto output = directory.path() / "results.h5"; fesa::Hdf5ResultsWriter writer; - ASSERT_TRUE(writer.write(output, *fixture.domain, *fixture.state, {}).isOk()); + ASSERT_TRUE(writer.write(output, *fixture.domain, *fixture.state, {}).IsOk()); ASSERT_GT(H5Fis_hdf5(output.string().c_str()), 0); const auto file = openFile(output); @@ -874,7 +874,7 @@ TEST(Hdf5ResultsWriter, WritesExactSchemaShapesAttributesAndIdentity) { "linear-static-3d-euler-beam"); EXPECT_EQ( readStringAttribute(metadata.get(), "solver_version"), - std::string{fesa::solverVersion()}); + std::string{fesa::SolverVersion()}); const std::string normalizedSource = std::filesystem::absolute(source).lexically_normal().generic_u8string(); EXPECT_EQ( @@ -1031,7 +1031,7 @@ TEST(Hdf5ResultsWriter, WritesMandatoryOutputsDespiteOutputRequests) { TempDirectory directory{"mandatory"}; auto fixture = makeFixture(directory.path() / "request-model.inp"); const fesa::Diagnostic ignoredRequest{ - fesa::Severity::warning, + fesa::Severity::kWarning, "ignored-output-request", {fixture.domain->sourcePath(), 70U}, "*OUTPUT", @@ -1042,7 +1042,7 @@ TEST(Hdf5ResultsWriter, WritesMandatoryOutputsDespiteOutputRequests) { fesa::Hdf5ResultsWriter writer; ASSERT_TRUE( writer.write(output, *fixture.domain, *fixture.state, {ignoredRequest}) - .isOk()); + .IsOk()); const auto file = openFile(output); for (const char* suffix : { "/nodal/displacement", @@ -1063,13 +1063,13 @@ TEST(Hdf5ResultsWriter, WritesWarningsAndDefaultCentroid) { TempDirectory directory{"warnings"}; auto fixture = makeFixture(directory.path() / "centroid.inp", true); std::vector diagnostics = { - {fesa::Severity::warning, + {fesa::Severity::kWarning, "ignored-output-request", {fixture.domain->sourcePath(), 80U}, "*OUTPUT", "FIELD", "Ignored output request."}, - {fesa::Severity::warning, + {fesa::Severity::kWarning, "ignored-keyword", {fixture.domain->sourcePath(), 20U}, "*PREPRINT", @@ -1078,7 +1078,7 @@ TEST(Hdf5ResultsWriter, WritesWarningsAndDefaultCentroid) { const auto output = directory.path() / "results.h5"; fesa::Hdf5ResultsWriter writer; - ASSERT_TRUE(writer.write(output, *fixture.domain, *fixture.state, diagnostics).isOk()); + ASSERT_TRUE(writer.write(output, *fixture.domain, *fixture.state, diagnostics).IsOk()); const auto file = openFile(output); auto stressRows = readStressRows(file.get()); ASSERT_EQ(stressRows.size(), 2U); @@ -1153,7 +1153,7 @@ TEST(Hdf5ResultsWriter, SuccessfullyReplacesExistingFinal) { writeBytes(final, {'o', 'l', 'd'}); fesa::Hdf5ResultsWriter writer; - ASSERT_TRUE(writer.write(final, *fixture.domain, *fixture.state, {}).isOk()); + ASSERT_TRUE(writer.write(final, *fixture.domain, *fixture.state, {}).IsOk()); EXPECT_GT(H5Fis_hdf5(final.string().c_str()), 0); EXPECT_EQ(entryCount(directory.path()), 1U); const auto file = openFile(final); @@ -1171,7 +1171,7 @@ TEST(Hdf5ResultsWriter, WritesExactShellMetadataAndModelIdentity) { const auto output = directory.path() / "results.h5"; fesa::Hdf5ResultsWriter writer; - ASSERT_TRUE(writer.write(output, *fixture.domain, *fixture.state, {}).isOk()); + ASSERT_TRUE(writer.write(output, *fixture.domain, *fixture.state, {}).IsOk()); const auto file = openFile(output); Hdf5Handle metadata{ @@ -1261,7 +1261,7 @@ TEST(Hdf5ResultsWriter, WritesExactMandatoryShellResultInventory) { const auto output = directory.path() / "results.h5"; fesa::Hdf5ResultsWriter writer; - ASSERT_TRUE(writer.write(output, *fixture.domain, *fixture.state, {}).isOk()); + ASSERT_TRUE(writer.write(output, *fixture.domain, *fixture.state, {}).IsOk()); const auto file = openFile(output); const std::string shellRoot = std::string{kStepRoot} + "/element/shell"; expectNumericDataset( @@ -1319,7 +1319,7 @@ TEST(Hdf5ResultsWriter, WritesShellInventoryDespiteRequestsAndOmitsForbiddenPath TempDirectory directory{"shell-mandatory"}; auto fixture = makeShellFixture(directory.path() / "shell.inp"); const fesa::Diagnostic ignoredRequest{ - fesa::Severity::warning, + fesa::Severity::kWarning, "ignored-output-request", {fixture.domain->sourcePath(), 80U}, "*ELEMENT OUTPUT", @@ -1330,7 +1330,7 @@ TEST(Hdf5ResultsWriter, WritesShellInventoryDespiteRequestsAndOmitsForbiddenPath fesa::Hdf5ResultsWriter writer; ASSERT_TRUE( writer.write(output, *fixture.domain, *fixture.state, {ignoredRequest}) - .isOk()); + .IsOk()); const auto file = openFile(output); for (const char* suffix : { "/element/shell/local_frame", diff --git a/tests/unit/math/matrix_test.cpp b/tests/unit/math/matrix_test.cpp index 65bde0e..575b848 100644 --- a/tests/unit/math/matrix_test.cpp +++ b/tests/unit/math/matrix_test.cpp @@ -1,4 +1,4 @@ -#include "fesa/math/matrix.hpp" +#include "fesa/math/matrix.h" #include @@ -10,100 +10,104 @@ namespace fesa { namespace { TEST(DenseMath, RowMajorMatrixMatchesKnownGemvGemm) { - const std::size_t wraparoundRows = - (std::numeric_limits::max)() / 2U + 1U; - EXPECT_THROW(static_cast(Matrix{wraparoundRows, 2}), std::length_error); + const std::size_t wraparound_rows = + (std::numeric_limits::max)() / 2U + 1U; + EXPECT_THROW(static_cast(Matrix{wraparound_rows, 2}), + std::length_error); - Matrix zeroRows{0, 3}; - Vector threeValues{3, 2.0}; - const Vector zeroRowProduct = zeroRows.multiply(threeValues); - EXPECT_EQ(zeroRowProduct.size(), 0U); + Matrix zero_rows{0, 3}; + Vector three_values{3, 2.0}; + const Vector zero_row_product = zero_rows.Multiply(three_values); + EXPECT_EQ(zero_row_product.Size(), 0U); - Matrix zeroColumns{2, 0}; - const Vector zeroColumnProduct = zeroColumns.multiply(Vector{0}); - ASSERT_EQ(zeroColumnProduct.size(), 2U); - EXPECT_DOUBLE_EQ(zeroColumnProduct[0], 0.0); - EXPECT_DOUBLE_EQ(zeroColumnProduct[1], 0.0); + Matrix zero_columns{2, 0}; + const Vector zero_column_product = zero_columns.Multiply(Vector{0}); + ASSERT_EQ(zero_column_product.Size(), 2U); + EXPECT_DOUBLE_EQ(zero_column_product[0], 0.0); + EXPECT_DOUBLE_EQ(zero_column_product[1], 0.0); - Matrix zeroInnerRight{0, 3}; - const Matrix zeroInnerProduct = zeroColumns.multiply(zeroInnerRight); - EXPECT_EQ(zeroInnerProduct.rows(), 2U); - EXPECT_EQ(zeroInnerProduct.columns(), 3U); - for (std::size_t row = 0; row < zeroInnerProduct.rows(); ++row) { - for (std::size_t column = 0; column < zeroInnerProduct.columns(); ++column) { - EXPECT_DOUBLE_EQ(zeroInnerProduct(row, column), 0.0); - } + Matrix zero_inner_right{0, 3}; + const Matrix zero_inner_product = zero_columns.Multiply(zero_inner_right); + EXPECT_EQ(zero_inner_product.Rows(), 2U); + EXPECT_EQ(zero_inner_product.Columns(), 3U); + for (std::size_t row = 0; row < zero_inner_product.Rows(); ++row) { + for (std::size_t column = 0; column < zero_inner_product.Columns(); + ++column) { + EXPECT_DOUBLE_EQ(zero_inner_product(row, column), 0.0); } + } - Matrix left{2, 3}; - left(0, 0) = 1.0; - left(0, 1) = 2.0; - left(0, 2) = 3.0; - left(1, 0) = 4.0; - left(1, 1) = 5.0; - left(1, 2) = 6.0; + Matrix left{2, 3}; + left(0, 0) = 1.0; + left(0, 1) = 2.0; + left(0, 2) = 3.0; + left(1, 0) = 4.0; + left(1, 1) = 5.0; + left(1, 2) = 6.0; - EXPECT_EQ(&left(0, 0) + 1, &left(0, 1)); - EXPECT_EQ(&left(0, 0) + 2, &left(0, 2)); - EXPECT_EQ(&left(0, 0) + 3, &left(1, 0)); - const Matrix& constLeft = left; - EXPECT_DOUBLE_EQ(constLeft(1, 2), 6.0); + EXPECT_EQ(&left(0, 0) + 1, &left(0, 1)); + EXPECT_EQ(&left(0, 0) + 2, &left(0, 2)); + EXPECT_EQ(&left(0, 0) + 3, &left(1, 0)); + const Matrix& const_left = left; + EXPECT_DOUBLE_EQ(const_left(1, 2), 6.0); - Matrix copied{left}; - copied(0, 0) = 42.0; - EXPECT_DOUBLE_EQ(left(0, 0), 1.0); + Matrix copied{left}; + copied(0, 0) = 42.0; + EXPECT_DOUBLE_EQ(left(0, 0), 1.0); - Matrix copyAssigned{0, 0}; - copyAssigned = left; - copyAssigned(1, 2) = -7.0; - EXPECT_DOUBLE_EQ(left(1, 2), 6.0); + Matrix copy_assigned{0, 0}; + copy_assigned = left; + copy_assigned(1, 2) = -7.0; + EXPECT_DOUBLE_EQ(left(1, 2), 6.0); - Matrix moved{std::move(copied)}; - EXPECT_EQ(copied.rows(), 0U); - EXPECT_EQ(copied.columns(), 0U); - EXPECT_EQ(moved.rows(), 2U); - EXPECT_EQ(moved.columns(), 3U); - EXPECT_DOUBLE_EQ(moved(0, 0), 42.0); - EXPECT_NO_THROW(static_cast(copied.multiply(Vector{0}))); + Matrix moved{std::move(copied)}; + EXPECT_EQ(copied.Rows(), 0U); + EXPECT_EQ(copied.Columns(), 0U); + EXPECT_EQ(moved.Rows(), 2U); + EXPECT_EQ(moved.Columns(), 3U); + EXPECT_DOUBLE_EQ(moved(0, 0), 42.0); + EXPECT_NO_THROW(static_cast(copied.Multiply(Vector{0}))); - Matrix moveAssigned{1, 1, -1.0}; - moveAssigned = std::move(copyAssigned); - EXPECT_EQ(copyAssigned.rows(), 0U); - EXPECT_EQ(copyAssigned.columns(), 0U); - EXPECT_EQ(moveAssigned.rows(), 2U); - EXPECT_EQ(moveAssigned.columns(), 3U); - EXPECT_DOUBLE_EQ(moveAssigned(1, 2), -7.0); + Matrix move_assigned{1, 1, -1.0}; + move_assigned = std::move(copy_assigned); + EXPECT_EQ(copy_assigned.Rows(), 0U); + EXPECT_EQ(copy_assigned.Columns(), 0U); + EXPECT_EQ(move_assigned.Rows(), 2U); + EXPECT_EQ(move_assigned.Columns(), 3U); + EXPECT_DOUBLE_EQ(move_assigned(1, 2), -7.0); - Vector vector{3}; - vector[0] = 7.0; - vector[1] = 8.0; - vector[2] = 9.0; - const Vector matrixVectorProduct = left.multiply(vector); - ASSERT_EQ(matrixVectorProduct.size(), 2U); - EXPECT_DOUBLE_EQ(matrixVectorProduct[0], 50.0); - EXPECT_DOUBLE_EQ(matrixVectorProduct[1], 122.0); + Vector vector{3}; + vector[0] = 7.0; + vector[1] = 8.0; + vector[2] = 9.0; + const Vector matrix_vector_product = left.Multiply(vector); + ASSERT_EQ(matrix_vector_product.Size(), 2U); + EXPECT_DOUBLE_EQ(matrix_vector_product[0], 50.0); + EXPECT_DOUBLE_EQ(matrix_vector_product[1], 122.0); - Matrix right{3, 2}; - right(0, 0) = 7.0; - right(0, 1) = 8.0; - right(1, 0) = 9.0; - right(1, 1) = 10.0; - right(2, 0) = 11.0; - right(2, 1) = 12.0; - const Matrix matrixProduct = left.multiply(right); - ASSERT_EQ(matrixProduct.rows(), 2U); - ASSERT_EQ(matrixProduct.columns(), 2U); - EXPECT_DOUBLE_EQ(matrixProduct(0, 0), 58.0); - EXPECT_DOUBLE_EQ(matrixProduct(0, 1), 64.0); - EXPECT_DOUBLE_EQ(matrixProduct(1, 0), 139.0); - EXPECT_DOUBLE_EQ(matrixProduct(1, 1), 154.0); + Matrix right{3, 2}; + right(0, 0) = 7.0; + right(0, 1) = 8.0; + right(1, 0) = 9.0; + right(1, 1) = 10.0; + right(2, 0) = 11.0; + right(2, 1) = 12.0; + const Matrix matrix_product = left.Multiply(right); + ASSERT_EQ(matrix_product.Rows(), 2U); + ASSERT_EQ(matrix_product.Columns(), 2U); + EXPECT_DOUBLE_EQ(matrix_product(0, 0), 58.0); + EXPECT_DOUBLE_EQ(matrix_product(0, 1), 64.0); + EXPECT_DOUBLE_EQ(matrix_product(1, 0), 139.0); + EXPECT_DOUBLE_EQ(matrix_product(1, 1), 154.0); - EXPECT_THROW(static_cast(left(2, 0)), std::out_of_range); - EXPECT_THROW(static_cast(left(0, 3)), std::out_of_range); - EXPECT_THROW(static_cast(constLeft(2, 0)), std::out_of_range); - EXPECT_THROW(static_cast(left.multiply(Vector{2})), std::invalid_argument); - EXPECT_THROW(static_cast(left.multiply(Matrix{4, 1})), std::invalid_argument); + EXPECT_THROW(static_cast(left(2, 0)), std::out_of_range); + EXPECT_THROW(static_cast(left(0, 3)), std::out_of_range); + EXPECT_THROW(static_cast(const_left(2, 0)), std::out_of_range); + EXPECT_THROW(static_cast(left.Multiply(Vector{2})), + std::invalid_argument); + EXPECT_THROW(static_cast(left.Multiply(Matrix{4, 1})), + std::invalid_argument); } -} // namespace -} // namespace fesa +} // namespace +} // namespace fesa diff --git a/tests/unit/math/sparse_matrix_test.cpp b/tests/unit/math/sparse_matrix_test.cpp index 0f3cfe7..84d201c 100644 --- a/tests/unit/math/sparse_matrix_test.cpp +++ b/tests/unit/math/sparse_matrix_test.cpp @@ -1,6 +1,4 @@ -#include "fesa/fem/dof_manager.hpp" -#include "fesa/math/matrix.hpp" -#include "fesa/math/sparse_matrix.hpp" +#include "fesa/math/sparse_matrix.h" #include @@ -10,6 +8,9 @@ #include #include +#include "fesa/fem/dof_manager.hpp" +#include "fesa/math/matrix.h" + namespace { using fesa::CooContribution; @@ -22,128 +23,112 @@ static_assert( "SparseMatrix must own CSR storage independently of dense Matrix."); TEST(SparseAssembly, ValidatesKnownCsrAndMultiply) { - const SparsePattern pattern{ - {0U, 2U, 2U, 4U, 5U}, - {0U, 2U, 1U, 3U, 3U}}; - std::vector contributions{ - {2U, 3U, 4.0, 2U, 0U}, - {0U, 2U, 2.0, 0U, 1U}, - {3U, 3U, 5.0, 3U, 0U}, - {0U, 0U, 1.0, 0U, 0U}, - {2U, 1U, 3.0, 1U, 0U}}; + const SparsePattern pattern{{0U, 2U, 2U, 4U, 5U}, {0U, 2U, 1U, 3U, 3U}}; + std::vector contributions{{2U, 3U, 4.0, 2U, 0U}, + {0U, 2U, 2.0, 0U, 1U}, + {3U, 3U, 5.0, 3U, 0U}, + {0U, 0U, 1.0, 0U, 0U}, + {2U, 1U, 3.0, 1U, 0U}}; - auto result = SparseMatrix::fromCoo( - 4U, 4U, std::move(contributions), pattern); - ASSERT_TRUE(result.hasValue()); - const SparseMatrix& matrix = result.value(); + auto result = + SparseMatrix::FromCoo(4U, 4U, std::move(contributions), pattern); + ASSERT_TRUE(result.HasValue()); + const SparseMatrix& matrix = result.Value(); - EXPECT_EQ(matrix.rows(), 4U); - EXPECT_EQ(matrix.columns(), 4U); - EXPECT_EQ(matrix.rowOffsets(), pattern.rowOffsets); - EXPECT_EQ(matrix.columnIndices(), pattern.columnIndices); - EXPECT_EQ(matrix.values(), (std::vector{1.0, 2.0, 3.0, 4.0, 5.0})); - EXPECT_TRUE(matrix.validate().isOk()); + EXPECT_EQ(matrix.Rows(), 4U); + EXPECT_EQ(matrix.Columns(), 4U); + EXPECT_EQ(matrix.RowOffsets(), pattern.rowOffsets); + EXPECT_EQ(matrix.ColumnIndices(), pattern.columnIndices); + EXPECT_EQ(matrix.Values(), (std::vector{1.0, 2.0, 3.0, 4.0, 5.0})); + EXPECT_TRUE(matrix.Validate().IsOk()); - Vector rhs{4U}; - rhs[0U] = 1.0; - rhs[1U] = 2.0; - rhs[2U] = 3.0; - rhs[3U] = 4.0; - const Vector product = matrix.multiply(rhs); - ASSERT_EQ(product.size(), 4U); - EXPECT_DOUBLE_EQ(product[0U], 7.0); - EXPECT_DOUBLE_EQ(product[1U], 0.0); - EXPECT_DOUBLE_EQ(product[2U], 22.0); - EXPECT_DOUBLE_EQ(product[3U], 20.0); - EXPECT_THROW(static_cast(matrix.multiply(Vector{3U})), std::invalid_argument); + Vector rhs{4U}; + rhs[0U] = 1.0; + rhs[1U] = 2.0; + rhs[2U] = 3.0; + rhs[3U] = 4.0; + const Vector product = matrix.Multiply(rhs); + ASSERT_EQ(product.Size(), 4U); + EXPECT_DOUBLE_EQ(product[0U], 7.0); + EXPECT_DOUBLE_EQ(product[1U], 0.0); + EXPECT_DOUBLE_EQ(product[2U], 22.0); + EXPECT_DOUBLE_EQ(product[3U], 20.0); + EXPECT_THROW(static_cast(matrix.Multiply(Vector{3U})), + std::invalid_argument); } TEST(SparseAssembly, ReducesDuplicatesInFixedTupleOrder) { - const SparsePattern pattern{{0U, 1U}, {0U}}; - const std::vector contributions{ - {0U, 0U, 1.0, 2U, 0U}, - {0U, 0U, -1.0e16, 1U, 0U}, - {0U, 0U, 1.0e16, 0U, 0U}}; + const SparsePattern pattern{{0U, 1U}, {0U}}; + const std::vector contributions{{0U, 0U, 1.0, 2U, 0U}, + {0U, 0U, -1.0e16, 1U, 0U}, + {0U, 0U, 1.0e16, 0U, 0U}}; - auto first = SparseMatrix::fromCoo(1U, 1U, contributions, pattern); - ASSERT_TRUE(first.hasValue()); - ASSERT_EQ(first.value().values().size(), 1U); - EXPECT_DOUBLE_EQ(first.value().values()[0U], 1.0); + auto first = SparseMatrix::FromCoo(1U, 1U, contributions, pattern); + ASSERT_TRUE(first.HasValue()); + ASSERT_EQ(first.Value().Values().size(), 1U); + EXPECT_DOUBLE_EQ(first.Value().Values()[0U], 1.0); - auto reversedContributions = contributions; - std::reverse(reversedContributions.begin(), reversedContributions.end()); - auto second = SparseMatrix::fromCoo( - 1U, 1U, std::move(reversedContributions), pattern); - ASSERT_TRUE(second.hasValue()); - EXPECT_EQ(second.value().rowOffsets(), first.value().rowOffsets()); - EXPECT_EQ(second.value().columnIndices(), first.value().columnIndices()); - EXPECT_EQ(second.value().values(), first.value().values()); + auto reversed_contributions = contributions; + std::reverse(reversed_contributions.begin(), reversed_contributions.end()); + auto second = + SparseMatrix::FromCoo(1U, 1U, std::move(reversed_contributions), pattern); + ASSERT_TRUE(second.HasValue()); + EXPECT_EQ(second.Value().RowOffsets(), first.Value().RowOffsets()); + EXPECT_EQ(second.Value().ColumnIndices(), first.Value().ColumnIndices()); + EXPECT_EQ(second.Value().Values(), first.Value().Values()); } TEST(SparseAssembly, RejectsInvalidIndexPatternAndShape) { - const SparsePattern oneEntry{{0U, 1U}, {0U}}; - const auto expectFailure = []( - std::size_t rows, - std::size_t columns, - std::vector contributions, - const SparsePattern& pattern) { - auto result = SparseMatrix::fromCoo( - rows, columns, std::move(contributions), pattern); - EXPECT_FALSE(result.hasValue()); - if (!result.hasValue()) { - EXPECT_FALSE(result.status().isOk()); - EXPECT_EQ(result.status().failureCategory(), fesa::FailureCategory::model); - EXPECT_FALSE(result.status().diagnostics().empty()); - } - }; + const SparsePattern one_entry{{0U, 1U}, {0U}}; + const auto expect_failure = [](std::size_t rows, std::size_t columns, + std::vector contributions, + const SparsePattern& pattern) { + auto result = + SparseMatrix::FromCoo(rows, columns, std::move(contributions), pattern); + EXPECT_FALSE(result.HasValue()); + if (!result.HasValue()) { + EXPECT_FALSE(result.GetStatus().IsOk()); + EXPECT_EQ(result.GetStatus().Category(), fesa::FailureCategory::kModel); + EXPECT_FALSE(result.GetStatus().Diagnostics().empty()); + } + }; - expectFailure(2U, 2U, {}, {{0U, 0U}, {}}); - expectFailure(1U, 1U, {}, {{1U, 1U}, {0U}}); - expectFailure(2U, 2U, {}, {{0U, 1U, 0U}, {0U}}); - expectFailure(1U, 2U, {}, {{0U, 2U}, {1U, 0U}}); - expectFailure(1U, 1U, {}, {{0U, 2U}, {0U, 0U}}); - expectFailure(1U, 1U, {}, {{0U, 1U}, {1U}}); - expectFailure(1U, 1U, {{1U, 0U, 1.0, 0U, 0U}}, oneEntry); - expectFailure(1U, 1U, {{0U, 1U, 1.0, 0U, 0U}}, oneEntry); - expectFailure(1U, 2U, {{0U, 1U, 1.0, 0U, 0U}}, {{0U, 1U}, {0U}}); - expectFailure( - 1U, - 1U, - {{0U, 0U, (std::numeric_limits::infinity)(), 0U, 0U}}, - oneEntry); - expectFailure( - 1U, - 1U, - {{0U, 0U, (std::numeric_limits::quiet_NaN)(), 0U, 0U}}, - oneEntry); - expectFailure( - 1U, - 1U, - {{0U, 0U, (std::numeric_limits::max)(), 0U, 0U}, - {0U, 0U, (std::numeric_limits::max)(), 1U, 0U}}, - oneEntry); + expect_failure(2U, 2U, {}, {{0U, 0U}, {}}); + expect_failure(1U, 1U, {}, {{1U, 1U}, {0U}}); + expect_failure(2U, 2U, {}, {{0U, 1U, 0U}, {0U}}); + expect_failure(1U, 2U, {}, {{0U, 2U}, {1U, 0U}}); + expect_failure(1U, 1U, {}, {{0U, 2U}, {0U, 0U}}); + expect_failure(1U, 1U, {}, {{0U, 1U}, {1U}}); + expect_failure(1U, 1U, {{1U, 0U, 1.0, 0U, 0U}}, one_entry); + expect_failure(1U, 1U, {{0U, 1U, 1.0, 0U, 0U}}, one_entry); + expect_failure(1U, 2U, {{0U, 1U, 1.0, 0U, 0U}}, {{0U, 1U}, {0U}}); + expect_failure(1U, 1U, + {{0U, 0U, (std::numeric_limits::infinity)(), 0U, 0U}}, + one_entry); + expect_failure(1U, 1U, + {{0U, 0U, (std::numeric_limits::quiet_NaN)(), 0U, 0U}}, + one_entry); + expect_failure(1U, 1U, + {{0U, 0U, (std::numeric_limits::max)(), 0U, 0U}, + {0U, 0U, (std::numeric_limits::max)(), 1U, 0U}}, + one_entry); } TEST(SparseAssembly, PreservesExpectedStructuralZeros) { - const SparsePattern pattern{ - {0U, 2U, 4U, 5U}, - {0U, 2U, 1U, 2U, 0U}}; - std::vector contributions{ - {0U, 0U, 2.0, 0U, 0U}, - {1U, 1U, 4.0, 0U, 1U}, - {1U, 1U, -4.0, 1U, 0U}}; + const SparsePattern pattern{{0U, 2U, 4U, 5U}, {0U, 2U, 1U, 2U, 0U}}; + std::vector contributions{ + {0U, 0U, 2.0, 0U, 0U}, {1U, 1U, 4.0, 0U, 1U}, {1U, 1U, -4.0, 1U, 0U}}; - auto result = SparseMatrix::fromCoo( - 3U, 3U, std::move(contributions), pattern); - ASSERT_TRUE(result.hasValue()); - EXPECT_EQ(result.value().rowOffsets(), pattern.rowOffsets); - EXPECT_EQ(result.value().columnIndices(), pattern.columnIndices); - EXPECT_EQ( - result.value().values(), - (std::vector{2.0, 0.0, 0.0, 0.0, 0.0})); - EXPECT_EQ( - std::count(result.value().values().begin(), result.value().values().end(), 0.0), - 4); + auto result = + SparseMatrix::FromCoo(3U, 3U, std::move(contributions), pattern); + ASSERT_TRUE(result.HasValue()); + EXPECT_EQ(result.Value().RowOffsets(), pattern.rowOffsets); + EXPECT_EQ(result.Value().ColumnIndices(), pattern.columnIndices); + EXPECT_EQ(result.Value().Values(), + (std::vector{2.0, 0.0, 0.0, 0.0, 0.0})); + EXPECT_EQ(std::count(result.Value().Values().begin(), + result.Value().Values().end(), 0.0), + 4); } -} // namespace +} // namespace diff --git a/tests/unit/math/vector_test.cpp b/tests/unit/math/vector_test.cpp index 00a0ba4..bd62869 100644 --- a/tests/unit/math/vector_test.cpp +++ b/tests/unit/math/vector_test.cpp @@ -1,4 +1,4 @@ -#include "fesa/math/vector.hpp" +#include "fesa/math/vector.h" #include @@ -10,77 +10,78 @@ namespace fesa { namespace { TEST(DenseMath, VectorOwnsAndChecksContiguousStorage) { - Vector empty{0}; - EXPECT_EQ(empty.size(), 0U); - EXPECT_DOUBLE_EQ(empty.norm(), 0.0); - EXPECT_NO_THROW(empty.scale(3.0)); - EXPECT_NO_THROW(empty.axpy(-2.0, Vector{0})); - EXPECT_THROW(static_cast(empty[0]), std::out_of_range); + Vector empty{0}; + EXPECT_EQ(empty.Size(), 0U); + EXPECT_DOUBLE_EQ(empty.Norm(), 0.0); + EXPECT_NO_THROW(empty.Scale(3.0)); + EXPECT_NO_THROW(empty.Axpy(-2.0, Vector{0})); + EXPECT_THROW(static_cast(empty[0]), std::out_of_range); - Vector original{3}; - original[0] = 1.0; - original[1] = -2.0; - original[2] = 3.0; + Vector original{3}; + original[0] = 1.0; + original[1] = -2.0; + original[2] = 3.0; - EXPECT_EQ(original.data() + 1, &original[1]); - EXPECT_EQ(original.data() + 2, &original[2]); - const Vector& constOriginal = original; - EXPECT_EQ(constOriginal.data() + 2, &constOriginal[2]); + EXPECT_EQ(original.Data() + 1, &original[1]); + EXPECT_EQ(original.Data() + 2, &original[2]); + const Vector& const_original = original; + EXPECT_EQ(const_original.Data() + 2, &const_original[2]); - Vector copied{original}; - EXPECT_NE(copied.data(), original.data()); - copied[0] = 99.0; - EXPECT_DOUBLE_EQ(original[0], 1.0); + Vector copied{original}; + EXPECT_NE(copied.Data(), original.Data()); + copied[0] = 99.0; + EXPECT_DOUBLE_EQ(original[0], 1.0); - Vector copyAssigned{0}; - copyAssigned = original; - EXPECT_NE(copyAssigned.data(), original.data()); - copyAssigned[1] = 17.0; - EXPECT_DOUBLE_EQ(original[1], -2.0); + Vector copy_assigned{0}; + copy_assigned = original; + EXPECT_NE(copy_assigned.Data(), original.Data()); + copy_assigned[1] = 17.0; + EXPECT_DOUBLE_EQ(original[1], -2.0); - Vector moved{std::move(copied)}; - EXPECT_EQ(copied.size(), 0U); - EXPECT_EQ(moved.size(), 3U); - EXPECT_DOUBLE_EQ(moved[0], 99.0); - EXPECT_NO_THROW(copied.scale(4.0)); + Vector moved{std::move(copied)}; + EXPECT_EQ(copied.Size(), 0U); + EXPECT_EQ(moved.Size(), 3U); + EXPECT_DOUBLE_EQ(moved[0], 99.0); + EXPECT_NO_THROW(copied.Scale(4.0)); - Vector moveAssigned{1, -1.0}; - moveAssigned = std::move(copyAssigned); - EXPECT_EQ(copyAssigned.size(), 0U); - EXPECT_EQ(moveAssigned.size(), 3U); - EXPECT_DOUBLE_EQ(moveAssigned[1], 17.0); + Vector move_assigned{1, -1.0}; + move_assigned = std::move(copy_assigned); + EXPECT_EQ(copy_assigned.Size(), 0U); + EXPECT_EQ(move_assigned.Size(), 3U); + EXPECT_DOUBLE_EQ(move_assigned[1], 17.0); - Vector rhs{3}; - rhs[0] = 4.0; - rhs[1] = 5.0; - rhs[2] = -6.0; - EXPECT_DOUBLE_EQ(original.dot(rhs), -24.0); - EXPECT_NEAR(original.norm(), std::sqrt(14.0), 1.0e-15); + Vector rhs{3}; + rhs[0] = 4.0; + rhs[1] = 5.0; + rhs[2] = -6.0; + EXPECT_DOUBLE_EQ(original.Dot(rhs), -24.0); + EXPECT_NEAR(original.Norm(), std::sqrt(14.0), 1.0e-15); - Vector scaled{original}; - scaled.scale(-0.5); - EXPECT_DOUBLE_EQ(scaled[0], -0.5); - EXPECT_DOUBLE_EQ(scaled[1], 1.0); - EXPECT_DOUBLE_EQ(scaled[2], -1.5); + Vector scaled{original}; + scaled.Scale(-0.5); + EXPECT_DOUBLE_EQ(scaled[0], -0.5); + EXPECT_DOUBLE_EQ(scaled[1], 1.0); + EXPECT_DOUBLE_EQ(scaled[2], -1.5); - Vector accumulated{3}; - accumulated[0] = 1.0; - accumulated[1] = 2.0; - accumulated[2] = 3.0; - Vector increment{3}; - increment[0] = 4.0; - increment[1] = -1.0; - increment[2] = 0.5; - accumulated.axpy(2.0, increment); - EXPECT_DOUBLE_EQ(accumulated[0], 9.0); - EXPECT_DOUBLE_EQ(accumulated[1], 0.0); - EXPECT_DOUBLE_EQ(accumulated[2], 4.0); + Vector accumulated{3}; + accumulated[0] = 1.0; + accumulated[1] = 2.0; + accumulated[2] = 3.0; + Vector increment{3}; + increment[0] = 4.0; + increment[1] = -1.0; + increment[2] = 0.5; + accumulated.Axpy(2.0, increment); + EXPECT_DOUBLE_EQ(accumulated[0], 9.0); + EXPECT_DOUBLE_EQ(accumulated[1], 0.0); + EXPECT_DOUBLE_EQ(accumulated[2], 4.0); - EXPECT_THROW(static_cast(original[3]), std::out_of_range); - EXPECT_THROW(static_cast(constOriginal[3]), std::out_of_range); - EXPECT_THROW(static_cast(original.dot(Vector{2})), std::invalid_argument); - EXPECT_THROW(original.axpy(1.0, Vector{2}), std::invalid_argument); + EXPECT_THROW(static_cast(original[3]), std::out_of_range); + EXPECT_THROW(static_cast(const_original[3]), std::out_of_range); + EXPECT_THROW(static_cast(original.Dot(Vector{2})), + std::invalid_argument); + EXPECT_THROW(original.Axpy(1.0, Vector{2}), std::invalid_argument); } -} // namespace -} // namespace fesa +} // namespace +} // namespace fesa diff --git a/tests/unit/model/domain_test.cpp b/tests/unit/model/domain_test.cpp index f2a9d27..456551d 100644 --- a/tests/unit/model/domain_test.cpp +++ b/tests/unit/model/domain_test.cpp @@ -59,7 +59,7 @@ fesa::ModelDefinition makeOwnedDefinition() { 1.0, {"models/owned.inp", 55U}}}; definition.warnings = {{ - fesa::Severity::warning, + fesa::Severity::kWarning, "ignored-output-request", {"models/owned.inp", 70U}, "*OUTPUT", @@ -73,17 +73,17 @@ fesa::ModelDefinition makeOwnedDefinition() { TEST(DomainModel, ImmutableOwnershipPreservesStableOrder) { auto definition = makeOwnedDefinition(); auto result = fesa::Domain::create(definition); - ASSERT_TRUE(result.hasValue()); + ASSERT_TRUE(result.HasValue()); definition.sourcePath = "mutated.inp"; definition.sourceContentIdentity = "mutated"; - definition.nodes[0].sourceId.sourceLabelText = "mutated"; + definition.nodes[0].sourceId.source_label_text = "mutated"; definition.nodes[0].coordinates[0] = -99.0; definition.sections[0].sectionPoints[0][0] = -99.0; definition.steps[0].loads[0].magnitude = 99.0; definition.warnings[0].code = "mutated"; - const fesa::Domain& domain = result.value(); + const fesa::Domain& domain = result.Value(); const fesa::Node* const firstNodeAddress = domain.nodes().data(); static_assert(std::is_same_v< decltype(std::declval().nodes()), @@ -95,10 +95,10 @@ TEST(DomainModel, ImmutableOwnershipPreservesStableOrder) { EXPECT_EQ(domain.sourcePath(), std::filesystem::path{"models/owned.inp"}); EXPECT_EQ(domain.sourceContentIdentity(), "fnv1a64:fedcba9876543210"); ASSERT_EQ(domain.nodes().size(), 2U); - EXPECT_EQ(domain.nodes()[0].sourceId.sourceLabel, 20); - EXPECT_EQ(domain.nodes()[0].sourceId.sourceLabelText, "0020"); + EXPECT_EQ(domain.nodes()[0].sourceId.source_label, 20); + EXPECT_EQ(domain.nodes()[0].sourceId.source_label_text, "0020"); EXPECT_DOUBLE_EQ(domain.nodes()[0].coordinates[0], 2.0); - EXPECT_EQ(domain.nodes()[1].sourceId.sourceLabel, 10); + EXPECT_EQ(domain.nodes()[1].sourceId.source_label, 10); EXPECT_EQ(domain.nodes().data(), firstNodeAddress); ASSERT_EQ(domain.elements().size(), 1U); @@ -162,23 +162,23 @@ TEST(DomainModel, MultipleIdentityInstancesDoNotMerge) { {"models/two-instances.inp", 60U}}}; auto result = fesa::Domain::create(std::move(definition)); - ASSERT_TRUE(result.hasValue()); - const fesa::Domain& domain = result.value(); + ASSERT_TRUE(result.HasValue()); + const fesa::Domain& domain = result.Value(); ASSERT_EQ(domain.nodes().size(), 4U); - EXPECT_EQ(domain.nodes()[0].sourceId.sourceLabel, 1); - EXPECT_EQ(domain.nodes()[2].sourceId.sourceLabel, 1); - EXPECT_EQ(domain.nodes()[0].sourceId.sourceLabelText, "1"); - EXPECT_EQ(domain.nodes()[2].sourceId.sourceLabelText, "1"); + EXPECT_EQ(domain.nodes()[0].sourceId.source_label, 1); + EXPECT_EQ(domain.nodes()[2].sourceId.source_label, 1); + EXPECT_EQ(domain.nodes()[0].sourceId.source_label_text, "1"); + EXPECT_EQ(domain.nodes()[2].sourceId.source_label_text, "1"); EXPECT_NE( - domain.nodes()[0].sourceId.instanceName, - domain.nodes()[2].sourceId.instanceName); + domain.nodes()[0].sourceId.instance_name, + domain.nodes()[2].sourceId.instance_name); ASSERT_EQ(domain.elements().size(), 2U); - EXPECT_EQ(domain.elements()[0].sourceId.sourceLabel, 1); - EXPECT_EQ(domain.elements()[1].sourceId.sourceLabel, 1); - EXPECT_EQ(domain.elements()[0].sourceId.instanceName, "Instance-A"); - EXPECT_EQ(domain.elements()[1].sourceId.instanceName, "Instance-B"); + EXPECT_EQ(domain.elements()[0].sourceId.source_label, 1); + EXPECT_EQ(domain.elements()[1].sourceId.source_label, 1); + EXPECT_EQ(domain.elements()[0].sourceId.instance_name, "Instance-A"); + EXPECT_EQ(domain.elements()[1].sourceId.instance_name, "Instance-B"); EXPECT_EQ(domain.elements()[0].nodeIndices[0], 0U); EXPECT_EQ(domain.elements()[1].nodeIndices[0], 2U); } @@ -212,13 +212,13 @@ TEST(DomainModel, ShellOwnershipPreservesResolvedAssignmentsAndOptionalFrames) { {0U, {0.0, 1.0, 0.0}, {1.0, 0.0, 0.0}, {0.0, 0.0, -1.0}}}; auto result = fesa::Domain::create(definition); - ASSERT_TRUE(result.hasValue()); + ASSERT_TRUE(result.HasValue()); definition.shellSections[0].thickness = -1.0; - definition.shellElements[0].sourceId.sourceLabelText = "mutated"; + definition.shellElements[0].sourceId.source_label_text = "mutated"; definition.shellNodeInitialFrames[0].director[2] = -1.0; - const fesa::Domain& domain = result.value(); + const fesa::Domain& domain = result.Value(); static_assert(std::is_same_v< decltype(std::declval().shellElements()), const std::vector&>); @@ -230,9 +230,9 @@ TEST(DomainModel, ShellOwnershipPreservesResolvedAssignmentsAndOptionalFrames) { const std::vector&>); ASSERT_EQ(domain.shellElements().size(), 2U); - EXPECT_EQ(domain.shellElements()[0].sourceId.sourceLabel, 20); - EXPECT_EQ(domain.shellElements()[0].sourceId.sourceLabelText, "0020"); - EXPECT_EQ(domain.shellElements()[1].sourceId.sourceLabel, 10); + EXPECT_EQ(domain.shellElements()[0].sourceId.source_label, 20); + EXPECT_EQ(domain.shellElements()[0].sourceId.source_label_text, "0020"); + EXPECT_EQ(domain.shellElements()[1].sourceId.source_label, 10); EXPECT_EQ(domain.shellElements()[0].sourceType, fesa::ShellSourceElementType::s4r); EXPECT_EQ(domain.shellElements()[1].sourceType, fesa::ShellSourceElementType::s4); EXPECT_EQ(domain.shellElements()[0].nodeIndices[3], 7U); @@ -258,6 +258,6 @@ TEST(DomainModel, ShellOwnershipPreservesResolvedAssignmentsAndOptionalFrames) { (std::array{0.0, 1.0, 0.0})); auto noFramesResult = fesa::Domain::create(fesa::ModelDefinition{}); - ASSERT_TRUE(noFramesResult.hasValue()); - EXPECT_TRUE(noFramesResult.value().shellNodeInitialFrames().empty()); + ASSERT_TRUE(noFramesResult.HasValue()); + EXPECT_TRUE(noFramesResult.Value().shellNodeInitialFrames().empty()); } diff --git a/tests/unit/model/model_types_test.cpp b/tests/unit/model/model_types_test.cpp index b0d07a6..9d3480d 100644 --- a/tests/unit/model/model_types_test.cpp +++ b/tests/unit/model/model_types_test.cpp @@ -39,22 +39,22 @@ TEST(DomainModel, SourceAndInternalIdentityRemainDistinct) { EXPECT_EQ( std::make_tuple( - firstIdentity.instanceName, - firstIdentity.sourceLabel, - firstIdentity.sourceLabelText), + firstIdentity.instance_name, + firstIdentity.source_label, + firstIdentity.source_label_text), std::make_tuple( - equalIdentity.instanceName, - equalIdentity.sourceLabel, - equalIdentity.sourceLabelText)); + equalIdentity.instance_name, + equalIdentity.source_label, + equalIdentity.source_label_text)); EXPECT_LT( std::make_tuple( - firstIdentity.instanceName, - firstIdentity.sourceLabel, - firstIdentity.sourceLabelText), + firstIdentity.instance_name, + firstIdentity.source_label, + firstIdentity.source_label_text), std::make_tuple( - laterIdentity.instanceName, - laterIdentity.sourceLabel, - laterIdentity.sourceLabelText)); + laterIdentity.instance_name, + laterIdentity.source_label, + laterIdentity.source_label_text)); const fesa::Node node{firstIdentity, {1.0, 2.0, 3.0}, nodeLocation}; const fesa::LinearElasticMaterial material{ @@ -116,7 +116,7 @@ TEST(DomainModel, SourceAndInternalIdentityRemainDistinct) { definition.instances = {instance}; definition.steps = {step}; definition.warnings = {{ - fesa::Severity::warning, + fesa::Severity::kWarning, "ignored-output-request", {"models/beam.inp", 75U}, "*OUTPUT", @@ -124,7 +124,7 @@ TEST(DomainModel, SourceAndInternalIdentityRemainDistinct) { "Output request does not alter mandatory FESA results."}}; ASSERT_EQ(definition.nodes.size(), 1U); - EXPECT_EQ(definition.nodes[0].sourceId.sourceLabelText, "0007"); + EXPECT_EQ(definition.nodes[0].sourceId.source_label_text, "0007"); EXPECT_EQ(definition.nodes[0].location.line, 11U); ASSERT_EQ(definition.instances.size(), 1U); EXPECT_EQ(definition.instances[0].partName, "BeamPart"); @@ -167,9 +167,9 @@ TEST(DomainModel, Mitc4ShellRecordsPreserveSourceAndInternalIdentity) { fesa::EntityIndex{7}, {"models/shell.inp", 21U}}; - EXPECT_EQ(s4Element.sourceId.instanceName, "Shell-Instance"); - EXPECT_EQ(s4Element.sourceId.sourceLabel, 41); - EXPECT_EQ(s4Element.sourceId.sourceLabelText, "0041"); + EXPECT_EQ(s4Element.sourceId.instance_name, "Shell-Instance"); + EXPECT_EQ(s4Element.sourceId.source_label, 41); + EXPECT_EQ(s4Element.sourceId.source_label_text, "0041"); EXPECT_EQ(s4Element.sourceType, fesa::ShellSourceElementType::s4); EXPECT_EQ(s4rElement.sourceType, fesa::ShellSourceElementType::s4r); EXPECT_NE(s4Element.sourceType, s4rElement.sourceType); diff --git a/tests/unit/model/shell_geometry_test.cpp b/tests/unit/model/shell_geometry_test.cpp index a84860e..3839666 100644 --- a/tests/unit/model/shell_geometry_test.cpp +++ b/tests/unit/model/shell_geometry_test.cpp @@ -89,10 +89,10 @@ const fesa::ShellNodeInitialFrame& frameFor( void expectFailureCode( const fesa::Result& result, const std::string& code) { - ASSERT_FALSE(result.hasValue()); - EXPECT_EQ(result.status().failureCategory(), fesa::FailureCategory::model); - ASSERT_EQ(result.status().diagnostics().size(), 1U); - EXPECT_EQ(result.status().diagnostics()[0].code, code); + ASSERT_FALSE(result.HasValue()); + EXPECT_EQ(result.GetStatus().Category(), fesa::FailureCategory::kModel); + ASSERT_EQ(result.GetStatus().Diagnostics().size(), 1U); + EXPECT_EQ(result.GetStatus().Diagnostics()[0].code, code); } } // namespace @@ -107,12 +107,12 @@ TEST(Mitc4Geometry, BuildsDeterministicFramesForPlanarRotatedAndWarpedElements) auto planar = fesa::preprocessShellGeometry( planarNodes, {element(10U, {0U, 1U, 2U, 3U})}, sections()); - ASSERT_TRUE(planar.hasValue()); - ASSERT_EQ(planar.value().elementData.size(), 1U); - expectVectorNear(planar.value().elementData[0].normalCandidate, {0.0, 0.0, 1.0}); - EXPECT_NEAR(planar.value().elementData[0].surfaceAreaWeight, 1.0, 1.0e-12); - ASSERT_EQ(planar.value().nodalFrames.size(), 4U); - for (const auto& frame : planar.value().nodalFrames) { + ASSERT_TRUE(planar.HasValue()); + ASSERT_EQ(planar.Value().elementData.size(), 1U); + expectVectorNear(planar.Value().elementData[0].normalCandidate, {0.0, 0.0, 1.0}); + EXPECT_NEAR(planar.Value().elementData[0].surfaceAreaWeight, 1.0, 1.0e-12); + ASSERT_EQ(planar.Value().nodalFrames.size(), 4U); + for (const auto& frame : planar.Value().nodalFrames) { expectVectorNear(frame.director, {0.0, 0.0, 1.0}); expectVectorNear(frame.tangentA, {1.0, 0.0, 0.0}); expectVectorNear(frame.tangentB, {0.0, 1.0, 0.0}); @@ -127,8 +127,8 @@ TEST(Mitc4Geometry, BuildsDeterministicFramesForPlanarRotatedAndWarpedElements) auto rotated = fesa::preprocessShellGeometry( rotatedNodes, {element(11U, {0U, 1U, 2U, 3U})}, sections()); - ASSERT_TRUE(rotated.hasValue()); - const auto& rotatedFrame = frameFor(rotated.value(), 0U); + ASSERT_TRUE(rotated.HasValue()); + const auto& rotatedFrame = frameFor(rotated.Value(), 0U); expectVectorNear(rotatedFrame.director, {1.0, 0.0, 0.0}); expectVectorNear(rotatedFrame.tangentA, {0.0, 1.0, 0.0}); expectVectorNear(rotatedFrame.tangentB, {0.0, 0.0, 1.0}); @@ -142,9 +142,9 @@ TEST(Mitc4Geometry, BuildsDeterministicFramesForPlanarRotatedAndWarpedElements) auto warped = fesa::preprocessShellGeometry( warpedNodes, {element(12U, {0U, 1U, 2U, 3U})}, sections()); - ASSERT_TRUE(warped.hasValue()); - EXPECT_GT(warped.value().elementData[0].surfaceAreaWeight, 2.0); - for (const auto& frame : warped.value().nodalFrames) { + ASSERT_TRUE(warped.HasValue()); + EXPECT_GT(warped.Value().elementData[0].surfaceAreaWeight, 2.0); + for (const auto& frame : warped.Value().nodalFrames) { expectRightHandedFrame(frame); } } @@ -166,13 +166,13 @@ TEST(Mitc4Geometry, AreaWeightsSharedDirectorsInStableSourceIdentityOrder) { auto second = fesa::preprocessShellGeometry( nodes, {flat, tilted}, sections()); - ASSERT_TRUE(first.hasValue()); - ASSERT_TRUE(second.hasValue()); + ASSERT_TRUE(first.HasValue()); + ASSERT_TRUE(second.HasValue()); const Vector3 expectedSharedDirector{ -1.0 / std::sqrt(5.0), 0.0, 2.0 / std::sqrt(5.0)}; for (const auto sharedNode : {1U, 2U}) { - const auto& firstFrame = frameFor(first.value(), sharedNode); - const auto& secondFrame = frameFor(second.value(), sharedNode); + const auto& firstFrame = frameFor(first.Value(), sharedNode); + const auto& secondFrame = frameFor(second.Value(), sharedNode); expectVectorNear(firstFrame.director, expectedSharedDirector); expectVectorNear(firstFrame.director, secondFrame.director, 0.0); expectVectorNear(firstFrame.tangentA, {0.0, 1.0, 0.0}); diff --git a/tests/unit/results/result_records_test.cpp b/tests/unit/results/result_records_test.cpp index 63e3fc0..31e1d08 100644 --- a/tests/unit/results/result_records_test.cpp +++ b/tests/unit/results/result_records_test.cpp @@ -20,12 +20,12 @@ fesa::DofManager makeEmptyDofs() { {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()); + 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()); } fesa::ShellResultRow makeShellRow( @@ -141,15 +141,15 @@ void expectShellCandidateRejectedWithoutMutation( const fesa::ShellStateCandidate& candidate, const fesa::ShellStateCandidate& committed) { const auto status = state.commitShellResults(expectedElements, candidate); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(status.failureCategory(), fesa::FailureCategory::model); + EXPECT_FALSE(status.IsOk()); + EXPECT_EQ(status.Category(), fesa::FailureCategory::kModel); expectShellStateEquals( state, committed.rows, committed.physicalStrainEnergy, committed.equilibrium, committed.verificationMetrics); - ASSERT_TRUE(state.commitShellResults(expectedElements, committed).isOk()); + ASSERT_TRUE(state.commitShellResults(expectedElements, committed).IsOk()); } } // namespace @@ -197,9 +197,9 @@ TEST(AnalysisState, PreservesStepFrameAndStableRowOrder) { 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].node.instance_name, "Beam-1"); + EXPECT_EQ(constState.endpointResults()[0].node.source_label, 10); + EXPECT_EQ(constState.endpointResults()[0].node.source_label_text, "010"); EXPECT_EQ( constState.endpointResults()[0].endAction, (std::array{1.0, 2.0, 3.0, 4.0, 5.0, 6.0})); @@ -237,7 +237,7 @@ TEST(AnalysisState, OwnsExactShellRowsInStableElementAndLocationOrder) { const auto status = state.commitShellResults(expectedElements, candidate); - ASSERT_TRUE(status.isOk()); + ASSERT_TRUE(status.IsOk()); const fesa::AnalysisState& constState = state; ASSERT_EQ(constState.shellResults().size(), 8U); EXPECT_EQ(constState.shellResults()[0].element, 3U); @@ -283,7 +283,7 @@ TEST(AnalysisState, CommitsFiniteShellGlobalEvidence) { const auto status = state.commitShellResults(expectedElements, candidate); - ASSERT_TRUE(status.isOk()); + ASSERT_TRUE(status.IsOk()); EXPECT_DOUBLE_EQ(state.physicalStrainEnergy(), 35.5); EXPECT_EQ( state.equilibrium(), @@ -299,7 +299,7 @@ TEST(AnalysisState, InvalidShellCandidatesLeavePriorStateUnchanged) { auto state = fesa::AnalysisState::create(dofs, {"Step-1", 0U}); const std::vector expectedElements{5U}; const auto committed = makeShellCandidate(expectedElements); - ASSERT_TRUE(state.commitShellResults(expectedElements, committed).isOk()); + ASSERT_TRUE(state.commitShellResults(expectedElements, committed).IsOk()); auto invalidLocation = makeShellCandidate(expectedElements); invalidLocation.rows[0].location = fesa::ShellMidsurfaceLocation::gp2; diff --git a/tests/unit/results/result_recovery_test.cpp b/tests/unit/results/result_recovery_test.cpp index 5d7581f..be14400 100644 --- a/tests/unit/results/result_recovery_test.cpp +++ b/tests/unit/results/result_recovery_test.cpp @@ -117,34 +117,34 @@ RecoveryFixture makeFixture( reverseSecond, sectionJump, nonzeroPrescription)); - if (!domainResult.hasValue()) { + if (!domainResult.HasValue()) { throw std::runtime_error{"Recovery fixture Domain construction failed."}; } auto domain = std::make_unique( - std::move(domainResult.value())); + std::move(domainResult.Value())); auto modelResult = fesa::AnalysisModel::create(*domain); - if (!modelResult.hasValue()) { + if (!modelResult.HasValue()) { throw std::runtime_error{"Recovery fixture AnalysisModel construction failed."}; } auto model = std::make_unique( - std::move(modelResult.value())); + std::move(modelResult.Value())); auto dofsResult = fesa::DofManager::create(*model); - if (!dofsResult.hasValue()) { + if (!dofsResult.HasValue()) { throw std::runtime_error{"Recovery fixture DofManager construction failed."}; } auto dofs = std::make_unique( - std::move(dofsResult.value())); + std::move(dofsResult.Value())); fesa::SerialParallelFor serial; auto stiffnessResult = fesa::SparseAssembler::assembleStiffness( *model, *dofs, serial); - if (!stiffnessResult.hasValue()) { + if (!stiffnessResult.HasValue()) { throw std::runtime_error{"Recovery fixture stiffness assembly failed."}; } auto stiffness = std::make_unique( - std::move(stiffnessResult.value())); + std::move(stiffnessResult.Value())); return { std::move(domain), std::move(model), @@ -226,38 +226,38 @@ fesa::ModelDefinition makeShellDefinition( ShellRecoveryFixture makeShellFixture(fesa::ModelDefinition definition) { auto domainResult = fesa::Domain::create(std::move(definition)); - if (!domainResult.hasValue()) { + if (!domainResult.HasValue()) { throw std::runtime_error{ "Shell recovery fixture Domain construction failed."}; } auto domain = std::make_unique( - std::move(domainResult.value())); + std::move(domainResult.Value())); auto modelResult = fesa::AnalysisModel::create(*domain); - if (!modelResult.hasValue()) { + if (!modelResult.HasValue()) { throw std::runtime_error{ "Shell recovery fixture AnalysisModel construction failed."}; } auto model = std::make_unique( - std::move(modelResult.value())); + std::move(modelResult.Value())); auto dofsResult = fesa::DofManager::create(*model); - if (!dofsResult.hasValue()) { + if (!dofsResult.HasValue()) { throw std::runtime_error{ "Shell recovery fixture DofManager construction failed."}; } auto dofs = std::make_unique( - std::move(dofsResult.value())); + std::move(dofsResult.Value())); fesa::SerialParallelFor serial; auto stiffnessResult = fesa::SparseAssembler::assembleStiffness( *model, *dofs, serial); - if (!stiffnessResult.hasValue()) { + if (!stiffnessResult.HasValue()) { throw std::runtime_error{ "Shell recovery fixture stiffness assembly failed."}; } auto stiffness = std::make_unique( - std::move(stiffnessResult.value())); + std::move(stiffnessResult.Value())); return { std::move(domain), std::move(model), @@ -290,7 +290,7 @@ fesa::AnalysisState makeShellPhysicalState( generalized[3U] * x + 0.5 * generalized[5U] * y; } state.externalForce() = - fixture.stiffness->multiply(state.displacement()); + fixture.stiffness->Multiply(state.displacement()); return state; } @@ -299,7 +299,7 @@ fesa::AnalysisState makeAxialEquilibriumState(const RecoveryFixture& fixture) { *fixture.dofs, {"Step-1", 0U}); state.displacement()[0U] = 0.1; state.displacement()[6U] = 0.3; - const fesa::Vector internal = fixture.stiffness->multiply(state.displacement()); + const fesa::Vector internal = fixture.stiffness->Multiply(state.displacement()); for (const std::size_t fullDof : fixture.dofs->freeDofs()) { state.externalForce()[fullDof] = internal[fullDof]; } @@ -321,15 +321,15 @@ fesa::AnalysisState makePatchState( state.displacement()[9U] = twist * kLength; state.displacement()[10U] = kappaY * kLength; state.displacement()[11U] = kappaZ * kLength; - state.externalForce() = fixture.stiffness->multiply(state.displacement()); + state.externalForce() = fixture.stiffness->Multiply(state.displacement()); return state; } void expectStatusCode(const fesa::Status& status, const std::string& code) { - ASSERT_FALSE(status.isOk()); - EXPECT_EQ(status.failureCategory(), fesa::FailureCategory::model); - ASSERT_EQ(status.diagnostics().size(), 1U); - EXPECT_EQ(status.diagnostics()[0U].code, code); + ASSERT_FALSE(status.IsOk()); + EXPECT_EQ(status.Category(), fesa::FailureCategory::kModel); + ASSERT_EQ(status.Diagnostics().size(), 1U); + EXPECT_EQ(status.Diagnostics()[0U].code, code); } void expectScaledNear( @@ -371,12 +371,12 @@ TEST(ResultRecovery, ComputesResidualReactionForNonzeroPrescription) { staleShellEvidence.physicalStrainEnergy = 123.0; staleShellEvidence.equilibrium = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; staleShellEvidence.verificationMetrics = {1.0e-11, 2.0e-11, 3.0e-11}; - ASSERT_TRUE(state.commitShellResults({}, staleShellEvidence).isOk()); + ASSERT_TRUE(state.commitShellResults({}, staleShellEvidence).IsOk()); const fesa::Status status = fesa::ResultRecovery::recover( *fixture.model, *fixture.dofs, *fixture.stiffness, state); - ASSERT_TRUE(status.isOk()); + ASSERT_TRUE(status.IsOk()); EXPECT_DOUBLE_EQ(state.internalForce()[0U], -20.0); EXPECT_DOUBLE_EQ(state.internalForce()[6U], 20.0); EXPECT_DOUBLE_EQ(state.residual()[0U], -20.0); @@ -422,7 +422,7 @@ TEST(ResultRecovery, EnforcesNormalizedFreeResidual) { *fixture.dofs, *fixture.stiffness, thresholdPass); - ASSERT_TRUE(thresholdStatus.isOk()); + ASSERT_TRUE(thresholdStatus.IsOk()); EXPECT_NE(thresholdPass.residual()[6U], 0.0); EXPECT_DOUBLE_EQ( thresholdPass.reaction()[6U], thresholdPass.residual()[6U]); @@ -435,7 +435,7 @@ TEST(ResultRecovery, EnforcesNormalizedFreeResidual) { *zeroFixture.dofs, *zeroFixture.stiffness, zeroEquilibrium) - .isOk()); + .IsOk()); auto wrongPrescription = makeAxialEquilibriumState(fixture); wrongPrescription.displacement()[0U] = 0.0; @@ -469,7 +469,7 @@ TEST(ResultRecovery, KeepsEndActionSectionAndGaussResultsDistinct) { ASSERT_TRUE(fesa::ResultRecovery::recover( *fixture.model, *fixture.dofs, *fixture.stiffness, state) - .isOk()); + .IsOk()); ASSERT_EQ(state.endpointResults().size(), 2U); ASSERT_EQ(state.gaussResults().size(), 2U); EXPECT_EQ(state.endpointResults()[0U].endpoint, 0); @@ -497,7 +497,7 @@ TEST(ResultRecovery, MatchesAxialTorsionAndTwoPlaneEndSigns) { ASSERT_TRUE(fesa::ResultRecovery::recover( *fixture.model, *fixture.dofs, *fixture.stiffness, state) - .isOk()); + .IsOk()); const double shearModulus = kYoungsModulus / (2.0 * (1.0 + kPoissonRatio)); const std::array expected = { @@ -529,7 +529,7 @@ TEST(ResultRecovery, OrdersStressPointsAndDefaultCentroid) { auto state = makePatchState(fixture, 0.01, 0.0, 0.02, -0.03); ASSERT_TRUE(fesa::ResultRecovery::recover( *fixture.model, *fixture.dofs, *fixture.stiffness, state) - .isOk()); + .IsOk()); ASSERT_EQ(state.stressResults().size(), 4U); for (std::size_t gauss = 0U; gauss < 2U; ++gauss) { for (std::size_t point = 0U; point < sectionPoints.size(); ++point) { @@ -554,7 +554,7 @@ TEST(ResultRecovery, OrdersStressPointsAndDefaultCentroid) { *defaultFixture.dofs, *defaultFixture.stiffness, defaultState) - .isOk()); + .IsOk()); ASSERT_EQ(defaultState.stressResults().size(), 2U); for (const auto& row : defaultState.stressResults()) { EXPECT_EQ(row.sectionPoint, 0U); @@ -573,17 +573,17 @@ TEST(ResultRecovery, RequiresInteriorEndpointConsistencyWithoutAveraging) { auto normalized = fesa::ResultRecovery::normalizeSectionResultantsToNodeStations( *fixture.model, rows, tolerances); - ASSERT_TRUE(normalized.hasValue()); - ASSERT_EQ(normalized.value().size(), 3U); - EXPECT_EQ(normalized.value()[1U].representativeElement, 0U); - EXPECT_DOUBLE_EQ(normalized.value()[1U].sectionResultant[0U], 5.0); + ASSERT_TRUE(normalized.HasValue()); + ASSERT_EQ(normalized.Value().size(), 3U); + EXPECT_EQ(normalized.Value()[1U].representativeElement, 0U); + EXPECT_DOUBLE_EQ(normalized.Value()[1U].sectionResultant[0U], 5.0); rows[2U].sectionResultant[0U] = 5.0 + 2.0e-6; auto mismatch = fesa::ResultRecovery::normalizeSectionResultantsToNodeStations( *fixture.model, rows, tolerances); - ASSERT_FALSE(mismatch.hasValue()); - expectStatusCode(mismatch.status(), "node-station-tolerance-failure"); + ASSERT_FALSE(mismatch.HasValue()); + expectStatusCode(mismatch.GetStatus(), "node-station-tolerance-failure"); rows = makeStationRows(fixture); rows[2U].sectionResultant[1U] = @@ -591,17 +591,17 @@ TEST(ResultRecovery, RequiresInteriorEndpointConsistencyWithoutAveraging) { auto nonfinite = fesa::ResultRecovery::normalizeSectionResultantsToNodeStations( *fixture.model, rows, tolerances); - ASSERT_FALSE(nonfinite.hasValue()); - expectStatusCode(nonfinite.status(), "nonfinite-node-station-value"); + ASSERT_FALSE(nonfinite.HasValue()); + expectStatusCode(nonfinite.GetStatus(), "nonfinite-node-station-value"); auto invalidTolerance = fesa::ResultRecovery::normalizeSectionResultantsToNodeStations( *fixture.model, makeStationRows(fixture), {1.0e-6, -1.0, 1.0e-6, 1.0e-6}); - ASSERT_FALSE(invalidTolerance.hasValue()); + ASSERT_FALSE(invalidTolerance.HasValue()); expectStatusCode( - invalidTolerance.status(), "invalid-node-station-tolerance"); + invalidTolerance.GetStatus(), "invalid-node-station-tolerance"); const std::filesystem::path source{"models/result-recovery.inp"}; const auto loadedFixture = makeFixture( @@ -611,8 +611,8 @@ TEST(ResultRecovery, RequiresInteriorEndpointConsistencyWithoutAveraging) { *loadedFixture.model, makeStationRows(loadedFixture), tolerances); - ASSERT_FALSE(loaded.hasValue()); - expectStatusCode(loaded.status(), "ineligible-node-station"); + ASSERT_FALSE(loaded.HasValue()); + expectStatusCode(loaded.GetStatus(), "ineligible-node-station"); const auto reversedFixture = makeFixture(true, {}, {}, true); auto reversed = @@ -620,8 +620,8 @@ TEST(ResultRecovery, RequiresInteriorEndpointConsistencyWithoutAveraging) { *reversedFixture.model, makeStationRows(reversedFixture), tolerances); - ASSERT_FALSE(reversed.hasValue()); - expectStatusCode(reversed.status(), "ineligible-node-station"); + ASSERT_FALSE(reversed.HasValue()); + expectStatusCode(reversed.GetStatus(), "ineligible-node-station"); const auto jumpFixture = makeFixture(true, {}, {}, false, true); auto jumped = @@ -629,8 +629,8 @@ TEST(ResultRecovery, RequiresInteriorEndpointConsistencyWithoutAveraging) { *jumpFixture.model, makeStationRows(jumpFixture), tolerances); - ASSERT_FALSE(jumped.hasValue()); - expectStatusCode(jumped.status(), "ineligible-node-station"); + ASSERT_FALSE(jumped.HasValue()); + expectStatusCode(jumped.GetStatus(), "ineligible-node-station"); } // MITC4-REC-001 @@ -647,12 +647,12 @@ TEST(ResultRecovery, RecoversShellRowsInStableElementAndGpOrder) { state.displacement()[node * 6U + 1U] = -0.05 * y + 0.1 * x; } state.externalForce() = - fixture.stiffness->multiply(state.displacement()); + fixture.stiffness->Multiply(state.displacement()); const auto status = fesa::ResultRecovery::recover( *fixture.model, *fixture.dofs, *fixture.stiffness, state); - ASSERT_TRUE(status.isOk()); + ASSERT_TRUE(status.IsOk()); ASSERT_EQ(state.shellResults().size(), 8U); const double gauss = 1.0 / std::sqrt(3.0); const std::array locations{ @@ -706,7 +706,7 @@ TEST(ResultRecovery, RecoversDirectBottomMiddleTopShellStress) { *fixture.dofs, *fixture.stiffness, state) - .isOk()); + .IsOk()); constexpr std::array positions{ fesa::ShellSectionPosition::bottom, fesa::ShellSectionPosition::middle, @@ -744,16 +744,16 @@ TEST(ResultRecovery, SumsOnlyPhysicalShellEnergyInSourceOrder) { state.displacement()[node * 6U + 5U] = drill[node]; } state.externalForce() = - fixture.stiffness->multiply(state.displacement()); - const double stabilizedEnergy = 0.5 * state.displacement().dot( - fixture.stiffness->multiply(state.displacement())); + fixture.stiffness->Multiply(state.displacement()); + const double stabilizedEnergy = 0.5 * state.displacement().Dot( + fixture.stiffness->Multiply(state.displacement())); ASSERT_TRUE(fesa::ResultRecovery::recover( *fixture.model, *fixture.dofs, *fixture.stiffness, state) - .isOk()); + .IsOk()); EXPECT_NEAR(state.physicalStrainEnergy(), 72.16, 1.0e-12); EXPECT_GT(stabilizedEnergy, state.physicalStrainEnergy()); } @@ -772,15 +772,15 @@ TEST(ResultRecovery, KeepsFullResidualAndComputesGlobalShellEquilibrium) { *fixture.dofs, {"Step-1", 0U}); auto fullLoad = fesa::LoadAssembler::assembleFullNodalLoad( *fixture.model, *fixture.dofs); - ASSERT_TRUE(fullLoad.hasValue()); - state.externalForce() = std::move(fullLoad.value()); + ASSERT_TRUE(fullLoad.HasValue()); + state.externalForce() = std::move(fullLoad.Value()); ASSERT_TRUE(fesa::ResultRecovery::recover( *fixture.model, *fixture.dofs, *fixture.stiffness, state) - .isOk()); + .IsOk()); ASSERT_EQ(state.shellResults().size(), 4U); for (std::size_t fullDof = 0U; fullDof < fixture.dofs->fullDofCount(); @@ -805,7 +805,7 @@ TEST(ResultRecovery, KeepsFullResidualAndComputesGlobalShellEquilibrium) { *freeFixture.dofs, *freeFixture.stiffness, perturbed) - .isOk()); + .IsOk()); for (const double metric : perturbed.verificationMetrics()) { EXPECT_GT(metric, 0.0); EXPECT_LE(metric, 1.0e-10); @@ -838,13 +838,13 @@ TEST(ResultRecovery, UsesGlobalOriginForShellMomentBalance) { *centeredFixture.dofs, *centeredFixture.stiffness, centered) - .isOk()); + .IsOk()); ASSERT_TRUE(fesa::ResultRecovery::recover( *translatedFixture.model, *translatedFixture.dofs, *translatedFixture.stiffness, translated) - .isOk()); + .IsOk()); std::array centeredForce{}; for (std::size_t component = 0U; component < 3U; ++component) { centeredForce[component] = centered.equilibrium()[component]; @@ -877,11 +877,11 @@ TEST(ResultRecovery, UsesScaleAwareShellMetricsAndRejectsExcess) { auto large = makeShellPhysicalState(fixture); constexpr double subunitScale = 1.0e-6; constexpr double largeScale = 1.0e6; - subunit.displacement().scale(subunitScale); - subunit.externalForce().scale(subunitScale); + subunit.displacement().Scale(subunitScale); + subunit.externalForce().Scale(subunitScale); subunit.externalForce()[0U] += 1.0e-9 * subunitScale; - large.displacement().scale(largeScale); - large.externalForce().scale(largeScale); + large.displacement().Scale(largeScale); + large.externalForce().Scale(largeScale); large.externalForce()[0U] += 1.0e-9 * largeScale; ASSERT_TRUE(fesa::ResultRecovery::recover( @@ -889,13 +889,13 @@ TEST(ResultRecovery, UsesScaleAwareShellMetricsAndRejectsExcess) { *fixture.dofs, *fixture.stiffness, subunit) - .isOk()); + .IsOk()); ASSERT_TRUE(fesa::ResultRecovery::recover( *fixture.model, *fixture.dofs, *fixture.stiffness, large) - .isOk()); + .IsOk()); for (std::size_t metric = 0U; metric < 3U; ++metric) { EXPECT_GT(subunit.verificationMetrics()[metric], 0.0); EXPECT_GT(large.verificationMetrics()[metric], 0.0); @@ -918,17 +918,17 @@ TEST(ResultRecovery, UsesScaleAwareShellMetricsAndRejectsExcess) { } const std::vector unbalancedEntry{ {0U, 0U, 1.0, 0U, 0U}}; - auto unbalancedStiffness = fesa::SparseMatrix::fromCoo( + auto unbalancedStiffness = fesa::SparseMatrix::FromCoo( constrainedFixture.dofs->fullDofCount(), constrainedFixture.dofs->fullDofCount(), unbalancedEntry, constrainedFixture.dofs->sparsePattern()); - ASSERT_TRUE(unbalancedStiffness.hasValue()); + ASSERT_TRUE(unbalancedStiffness.HasValue()); expectStatusCode( fesa::ResultRecovery::recover( *constrainedFixture.model, *constrainedFixture.dofs, - unbalancedStiffness.value(), + unbalancedStiffness.Value(), rejected), "global-equilibrium-tolerance-failure"); } @@ -942,7 +942,7 @@ TEST(ResultRecovery, InvalidLaterShellLeavesEntirePriorStateUnchanged) { *validFixture.dofs, *validFixture.stiffness, state) - .isOk()); + .IsOk()); ASSERT_EQ(state.shellResults().size(), 8U); const auto priorFirstRow = state.shellResults().front(); const double priorEnergy = state.physicalStrainEnergy(); @@ -957,17 +957,17 @@ TEST(ResultRecovery, InvalidLaterShellLeavesEntirePriorStateUnchanged) { state.displacement()[5U * 6U] = (std::numeric_limits::max)(); state.externalForce() = fesa::Vector{validFixture.dofs->fullDofCount()}; - auto zeroStiffness = fesa::SparseMatrix::fromCoo( + auto zeroStiffness = fesa::SparseMatrix::FromCoo( validFixture.dofs->fullDofCount(), validFixture.dofs->fullDofCount(), {}, validFixture.dofs->sparsePattern()); - ASSERT_TRUE(zeroStiffness.hasValue()); + ASSERT_TRUE(zeroStiffness.HasValue()); const auto status = fesa::ResultRecovery::recover( *validFixture.model, *validFixture.dofs, - zeroStiffness.value(), + zeroStiffness.Value(), state); expectStatusCode(status, "invalid-shell-recovery"); diff --git a/tests/unit/results/results_writer_test.cpp b/tests/unit/results/results_writer_test.cpp index 4ced0b2..e89422e 100644 --- a/tests/unit/results/results_writer_test.cpp +++ b/tests/unit/results/results_writer_test.cpp @@ -1,8 +1,8 @@ #include "fesa/results/results_writer.hpp" #include "fesa/analysis/analysis_state.hpp" -#include "fesa/core/diagnostic.hpp" -#include "fesa/core/status.hpp" +#include "fesa/core/diagnostic.h" +#include "fesa/core/status.h" #include "fesa/model/domain.hpp" #include diff --git a/tests/unit/solvers/linear/linear_solver_test.cpp b/tests/unit/solvers/linear/linear_solver_test.cpp index cb4b976..30daffa 100644 --- a/tests/unit/solvers/linear/linear_solver_test.cpp +++ b/tests/unit/solvers/linear/linear_solver_test.cpp @@ -1,8 +1,4 @@ -#include "fesa/solvers/linear/linear_solver.hpp" -#include "fesa/solvers/linear/mkl_pardiso_solver.hpp" - -#include "fesa/fem/dof_manager.hpp" -#include "fesa/math/sparse_matrix.hpp" +#include "fesa/solvers/linear/linear_solver.h" #include @@ -10,137 +6,122 @@ #include #include +#include "fesa/fem/dof_manager.hpp" +#include "fesa/math/sparse_matrix.h" +#include "fesa/solvers/linear/mkl_pardiso_solver.h" + namespace { -fesa::SparseMatrix makeDenseCsr( - const std::size_t rows, - const std::size_t columns, - const std::vector& values) { - EXPECT_EQ(values.size(), rows * columns); +fesa::SparseMatrix MakeDenseCsr(const std::size_t rows, + const std::size_t columns, + const std::vector& values) { + EXPECT_EQ(values.size(), rows * columns); - fesa::SparsePattern pattern; - std::vector contributions; - pattern.rowOffsets.reserve(rows + 1U); - pattern.rowOffsets.push_back(0U); - for (std::size_t row = 0U; row < rows; ++row) { - for (std::size_t column = 0U; column < columns; ++column) { - pattern.columnIndices.push_back(column); - contributions.push_back({ - row, - column, - values[row * columns + column], - row, - column}); - } - pattern.rowOffsets.push_back(pattern.columnIndices.size()); + fesa::SparsePattern pattern; + std::vector contributions; + pattern.rowOffsets.reserve(rows + 1U); + pattern.rowOffsets.push_back(0U); + for (std::size_t row = 0U; row < rows; ++row) { + for (std::size_t column = 0U; column < columns; ++column) { + pattern.columnIndices.push_back(column); + contributions.push_back( + {row, column, values[row * columns + column], row, column}); } + pattern.rowOffsets.push_back(pattern.columnIndices.size()); + } - auto matrix = fesa::SparseMatrix::fromCoo( - rows, columns, std::move(contributions), pattern); - EXPECT_TRUE(matrix.hasValue()); - return std::move(matrix.value()); + auto matrix = fesa::SparseMatrix::FromCoo(rows, columns, + std::move(contributions), pattern); + EXPECT_TRUE(matrix.HasValue()); + return std::move(matrix.Value()); } -void expectSolverFailure(const fesa::Status& status) { - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(status.failureCategory(), fesa::FailureCategory::solver); - ASSERT_FALSE(status.diagnostics().empty()); - EXPECT_EQ(status.diagnostics().front().severity, fesa::Severity::error); +void ExpectSolverFailure(const fesa::Status& status) { + EXPECT_FALSE(status.IsOk()); + EXPECT_EQ(status.Category(), fesa::FailureCategory::kSolver); + ASSERT_FALSE(status.Diagnostics().empty()); + EXPECT_EQ(status.Diagnostics().front().severity, fesa::Severity::kError); } -} // namespace +} // namespace TEST(MklPardisoSolver, RejectsInvalidCsrStateAndDimensions) { - static_assert(std::is_base_of_v); - static_assert(std::has_virtual_destructor_v); + static_assert(std::is_base_of_v); + static_assert(std::has_virtual_destructor_v); - fesa::MklPardisoSolver solver; - fesa::Vector untouched{2U}; - untouched[0U] = 17.0; - untouched[1U] = -4.0; - const auto beforeFactorize = - solver.solve(fesa::Vector{2U, 1.0}, untouched); - expectSolverFailure(beforeFactorize); - EXPECT_EQ( - beforeFactorize.diagnostics().front().code, - "solver-not-factorized"); - EXPECT_DOUBLE_EQ(untouched[0U], 17.0); - EXPECT_DOUBLE_EQ(untouched[1U], -4.0); + fesa::MklPardisoSolver solver; + fesa::Vector untouched{2U}; + untouched[0U] = 17.0; + untouched[1U] = -4.0; + const auto before_factorize = solver.Solve(fesa::Vector{2U, 1.0}, untouched); + ExpectSolverFailure(before_factorize); + EXPECT_EQ(before_factorize.Diagnostics().front().code, + "solver-not-factorized"); + EXPECT_DOUBLE_EQ(untouched[0U], 17.0); + EXPECT_DOUBLE_EQ(untouched[1U], -4.0); - // A fully constrained model has a valid 0x0 Kff. It still observes the - // factorize-then-solve lifecycle without invoking a numerical backend. - const auto empty = makeDenseCsr(0U, 0U, {}); - ASSERT_TRUE(solver.factorize(empty).isOk()); - fesa::Vector emptySolution{0U}; - EXPECT_TRUE(solver.solve(fesa::Vector{0U}, emptySolution).isOk()); - EXPECT_EQ(emptySolution.size(), 0U); + // A fully constrained model has a valid 0x0 Kff. It still observes the + // factorize-then-solve lifecycle without invoking a numerical backend. + const auto empty = MakeDenseCsr(0U, 0U, {}); + ASSERT_TRUE(solver.Factorize(empty).IsOk()); + fesa::Vector empty_solution{0U}; + EXPECT_TRUE(solver.Solve(fesa::Vector{0U}, empty_solution).IsOk()); + EXPECT_EQ(empty_solution.Size(), 0U); - // Refactorization from the trivial state must establish ordinary PARDISO - // state rather than retaining a zero-equation shortcut. - const auto spd = makeDenseCsr(2U, 2U, {4.0, 1.0, 1.0, 3.0}); - ASSERT_TRUE(solver.factorize(spd).isOk()); - fesa::Vector solution{2U}; - ASSERT_TRUE(solver.solve(fesa::Vector{2U, 1.0}, solution).isOk()); - EXPECT_NEAR(solution[0U], 2.0 / 11.0, 1.0e-14); - EXPECT_NEAR(solution[1U], 3.0 / 11.0, 1.0e-14); + // Refactorization from the trivial state must establish ordinary PARDISO + // state rather than retaining a zero-equation shortcut. + const auto spd = MakeDenseCsr(2U, 2U, {4.0, 1.0, 1.0, 3.0}); + ASSERT_TRUE(solver.Factorize(spd).IsOk()); + fesa::Vector solution{2U}; + ASSERT_TRUE(solver.Solve(fesa::Vector{2U, 1.0}, solution).IsOk()); + EXPECT_NEAR(solution[0U], 2.0 / 11.0, 1.0e-14); + EXPECT_NEAR(solution[1U], 3.0 / 11.0, 1.0e-14); - const double solvedFirst = solution[0U]; - const double solvedSecond = solution[1U]; - expectSolverFailure(solver.solve(fesa::Vector{1U, 1.0}, solution)); - EXPECT_DOUBLE_EQ(solution[0U], solvedFirst); - EXPECT_DOUBLE_EQ(solution[1U], solvedSecond); + const double solved_first = solution[0U]; + const double solved_second = solution[1U]; + ExpectSolverFailure(solver.Solve(fesa::Vector{1U, 1.0}, solution)); + EXPECT_DOUBLE_EQ(solution[0U], solved_first); + EXPECT_DOUBLE_EQ(solution[1U], solved_second); - fesa::Vector wrongSolution{1U}; - wrongSolution[0U] = 41.0; - expectSolverFailure(solver.solve(fesa::Vector{2U, 1.0}, wrongSolution)); - EXPECT_DOUBLE_EQ(wrongSolution[0U], 41.0); + fesa::Vector wrong_solution{1U}; + wrong_solution[0U] = 41.0; + ExpectSolverFailure(solver.Solve(fesa::Vector{2U, 1.0}, wrong_solution)); + EXPECT_DOUBLE_EQ(wrong_solution[0U], 41.0); - const auto rectangular = makeDenseCsr( - 2U, 3U, {2.0, 0.0, 0.0, 0.0, 3.0, 0.0}); - const auto rectangularStatus = solver.factorize(rectangular); - expectSolverFailure(rectangularStatus); - EXPECT_EQ( - rectangularStatus.diagnostics().front().code, - "solver-matrix-not-square"); + const auto rectangular = MakeDenseCsr(2U, 3U, {2.0, 0.0, 0.0, 0.0, 3.0, 0.0}); + const auto rectangular_status = solver.Factorize(rectangular); + ExpectSolverFailure(rectangular_status); + EXPECT_EQ(rectangular_status.Diagnostics().front().code, + "solver-matrix-not-square"); - fesa::SparsePattern invalidPattern{{0U, 2U}, {0U}}; - auto invalidCsr = fesa::SparseMatrix::fromCoo( - 1U, - 1U, - {{0U, 0U, 1.0, 0U, 0U}}, - invalidPattern); - EXPECT_FALSE(invalidCsr.hasValue()); + fesa::SparsePattern invalid_pattern{{0U, 2U}, {0U}}; + auto invalid_csr = fesa::SparseMatrix::FromCoo( + 1U, 1U, {{0U, 0U, 1.0, 0U, 0U}}, invalid_pattern); + EXPECT_FALSE(invalid_csr.HasValue()); - const auto nonsymmetric = makeDenseCsr(2U, 2U, {2.0, 1.0, 0.0, 3.0}); - const auto nonsymmetricStatus = solver.factorize(nonsymmetric); - expectSolverFailure(nonsymmetricStatus); - EXPECT_EQ( - nonsymmetricStatus.diagnostics().front().code, - "solver-matrix-not-symmetric"); + const auto nonsymmetric = MakeDenseCsr(2U, 2U, {2.0, 1.0, 0.0, 3.0}); + const auto nonsymmetric_status = solver.Factorize(nonsymmetric); + ExpectSolverFailure(nonsymmetric_status); + EXPECT_EQ(nonsymmetric_status.Diagnostics().front().code, + "solver-matrix-not-symmetric"); - const auto scaledNonsymmetric = makeDenseCsr( - 2U, 2U, {2.0e-20, 1.0e-20, 1.1e-20, 3.0e-20}); - const auto scaledNonsymmetricStatus = - solver.factorize(scaledNonsymmetric); - // Stop this case before inspecting diagnostics when the production code - // incorrectly accepts the matrix; this keeps the RED failure deterministic. - ASSERT_FALSE(scaledNonsymmetricStatus.isOk()); - expectSolverFailure(scaledNonsymmetricStatus); - EXPECT_EQ( - scaledNonsymmetricStatus.diagnostics().front().code, - "solver-matrix-not-symmetric"); - - fesa::SparsePattern noDiagonalPattern{{0U, 1U, 2U}, {1U, 0U}}; - auto noDiagonal = fesa::SparseMatrix::fromCoo( - 2U, - 2U, - {{0U, 1U, 1.0, 0U, 0U}, {1U, 0U, 1.0, 1U, 0U}}, - noDiagonalPattern); - ASSERT_TRUE(noDiagonal.hasValue()); - const auto noDiagonalStatus = solver.factorize(noDiagonal.value()); - expectSolverFailure(noDiagonalStatus); - EXPECT_EQ( - noDiagonalStatus.diagnostics().front().code, - "solver-missing-diagonal"); + const auto scaled_nonsymmetric = + MakeDenseCsr(2U, 2U, {2.0e-20, 1.0e-20, 1.1e-20, 3.0e-20}); + const auto scaled_nonsymmetric_status = solver.Factorize(scaled_nonsymmetric); + // Stop this case before inspecting diagnostics when the production code + // incorrectly accepts the matrix; this keeps the RED failure deterministic. + ASSERT_FALSE(scaled_nonsymmetric_status.IsOk()); + ExpectSolverFailure(scaled_nonsymmetric_status); + EXPECT_EQ(scaled_nonsymmetric_status.Diagnostics().front().code, + "solver-matrix-not-symmetric"); + fesa::SparsePattern no_diagonal_pattern{{0U, 1U, 2U}, {1U, 0U}}; + auto no_diagonal = fesa::SparseMatrix::FromCoo( + 2U, 2U, {{0U, 1U, 1.0, 0U, 0U}, {1U, 0U, 1.0, 1U, 0U}}, + no_diagonal_pattern); + ASSERT_TRUE(no_diagonal.HasValue()); + const auto no_diagonal_status = solver.Factorize(no_diagonal.Value()); + ExpectSolverFailure(no_diagonal_status); + EXPECT_EQ(no_diagonal_status.Diagnostics().front().code, + "solver-missing-diagonal"); } diff --git a/tests/unit/solvers/linear/mkl_pardiso_solver_test.cpp b/tests/unit/solvers/linear/mkl_pardiso_solver_test.cpp index 28c84c0..8d9b769 100644 --- a/tests/unit/solvers/linear/mkl_pardiso_solver_test.cpp +++ b/tests/unit/solvers/linear/mkl_pardiso_solver_test.cpp @@ -1,7 +1,4 @@ -#include "fesa/solvers/linear/mkl_pardiso_solver.hpp" - -#include "fesa/fem/dof_manager.hpp" -#include "fesa/math/sparse_matrix.hpp" +#include "fesa/solvers/linear/mkl_pardiso_solver.h" #include @@ -12,261 +9,242 @@ #include #include +#include "fesa/fem/dof_manager.hpp" +#include "fesa/math/sparse_matrix.h" + namespace { -fesa::SparseMatrix makeDenseCsr( - const std::size_t size, - const std::vector& values) { - EXPECT_EQ(values.size(), size * size); +fesa::SparseMatrix MakeDenseCsr(const std::size_t size, + const std::vector& values) { + EXPECT_EQ(values.size(), size * size); - fesa::SparsePattern pattern; - std::vector contributions; - pattern.rowOffsets.reserve(size + 1U); - pattern.rowOffsets.push_back(0U); - for (std::size_t row = 0U; row < size; ++row) { - for (std::size_t column = 0U; column < size; ++column) { - pattern.columnIndices.push_back(column); - contributions.push_back({ - row, - column, - values[row * size + column], - row, - column}); - } - pattern.rowOffsets.push_back(pattern.columnIndices.size()); + fesa::SparsePattern pattern; + std::vector contributions; + pattern.rowOffsets.reserve(size + 1U); + pattern.rowOffsets.push_back(0U); + for (std::size_t row = 0U; row < size; ++row) { + for (std::size_t column = 0U; column < size; ++column) { + pattern.columnIndices.push_back(column); + contributions.push_back( + {row, column, values[row * size + column], row, column}); } + pattern.rowOffsets.push_back(pattern.columnIndices.size()); + } - auto matrix = fesa::SparseMatrix::fromCoo( - size, size, std::move(contributions), pattern); - EXPECT_TRUE(matrix.hasValue()); - return std::move(matrix.value()); + auto matrix = fesa::SparseMatrix::FromCoo(size, size, + std::move(contributions), pattern); + EXPECT_TRUE(matrix.HasValue()); + return std::move(matrix.Value()); } -fesa::Vector makeVector(const std::initializer_list values) { - fesa::Vector result{values.size()}; - std::size_t index = 0U; - for (const double value : values) { - result[index++] = value; - } - return result; +fesa::Vector MakeVector(const std::initializer_list values) { + fesa::Vector result{values.size()}; + std::size_t index = 0U; + for (const double value : values) { + result[index++] = value; + } + return result; } -double normalizedResidual( - const fesa::SparseMatrix& matrix, - const fesa::Vector& solution, - const fesa::Vector& rhs) { - auto residual = matrix.multiply(solution); - residual.axpy(-1.0, rhs); - const double numerator = residual.norm(); - const double denominator = rhs.norm(); - if (!std::isfinite(numerator) || !std::isfinite(denominator)) { - return (std::numeric_limits::infinity)(); - } - if (denominator == 0.0) { - return numerator == 0.0 ? 0.0 : - (std::numeric_limits::infinity)(); - } - return numerator / denominator; +double NormalizedResidual(const fesa::SparseMatrix& matrix, + const fesa::Vector& solution, + const fesa::Vector& rhs) { + auto residual = matrix.Multiply(solution); + residual.Axpy(-1.0, rhs); + const double numerator = residual.Norm(); + const double denominator = rhs.Norm(); + if (!std::isfinite(numerator) || !std::isfinite(denominator)) { + return (std::numeric_limits::infinity)(); + } + if (denominator == 0.0) { + return numerator == 0.0 ? 0.0 : (std::numeric_limits::infinity)(); + } + return numerator / denominator; } -double relativeError( - const fesa::Vector& actual, - const fesa::Vector& expected) { - auto difference = actual; - difference.axpy(-1.0, expected); - const double numerator = difference.norm(); - const double denominator = expected.norm(); - if (!std::isfinite(numerator) || !std::isfinite(denominator)) { - return (std::numeric_limits::infinity)(); - } - if (denominator == 0.0) { - return numerator == 0.0 ? 0.0 : - (std::numeric_limits::infinity)(); - } - return numerator / denominator; +double RelativeError(const fesa::Vector& actual, const fesa::Vector& expected) { + auto difference = actual; + difference.Axpy(-1.0, expected); + const double numerator = difference.Norm(); + const double denominator = expected.Norm(); + if (!std::isfinite(numerator) || !std::isfinite(denominator)) { + return (std::numeric_limits::infinity)(); + } + if (denominator == 0.0) { + return numerator == 0.0 ? 0.0 : (std::numeric_limits::infinity)(); + } + return numerator / denominator; } -void expectStructuredSolverFailure(const fesa::Status& status) { - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(status.failureCategory(), fesa::FailureCategory::solver); - ASSERT_EQ(status.diagnostics().size(), 1U); - EXPECT_EQ(status.diagnostics()[0U].severity, fesa::Severity::error); - EXPECT_FALSE(status.diagnostics()[0U].code.empty()); - EXPECT_FALSE(status.diagnostics()[0U].message.empty()); +void ExpectStructuredSolverFailure(const fesa::Status& status) { + EXPECT_FALSE(status.IsOk()); + EXPECT_EQ(status.Category(), fesa::FailureCategory::kSolver); + ASSERT_EQ(status.Diagnostics().size(), 1U); + EXPECT_EQ(status.Diagnostics()[0U].severity, fesa::Severity::kError); + EXPECT_FALSE(status.Diagnostics()[0U].code.empty()); + EXPECT_FALSE(status.Diagnostics()[0U].message.empty()); } -} // namespace +} // namespace TEST(MklPardisoSolver, SolvesKnownSpdWithNormalizedResidual) { - const auto matrix = makeDenseCsr(3U, { - 6.0, 2.0, 1.0, - 2.0, 5.0, 2.0, - 1.0, 2.0, 4.0}); - const auto expected = makeVector({1.0, -2.0, 3.0}); - const auto rhs = matrix.multiply(expected); + const auto matrix = + MakeDenseCsr(3U, {6.0, 2.0, 1.0, 2.0, 5.0, 2.0, 1.0, 2.0, 4.0}); + const auto expected = MakeVector({1.0, -2.0, 3.0}); + const auto rhs = matrix.Multiply(expected); - fesa::MklPardisoSolver concreteSolver; - fesa::LinearSolver& solver = concreteSolver; - ASSERT_TRUE(solver.factorize(matrix).isOk()); + fesa::MklPardisoSolver concrete_solver; + fesa::LinearSolver& solver = concrete_solver; + ASSERT_TRUE(solver.Factorize(matrix).IsOk()); - fesa::Vector solution{3U}; - ASSERT_TRUE(solver.solve(rhs, solution).isOk()); - EXPECT_LE(normalizedResidual(matrix, solution, rhs), 1.0e-10); - EXPECT_LE(relativeError(solution, expected), 1.0e-9); + fesa::Vector solution{3U}; + ASSERT_TRUE(solver.Solve(rhs, solution).IsOk()); + EXPECT_LE(NormalizedResidual(matrix, solution, rhs), 1.0e-10); + EXPECT_LE(RelativeError(solution, expected), 1.0e-9); } TEST(MklPardisoSolver, ReusesOneFactorizationForRepeatedRhs) { - const auto matrix = makeDenseCsr(2U, {4.0, 1.0, 1.0, 3.0}); - const auto expectedFirst = makeVector({1.0, 2.0}); - const auto expectedSecond = makeVector({-2.0, 0.5}); - const auto rhsFirst = matrix.multiply(expectedFirst); - const auto rhsSecond = matrix.multiply(expectedSecond); + const auto matrix = MakeDenseCsr(2U, {4.0, 1.0, 1.0, 3.0}); + const auto expected_first = MakeVector({1.0, 2.0}); + const auto expected_second = MakeVector({-2.0, 0.5}); + const auto rhs_first = matrix.Multiply(expected_first); + const auto rhs_second = matrix.Multiply(expected_second); - fesa::MklPardisoSolver solver; - ASSERT_TRUE(solver.factorize(matrix).isOk()); - fesa::Vector first{2U}; - fesa::Vector second{2U}; - ASSERT_TRUE(solver.solve(rhsFirst, first).isOk()); - ASSERT_TRUE(solver.solve(rhsSecond, second).isOk()); + fesa::MklPardisoSolver solver; + ASSERT_TRUE(solver.Factorize(matrix).IsOk()); + fesa::Vector first{2U}; + fesa::Vector second{2U}; + ASSERT_TRUE(solver.Solve(rhs_first, first).IsOk()); + ASSERT_TRUE(solver.Solve(rhs_second, second).IsOk()); - EXPECT_LE(relativeError(first, expectedFirst), 1.0e-9); - EXPECT_LE(relativeError(second, expectedSecond), 1.0e-9); - EXPECT_LE(normalizedResidual(matrix, first, rhsFirst), 1.0e-10); - EXPECT_LE(normalizedResidual(matrix, second, rhsSecond), 1.0e-10); + EXPECT_LE(RelativeError(first, expected_first), 1.0e-9); + EXPECT_LE(RelativeError(second, expected_second), 1.0e-9); + EXPECT_LE(NormalizedResidual(matrix, first, rhs_first), 1.0e-10); + EXPECT_LE(NormalizedResidual(matrix, second, rhs_second), 1.0e-10); } TEST(MklPardisoSolver, RefactorizesWithoutLeakingState) { - const auto firstMatrix = makeDenseCsr(2U, {4.0, 1.0, 1.0, 3.0}); - const auto secondMatrix = makeDenseCsr(2U, {2.0, 0.0, 0.0, 5.0}); - const auto firstExpected = makeVector({1.0, 2.0}); - const auto secondExpected = makeVector({-3.0, 4.0}); + const auto first_matrix = MakeDenseCsr(2U, {4.0, 1.0, 1.0, 3.0}); + const auto second_matrix = MakeDenseCsr(2U, {2.0, 0.0, 0.0, 5.0}); + const auto first_expected = MakeVector({1.0, 2.0}); + const auto second_expected = MakeVector({-3.0, 4.0}); - fesa::MklPardisoSolver solver; - ASSERT_TRUE(solver.factorize(firstMatrix).isOk()); - fesa::Vector firstSolution{2U}; - ASSERT_TRUE( - solver.solve(firstMatrix.multiply(firstExpected), firstSolution).isOk()); - EXPECT_LE(relativeError(firstSolution, firstExpected), 1.0e-9); + fesa::MklPardisoSolver solver; + ASSERT_TRUE(solver.Factorize(first_matrix).IsOk()); + fesa::Vector first_solution{2U}; + ASSERT_TRUE( + solver.Solve(first_matrix.Multiply(first_expected), first_solution) + .IsOk()); + EXPECT_LE(RelativeError(first_solution, first_expected), 1.0e-9); - ASSERT_TRUE(solver.factorize(secondMatrix).isOk()); - fesa::Vector secondSolution{2U}; - const auto secondRhs = secondMatrix.multiply(secondExpected); - ASSERT_TRUE(solver.solve(secondRhs, secondSolution).isOk()); - EXPECT_LE(relativeError(secondSolution, secondExpected), 1.0e-9); - EXPECT_LE( - normalizedResidual(secondMatrix, secondSolution, secondRhs), 1.0e-10); + ASSERT_TRUE(solver.Factorize(second_matrix).IsOk()); + fesa::Vector second_solution{2U}; + const auto second_rhs = second_matrix.Multiply(second_expected); + ASSERT_TRUE(solver.Solve(second_rhs, second_solution).IsOk()); + EXPECT_LE(RelativeError(second_solution, second_expected), 1.0e-9); + EXPECT_LE(NormalizedResidual(second_matrix, second_solution, second_rhs), + 1.0e-10); } TEST(MklPardisoSolver, ClassifiesSingularIndefiniteAndNonfiniteFailures) { + fesa::MklPardisoSolver solver; + + const auto singular = MakeDenseCsr(2U, {1.0, 1.0, 1.0, 1.0}); + const auto singular_status = solver.Factorize(singular); + ExpectStructuredSolverFailure(singular_status); + EXPECT_TRUE(singular_status.Diagnostics()[0U].code == + "pardiso-zero-or-negative-pivot" || + singular_status.Diagnostics()[0U].code == + "pardiso-singular-diagonal"); + EXPECT_NE( + singular_status.Diagnostics()[0U].entity_identity.find("phase=22,error="), + std::string::npos); + + const auto indefinite = MakeDenseCsr(2U, {1.0, 2.0, 2.0, 1.0}); + const auto indefinite_status = solver.Factorize(indefinite); + ExpectStructuredSolverFailure(indefinite_status); + EXPECT_TRUE(indefinite_status.Diagnostics()[0U].code == + "pardiso-zero-or-negative-pivot" || + indefinite_status.Diagnostics()[0U].code == + "pardiso-singular-diagonal"); + EXPECT_NE(indefinite_status.Diagnostics()[0U].entity_identity.find( + "phase=22,error="), + std::string::npos); + + const auto spd = MakeDenseCsr(2U, {3.0, 1.0, 1.0, 2.0}); + ASSERT_TRUE(solver.Factorize(spd).IsOk()); + auto rhs = MakeVector({1.0, 2.0}); + rhs[1U] = (std::numeric_limits::infinity)(); + fesa::Vector solution{2U}; + solution[0U] = 23.0; + solution[1U] = -9.0; + const auto rhs_status = solver.Solve(rhs, solution); + ExpectStructuredSolverFailure(rhs_status); + EXPECT_EQ(rhs_status.Diagnostics()[0U].code, "nonfinite-solver-rhs"); + EXPECT_DOUBLE_EQ(solution[0U], 23.0); + EXPECT_DOUBLE_EQ(solution[1U], -9.0); + + fesa::SparsePattern pattern{{0U, 1U}, {0U}}; + auto nonfinite_matrix = fesa::SparseMatrix::FromCoo( + 1U, 1U, {{0U, 0U, (std::numeric_limits::quiet_NaN)(), 0U, 0U}}, + pattern); + EXPECT_FALSE(nonfinite_matrix.HasValue()); + EXPECT_EQ(nonfinite_matrix.GetStatus().Diagnostics()[0U].code, + "nonfinite-sparse-value"); +} + +TEST(MklPardisoSolver, + ConditioningSweepPassesResolvedCasesAndFailsUnresolvedCasesExplicitly) { + const std::vector common_scales{1.0e-12, 1.0, 1.0e12}; + for (const double scale : common_scales) { + const auto matrix = + MakeDenseCsr(2U, {4.0 * scale, 1.0 * scale, 1.0 * scale, 3.0 * scale}); + const auto expected = MakeVector({1.25, -0.75}); + const auto rhs = matrix.Multiply(expected); + + fesa::MklPardisoSolver solver; + ASSERT_TRUE(solver.Factorize(matrix).IsOk()) << "scale=" << scale; + fesa::Vector solution{2U}; + ASSERT_TRUE(solver.Solve(rhs, solution).IsOk()) << "scale=" << scale; + EXPECT_LE(NormalizedResidual(matrix, solution, rhs), 1.0e-10); + EXPECT_LE(RelativeError(solution, expected), 1.0e-9); + } + + const double resolved_ratio = 1.0e-8; + const auto resolved_matrix = + MakeDenseCsr(2U, {1.0, 0.0, 0.0, resolved_ratio}); + const auto resolved_expected = MakeVector({0.5, -2.0}); + const auto resolved_rhs = resolved_matrix.Multiply(resolved_expected); + fesa::MklPardisoSolver resolved_solver; + ASSERT_TRUE(resolved_solver.Factorize(resolved_matrix).IsOk()); + fesa::Vector resolved_solution{2U}; + ASSERT_TRUE(resolved_solver.Solve(resolved_rhs, resolved_solution).IsOk()); + EXPECT_LE( + NormalizedResidual(resolved_matrix, resolved_solution, resolved_rhs), + 1.0e-10); + EXPECT_LE(RelativeError(resolved_solution, resolved_expected), 1.0e-9); + + const std::vector unresolved_candidates{1.0e-16, 1.0e-300}; + for (const double ratio : unresolved_candidates) { + const auto matrix = MakeDenseCsr(2U, {1.0, 0.0, 0.0, ratio}); + const auto expected = MakeVector({0.5, -2.0}); + const auto rhs = matrix.Multiply(expected); fesa::MklPardisoSolver solver; - const auto singular = makeDenseCsr(2U, {1.0, 1.0, 1.0, 1.0}); - const auto singularStatus = solver.factorize(singular); - expectStructuredSolverFailure(singularStatus); - EXPECT_TRUE( - singularStatus.diagnostics()[0U].code == - "pardiso-zero-or-negative-pivot" || - singularStatus.diagnostics()[0U].code == - "pardiso-singular-diagonal"); - EXPECT_NE( - singularStatus.diagnostics()[0U].entityIdentity.find( - "phase=22,error="), - std::string::npos); + const auto factor_status = solver.Factorize(matrix); + if (!factor_status.IsOk()) { + ExpectStructuredSolverFailure(factor_status); + continue; + } - const auto indefinite = makeDenseCsr(2U, {1.0, 2.0, 2.0, 1.0}); - const auto indefiniteStatus = solver.factorize(indefinite); - expectStructuredSolverFailure(indefiniteStatus); - EXPECT_TRUE( - indefiniteStatus.diagnostics()[0U].code == - "pardiso-zero-or-negative-pivot" || - indefiniteStatus.diagnostics()[0U].code == - "pardiso-singular-diagonal"); - EXPECT_NE( - indefiniteStatus.diagnostics()[0U].entityIdentity.find( - "phase=22,error="), - std::string::npos); - - const auto spd = makeDenseCsr(2U, {3.0, 1.0, 1.0, 2.0}); - ASSERT_TRUE(solver.factorize(spd).isOk()); - auto rhs = makeVector({1.0, 2.0}); - rhs[1U] = (std::numeric_limits::infinity)(); fesa::Vector solution{2U}; - solution[0U] = 23.0; - solution[1U] = -9.0; - const auto rhsStatus = solver.solve(rhs, solution); - expectStructuredSolverFailure(rhsStatus); - EXPECT_EQ(rhsStatus.diagnostics()[0U].code, "nonfinite-solver-rhs"); - EXPECT_DOUBLE_EQ(solution[0U], 23.0); - EXPECT_DOUBLE_EQ(solution[1U], -9.0); - - fesa::SparsePattern pattern{{0U, 1U}, {0U}}; - auto nonfiniteMatrix = fesa::SparseMatrix::fromCoo( - 1U, - 1U, - {{0U, - 0U, - (std::numeric_limits::quiet_NaN)(), - 0U, - 0U}}, - pattern); - EXPECT_FALSE(nonfiniteMatrix.hasValue()); - EXPECT_EQ( - nonfiniteMatrix.status().diagnostics()[0U].code, - "nonfinite-sparse-value"); -} - -TEST(MklPardisoSolver, ConditioningSweepPassesResolvedCasesAndFailsUnresolvedCasesExplicitly) { - const std::vector commonScales{1.0e-12, 1.0, 1.0e12}; - for (const double scale : commonScales) { - const auto matrix = makeDenseCsr(2U, { - 4.0 * scale, 1.0 * scale, - 1.0 * scale, 3.0 * scale}); - const auto expected = makeVector({1.25, -0.75}); - const auto rhs = matrix.multiply(expected); - - fesa::MklPardisoSolver solver; - ASSERT_TRUE(solver.factorize(matrix).isOk()) << "scale=" << scale; - fesa::Vector solution{2U}; - ASSERT_TRUE(solver.solve(rhs, solution).isOk()) << "scale=" << scale; - EXPECT_LE(normalizedResidual(matrix, solution, rhs), 1.0e-10); - EXPECT_LE(relativeError(solution, expected), 1.0e-9); + const auto solve_status = solver.Solve(rhs, solution); + if (!solve_status.IsOk()) { + ExpectStructuredSolverFailure(solve_status); + continue; } - const double resolvedRatio = 1.0e-8; - const auto resolvedMatrix = makeDenseCsr( - 2U, {1.0, 0.0, 0.0, resolvedRatio}); - const auto resolvedExpected = makeVector({0.5, -2.0}); - const auto resolvedRhs = resolvedMatrix.multiply(resolvedExpected); - fesa::MklPardisoSolver resolvedSolver; - ASSERT_TRUE(resolvedSolver.factorize(resolvedMatrix).isOk()); - fesa::Vector resolvedSolution{2U}; - ASSERT_TRUE(resolvedSolver.solve(resolvedRhs, resolvedSolution).isOk()); - EXPECT_LE( - normalizedResidual(resolvedMatrix, resolvedSolution, resolvedRhs), - 1.0e-10); - EXPECT_LE(relativeError(resolvedSolution, resolvedExpected), 1.0e-9); - - const std::vector unresolvedCandidates{1.0e-16, 1.0e-300}; - for (const double ratio : unresolvedCandidates) { - const auto matrix = makeDenseCsr(2U, {1.0, 0.0, 0.0, ratio}); - const auto expected = makeVector({0.5, -2.0}); - const auto rhs = matrix.multiply(expected); - fesa::MklPardisoSolver solver; - - const auto factorStatus = solver.factorize(matrix); - if (!factorStatus.isOk()) { - expectStructuredSolverFailure(factorStatus); - continue; - } - - fesa::Vector solution{2U}; - const auto solveStatus = solver.solve(rhs, solution); - if (!solveStatus.isOk()) { - expectStructuredSolverFailure(solveStatus); - continue; - } - - EXPECT_LE(normalizedResidual(matrix, solution, rhs), 1.0e-10); - EXPECT_LE(relativeError(solution, expected), 1.0e-9); - } + EXPECT_LE(NormalizedResidual(matrix, solution, rhs), 1.0e-10); + EXPECT_LE(RelativeError(solution, expected), 1.0e-9); + } }