feat(quadrature): quadrature system brought over
Ported and dramatically cleaned up the quadrature system. This includes centralizing all field definitions
This commit is contained in:
52
src/include/serif/utils/error/codes.hpp
Normal file
52
src/include/serif/utils/error/codes.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
#include "serif/meta/enums.hpp"
|
||||
|
||||
namespace serif::utils::errors {
|
||||
// Each section should start at another hundred to allow for simple reading of error codes by users
|
||||
// C++ enums automatically count up from the previous value so you only need to specify the first
|
||||
// member of that category. By convention please make the first member the most general
|
||||
// class of error in that category, subsequent errors can specify further.
|
||||
// subcategories should follow the same patter but in 10s
|
||||
|
||||
// Further, by convention please keep all names upper case. The meta functions
|
||||
// will convert all lower case names to upper case but its good convention to
|
||||
// stay consistent here.
|
||||
|
||||
// At some point someone should go through and come up with some well defined schema for all this
|
||||
enum class SERiFErrorCode : uint16_t {
|
||||
UNKNOWN_ERROR = 0,
|
||||
|
||||
// RUNTIME FAILURES (>100)
|
||||
// Invalid argument errors
|
||||
INVALID_ARGUMENT = 100,
|
||||
INVALID_QUADRATURE_ORDER,
|
||||
|
||||
INVALID_DOMAIN_ERROR=110,
|
||||
NEGATIVE_VALUE_ERROR,
|
||||
NON_FINITE_VALUE_ERROR,
|
||||
|
||||
// Jacobian Errors
|
||||
JACOBIAN_ERROR = 200,
|
||||
INVERTED_JACOBIAN,
|
||||
|
||||
// EOS Errors
|
||||
EOS_ERROR = 300,
|
||||
EOS_UNSUPPORTED_RELATION,
|
||||
EOS_UNSUPPORTED_DERIVATIVE,
|
||||
EOS_WRONG_INPUT_COUNT,
|
||||
EOS_WRONG_INPUT_QUANTITY,
|
||||
EOS_NONFINITE_INPUT,
|
||||
EOS_OUTSIDE_DOMAIN,
|
||||
EOS_NONFINITE_RESULT,
|
||||
EOS_INVALID_THERMODYNAMIC_INPUT,
|
||||
|
||||
// COMPILE TIME FAILURES (>1000)
|
||||
// Type Invariant Failures
|
||||
FAILED_TYPE_INVARIANT = 1000
|
||||
};
|
||||
|
||||
inline auto SERiFErrorCodeNameToTypeMap = meta::enum_to_string_enum_map<SERiFErrorCode>();
|
||||
inline auto SERiFErrorCodeTypeToNameMap = meta::enum_to_enum_string_map<SERiFErrorCode>();
|
||||
}
|
||||
78
src/include/serif/utils/error/errors.hpp
Normal file
78
src/include/serif/utils/error/errors.hpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <format>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#include "serif/utils/misc/terminal/colors.hpp"
|
||||
#include "serif/utils/misc/concepts/exceptions.hpp"
|
||||
#include "serif/utils/error/codes.hpp"
|
||||
#include "serif/utils/types/strings.hpp"
|
||||
|
||||
// The *ONLY* place in SERiF that throw should
|
||||
// be called is here. All other error throwing locations should
|
||||
// call serif_error to ensure we have consistent handling of errors
|
||||
|
||||
// If you want to throw a warning then look at the parallel warnings module and call serif_warning
|
||||
// Note errors cannot be disabled at compile time, warnings can be disabled to a noop at compile time.
|
||||
|
||||
namespace serif::utils::errors {
|
||||
template <misc::concepts::IsException ExceptionT, SERiFErrorCode code>
|
||||
void serif_error(const std::string_view message, auto... args) {
|
||||
std::string error_msg;
|
||||
std::string code_name{SERiFErrorCodeTypeToNameMap.at(code)};
|
||||
|
||||
std::ranges::transform(code_name, code_name.begin(), [](const unsigned char c){ return std::toupper(c); });
|
||||
if (misc::terminal::supports_ansi(stderr)) {
|
||||
error_msg = std::format("{}{}{}: {}", misc::terminal::a_red, "Error", misc::terminal::a_reset, std::vformat(message, std::make_format_args(args...)));
|
||||
// format error code with color if terminal supports ANSI
|
||||
error_msg = std::format("{} (code No.: {} [{}{}{}])", error_msg, static_cast<std::underlying_type_t<SERiFErrorCode>>(code), misc::terminal::a_red, code_name, misc::terminal::a_reset);
|
||||
} else {
|
||||
error_msg = std::format("Error: {}", std::vformat(message, std::make_format_args(args...)));
|
||||
// Add error code without color if terminal does not support ANSI
|
||||
error_msg = std::format("{} (code No.: {} [{}])", error_msg, static_cast<std::underlying_type_t<SERiFErrorCode>>(code), code_name);
|
||||
}
|
||||
|
||||
throw ExceptionT(error_msg);
|
||||
}
|
||||
|
||||
template <SERiFErrorCode code>
|
||||
void serif_assert(const bool condition, const std::string_view message, auto... args) {
|
||||
// collapse to a noop if NDEBUG is defined
|
||||
#ifndef NDEBUG
|
||||
constexpr bool is_debug_build = true;
|
||||
#else
|
||||
constexpr bool is_debug_build = false;
|
||||
#endif
|
||||
if constexpr(is_debug_build) {
|
||||
if (!condition) {
|
||||
serif_error<std::logic_error, code>(message, args...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <auto Message, auto Code>
|
||||
struct serif_error_printer;
|
||||
|
||||
template <SERiFErrorCode code, types::ct_error_string message, bool condition>
|
||||
constexpr void serif_assert() {
|
||||
#ifndef NDEBUG
|
||||
constexpr bool is_debug_build = true;
|
||||
#else
|
||||
constexpr bool is_debug_build = false;
|
||||
#endif
|
||||
|
||||
if constexpr (is_debug_build) {
|
||||
if constexpr (!condition) {
|
||||
// Trigger an intentional error to display the message and code at compile time
|
||||
|
||||
// Note if we move to C++26 we can replace this with a more standard and less convoluted
|
||||
// formated static_assert.
|
||||
sizeof(serif_error_printer<message, code>);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/include/serif/utils/misc/concepts/concat.hpp
Normal file
25
src/include/serif/utils/misc/concepts/concat.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "serif/utils/types/type_list.hpp"
|
||||
|
||||
namespace serif::utils::misc::concepts {
|
||||
template <typename... Lists>
|
||||
struct Concat;
|
||||
|
||||
template <>
|
||||
struct Concat<> {
|
||||
using Type = types::TypeList<>;
|
||||
};
|
||||
|
||||
template <typename... Ts>
|
||||
struct Concat<types::TypeList<Ts...>> {
|
||||
using Type = types::TypeList<Ts...>;
|
||||
};
|
||||
|
||||
template <typename... LhsTs, typename... RhsTs, typename... Rest>
|
||||
struct Concat<types::TypeList<LhsTs...>, types::TypeList<RhsTs...>, Rest...> {
|
||||
using Type = typename Concat<types::TypeList<LhsTs..., RhsTs...>, Rest...>::Type;
|
||||
};
|
||||
|
||||
template <typename... Lists>
|
||||
using ConcatT = Concat<Lists...>::Type;
|
||||
}
|
||||
18
src/include/serif/utils/misc/concepts/enumeration.hpp
Normal file
18
src/include/serif/utils/misc/concepts/enumeration.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "serif/utils/misc/concepts/concat.hpp"
|
||||
#include "serif/utils/types/type_list.hpp"
|
||||
|
||||
namespace serif::utils::misc::concepts {
|
||||
// Might make sense to rename these to FlatMap or some such
|
||||
template <typename TypeList, template <typename> typename Descriptor>
|
||||
struct EnumerateTypes;
|
||||
|
||||
template <template <typename> typename Descriptor, typename... TypeTs>
|
||||
struct EnumerateTypes<types::TypeList<TypeTs...>, Descriptor> {
|
||||
using Type = ConcatT<Descriptor<TypeTs>...>;
|
||||
};
|
||||
|
||||
template <typename TypeList, template <typename> typename Descriptor>
|
||||
using EnumerateTypesT = EnumerateTypes<TypeList, Descriptor>::Type;
|
||||
}
|
||||
37
src/include/serif/utils/misc/concepts/filter.hpp
Normal file
37
src/include/serif/utils/misc/concepts/filter.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include <type_traits>
|
||||
#include "serif/utils/types/type_list.hpp"
|
||||
|
||||
namespace serif::utils::misc::concepts {
|
||||
template <typename T, typename List>
|
||||
struct Prepend;
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
struct Prepend<T, types::TypeList<Ts...>> {
|
||||
using Type = types::TypeList<T, Ts...>;
|
||||
};
|
||||
|
||||
template <typename T, typename List>
|
||||
using PrependT = Prepend<T, List>::Type;
|
||||
|
||||
|
||||
template <typename List, template <typename> typename Predicate>
|
||||
struct Filter;
|
||||
|
||||
template <template <typename> typename Predicate>
|
||||
struct Filter<types::TypeList<>, Predicate> {
|
||||
using Type = types::TypeList<>;
|
||||
};
|
||||
|
||||
template <typename Head, typename... Tail, template <typename> typename Predicate>
|
||||
struct Filter<types::TypeList<Head, Tail...>, Predicate> {
|
||||
private:
|
||||
using FilteredTail = Filter<types::TypeList<Tail...>, Predicate>::Type;
|
||||
|
||||
public:
|
||||
using Type = std::conditional_t<Predicate<Head>::value, PrependT<Head, FilteredTail>, FilteredTail>;
|
||||
};
|
||||
|
||||
template <typename List, template <typename> typename Predicate>
|
||||
using FilterT = Filter<List, Predicate>::Type;
|
||||
}
|
||||
@@ -5,39 +5,36 @@
|
||||
|
||||
#include "serif/utils/misc/concepts/numeric.hpp"
|
||||
#include "serif/utils/misc/concepts/exceptions.hpp"
|
||||
#include "serif/utils/error/errors.hpp"
|
||||
|
||||
namespace serif::utils::misc {
|
||||
template <typename ExceptionType, concepts::IsNumeric Scalar, concepts::IsEnum ErrorCode>
|
||||
requires concepts::IsExceptionWithErrorCode<ExceptionType, ErrorCode>
|
||||
void validate_finite(const Scalar value, ErrorCode error_code) {
|
||||
template <typename ExceptionType, errors::SERiFErrorCode ErrorCode, concepts::IsNumeric Scalar>
|
||||
void validate_finite(const Scalar value) {
|
||||
if (!std::isfinite(value)) {
|
||||
throw ExceptionType(error_code, std::format("The value must be finite. Instead a value of {} has been provided", value));
|
||||
errors::serif_error<ExceptionType, ErrorCode>("The value must be finite. Instead a value of {} has been provided", value);
|
||||
};
|
||||
}
|
||||
|
||||
template <typename ExceptionType, concepts::IsComparableToNumeric Scalar, concepts::IsEnum ErrorCode>
|
||||
requires concepts::IsExceptionWithErrorCode<ExceptionType, ErrorCode>
|
||||
void validate_greater_than(const Scalar value, const Scalar predicate, ErrorCode error_code) {
|
||||
validate_finite<ExceptionType>(value, error_code);
|
||||
template <typename ExceptionType, errors::SERiFErrorCode ErrorCode, concepts::IsComparableToNumeric Scalar>
|
||||
void validate_greater_than(const Scalar value, const Scalar predicate) {
|
||||
validate_finite<ExceptionType, ErrorCode>(value);
|
||||
if (value < predicate) {
|
||||
throw ExceptionType(error_code, std::format("The value must be finite and greater than {}. While the value is finite it is also less than {}. A value of {} has been provided", predicate, predicate, value));
|
||||
errors::serif_error<ExceptionType, ErrorCode>("The value must be finite and greater than {}. While the value is finite it is also less than {}. A value of {} has been provided", predicate, predicate, value);
|
||||
};
|
||||
}
|
||||
|
||||
template <typename ExceptionType, concepts::IsComparableToNumeric Scalar, concepts::IsEnum ErrorCode>
|
||||
requires concepts::IsExceptionWithErrorCode<ExceptionType, ErrorCode>
|
||||
void validate_less_than(const Scalar value, const Scalar predicate, ErrorCode error_code) {
|
||||
validate_finite<ExceptionType>(value, error_code);
|
||||
template <typename ExceptionType, errors::SERiFErrorCode ErrorCode, concepts::IsComparableToNumeric Scalar>
|
||||
void validate_less_than(const Scalar value, const Scalar predicate) {
|
||||
validate_finite<ExceptionType, ErrorCode>(value);
|
||||
if (value > predicate) {
|
||||
throw ExceptionType(error_code, std::format("The value must be finite and less than {}. While the value is finite it is also greater than {}. A value of {} has been provided", predicate, predicate, value));
|
||||
errors::serif_error<ExceptionType, ErrorCode>("The value must be finite and less than {}. While the value is finite it is also greater than {}. A value of {} has been provided", predicate, predicate, value);
|
||||
};
|
||||
}
|
||||
|
||||
template <typename ExceptionType, concepts::IsComparableToNumeric Scalar, concepts::IsEnum ErrorCode>
|
||||
requires concepts::IsExceptionWithErrorCode<ExceptionType, ErrorCode>
|
||||
void validate_nonnegativity(const Scalar value, ErrorCode error_code) {
|
||||
validate_finite<ExceptionType>(value, error_code);
|
||||
validate_greater_than<ExceptionType>(value,static_cast<Scalar>(0), error_code);
|
||||
template <typename ExceptionType, errors::SERiFErrorCode ErrorCode, concepts::IsComparableToNumeric Scalar>
|
||||
void validate_nonnegativity(const Scalar value) {
|
||||
validate_finite<ExceptionType, ErrorCode>(value);
|
||||
validate_greater_than<ExceptionType, ErrorCode>(value,static_cast<Scalar>(0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
74
src/include/serif/utils/misc/terminal/colors.hpp
Normal file
74
src/include/serif/utils/misc/terminal/colors.hpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace serif::utils::misc::terminal {
|
||||
enum class ANSIColors : int {
|
||||
BLACK = 30,
|
||||
RED = 31,
|
||||
GREEN = 32,
|
||||
YELLOW = 33,
|
||||
BLUE = 34,
|
||||
MAGENTA = 35,
|
||||
CYAN = 36,
|
||||
WHITE = 37,
|
||||
BRIGHT_BLACK = 90,
|
||||
BRIGHT_RED = 91,
|
||||
BRIGHT_GREEN = 92,
|
||||
RESET = 0
|
||||
};
|
||||
|
||||
consteval std::string_view resolve_ansi_color(const ANSIColors color) noexcept {
|
||||
switch (color) {
|
||||
case ANSIColors::BLACK: return "\033[30m";
|
||||
case ANSIColors::RED: return "\033[31m";
|
||||
case ANSIColors::GREEN: return "\033[32m";
|
||||
case ANSIColors::YELLOW: return "\033[33m";
|
||||
case ANSIColors::BLUE: return "\033[34m";
|
||||
case ANSIColors::MAGENTA: return "\033[35m";
|
||||
case ANSIColors::CYAN: return "\033[36m";
|
||||
case ANSIColors::WHITE: return "\033[37m";
|
||||
case ANSIColors::BRIGHT_BLACK: return "\033[90m";
|
||||
case ANSIColors::BRIGHT_RED: return "\033[91m";
|
||||
case ANSIColors::BRIGHT_GREEN: return "\033[92m";
|
||||
case ANSIColors::RESET: return "\033[0m";
|
||||
}
|
||||
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
constexpr std::string_view a_black = resolve_ansi_color(ANSIColors::BLACK);
|
||||
constexpr std::string_view a_red = resolve_ansi_color(ANSIColors::RED);
|
||||
constexpr std::string_view a_green = resolve_ansi_color(ANSIColors::GREEN);
|
||||
constexpr std::string_view a_yellow = resolve_ansi_color(ANSIColors::YELLOW);
|
||||
constexpr std::string_view a_blue = resolve_ansi_color(ANSIColors::BLUE);
|
||||
constexpr std::string_view a_magenta = resolve_ansi_color(ANSIColors::MAGENTA);
|
||||
constexpr std::string_view a_cyan = resolve_ansi_color(ANSIColors::CYAN);
|
||||
constexpr std::string_view a_white = resolve_ansi_color(ANSIColors::WHITE);
|
||||
constexpr std::string_view a_bright_black = resolve_ansi_color(ANSIColors::BRIGHT_BLACK);
|
||||
constexpr std::string_view a_bright_red = resolve_ansi_color(ANSIColors::BRIGHT_RED);
|
||||
constexpr std::string_view a_bright_green = resolve_ansi_color(ANSIColors::BRIGHT_GREEN);
|
||||
constexpr std::string_view a_reset = resolve_ansi_color(ANSIColors::RESET);
|
||||
|
||||
inline bool supports_ansi(std::FILE* stream = stdout) noexcept {
|
||||
if (const char* noColor = std::getenv("NO_COLOR");
|
||||
noColor != nullptr && *noColor != '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (::isatty(::fileno(stream)) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* term = std::getenv("TERM");
|
||||
|
||||
if (term == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::string_view(term) != "dumb";
|
||||
}
|
||||
}
|
||||
31
src/include/serif/utils/types/strings.hpp
Normal file
31
src/include/serif/utils/types/strings.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <string_view>
|
||||
|
||||
namespace serif::utils::types {
|
||||
template<std::size_t N>
|
||||
struct ctstring {
|
||||
char data[N]{};
|
||||
|
||||
constexpr ctstring(const char (&str)[N]) {
|
||||
std::copy_n(str, N, data);
|
||||
}
|
||||
|
||||
constexpr std::string_view view() const { return {data, N - 1}; }
|
||||
};
|
||||
|
||||
template<std::size_t N>
|
||||
struct ct_error_string {
|
||||
char data[289+N]{};
|
||||
char padding[289]{"NOTE: This error message is generated at compile time. C++ < 26 limits how compile time errors can be formatted, This is the relevant error message not downstream warnings about implicit instantiations of an undefined template. Those are used to force the compiler to display this message"};
|
||||
|
||||
constexpr ct_error_string(const char (&str)[N]) {
|
||||
std::copy_n(str, N, data);
|
||||
std::copy_n(padding, 289, data+N);
|
||||
}
|
||||
|
||||
constexpr std::string_view view() const { return {data, N - 1}; }
|
||||
};
|
||||
}
|
||||
6
src/include/serif/utils/types/type_list.hpp
Normal file
6
src/include/serif/utils/types/type_list.hpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace serif::utils::types {
|
||||
template <typename...Ts>
|
||||
struct TypeList {};
|
||||
}
|
||||
24
src/include/serif/utils/warnings/warning.hpp
Normal file
24
src/include/serif/utils/warnings/warning.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
// TODO: Sort out how to get this from a meson option and ideally not use any macros or at least resolve them to type safe compile time values
|
||||
#define DISABLE_WARNING 0
|
||||
|
||||
#include <string_view>
|
||||
#include <format>
|
||||
#include <print>
|
||||
#include <cstdio>
|
||||
#include "serif/utils/misc/terminal/colors.hpp"
|
||||
|
||||
// We may want to do a similar thing to what we did with errors and introduce a SERiFWarningCode enum and a serif_warning function that takes a warning code and a message. This would allow us to have more structured warnings and potentially allow users to filter or handle warnings based on their codes. However, for now, we will keep it simple with just a message.
|
||||
|
||||
namespace serif::utils::warnings {
|
||||
void serif_warning(const std::string_view message, auto... args) {
|
||||
if constexpr (DISABLE_WARNING == 0) { // This is a constexpr so that the compiler can optimize out the warnings to a no-op
|
||||
if (misc::terminal::supports_ansi(stderr)) {
|
||||
std::println(stderr, "{}{}{}: {}", misc::terminal::a_yellow, "Warning", misc::terminal::a_reset, std::vformat(message, std::make_format_args(args...)));
|
||||
} else {
|
||||
std::println(stderr, "Warning: {}", std::vformat(message, std::make_format_args(args...)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user