feat(solver-bootstrap): step 3 — core-ids-and-diagnostics
This commit is contained in:
@@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <fesa/core/source_location.hpp>
|
||||||
|
|
||||||
|
namespace fesa {
|
||||||
|
|
||||||
|
enum class DiagnosticStage {
|
||||||
|
io,
|
||||||
|
syntax,
|
||||||
|
semantic,
|
||||||
|
model,
|
||||||
|
equation,
|
||||||
|
solver,
|
||||||
|
results,
|
||||||
|
validation
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Severity { warning, error };
|
||||||
|
|
||||||
|
struct Diagnostic final {
|
||||||
|
DiagnosticStage stage;
|
||||||
|
Severity severity;
|
||||||
|
std::string code;
|
||||||
|
std::string message;
|
||||||
|
std::optional<SourceLocation> source;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fesa
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <compare>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace fesa {
|
||||||
|
|
||||||
|
template <class Tag>
|
||||||
|
class EntityId final {
|
||||||
|
public:
|
||||||
|
explicit constexpr EntityId(const std::int64_t value) : value_{value} {
|
||||||
|
if (value < 0) {
|
||||||
|
throw std::invalid_argument{"EntityId value must not be negative."};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr std::int64_t value() const noexcept {
|
||||||
|
return value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator<=>(const EntityId&) const = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::int64_t value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fesa
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
namespace fesa {
|
||||||
|
|
||||||
|
struct SourceLocation final {
|
||||||
|
std::filesystem::path file;
|
||||||
|
std::size_t line;
|
||||||
|
std::size_t column;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fesa
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <fesa/core/diagnostic.hpp>
|
||||||
|
|
||||||
|
namespace fesa {
|
||||||
|
|
||||||
|
struct Status final {
|
||||||
|
bool succeeded;
|
||||||
|
std::vector<Diagnostic> diagnostics;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fesa
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace fesa {
|
||||||
|
|
||||||
|
struct Vec3 final {
|
||||||
|
double x;
|
||||||
|
double y;
|
||||||
|
double z;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] inline bool is_finite(const Vec3 value) noexcept {
|
||||||
|
return std::isfinite(value.x) && std::isfinite(value.y) &&
|
||||||
|
std::isfinite(value.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fesa
|
||||||
@@ -36,3 +36,24 @@ set_property(
|
|||||||
PROPERTY ENVIRONMENT_MODIFICATION
|
PROPERTY ENVIRONMENT_MODIFICATION
|
||||||
${FESA_DEPENDENCY_RUNTIME_MODIFICATIONS}
|
${FESA_DEPENDENCY_RUNTIME_MODIFICATIONS}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_executable(fesa_core_value_tests
|
||||||
|
unit/core/diagnostic_test.cpp
|
||||||
|
unit/core/entity_id_test.cpp
|
||||||
|
unit/core/status_test.cpp
|
||||||
|
unit/core/vec3_test.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_features(fesa_core_value_tests PRIVATE cxx_std_20)
|
||||||
|
target_compile_options(fesa_core_value_tests PRIVATE /W4 /permissive- /EHsc)
|
||||||
|
|
||||||
|
target_link_libraries(fesa_core_value_tests
|
||||||
|
PRIVATE
|
||||||
|
fesa_core
|
||||||
|
GTest::gtest_main
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(
|
||||||
|
NAME CoreValueTypes
|
||||||
|
COMMAND "$<TARGET_FILE:fesa_core_value_tests>"
|
||||||
|
)
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#include <filesystem>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <fesa/core/diagnostic.hpp>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST(Diagnostic, PreservesSourceLocation) {
|
||||||
|
const fesa::Diagnostic diagnostic{
|
||||||
|
fesa::DiagnosticStage::syntax,
|
||||||
|
fesa::Severity::error,
|
||||||
|
"ABAQUS_INVALID_NODE",
|
||||||
|
"Node data is incomplete.",
|
||||||
|
fesa::SourceLocation{
|
||||||
|
std::filesystem::path{"models/beam.inp"}, 42, 9}};
|
||||||
|
|
||||||
|
ASSERT_TRUE(diagnostic.source.has_value());
|
||||||
|
EXPECT_EQ(
|
||||||
|
diagnostic.source->file, std::filesystem::path{"models/beam.inp"});
|
||||||
|
EXPECT_EQ(diagnostic.source->line, 42U);
|
||||||
|
EXPECT_EQ(diagnostic.source->column, 9U);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Diagnostic, AllowsDiagnosticsWithoutSourceLocation) {
|
||||||
|
const fesa::Diagnostic diagnostic{
|
||||||
|
fesa::DiagnosticStage::solver,
|
||||||
|
fesa::Severity::warning,
|
||||||
|
"SOLVER_RESIDUAL",
|
||||||
|
"Residual is above the reporting threshold.",
|
||||||
|
std::nullopt};
|
||||||
|
|
||||||
|
EXPECT_FALSE(diagnostic.source.has_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <fesa/core/entity_id.hpp>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct NodeTag;
|
||||||
|
struct ElementTag;
|
||||||
|
|
||||||
|
using NodeId = fesa::EntityId<NodeTag>;
|
||||||
|
using ElementId = fesa::EntityId<ElementTag>;
|
||||||
|
|
||||||
|
static_assert(std::is_constructible_v<NodeId, std::int64_t>);
|
||||||
|
static_assert(!std::is_convertible_v<std::int64_t, NodeId>);
|
||||||
|
static_assert(!std::is_convertible_v<NodeId, ElementId>);
|
||||||
|
|
||||||
|
TEST(EntityId, PreservesValueAndSupportsTypedOrdering) {
|
||||||
|
constexpr NodeId first{7};
|
||||||
|
constexpr NodeId second{11};
|
||||||
|
|
||||||
|
static_assert(first.value() == 7);
|
||||||
|
static_assert(first < second);
|
||||||
|
|
||||||
|
EXPECT_EQ(first.value(), 7);
|
||||||
|
EXPECT_LT(first, second);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EntityId, RejectsNegativeValues) {
|
||||||
|
EXPECT_THROW((NodeId{-1}), std::invalid_argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#include <filesystem>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <fesa/core/status.hpp>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST(CoreStatus, PreservesFailureDiagnostics) {
|
||||||
|
const fesa::Status status{
|
||||||
|
false,
|
||||||
|
{fesa::Diagnostic{
|
||||||
|
fesa::DiagnosticStage::model,
|
||||||
|
fesa::Severity::error,
|
||||||
|
"MODEL_NONFINITE_VECTOR",
|
||||||
|
"A vector component is not finite.",
|
||||||
|
fesa::SourceLocation{
|
||||||
|
std::filesystem::path{"models/invalid.inp"}, 8, 3}}}};
|
||||||
|
|
||||||
|
EXPECT_FALSE(status.succeeded);
|
||||||
|
ASSERT_EQ(status.diagnostics.size(), 1U);
|
||||||
|
EXPECT_EQ(status.diagnostics.front().code, "MODEL_NONFINITE_VECTOR");
|
||||||
|
ASSERT_TRUE(status.diagnostics.front().source.has_value());
|
||||||
|
EXPECT_EQ(status.diagnostics.front().source->line, 8U);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CoreStatus, RepresentsSuccessWithoutDiagnostics) {
|
||||||
|
const fesa::Status status{true, {}};
|
||||||
|
|
||||||
|
EXPECT_TRUE(status.succeeded);
|
||||||
|
EXPECT_TRUE(status.diagnostics.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <fesa/core/vec3.hpp>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST(CoreVec3, ReportsOnlyFiniteVectorsAsFinite) {
|
||||||
|
EXPECT_TRUE(fesa::is_finite(fesa::Vec3{1.0, -2.0, 0.0}));
|
||||||
|
|
||||||
|
const double infinity = std::numeric_limits<double>::infinity();
|
||||||
|
const double nan = std::numeric_limits<double>::quiet_NaN();
|
||||||
|
|
||||||
|
EXPECT_FALSE(fesa::is_finite(fesa::Vec3{infinity, 0.0, 0.0}));
|
||||||
|
EXPECT_FALSE(fesa::is_finite(fesa::Vec3{0.0, -infinity, 0.0}));
|
||||||
|
EXPECT_FALSE(fesa::is_finite(fesa::Vec3{0.0, 0.0, nan}));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
Reference in New Issue
Block a user