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:
40
EmilysRulesForSERiFCode.md
Normal file
40
EmilysRulesForSERiFCode.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Emily's Rules for writing good SERiF code
|
||||
If you will bear with me as I indulge in some prose. Astronomers are, as a rule, bad at programming.
|
||||
We are a discipline focused on the abstract, though not that abstraction that is code. There are many
|
||||
effects this has had on astrophysical code, though I would argue that the primary effect is
|
||||
a paucity of good code. Do not misunderstand me reader, I do think there is good astrophysical
|
||||
code that exists; it is only that I think that more good code should exist, that more good code could exist.
|
||||
|
||||
There is perhaps an inescapable degree of egotism inherent in acting as the initial and lead developer
|
||||
for a code such as this. I have, by necessity, had to impose my own views on what makes a software product
|
||||
pleasant to develop for and pleasant to use. These are likely wrong, in so far as any views can be wrong.
|
||||
I think it likely that no one is 'correct' in these things. Rather, what is important is that there be some
|
||||
views. Yes you could, I'm sure, poke holes in any one of the points below. However, these points
|
||||
have been more or less agreed upon for this code base and therefor should be followed, for consistency if not
|
||||
for correctness.
|
||||
|
||||
- Always remember the users of the code are astronomers not developers. All user facing code *must* be understandable by a senior undergraduate physics major.
|
||||
- We are writing physics, user facing code should always prefer to describe physical intent.
|
||||
- Other concerns (e.g. memory, numerics, IO, etc...) may be accessible through options; however, those options names should make it clear to users that they are straying into dangerous water
|
||||
- The hierarchy of abstraction is physics > numerics > IO > memory. A user should need to put more effort into adjusting numerics than physics, and more effort into adjusting IO and memory than numerics.
|
||||
- Here we write a library not an application. Do not concern yourself with such petty things as an entry point, command line arguments, etc... Rather, we provide tools for others to build applications with.
|
||||
- The tools we provide should allow a user to construct a stellar model in less than 20 lines of code.
|
||||
- Those same tools should also provide options which allow an advanced user to take near full control over their numerics and physics.
|
||||
- Code should be self documenting, vowels don't bite. Leave them in your names.
|
||||
- Developers are also astronomers, code should therefore not try to be too clever with syntax tricks.
|
||||
- At the same time languages are advanced and complex, do not prevent yourself from writing code just because it is may be hard to understand. If you think that your way is best then it likely is. Just make sure you explain why you are doing what you are doing in a comment.
|
||||
- Write comments that explain why not what
|
||||
- All functions must have a docstring.
|
||||
- Prefer compile time verification over runtime verification.
|
||||
- Compile time invariants should be exercised with static_asserts in the compile_time_checks static library. A failing invariant should prevent the code from compiling
|
||||
- Use the minimum header set you can
|
||||
- Do not use exceptions for normal control flow.
|
||||
- The library can throw exceptions; however, generally only the user should catch them.
|
||||
- Within the library error states should be reported as a value (std::optional, std::expected, etc...)
|
||||
- When an exception is thrown make it detailed. It should include what went wrong, where it went wrong, why it is wrong, if applicable what was received instead, and what the user can do to fix it.
|
||||
- Prefer more and smaller files over fewer and larger files.
|
||||
- Prefer more and smaller classes over fewer and larger classes.
|
||||
- Prefer more and smaller functions over fewer and larger functions.
|
||||
- Do not include the full mfem.hpp header. Its slow, I will send you an angry email.
|
||||
- AI is a tool, its okay to use it, but use it as a tool not as a crutch. You must understand all the code you write.
|
||||
- The less something is related to physics the more you may consider AI. A build system configuration for example is a good candidate for AI. A physics function is not.
|
||||
21
build-config/magicenum/LICENSE
Normal file
21
build-config/magicenum/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 - 2026 Daniil Goncharov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
375
build-config/magicenum/README.md
Normal file
375
build-config/magicenum/README.md
Normal file
@@ -0,0 +1,375 @@
|
||||
[](https://github.com/Neargye/magic_enum/releases)
|
||||
[](https://conan.io/center/recipes/magic_enum)
|
||||
[](https://github.com/microsoft/vcpkg/tree/master/ports/magic-enum)
|
||||
[](https://www.cppget.org/magic_enum?q=magic_enum)
|
||||
[](https://github.com/mesonbuild/wrapdb/blob/master/subprojects/magic_enum.wrap)
|
||||
[](LICENSE)
|
||||
[](https://godbolt.org/z/feqcPa5G6)
|
||||
[](https://securityscorecards.dev/viewer/?uri=github.com/Neargye/magic_enum)
|
||||
|
||||
# Magic Enum C++
|
||||
|
||||
Header-only C++17 library provides static reflection for enums, work with any enum type without any macro or boilerplate code.
|
||||
|
||||
## Documentation
|
||||
|
||||
* [Reference](doc/reference.md)
|
||||
* [Limitations](doc/limitations.md)
|
||||
* [Integration](#integration)
|
||||
|
||||
## [Features & Examples](example/)
|
||||
|
||||
* Basic
|
||||
|
||||
```cpp
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
#include <iostream>
|
||||
|
||||
enum class Color { RED = -10, BLUE = 0, GREEN = 10 };
|
||||
|
||||
int main() {
|
||||
Color c1 = Color::RED;
|
||||
std::cout << magic_enum::enum_name(c1) << std::endl; // RED
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
* Enum value to string
|
||||
|
||||
```cpp
|
||||
Color color = Color::RED;
|
||||
auto color_name = magic_enum::enum_name(color);
|
||||
// color_name -> "RED"
|
||||
```
|
||||
|
||||
* String to enum value
|
||||
|
||||
```cpp
|
||||
std::string color_name{"GREEN"};
|
||||
auto color = magic_enum::enum_cast<Color>(color_name);
|
||||
if (color.has_value()) {
|
||||
// color.value() -> Color::GREEN
|
||||
}
|
||||
|
||||
// case insensitive enum_cast
|
||||
auto color_case_insensitive = magic_enum::enum_cast<Color>(color_name, magic_enum::case_insensitive);
|
||||
|
||||
// enum_cast with BinaryPredicate
|
||||
auto color_with_predicate = magic_enum::enum_cast<Color>(color_name, [](char lhs, char rhs) { return std::tolower(static_cast<unsigned char>(lhs)) == std::tolower(static_cast<unsigned char>(rhs)); });
|
||||
|
||||
// enum_cast with default
|
||||
auto color_or_default = magic_enum::enum_cast<Color>(color_name).value_or(Color::RED);
|
||||
```
|
||||
|
||||
* Integer to enum value
|
||||
|
||||
```cpp
|
||||
int color_integer = 0;
|
||||
auto color = magic_enum::enum_cast<Color>(color_integer);
|
||||
if (color.has_value()) {
|
||||
// color.value() -> Color::BLUE
|
||||
}
|
||||
|
||||
auto color_or_default = magic_enum::enum_cast<Color>(123).value_or(Color::RED);
|
||||
```
|
||||
|
||||
* Indexed access to enum value
|
||||
|
||||
```cpp
|
||||
std::size_t i = 0;
|
||||
Color color = magic_enum::enum_value<Color>(i);
|
||||
// color -> Color::RED
|
||||
```
|
||||
|
||||
* Enum value sequence
|
||||
|
||||
```cpp
|
||||
constexpr auto colors = magic_enum::enum_values<Color>();
|
||||
// colors -> {Color::RED, Color::BLUE, Color::GREEN}
|
||||
// colors[0] -> Color::RED
|
||||
```
|
||||
|
||||
* Number of enum values
|
||||
|
||||
```cpp
|
||||
constexpr std::size_t color_count = magic_enum::enum_count<Color>();
|
||||
// color_count -> 3
|
||||
```
|
||||
|
||||
* Enum value to integer
|
||||
|
||||
```cpp
|
||||
Color color = Color::RED;
|
||||
auto color_integer = magic_enum::enum_integer(color); // or magic_enum::enum_underlying(color);
|
||||
// color_integer -> -10
|
||||
```
|
||||
|
||||
* Enum name sequence
|
||||
|
||||
```cpp
|
||||
constexpr auto color_names = magic_enum::enum_names<Color>();
|
||||
// color_names -> {"RED", "BLUE", "GREEN"}
|
||||
// color_names[0] -> "RED"
|
||||
```
|
||||
|
||||
* Enum entry sequence
|
||||
|
||||
```cpp
|
||||
constexpr auto color_entries = magic_enum::enum_entries<Color>();
|
||||
// color_entries -> {{Color::RED, "RED"}, {Color::BLUE, "BLUE"}, {Color::GREEN, "GREEN"}}
|
||||
// color_entries[0].first -> Color::RED
|
||||
// color_entries[0].second -> "RED"
|
||||
```
|
||||
|
||||
* Enum fusion for multi-level switch/case statements
|
||||
|
||||
```cpp
|
||||
switch (magic_enum::enum_fuse(color, direction).value()) {
|
||||
case magic_enum::enum_fuse(Color::RED, Directions::Up).value(): // ...
|
||||
case magic_enum::enum_fuse(Color::BLUE, Directions::Down).value(): // ...
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
* Runtime enum value as constexpr constant
|
||||
|
||||
```cpp
|
||||
Color color = Color::RED;
|
||||
magic_enum::enum_switch([](auto val) {
|
||||
constexpr Color c_color = val;
|
||||
// ...
|
||||
}, color);
|
||||
```
|
||||
|
||||
* Iterate over enum values as constexpr constants
|
||||
|
||||
```cpp
|
||||
magic_enum::enum_for_each<Color>([](auto val) {
|
||||
constexpr Color c_color = val;
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
* Move through enum values
|
||||
|
||||
```cpp
|
||||
magic_enum::enum_next_value(Color::RED); // -> optional containing Color::BLUE
|
||||
magic_enum::enum_prev_value_circular(Color::RED); // -> Color::GREEN
|
||||
```
|
||||
|
||||
* Check whether enum contains value
|
||||
|
||||
```cpp
|
||||
magic_enum::enum_contains(Color::GREEN); // -> true
|
||||
magic_enum::enum_contains<Color>(0); // -> true
|
||||
magic_enum::enum_contains<Color>(123); // -> false
|
||||
magic_enum::enum_contains<Color>("GREEN"); // -> true
|
||||
magic_enum::enum_contains<Color>("fda"); // -> false
|
||||
```
|
||||
|
||||
* Check whether value can be reflected
|
||||
|
||||
```cpp
|
||||
magic_enum::enum_reflected(Color::GREEN); // -> true
|
||||
```
|
||||
|
||||
* Enum index in sequence
|
||||
|
||||
```cpp
|
||||
constexpr auto color_index = magic_enum::enum_index(Color::BLUE);
|
||||
// color_index.value() -> 1
|
||||
// color_index.has_value() -> true
|
||||
```
|
||||
|
||||
* Flag operations
|
||||
|
||||
```cpp
|
||||
enum Directions : std::uint64_t {
|
||||
Left = 1,
|
||||
Down = 2,
|
||||
Up = 4,
|
||||
Right = 8,
|
||||
};
|
||||
template <>
|
||||
struct magic_enum::customize::enum_range<Directions> {
|
||||
static constexpr bool is_flags = true;
|
||||
};
|
||||
using namespace magic_enum::bitwise_operators; // Use with care; operators are enabled for all enums.
|
||||
|
||||
magic_enum::enum_flags_name(Directions::Up | Directions::Right); // -> "Up|Right"
|
||||
magic_enum::enum_flags_name(Directions::Up | Directions::Right, ','); // -> "Up,Right"
|
||||
magic_enum::enum_flags_contains(Directions::Up | Directions::Right); // -> true
|
||||
magic_enum::enum_flags_cast<Directions>(3).value(); // -> Directions::Left|Directions::Down
|
||||
magic_enum::enum_flags_cast<Directions>("Left,Down", ',').value(); // -> Directions::Left|Directions::Down
|
||||
magic_enum::enum_flags_test(Directions::Up | Directions::Right, Directions::Up); // -> true
|
||||
magic_enum::enum_flags_test_any(Directions::Left | Directions::Down, Directions::Down | Directions::Right); // -> true
|
||||
magic_enum::is_flags_v<Directions>; // -> true
|
||||
```
|
||||
|
||||
* Enum type name
|
||||
|
||||
```cpp
|
||||
Color color = Color::RED;
|
||||
auto type_name = magic_enum::enum_type_name<decltype(color)>();
|
||||
// type_name -> "Color"
|
||||
```
|
||||
|
||||
* I/O stream operators for enums
|
||||
|
||||
```cpp
|
||||
using magic_enum::iostream_operators::operator<<; // out-of-the-box ostream operators for enums.
|
||||
Color color = Color::BLUE;
|
||||
std::cout << color << std::endl; // "BLUE"
|
||||
```
|
||||
|
||||
```cpp
|
||||
using magic_enum::iostream_operators::operator>>; // out-of-the-box istream operators for enums.
|
||||
Color color;
|
||||
std::cin >> color;
|
||||
```
|
||||
|
||||
* Bitwise operators for enums
|
||||
|
||||
```cpp
|
||||
enum class Flags { A = 1 << 0, B = 1 << 1, C = 1 << 2, D = 1 << 3 };
|
||||
using namespace magic_enum::bitwise_operators; // Use with care; operators are enabled for all enums.
|
||||
// Support operators: ~, |, &, ^, |=, &=, ^=.
|
||||
Flags flags = Flags::A | (Flags::B & ~Flags::C);
|
||||
```
|
||||
|
||||
* Formatting
|
||||
|
||||
```cpp
|
||||
#include <format>
|
||||
#include <magic_enum/magic_enum_format.hpp>
|
||||
|
||||
std::format("{}", Color::RED); // -> "RED"
|
||||
std::format("{}", Color{42}); // -> "42"
|
||||
```
|
||||
|
||||
Include `{fmt}` before `magic_enum_format.hpp` to enable `{fmt}` formatter support.
|
||||
|
||||
* [Unscoped enum](https://en.cppreference.com/w/cpp/language/enum#Unscoped_enumeration) trait
|
||||
|
||||
```cpp
|
||||
enum color { red, green, blue };
|
||||
enum class direction { left, right };
|
||||
|
||||
magic_enum::is_unscoped_enum_v<color> -> true
|
||||
magic_enum::is_unscoped_enum_v<direction> -> false
|
||||
```
|
||||
|
||||
* [Scoped enum](https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations) trait
|
||||
|
||||
```cpp
|
||||
enum color { red, green, blue };
|
||||
enum class direction { left, right };
|
||||
|
||||
magic_enum::is_scoped_enum_v<color> -> false
|
||||
magic_enum::is_scoped_enum_v<direction> -> true
|
||||
```
|
||||
|
||||
* Compile-time enum value to string. This overload compiles faster and is not restricted by `enum_range` [limitation](doc/limitations.md).
|
||||
|
||||
```cpp
|
||||
constexpr Color color = Color::BLUE;
|
||||
constexpr auto color_name = magic_enum::enum_name<color>();
|
||||
// color_name -> "BLUE"
|
||||
```
|
||||
|
||||
* `containers::array` array container for enums.
|
||||
|
||||
```cpp
|
||||
constexpr auto color_rgb_values = magic_enum::containers::make_array<Color>(RGB{255, 0, 0}, RGB{0, 255, 0}, RGB{0, 0, 255});
|
||||
|
||||
magic_enum::containers::array<Color, RGB> color_rgb_array {};
|
||||
color_rgb_array[Color::RED] = {255, 0, 0};
|
||||
color_rgb_array[Color::GREEN] = {0, 255, 0};
|
||||
color_rgb_array[Color::BLUE] = {0, 0, 255};
|
||||
magic_enum::containers::get<Color::BLUE>(color_rgb_array); // -> RGB{0, 0, 255}
|
||||
```
|
||||
|
||||
* `containers::bitset` bitset container for enums.
|
||||
|
||||
```cpp
|
||||
constexpr magic_enum::containers::bitset<Color> color_bitset {Color::RED, Color::GREEN};
|
||||
color_bitset.test(Color::RED); // -> true
|
||||
color_bitset.test(Color::BLUE); // -> false
|
||||
|
||||
std::uint8_t incoming = 0b00000011;
|
||||
auto raw_bitset = magic_enum::containers::bitset<Color> {magic_enum::containers::raw_access, incoming};
|
||||
```
|
||||
|
||||
* `containers::set` set container for enums.
|
||||
|
||||
```cpp
|
||||
auto color_set = magic_enum::containers::set<Color>();
|
||||
bool empty = color_set.empty();
|
||||
// empty -> true
|
||||
color_set.insert(Color::GREEN);
|
||||
color_set.insert(Color::BLUE);
|
||||
color_set.insert(Color::RED);
|
||||
std::size_t size = color_set.size();
|
||||
// size -> 3
|
||||
|
||||
using color_name_set = magic_enum::containers::set<Color, magic_enum::containers::name_less<>>;
|
||||
color_name_set colors_by_name {Color::RED, Color::GREEN, Color::BLUE};
|
||||
```
|
||||
|
||||
* [Underlying type](https://en.cppreference.com/w/cpp/types/underlying_type)
|
||||
|
||||
```cpp
|
||||
magic_enum::underlying_type<Color>::type -> int
|
||||
magic_enum::underlying_type_t<Color> -> int
|
||||
```
|
||||
|
||||
## Integration
|
||||
|
||||
* Copy required headers from [`include/magic_enum`](include/magic_enum) or use [release archive](https://github.com/Neargye/magic_enum/releases/latest). `magic_enum_all.hpp` includes all public headers.
|
||||
* Use CMake with `add_subdirectory` or `find_package(magic_enum CONFIG REQUIRED)`, then link `magic_enum::magic_enum`.
|
||||
* Use [vcpkg](https://github.com/microsoft/vcpkg/tree/master/ports/magic-enum), [Conan](https://conan.io/center/recipes/magic_enum), [Build2](https://cppget.org/magic_enum?q=magic_enum), or [Meson](https://github.com/mesonbuild/wrapdb/blob/master/subprojects/magic_enum.wrap).
|
||||
* Fetch sources with CMake [`FetchContent`](https://cmake.org/cmake/help/latest/module/FetchContent.html) or [CPM.cmake](https://github.com/cpm-cmake/CPM.cmake). Release tags use `vx.y.z` format.
|
||||
* Use Bazel with `MODULE.bazel` or `http_archive`; target is `@magic_enum//:magic_enum`.
|
||||
* Use ROS with `<depend>magic_enum</depend>` in `package.xml`, then link `magic_enum::magic_enum`.
|
||||
|
||||
* **CMake targets**:
|
||||
- `magic_enum::magic_enum` is the header-only target.
|
||||
- `magic_enum::magic_enum_module` is the C++20 module target. Enable it with `MAGIC_ENUM_USE_MODULES=ON`. CMake 3.28+ is required.
|
||||
|
||||
Build the module target:
|
||||
```sh
|
||||
cmake -S . -B build -G Ninja -DMAGIC_ENUM_USE_MODULES=ON
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
Link the module target:
|
||||
```cmake
|
||||
find_package(magic_enum CONFIG REQUIRED)
|
||||
target_link_libraries(your_executable PRIVATE magic_enum::magic_enum_module)
|
||||
set_target_properties(your_executable PROPERTIES CXX_EXTENSIONS OFF CXX_SCAN_FOR_MODULES ON)
|
||||
```
|
||||
|
||||
Import the module:
|
||||
```cpp
|
||||
import magic_enum;
|
||||
|
||||
enum class Color { RED, GREEN, BLUE };
|
||||
auto name = magic_enum::enum_name(Color::RED); // "RED"
|
||||
```
|
||||
|
||||
Do not use `#include <magic_enum/...>` and `import magic_enum;` in the same program. Use the same compiler, standard library, and C++ standard when building and consuming an installed module. The pkg-config package supports only the header-only target.
|
||||
|
||||
Optional settings:
|
||||
- Set `MAGIC_ENUM_MODULE_WITH_FMT=ON` to enable `{fmt}` support through `fmt::fmt`. It is disabled by default. The `{fmt}` C++ module is not supported.
|
||||
- Set `MAGIC_ENUM_MODULE_IMPORT_STD=ON` to enable `import std` support. This requires a compatible CMake toolchain.
|
||||
|
||||
## Header-only compiler compatibility
|
||||
|
||||
* Clang/LLVM >= 5
|
||||
* MSVC++ >= 15.3 / Visual Studio >= 2017
|
||||
* Xcode >= 10
|
||||
* GCC >= 9
|
||||
|
||||
C++26 reflection is selected automatically when available; see [limitations](doc/limitations.md#c26-standard-reflection).
|
||||
|
||||
## Licensed under the [MIT License](LICENSE)
|
||||
13
build-config/magicenum/SECURITY.md
Normal file
13
build-config/magicenum/SECURITY.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Security updates are applied only to the latest release.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
|
||||
|
||||
Please disclose it at [security advisory](https://github.com/Neargye/magic_enum/security/advisories/new).
|
||||
|
||||
This project is maintained by a team of volunteers on a reasonable-effort basis. As such, vulnerabilities will be disclosed in a best effort base.
|
||||
2066
build-config/magicenum/include/magic_enum/magic_enum.hpp
Normal file
2066
build-config/magicenum/include/magic_enum/magic_enum.hpp
Normal file
File diff suppressed because it is too large
Load Diff
44
build-config/magicenum/include/magic_enum/magic_enum_all.hpp
Normal file
44
build-config/magicenum/include/magic_enum/magic_enum_all.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// __ __ _ ______ _____
|
||||
// | \/ | (_) | ____| / ____|_ _
|
||||
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||
// __/ | https://github.com/Neargye/magic_enum
|
||||
// |___/ version 0.9.8
|
||||
//
|
||||
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2019 - 2026 Daniil Goncharov <neargye@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef NEARGYE_MAGIC_ENUM_ALL_HPP
|
||||
#define NEARGYE_MAGIC_ENUM_ALL_HPP
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
#include "magic_enum_containers.hpp"
|
||||
#include "magic_enum_flags.hpp"
|
||||
#include "magic_enum_format.hpp"
|
||||
#include "magic_enum_fuse.hpp"
|
||||
#include "magic_enum_iostream.hpp"
|
||||
#include "magic_enum_switch.hpp"
|
||||
#include "magic_enum_utility.hpp"
|
||||
|
||||
#endif // NEARGYE_MAGIC_ENUM_ALL_HPP
|
||||
1457
build-config/magicenum/include/magic_enum/magic_enum_containers.hpp
Normal file
1457
build-config/magicenum/include/magic_enum/magic_enum_containers.hpp
Normal file
File diff suppressed because it is too large
Load Diff
197
build-config/magicenum/include/magic_enum/magic_enum_flags.hpp
Normal file
197
build-config/magicenum/include/magic_enum/magic_enum_flags.hpp
Normal file
@@ -0,0 +1,197 @@
|
||||
// __ __ _ ______ _____
|
||||
// | \/ | (_) | ____| / ____|_ _
|
||||
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||
// __/ | https://github.com/Neargye/magic_enum
|
||||
// |___/ version 0.9.8
|
||||
//
|
||||
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2019 - 2026 Daniil Goncharov <neargye@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef NEARGYE_MAGIC_ENUM_FLAGS_HPP
|
||||
#define NEARGYE_MAGIC_ENUM_FLAGS_HPP
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
namespace magic_enum {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename E>
|
||||
constexpr auto values_ors() noexcept {
|
||||
using U = make_unsigned_t<std::underlying_type_t<E>>;
|
||||
auto ors = U{0};
|
||||
for (const auto value : values_v<E, enum_subtype::flags>) {
|
||||
ors |= static_cast<U>(value);
|
||||
}
|
||||
|
||||
return ors;
|
||||
}
|
||||
|
||||
} // namespace magic_enum::detail
|
||||
|
||||
// Returns name from flag enum value.
|
||||
// If flag enum value has no name or is out of range, returns empty string.
|
||||
template <typename E>
|
||||
[[nodiscard]] auto enum_flags_name(E value, char_type sep = char_type{'|'}) -> detail::enable_if_t<E, string> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = detail::make_unsigned_t<underlying_type_t<D>>;
|
||||
constexpr auto S = detail::enum_subtype::flags;
|
||||
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||
|
||||
const auto flag_value = static_cast<U>(value);
|
||||
string name;
|
||||
auto check_value = U{0};
|
||||
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
|
||||
if (const auto v = static_cast<U>(detail::values_v<D, S>[i]); (flag_value & v) != U{0}) {
|
||||
if (const auto n = detail::names_v<D, S>[i]; !n.empty()) {
|
||||
check_value |= v;
|
||||
if (!name.empty()) {
|
||||
name.append(1, sep);
|
||||
}
|
||||
name.append(n.data(), n.size());
|
||||
} else {
|
||||
return {}; // Value out of range.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (check_value != U{0} && check_value == flag_value) {
|
||||
return name;
|
||||
}
|
||||
return {}; // Invalid value or out of range.
|
||||
}
|
||||
|
||||
// Returns flag enum value from integer value.
|
||||
// Returns optional containing flag enum value.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_flags_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
using V = detail::make_unsigned_t<U>;
|
||||
constexpr auto S = detail::enum_subtype::flags;
|
||||
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||
|
||||
if constexpr (detail::count_v<D, S> == 0) {
|
||||
static_cast<void>(value);
|
||||
return {}; // Empty enum.
|
||||
} else {
|
||||
const auto flag_value = static_cast<V>(value);
|
||||
constexpr auto mask = detail::values_ors<D>();
|
||||
if (flag_value != V{0} && (flag_value & static_cast<V>(~mask)) == V{0}) {
|
||||
return static_cast<D>(value);
|
||||
}
|
||||
return {}; // Invalid value or out of range.
|
||||
}
|
||||
}
|
||||
|
||||
// Returns flag enum value from name.
|
||||
// Returns optional containing flag enum value.
|
||||
template <typename E, typename BinaryPredicate = std::equal_to<>>
|
||||
[[nodiscard]] constexpr auto enum_flags_cast(string_view value, [[maybe_unused]] char_type sep = char_type{'|'}, [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable_v<BinaryPredicate>) -> detail::enable_if_t<E, optional<std::decay_t<E>>, BinaryPredicate> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = detail::make_unsigned_t<underlying_type_t<D>>;
|
||||
constexpr auto S = detail::enum_subtype::flags;
|
||||
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||
|
||||
if constexpr (detail::count_v<D, S> == 0) {
|
||||
static_cast<void>(value);
|
||||
return {}; // Empty enum.
|
||||
} else {
|
||||
auto result = U{0};
|
||||
// Avoid GCC C++26 wrong-code with find/remove_prefix; see https://github.com/Neargye/magic_enum/issues/467.
|
||||
for (std::size_t first = 0; first < value.size();) {
|
||||
auto last = first;
|
||||
while (last < value.size() && value[last] != sep) {
|
||||
++last;
|
||||
}
|
||||
const auto s = value.substr(first, last - first);
|
||||
auto flag = U{0};
|
||||
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
|
||||
if (detail::cmp_equal(s, detail::names_v<D, S>[i], p)) {
|
||||
flag = static_cast<U>(detail::values_v<D, S>[i]);
|
||||
result |= flag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flag == U{0}) {
|
||||
return {}; // Invalid value or out of range.
|
||||
}
|
||||
first = (last < value.size()) ? last + 1 : last;
|
||||
}
|
||||
|
||||
if (result != U{0}) {
|
||||
return static_cast<D>(result);
|
||||
}
|
||||
return {}; // Invalid value or out of range.
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if flag enum contains specified value.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_flags_contains(E value) noexcept -> detail::enable_if_t<E, bool> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
|
||||
return static_cast<bool>(enum_flags_cast<D>(static_cast<U>(value)));
|
||||
}
|
||||
|
||||
// Returns true if flag enum contains specified integer value.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_flags_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, bool> {
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
return static_cast<bool>(enum_flags_cast<D>(value));
|
||||
}
|
||||
|
||||
// Returns true if flag enum contains enumerator with specified name.
|
||||
template <typename E, typename BinaryPredicate = std::equal_to<>>
|
||||
[[nodiscard]] constexpr auto enum_flags_contains(string_view value, char_type sep = char_type{'|'}, BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable_v<BinaryPredicate>) -> detail::enable_if_t<E, bool, BinaryPredicate> {
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
return static_cast<bool>(enum_flags_cast<D, BinaryPredicate&>(value, sep, p));
|
||||
}
|
||||
|
||||
// Returns true if `flags` contains `flag`.
|
||||
// Returns false if `flag` equals 0 because 0 is not a flag.
|
||||
template <typename E>
|
||||
constexpr auto enum_flags_test(E flags, E flag) noexcept -> detail::enable_if_t<E, bool> {
|
||||
using U = detail::make_unsigned_t<underlying_type_t<E>>;
|
||||
|
||||
const auto flag_value = static_cast<U>(flag);
|
||||
return flag_value != U{0} && (static_cast<U>(flags) & flag_value) == flag_value;
|
||||
}
|
||||
|
||||
// Returns true if `lhs` and `rhs` share any flags.
|
||||
// Returns false if either value equals 0 because 0 is not a flag.
|
||||
template <typename E>
|
||||
constexpr auto enum_flags_test_any(E lhs, E rhs) noexcept -> detail::enable_if_t<E, bool> {
|
||||
using U = detail::make_unsigned_t<underlying_type_t<E>>;
|
||||
|
||||
return (static_cast<U>(lhs) & static_cast<U>(rhs)) != U{0};
|
||||
}
|
||||
|
||||
} // namespace magic_enum
|
||||
|
||||
#endif // NEARGYE_MAGIC_ENUM_FLAGS_HPP
|
||||
@@ -0,0 +1,90 @@
|
||||
// __ __ _ ______ _____
|
||||
// | \/ | (_) | ____| / ____|_ _
|
||||
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||
// __/ | https://github.com/Neargye/magic_enum
|
||||
// |___/ version 0.9.8
|
||||
//
|
||||
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2019 - 2026 Daniil Goncharov <neargye@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef NEARGYE_MAGIC_ENUM_FORMAT_HPP
|
||||
#define NEARGYE_MAGIC_ENUM_FORMAT_HPP
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
#include "magic_enum_flags.hpp"
|
||||
|
||||
namespace magic_enum::detail {
|
||||
|
||||
template <typename E, std::enable_if_t<std::is_enum_v<std::decay_t<E>>, int> = 0>
|
||||
std::string format_as(E e) {
|
||||
using D = std::decay_t<E>;
|
||||
static_assert(std::is_same_v<char, magic_enum::string_view::value_type>, "magic_enum::formatter requires string_view::value_type type same as char.");
|
||||
if constexpr (magic_enum::detail::supported<D>::value) {
|
||||
if constexpr (magic_enum::detail::subtype_v<D> == magic_enum::detail::enum_subtype::flags) {
|
||||
if (const auto name = magic_enum::enum_flags_name<D>(e); !name.empty()) {
|
||||
return {name.data(), name.size()};
|
||||
}
|
||||
} else {
|
||||
if (const auto name = magic_enum::enum_name<D>(e); !name.empty()) {
|
||||
return {name.data(), name.size()};
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::to_string(magic_enum::enum_integer<D>(e));
|
||||
}
|
||||
|
||||
} // namespace magic_enum::detail
|
||||
|
||||
#ifndef MAGIC_ENUM_USE_STD_MODULE
|
||||
# if __has_include(<format>) && ((defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) || __cplusplus >= 202002L)
|
||||
# include <format>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_lib_format) && __cpp_lib_format >= 201907L
|
||||
|
||||
template <typename E>
|
||||
struct std::formatter<E, std::enable_if_t<std::is_enum_v<std::decay_t<E>>, char>> : std::formatter<std::string_view, char> {
|
||||
template <typename FormatContext>
|
||||
auto format(E e, FormatContext& ctx) const {
|
||||
return std::formatter<std::string_view, char>::format(magic_enum::detail::format_as<E>(e), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(FMT_VERSION)
|
||||
|
||||
template <typename E>
|
||||
struct fmt::formatter<E, std::enable_if_t<std::is_enum_v<std::decay_t<E>>, char>> : fmt::formatter<std::string_view, char> {
|
||||
template <typename FormatContext>
|
||||
auto format(E e, FormatContext& ctx) const {
|
||||
return fmt::formatter<std::string_view, char>::format(magic_enum::detail::format_as<E>(e), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // NEARGYE_MAGIC_ENUM_FORMAT_HPP
|
||||
@@ -0,0 +1,94 @@
|
||||
// __ __ _ ______ _____
|
||||
// | \/ | (_) | ____| / ____|_ _
|
||||
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||
// __/ | https://github.com/Neargye/magic_enum
|
||||
// |___/ version 0.9.8
|
||||
//
|
||||
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2019 - 2026 Daniil Goncharov <neargye@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef NEARGYE_MAGIC_ENUM_FUSE_HPP
|
||||
#define NEARGYE_MAGIC_ENUM_FUSE_HPP
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
namespace magic_enum {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename E>
|
||||
constexpr std::size_t fuse_bit_width() noexcept {
|
||||
return log2((enum_count<E>() << 1) - 1);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
constexpr optional<std::uintmax_t> fuse_one_enum(optional<std::uintmax_t> hash, E value) noexcept {
|
||||
if (hash) {
|
||||
if (const auto index = enum_index(value)) {
|
||||
return (*hash << fuse_bit_width<E>()) | *index;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
constexpr optional<std::uintmax_t> fuse_enum(E value) noexcept {
|
||||
return fuse_one_enum(0, value);
|
||||
}
|
||||
|
||||
template <typename E, typename... Es>
|
||||
constexpr optional<std::uintmax_t> fuse_enum(E head, Es... tail) noexcept {
|
||||
return fuse_one_enum(fuse_enum(tail...), head);
|
||||
}
|
||||
|
||||
template <typename... Es>
|
||||
constexpr auto typesafe_fuse_enum(Es... values) noexcept {
|
||||
enum class enum_fuse_t : std::uintmax_t;
|
||||
const auto fuse = fuse_enum(values...);
|
||||
if (fuse) {
|
||||
return optional<enum_fuse_t>{static_cast<enum_fuse_t>(*fuse)};
|
||||
}
|
||||
return optional<enum_fuse_t>{};
|
||||
}
|
||||
|
||||
} // namespace magic_enum::detail
|
||||
|
||||
// Returns a bijective mix of several enum values. This can be used to emulate 2D switch/case statements.
|
||||
template <typename... Es>
|
||||
[[nodiscard]] constexpr auto enum_fuse(Es... values) noexcept {
|
||||
static_assert((std::is_enum_v<std::decay_t<Es>> && ...), "magic_enum::enum_fuse requires enum type.");
|
||||
static_assert(sizeof...(Es) >= 2, "magic_enum::enum_fuse requires at least 2 values.");
|
||||
static_assert((detail::fuse_bit_width<std::decay_t<Es>>() + ...) <= (sizeof(std::uintmax_t) * 8), "magic_enum::enum_fuse does not work for large enums");
|
||||
#if defined(MAGIC_ENUM_NO_TYPESAFE_ENUM_FUSE)
|
||||
const auto fuse = detail::fuse_enum<std::decay_t<Es>...>(values...);
|
||||
#else
|
||||
const auto fuse = detail::typesafe_fuse_enum<std::decay_t<Es>...>(values...);
|
||||
#endif
|
||||
return MAGIC_ENUM_ASSERT(fuse), fuse;
|
||||
}
|
||||
|
||||
} // namespace magic_enum
|
||||
|
||||
#endif // NEARGYE_MAGIC_ENUM_FUSE_HPP
|
||||
@@ -0,0 +1,117 @@
|
||||
// __ __ _ ______ _____
|
||||
// | \/ | (_) | ____| / ____|_ _
|
||||
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||
// __/ | https://github.com/Neargye/magic_enum
|
||||
// |___/ version 0.9.8
|
||||
//
|
||||
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2019 - 2026 Daniil Goncharov <neargye@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef NEARGYE_MAGIC_ENUM_IOSTREAM_HPP
|
||||
#define NEARGYE_MAGIC_ENUM_IOSTREAM_HPP
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
#include "magic_enum_flags.hpp"
|
||||
|
||||
#ifndef MAGIC_ENUM_USE_STD_MODULE
|
||||
# include <iosfwd>
|
||||
#endif
|
||||
|
||||
namespace magic_enum {
|
||||
|
||||
namespace ostream_operators {
|
||||
|
||||
template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
|
||||
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, E value) {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
|
||||
if constexpr (detail::supported<D>::value) {
|
||||
if constexpr (detail::subtype_v<D> == detail::enum_subtype::flags) {
|
||||
if (const auto name = enum_flags_name<D>(value); !name.empty()) {
|
||||
for (std::size_t i = 0; i < name.size(); ++i) {
|
||||
os.put(name.data()[i]);
|
||||
}
|
||||
return os;
|
||||
}
|
||||
} else {
|
||||
if (const auto name = enum_name<D>(value); !name.empty()) {
|
||||
for (std::size_t i = 0; i < name.size(); ++i) {
|
||||
os.put(name.data()[i]);
|
||||
}
|
||||
return os;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (os << static_cast<U>(value));
|
||||
}
|
||||
|
||||
template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
|
||||
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, optional<E> value) {
|
||||
return value ? (os << *value) : os;
|
||||
}
|
||||
|
||||
} // namespace magic_enum::ostream_operators
|
||||
|
||||
namespace istream_operators {
|
||||
|
||||
template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
|
||||
std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& is, E& value) {
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
std::basic_string<Char, Traits> s;
|
||||
is >> s;
|
||||
if constexpr (detail::supported<D>::value) {
|
||||
if constexpr (detail::subtype_v<D> == detail::enum_subtype::flags) {
|
||||
if (const auto v = enum_flags_cast<D>(s)) {
|
||||
value = *v;
|
||||
} else {
|
||||
is.setstate(std::basic_ios<Char>::failbit);
|
||||
}
|
||||
} else {
|
||||
if (const auto v = enum_cast<D>(s)) {
|
||||
value = *v;
|
||||
} else {
|
||||
is.setstate(std::basic_ios<Char>::failbit);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
is.setstate(std::basic_ios<Char>::failbit);
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
} // namespace magic_enum::istream_operators
|
||||
|
||||
namespace iostream_operators {
|
||||
|
||||
using magic_enum::ostream_operators::operator<<;
|
||||
using magic_enum::istream_operators::operator>>;
|
||||
|
||||
} // namespace magic_enum::iostream_operators
|
||||
|
||||
} // namespace magic_enum
|
||||
|
||||
#endif // NEARGYE_MAGIC_ENUM_IOSTREAM_HPP
|
||||
201
build-config/magicenum/include/magic_enum/magic_enum_switch.hpp
Normal file
201
build-config/magicenum/include/magic_enum/magic_enum_switch.hpp
Normal file
@@ -0,0 +1,201 @@
|
||||
// __ __ _ ______ _____
|
||||
// | \/ | (_) | ____| / ____|_ _
|
||||
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||
// __/ | https://github.com/Neargye/magic_enum
|
||||
// |___/ version 0.9.8
|
||||
//
|
||||
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2019 - 2026 Daniil Goncharov <neargye@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef NEARGYE_MAGIC_ENUM_SWITCH_HPP
|
||||
#define NEARGYE_MAGIC_ENUM_SWITCH_HPP
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
namespace magic_enum {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct default_result_type {};
|
||||
|
||||
template <typename T>
|
||||
struct identity {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
struct nonesuch {};
|
||||
|
||||
template <typename F, typename V, bool = std::is_invocable_v<F, V>>
|
||||
struct invoke_result : identity<nonesuch> {};
|
||||
|
||||
template <typename F, typename V>
|
||||
struct invoke_result<F, V, true> : std::invoke_result<F, V> {};
|
||||
|
||||
template <typename F, typename V>
|
||||
using invoke_result_t = typename invoke_result<F, V>::type;
|
||||
|
||||
template <typename E, enum_subtype S, typename F, std::size_t... J>
|
||||
constexpr auto common_invocable(std::index_sequence<J...>) noexcept {
|
||||
static_assert(std::is_enum_v<E>, "magic_enum::detail::invocable_index requires enum type.");
|
||||
|
||||
if constexpr (count_v<E, S> == 0) {
|
||||
return identity<nonesuch>{};
|
||||
} else {
|
||||
return std::common_type<invoke_result_t<F, enum_constant<values_v<E, S>[J]>>...>{};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E, enum_subtype S, typename Result, typename F, bool HasResult>
|
||||
constexpr auto result_type() noexcept {
|
||||
static_assert(std::is_enum_v<E>, "magic_enum::detail::result_type requires enum type.");
|
||||
|
||||
constexpr auto seq = std::make_index_sequence<count_v<E, S>>{};
|
||||
using R = std::decay_t<typename decltype(common_invocable<E, S, F>(seq))::type>;
|
||||
using D = std::decay_t<Result>;
|
||||
if constexpr (std::is_same_v<Result, default_result_type>) {
|
||||
if constexpr (std::is_same_v<R, nonesuch>) {
|
||||
return identity<void>{};
|
||||
} else {
|
||||
return identity<R>{};
|
||||
}
|
||||
} else {
|
||||
if constexpr (std::is_convertible_v<R, D> && (!HasResult || std::is_convertible_v<Result, D>)) {
|
||||
return identity<D>{};
|
||||
} else if constexpr (std::is_convertible_v<Result, R>) {
|
||||
return identity<R>{};
|
||||
} else {
|
||||
return identity<nonesuch>{};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E, enum_subtype S, typename Result, typename F, bool HasResult = false, typename D = std::decay_t<E>, typename R = typename decltype(result_type<D, S, Result, F, HasResult>())::type>
|
||||
using result_t = std::enable_if_t<std::is_enum_v<D> && !std::is_same_v<R, nonesuch>, R>;
|
||||
|
||||
template <typename E, enum_subtype S, typename Result, typename F>
|
||||
using result_with_fallback_t = result_t<E, S, Result, F, true>;
|
||||
|
||||
#if !defined(MAGIC_ENUM_ENABLE_HASH) && !defined(MAGIC_ENUM_ENABLE_HASH_SWITCH)
|
||||
|
||||
template <typename T = void>
|
||||
inline constexpr auto default_result_type_lambda = []() noexcept(std::is_nothrow_default_constructible_v<T>) { return T{}; };
|
||||
|
||||
template <>
|
||||
inline constexpr auto default_result_type_lambda<void> = []() noexcept {};
|
||||
|
||||
template <std::size_t J, std::size_t End, typename R, typename E, enum_subtype S, typename F, typename Def>
|
||||
constexpr decltype(auto) linear_switch_impl(F&& f, E value, Def&& def) {
|
||||
if constexpr (J < End) {
|
||||
using V = enum_constant<enum_value<E, J, S>()>;
|
||||
if (enum_value_equal(value, V::value)) {
|
||||
if constexpr (std::is_invocable_r_v<R, F, V>) {
|
||||
return static_cast<R>(detail::invoke_constant(std::forward<F>(f), V{}));
|
||||
} else {
|
||||
return def();
|
||||
}
|
||||
} else {
|
||||
return linear_switch_impl<J + 1, End, R, E, S>(std::forward<F>(f), value, std::forward<Def>(def));
|
||||
}
|
||||
} else {
|
||||
return def();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename R, typename E, enum_subtype S, typename F, typename Def>
|
||||
constexpr decltype(auto) linear_switch(F&& f, E value, Def&& def) {
|
||||
static_assert(is_enum_v<E>, "magic_enum::detail::linear_switch requires enum type.");
|
||||
|
||||
if constexpr (count_v<E, S> == 0) {
|
||||
return def();
|
||||
} else {
|
||||
return linear_switch_impl<0, count_v<E, S>, R, E, S>(std::forward<F>(f), value, std::forward<Def>(def));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace magic_enum::detail
|
||||
|
||||
template <typename Result = detail::default_result_type, typename E, detail::enum_subtype S = detail::subtype_v<E>, typename F, typename R = detail::result_t<E, S, Result, F>>
|
||||
constexpr decltype(auto) enum_switch(F&& f, E value) {
|
||||
using D = std::decay_t<E>;
|
||||
static_assert(std::is_enum_v<D>, "magic_enum::enum_switch requires enum type.");
|
||||
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||
|
||||
#if defined(MAGIC_ENUM_ENABLE_HASH) || defined(MAGIC_ENUM_ENABLE_HASH_SWITCH)
|
||||
return detail::hash_switch_values<D, S, detail::case_call_t::value>(
|
||||
std::forward<F>(f),
|
||||
value,
|
||||
detail::default_result_type_lambda<R>,
|
||||
[](D lhs, D rhs) { return detail::enum_value_equal(lhs, rhs); });
|
||||
#else
|
||||
return detail::linear_switch<R, D, S>(
|
||||
std::forward<F>(f),
|
||||
value,
|
||||
detail::default_result_type_lambda<R>);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Result = detail::default_result_type, detail::enum_subtype S, typename E, typename F, typename R = detail::result_t<E, S, Result, F>>
|
||||
constexpr decltype(auto) enum_switch(F&& f, E value) {
|
||||
return enum_switch<Result, E, S>(std::forward<F>(f), value);
|
||||
}
|
||||
|
||||
template <typename Result, typename E, detail::enum_subtype S = detail::subtype_v<E>, typename F, typename R = detail::result_with_fallback_t<E, S, Result, F>>
|
||||
constexpr decltype(auto) enum_switch(F&& f, E value, Result&& result) {
|
||||
using D = std::decay_t<E>;
|
||||
static_assert(std::is_enum_v<D>, "magic_enum::enum_switch requires enum type.");
|
||||
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||
|
||||
#if defined(MAGIC_ENUM_ENABLE_HASH) || defined(MAGIC_ENUM_ENABLE_HASH_SWITCH)
|
||||
return detail::hash_switch_values<D, S, detail::case_call_t::value>(
|
||||
std::forward<F>(f),
|
||||
value,
|
||||
[&result]() -> R { return std::forward<Result>(result); },
|
||||
[](D lhs, D rhs) { return detail::enum_value_equal(lhs, rhs); });
|
||||
#else
|
||||
return detail::linear_switch<R, D, S>(
|
||||
std::forward<F>(f),
|
||||
value,
|
||||
[&result]() -> R { return std::forward<Result>(result); });
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Result, detail::enum_subtype S, typename E, typename F, typename R = detail::result_with_fallback_t<E, S, Result, F>>
|
||||
constexpr decltype(auto) enum_switch(F&& f, E value, Result&& result) {
|
||||
return enum_switch<Result, E, S>(std::forward<F>(f), value, std::forward<Result>(result));
|
||||
}
|
||||
|
||||
} // namespace magic_enum
|
||||
|
||||
template <>
|
||||
struct std::common_type<magic_enum::detail::nonesuch, magic_enum::detail::nonesuch> : magic_enum::detail::identity<magic_enum::detail::nonesuch> {};
|
||||
|
||||
template <typename T>
|
||||
struct std::common_type<T, magic_enum::detail::nonesuch> : magic_enum::detail::identity<T> {};
|
||||
|
||||
template <typename T>
|
||||
struct std::common_type<magic_enum::detail::nonesuch, T> : magic_enum::detail::identity<T> {};
|
||||
|
||||
#endif // NEARGYE_MAGIC_ENUM_SWITCH_HPP
|
||||
149
build-config/magicenum/include/magic_enum/magic_enum_utility.hpp
Normal file
149
build-config/magicenum/include/magic_enum/magic_enum_utility.hpp
Normal file
@@ -0,0 +1,149 @@
|
||||
// __ __ _ ______ _____
|
||||
// | \/ | (_) | ____| / ____|_ _
|
||||
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||
// __/ | https://github.com/Neargye/magic_enum
|
||||
// |___/ version 0.9.8
|
||||
//
|
||||
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2019 - 2026 Daniil Goncharov <neargye@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef NEARGYE_MAGIC_ENUM_UTILITY_HPP
|
||||
#define NEARGYE_MAGIC_ENUM_UTILITY_HPP
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
#ifndef MAGIC_ENUM_USE_STD_MODULE
|
||||
# include <tuple>
|
||||
#endif
|
||||
|
||||
namespace magic_enum {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename E, enum_subtype S, typename F, std::size_t J>
|
||||
using enum_for_each_result_t = std::decay_t<std::invoke_result_t<F&, enum_constant<values_v<E, S>[J]>>>;
|
||||
|
||||
template <typename E, enum_subtype S, typename F, std::size_t... J>
|
||||
constexpr auto for_each(F&& f, std::index_sequence<J...>) {
|
||||
constexpr bool has_void_return = (std::is_void_v<std::invoke_result_t<F&, enum_constant<values_v<E, S>[J]>>> || ...);
|
||||
constexpr bool all_same_return = (std::is_same_v<std::invoke_result_t<F&, enum_constant<values_v<E, S>[0]>>, std::invoke_result_t<F&, enum_constant<values_v<E, S>[J]>>> && ...);
|
||||
|
||||
if constexpr (has_void_return) {
|
||||
(detail::invoke_constant(f, enum_constant<values_v<E, S>[J]>{}), ...);
|
||||
} else if constexpr (all_same_return) {
|
||||
return std::array<enum_for_each_result_t<E, S, F, 0>, sizeof...(J)>{{detail::invoke_constant(f, enum_constant<values_v<E, S>[J]>{})...}};
|
||||
} else {
|
||||
return std::tuple<enum_for_each_result_t<E, S, F, J>...>{detail::invoke_constant(f, enum_constant<values_v<E, S>[J]>{})...};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E, enum_subtype S, typename F, std::size_t... J>
|
||||
constexpr bool all_invocable(std::index_sequence<J...>) {
|
||||
if constexpr (count_v<E, S> == 0) {
|
||||
return false;
|
||||
} else {
|
||||
return (std::is_invocable_v<F&, enum_constant<values_v<E, S>[J]>> && ...);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace magic_enum::detail
|
||||
|
||||
template <typename E, detail::enum_subtype S = detail::subtype_v<E>, typename F, detail::enable_if_t<E, int> = 0>
|
||||
constexpr auto enum_for_each(F&& f) {
|
||||
using D = std::decay_t<E>;
|
||||
static_assert(std::is_enum_v<D>, "magic_enum::enum_for_each requires enum type.");
|
||||
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||
constexpr auto sep = std::make_index_sequence<detail::count_v<D, S>>{};
|
||||
|
||||
if constexpr (detail::all_invocable<D, S, F>(sep)) {
|
||||
return detail::for_each<D, S>(std::forward<F>(f), sep);
|
||||
} else {
|
||||
static_assert(detail::always_false_v<D>, "magic_enum::enum_for_each requires invocable of all enum value.");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
|
||||
[[nodiscard]] constexpr auto enum_next_value(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
|
||||
using D = std::decay_t<E>;
|
||||
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
|
||||
|
||||
if (const auto i = enum_index<D, S>(value)) {
|
||||
const auto index = static_cast<std::ptrdiff_t>(*i);
|
||||
if ((n > 0 && n >= count - index) || (n < 0 && n < -index)) {
|
||||
return {};
|
||||
}
|
||||
return enum_value<D, S>(static_cast<std::size_t>(index + n));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
|
||||
[[nodiscard]] constexpr auto enum_next_value_circular(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
|
||||
using D = std::decay_t<E>;
|
||||
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
|
||||
|
||||
if (const auto i = enum_index<D, S>(value)) {
|
||||
auto index = (static_cast<std::ptrdiff_t>(*i) + (n % count)) % count;
|
||||
if (index < 0) {
|
||||
index += count;
|
||||
}
|
||||
return enum_value<D, S>(static_cast<std::size_t>(index));
|
||||
}
|
||||
return MAGIC_ENUM_ASSERT(false), value;
|
||||
}
|
||||
|
||||
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
|
||||
[[nodiscard]] constexpr auto enum_prev_value(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
|
||||
using D = std::decay_t<E>;
|
||||
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
|
||||
|
||||
if (const auto i = enum_index<D, S>(value)) {
|
||||
const auto index = static_cast<std::ptrdiff_t>(*i);
|
||||
if ((n > 0 && n > index) || (n < 0 && n <= index - count)) {
|
||||
return {};
|
||||
}
|
||||
return enum_value<D, S>(static_cast<std::size_t>(index - n));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
|
||||
[[nodiscard]] constexpr auto enum_prev_value_circular(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
|
||||
using D = std::decay_t<E>;
|
||||
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
|
||||
|
||||
if (const auto i = enum_index<D, S>(value)) {
|
||||
auto index = (static_cast<std::ptrdiff_t>(*i) - (n % count)) % count;
|
||||
if (index < 0) {
|
||||
index += count;
|
||||
}
|
||||
return enum_value<D, S>(static_cast<std::size_t>(index));
|
||||
}
|
||||
return MAGIC_ENUM_ASSERT(false), value;
|
||||
}
|
||||
|
||||
} // namespace magic_enum
|
||||
|
||||
#endif // NEARGYE_MAGIC_ENUM_UTILITY_HPP
|
||||
13
build-config/magicenum/meson.build
Normal file
13
build-config/magicenum/meson.build
Normal file
@@ -0,0 +1,13 @@
|
||||
magic_enum_include = include_directories('include')
|
||||
|
||||
magic_enum_args = []
|
||||
|
||||
if get_option('magic_enum_hash')
|
||||
magic_enum_args += '-DMAGIC_ENUM_ENABLE_HASH'
|
||||
endif
|
||||
|
||||
magic_enum_dep = declare_dependency(
|
||||
include_directories: magic_enum_include,
|
||||
compile_args: magic_enum_args,
|
||||
)
|
||||
|
||||
6
build-config/magicenum/meson_options.txt
Normal file
6
build-config/magicenum/meson_options.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
option(
|
||||
'hash',
|
||||
type : 'boolean',
|
||||
value : false,
|
||||
description : 'Do hashing at build time - longer build times, but O(1) string lookup'
|
||||
)
|
||||
@@ -1,2 +1,4 @@
|
||||
subdir('mfem')
|
||||
subdir('nameof')
|
||||
subdir('magicenum')
|
||||
|
||||
|
||||
@@ -325,49 +325,107 @@ if mfem_has_cuda
|
||||
message(cuda_check_result.stdout().strip())
|
||||
endif
|
||||
system_mfem = disabler()
|
||||
system_mfem_dependencies = []
|
||||
mfem_provider = 'source bundle'
|
||||
|
||||
if effective_allow_preinstalled
|
||||
system_candidate = dependency('mfem', version: '>=4.10', required: false, allow_fallback: false)
|
||||
system_candidate = dependency(
|
||||
'mfem',
|
||||
version: '>=4.10',
|
||||
required: false,
|
||||
allow_fallback: false,
|
||||
)
|
||||
|
||||
if system_candidate.found()
|
||||
system_compatible = true
|
||||
system_candidate_dependencies = [system_candidate]
|
||||
|
||||
if mfem_has_mpi
|
||||
system_mpi = dependency(
|
||||
'mpi',
|
||||
language: 'cpp',
|
||||
required: false,
|
||||
)
|
||||
|
||||
if system_mpi.found()
|
||||
system_candidate_dependencies += [system_mpi]
|
||||
else
|
||||
system_compatible = false
|
||||
endif
|
||||
endif
|
||||
|
||||
foreach i : range(mfem_feature_names.length())
|
||||
if mfem_feature_states[i]
|
||||
feature_macro = mfem_cmake_names[i]
|
||||
|
||||
macro_ok = cpp.compiles(
|
||||
'#include <mfem/config/config.hpp>\n#ifndef ' + feature_macro + '\n#error missing\n#endif\nint main(){return 0;}',
|
||||
dependencies: system_candidate,
|
||||
'#include <mfem/config/config.hpp>\n' +
|
||||
'#ifndef ' + feature_macro + '\n' +
|
||||
'#error missing\n' +
|
||||
'#endif\n' +
|
||||
'int main(){return 0;}',
|
||||
dependencies: system_candidate_dependencies,
|
||||
name: 'system MFEM provides ' + feature_macro,
|
||||
)
|
||||
|
||||
system_compatible = system_compatible and macro_ok
|
||||
endif
|
||||
endforeach
|
||||
precision_macro = get_option('mfem_precision') == 'single' ? 'MFEM_USE_SINGLE' : 'MFEM_USE_DOUBLE'
|
||||
|
||||
precision_macro = get_option('mfem_precision') == 'single' \
|
||||
? 'MFEM_USE_SINGLE' \
|
||||
: 'MFEM_USE_DOUBLE'
|
||||
|
||||
system_compatible = system_compatible and cpp.compiles(
|
||||
'#include <mfem/config/config.hpp>\n#ifndef ' + precision_macro + '\n#error precision mismatch\n#endif\nint main(){return 0;}',
|
||||
dependencies: system_candidate,
|
||||
'#include <mfem/config/config.hpp>\n' +
|
||||
'#ifndef ' + precision_macro + '\n' +
|
||||
'#error precision mismatch\n' +
|
||||
'#endif\n' +
|
||||
'int main(){return 0;}',
|
||||
dependencies: system_candidate_dependencies,
|
||||
name: 'system MFEM uses requested precision',
|
||||
)
|
||||
|
||||
foreach i : range(mfem_feature_names.length())
|
||||
feature_opt = get_option('mfem_' + mfem_feature_names[i])
|
||||
feature_macro = mfem_cmake_names[i]
|
||||
|
||||
if feature_opt.disabled()
|
||||
macro_absent = cpp.compiles(
|
||||
'#include <mfem/config/config.hpp>\n#ifdef ' + feature_macro + '\n#error explicitly disabled\n#endif\nint main(){return 0;}',
|
||||
dependencies: system_candidate,
|
||||
'#include <mfem/config/config.hpp>\n' +
|
||||
'#ifdef ' + feature_macro + '\n' +
|
||||
'#error explicitly disabled\n' +
|
||||
'#endif\n' +
|
||||
'int main(){return 0;}',
|
||||
dependencies: system_candidate_dependencies,
|
||||
name: 'system MFEM omits disabled ' + feature_macro,
|
||||
)
|
||||
|
||||
system_compatible = system_compatible and macro_absent
|
||||
endif
|
||||
endforeach
|
||||
|
||||
if system_compatible
|
||||
system_compatible = cpp.compiles(
|
||||
'#include <mfem.hpp>\n' +
|
||||
'int main(){return 0;}',
|
||||
dependencies: system_candidate_dependencies,
|
||||
name: 'system MFEM public headers are consumable',
|
||||
)
|
||||
endif
|
||||
|
||||
if system_compatible
|
||||
system_mfem = system_candidate
|
||||
system_mfem_dependencies = system_candidate_dependencies
|
||||
mfem_provider = 'system'
|
||||
else
|
||||
message('The system MFEM does not satisfy the selected feature set; using the pinned source bundle.')
|
||||
message(
|
||||
'The system MFEM does not satisfy the selected feature set; ' +
|
||||
'using the pinned source bundle.'
|
||||
)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
uses_unmodelled_system_tpl = false
|
||||
foreach feature_name : mfem_feature_names
|
||||
if (
|
||||
@@ -399,7 +457,10 @@ mfem_build_target = []
|
||||
mpi_launcher_from_dependency = false
|
||||
|
||||
if system_mfem.found()
|
||||
mfem_dep = system_mfem
|
||||
mfem_dep = declare_dependency(
|
||||
dependencies: system_mfem_dependencies,
|
||||
version: system_mfem.version(),
|
||||
)
|
||||
else
|
||||
mfem_source = subproject('mfem').get_variable('mfem_source_dir')
|
||||
|
||||
|
||||
21
build-config/nameof/LICENSE
Normal file
21
build-config/nameof/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 - 2026 Daniil Goncharov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
1582
build-config/nameof/include/nameof.hpp
Normal file
1582
build-config/nameof/include/nameof.hpp
Normal file
File diff suppressed because it is too large
Load Diff
5
build-config/nameof/meson.build
Normal file
5
build-config/nameof/meson.build
Normal file
@@ -0,0 +1,5 @@
|
||||
nameof_include_dir = include_directories('include')
|
||||
|
||||
nameof_dep = declare_dependency(
|
||||
include_directories: nameof_include_dir,
|
||||
)
|
||||
@@ -10,6 +10,13 @@ project(
|
||||
],
|
||||
)
|
||||
|
||||
# these silence some warnings from mfem. Really these should be specific to mfem.
|
||||
add_global_arguments('-Wno-unused', language: 'c')
|
||||
add_global_arguments('-Wno-unused', language: 'cpp')
|
||||
add_global_arguments('-Wno-unused-parameter', language: 'c')
|
||||
add_global_arguments('-Wno-unused-parameter', language: 'cpp')
|
||||
|
||||
|
||||
python_mod = import('python')
|
||||
fs = import('fs')
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
@@ -2,6 +2,7 @@ option('allow_preinstalled', type: 'boolean', value: true, description: 'Allow o
|
||||
option('feature_profile', type: 'combo', choices: ['minimal', 'portable', 'full'], value: 'portable', description: 'Defaults for auto features. full is strict and requires all selected non-bundled TPLs in dependency_prefix.')
|
||||
option('build_python', type: 'boolean', value: false, description: 'Build the nanobind Python extension and install native assets inside the Python package.')
|
||||
option('build_tests', type: 'boolean', value: true, description: 'Register serial, parallel, and Python smoke tests when applicable.')
|
||||
option('build_checks', type: 'boolean', value: true, description: 'Build the compile-time checks for SERiF type invariants.')
|
||||
option('install_mfem', type: 'boolean', value: true, description: 'Install the source-built MFEM bundle and its runtime dependencies.')
|
||||
option('dependency_prefix', type: 'string', value: '', description: 'Explicit prefix containing optional TPLs. Used even when allow_preinstalled=false.')
|
||||
option('jobs', type: 'integer', min: 1, max: 1024, value: 4, description: 'Parallel jobs for the CMake/Make source bundle.')
|
||||
@@ -9,7 +10,9 @@ option('mfem_cuda_arch', type: 'string', value: 'native', description: 'CUDA arc
|
||||
option('mfem_hip_arch', type: 'string', value: '', description: 'HIP GPU target, for example gfx90a. Empty lets the toolchain choose.')
|
||||
option('mfem_precision', type: 'combo', choices: ['double', 'single'], value: 'double', description: 'Floating-point precision used consistently by MFEM and Hypre.')
|
||||
option('python_install_native', type: 'boolean', value: true, description: 'Install MFEM headers, CMake/pkg-config data, and runtime libraries under the Python package.')
|
||||
option('macos_deployment_target', type: 'string', value: '15.0', description: 'Minimum macOS version for native artifacts (15.0 supports C++23 std::print); ignored on other systems. Empty uses the toolchain default.')
|
||||
option('macos_deployment_target', type: 'string', value: '', description: 'Minimum macOS version for native artifacts (15.0 supports C++23 std::print); ignored on other systems. Empty uses the toolchain default.')
|
||||
|
||||
option('magic_enum_hash', type: 'boolean', value: false, description: 'Enable hash support in magic_enum. This is a compile-time option that enables std::hash for magic_enum types.')
|
||||
|
||||
option('mfem_mpi', type: 'feature', value: 'auto', description: 'MPI parallel MFEM; implies Hypre.')
|
||||
option('mfem_metis', type: 'feature', value: 'auto', description: 'METIS graph partitioning.')
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#include "serif/discretization/blocks/concepts.hpp"
|
||||
#include "serif/discretization/blocks/traits.hpp"
|
||||
#include "serif/dimensions/dimensions.hpp"
|
||||
|
||||
namespace {
|
||||
using namespace serif::discretization;
|
||||
|
||||
using Density = blocks::density::mass;
|
||||
using Displacement = blocks::displacement::vector;
|
||||
using Coefficients = blocks::surface_deformation::coefficients;
|
||||
|
||||
static_assert(blocks::SpatialTerm<Density>, "Density should be a spatial term");
|
||||
static_assert(blocks::SpatialTerm<Displacement>, "Displacement should be a spatial term");
|
||||
static_assert(!blocks::SpatialTerm<Coefficients>, "Coefficients should not be a spatial term");
|
||||
|
||||
static_assert(blocks::HasQuantity<Density>, "Density should have a quantity");
|
||||
static_assert(blocks::HasValueBlock<Coefficients>, "Coefficients should have a value block");
|
||||
static_assert(!blocks::HasResidualBlock<Coefficients>, "Coefficients should not have a residual block");
|
||||
|
||||
using DensitySpace = blocks::FunctionalSpaceOf<Density>;
|
||||
|
||||
static_assert(DensitySpace::functional_space.family == functional_space::Family::l2, "Density should be in an L2 space");
|
||||
|
||||
static_assert(DensitySpace::order == 2, "Density should be in a second order space");
|
||||
static_assert(DensitySpace::rank::value == 0, "Density should be a scalar field");
|
||||
static_assert(std::same_as<blocks::SupportOf<Density>, domain::StellarDomains>, "Density should be supported on stellar domains");
|
||||
static_assert(std::same_as<blocks::QuantityOf<Density>, serif::dimensions::Density>, "Density should have a quantity of type Density");
|
||||
}
|
||||
26
src/compile_time_checks/serif/discretization/forms/forms.cpp
Normal file
26
src/compile_time_checks/serif/discretization/forms/forms.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include <type_traits>
|
||||
|
||||
#include "serif/discretization/forms/runtime.hpp"
|
||||
#include "serif/discretization/forms/concept.hpp"
|
||||
#include "serif/discretization/forms/forms.hpp"
|
||||
|
||||
// These are left in an anonymous namespace to avoid polluting the global namespace with these symbols at link time.
|
||||
namespace {
|
||||
using namespace serif::discretization::forms;
|
||||
using namespace serif::discretization::blocks;
|
||||
|
||||
// Note we define custom versions of forms that likely exist in the codebase. We are not here testing that the
|
||||
// forms have a particular structure; rather, we are testing that given some known structure
|
||||
// the form system behaves as we expect it to.
|
||||
using HypotheticalTestingGravityMass = mass<gravity::gradient, gravity::gradient>;
|
||||
using HypotheticalTestingGravityDivergence = divergence<gravity::gradient, gravity::potential>;
|
||||
|
||||
static_assert(Form<HypotheticalTestingGravityMass>, "GravityMass should satisfy the Form concept. A failure here indicates that the mass form is not properly defined for the gravity field. Please check out the forms submodule");
|
||||
static_assert(Form<HypotheticalTestingGravityDivergence>, "GravityDivergence should satisfy the Form concept. A failure here indicates that the divergence form is not properly defined for the gravity field. Please check out the forms submodule");
|
||||
|
||||
static_assert(std::same_as<HypotheticalTestingGravityMass::test_type, gravity::gradient>, "GravityMass::test_type should be gravity::gradient. A failure here indicates that the mass form is not properly defined for the gravity field. Please check out the forms submodule");
|
||||
static_assert(std::same_as<HypotheticalTestingGravityMass::trial_type, gravity::gradient>, "GravityMass::trial_type should be gravity::gradient. A failure here indicates that the mass form is not properly defined for the gravity field. Please check out the forms submodule");
|
||||
|
||||
static_assert(std::same_as<HypotheticalTestingGravityDivergence::trial_type, gravity::gradient>, "GravityDivergence::trial_type should be gravity::gradient. A failure here indicates that the divergence form is not properly defined for the gravity field. Please check out the forms submodule");
|
||||
static_assert(std::same_as<HypotheticalTestingGravityDivergence::test_type, gravity::potential>, "GravityDivergence::test_type should be gravity::potential. A failure here indicates that the divergence form is not properly defined for the gravity field. Please check out the forms submodule");
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "serif/discretization/forms/operands.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
#include <concepts>
|
||||
|
||||
namespace {
|
||||
using namespace serif::discretization;
|
||||
|
||||
using DensityValue = forms::Operand<blocks::density::mass>;
|
||||
using GravityValue = forms::Operand<blocks::gravity::gradient>;
|
||||
using GravityDivergence = forms::Operand<blocks::gravity::gradient, forms::Operation::Divergence>;
|
||||
using GravityNormalTrace = forms::Operand<blocks::gravity::gradient, forms::Operation::NormalTrace>;
|
||||
using DisplacementGradient = forms::Operand<blocks::displacement::vector, forms::Operation::Gradient>;
|
||||
|
||||
static_assert(forms::IsOperand<DensityValue>, "DensityValue should be a valid operand");
|
||||
static_assert(forms::IsOperand<GravityValue>, "GravityValue should be a valid operand");
|
||||
static_assert(forms::IsOperand<GravityDivergence>, "GravityDivergence should be a valid operand");
|
||||
static_assert(forms::IsOperand<GravityNormalTrace>, "GravityNormalTrace should be a valid operand");
|
||||
static_assert(forms::IsOperand<DisplacementGradient>, "DisplacementGradient should be a valid operand");
|
||||
|
||||
static_assert(std::same_as<GravityDivergence::term_type, blocks::gravity::gradient>, "GravityDivergence should have the correct term type");
|
||||
static_assert(std::same_as<GravityDivergence::operation_type, forms::Operation::Divergence>, "GravityDivergence should have the correct operation type");
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "serif/discretization/quadrature/backend/mfem/mfem_resolver.hpp"
|
||||
#include "serif/discretization/quadrature/backend/mfem/mfem_static_order.hpp"
|
||||
|
||||
#include "serif/utils/error/errors.hpp"
|
||||
|
||||
namespace {
|
||||
using namespace serif::discretization;
|
||||
using namespace serif::utils::errors;
|
||||
using namespace quadrature::backend::mfem;
|
||||
|
||||
using GravityMassForm = forms::mass<blocks::gravity::gradient, blocks::gravity::gradient>;
|
||||
using GravityDivergenceForm = forms::divergence<blocks::gravity::gradient, blocks::gravity::potential>;
|
||||
using GravityBoundaryForm = forms::boundary_flux<blocks::gravity::gradient, blocks::gravity::gradient>;
|
||||
|
||||
using DensityProjectionForm = forms::projection<blocks::density::mass, blocks::density::mass>;
|
||||
|
||||
|
||||
// Note when using serif_assert (since it is a consteval function) it must be within
|
||||
// a function (C++ does not allow arbitrary calls to consteval functions outside of a function context). This is why we have this function here. It is not called, but it is used to force the compiler to evaluate the consteval function and thus trigger the compile time checks.
|
||||
consteval void compile_checks() {
|
||||
serif::utils::errors::serif_assert<SERiFErrorCode::FAILED_TYPE_INVARIANT, "Gravity Mass Form should have a static, compile time, order of 6", mfem_form_static_order<GravityMassForm> == 6>();
|
||||
serif::utils::errors::serif_assert<SERiFErrorCode::FAILED_TYPE_INVARIANT, "Gravity Divergence Form should have a static, compile time, order of 4", mfem_form_static_order<GravityDivergenceForm> == 4>();
|
||||
serif::utils::errors::serif_assert<SERiFErrorCode::FAILED_TYPE_INVARIANT, "Gravity Boundary Form should have a static, compile time, order of 4", mfem_form_static_order<GravityBoundaryForm> == 4>();
|
||||
serif::utils::errors::serif_assert<SERiFErrorCode::FAILED_TYPE_INVARIANT, "Density Projection Form should have a static, compile time, order of 4", mfem_form_static_order<DensityProjectionForm> == 4>();
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <type_traits>
|
||||
#include <compare>
|
||||
#include <concepts>
|
||||
#include <tuple>
|
||||
|
||||
#include "serif/utils/misc/std_helper/cleaning.hpp"
|
||||
#include "serif/utils/misc/concepts/numeric.hpp"
|
||||
@@ -114,6 +115,7 @@ namespace serif::dimensions {
|
||||
template <typename Candidate>
|
||||
concept QuantityValueType = IsQuantityValue<std::remove_cvref_t<Candidate>>::value;
|
||||
|
||||
|
||||
template <typename Candidate>
|
||||
struct QuantityOf;
|
||||
|
||||
@@ -135,4 +137,7 @@ namespace serif::dimensions {
|
||||
struct QuantityAt<Index, QuantityList<Quantities...>> {
|
||||
using Type = std::tuple_element_t<Index, std::tuple<Quantities...>>;
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
concept ThermodynamicQuantityValueType = QuantityValueType<Candidate> && ThermodynamicQuantityType<QuantityOfT<Candidate>>;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,12 @@
|
||||
namespace serif::dimensions::runtime {
|
||||
class ThermodynamicQuantityID final {
|
||||
public:
|
||||
explicit constexpr ThermodynamicQuantityID(std::string_view name) noexcept;
|
||||
explicit constexpr ThermodynamicQuantityID(std::string_view name) noexcept : m_name(name) {};
|
||||
|
||||
[[nodiscard]] constexpr std::string_view name() const noexcept {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::string_view name() const noexcept;
|
||||
[[nodiscard]] friend constexpr bool operator==(const ThermodynamicQuantityID &, const ThermodynamicQuantityID &) noexcept = default;
|
||||
private:
|
||||
std::string_view m_name; // We can use a string view here since the constructor is constexpr and the string view will be valid for the lifetime of the program.
|
||||
@@ -26,6 +29,8 @@ namespace serif::dimensions::runtime {
|
||||
std::span<const ThermodynamicQuantityID> inputQuantities;
|
||||
std::uint64_t partialDerivativeMask;
|
||||
|
||||
[[nodiscard]] constexpr bool hasPartialDerivative(const std::size_t inputIndex) const noexcept;
|
||||
[[nodiscard]] constexpr bool hasPartialDerivative(const std::size_t inputIndex) const noexcept {
|
||||
return inputIndex < inputQuantities.size() && (partialDerivativeMask & (std::uint64_t{1} << inputIndex)) != 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include "serif/discretization/blocks/fields.hpp"
|
||||
|
||||
namespace serif::discretization::blocks {
|
||||
template <typename FieldCandidate>
|
||||
concept Field = std::derived_from<FieldCandidate, field>;
|
||||
|
||||
template <typename T>
|
||||
concept Term = std::derived_from<std::remove_cvref_t<T>, term_base> && // a term must derive from term_base
|
||||
requires {typename std::remove_cvref_t<T>::field_type;} && // a term must have a field_type member
|
||||
Field<typename std::remove_cvref_t<T>::field_type>; // That field type member must be a valid field
|
||||
|
||||
template <typename T>
|
||||
concept HasFunctionalSpace =
|
||||
Term<T> &&
|
||||
requires {
|
||||
typename std::remove_cvref_t<T>::FunctionalSpace;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept HasSupport =
|
||||
Term<T> &&
|
||||
requires {
|
||||
typename std::remove_cvref_t<T>::Support;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept HasQuantity =
|
||||
Term<T> &&
|
||||
requires {
|
||||
typename std::remove_cvref_t<T>::Quantity;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept HasValueBlock =
|
||||
Term<T> &&
|
||||
requires {
|
||||
typename std::remove_cvref_t<T>::value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept HasResidualBlock =
|
||||
Term<T> &&
|
||||
requires {
|
||||
typename std::remove_cvref_t<T>::residual;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept SpatialTerm =
|
||||
Term<T> &&
|
||||
HasFunctionalSpace<T> &&
|
||||
HasSupport<T>;
|
||||
|
||||
template <typename T>
|
||||
concept PhysicalSpatialTerm =
|
||||
SpatialTerm<T> &&
|
||||
HasQuantity<T>;
|
||||
|
||||
template <typename T>
|
||||
struct IsSpatialTerm : std::bool_constant<SpatialTerm<T>> {};
|
||||
|
||||
template <typename T>
|
||||
struct IsPhysicalSpatialTerm : std::bool_constant<PhysicalSpatialTerm<T>> {};
|
||||
}
|
||||
@@ -1,65 +1,130 @@
|
||||
#pragma once
|
||||
#include <string_view>
|
||||
|
||||
#include "base.hpp"
|
||||
#include "serif/dimensions/dimensions.hpp"
|
||||
#include "serif/discretization/blocks/base.hpp"
|
||||
#include "serif/discretization/functional_space/space.hpp"
|
||||
#include "serif/discretization/domain/concepts.hpp"
|
||||
#include "serif/discretization/domain/physical_domains.hpp"
|
||||
#include "serif/utils/types/type_list.hpp"
|
||||
|
||||
#include "serif/meta/names.hpp"
|
||||
|
||||
namespace serif::discretization::blocks {
|
||||
struct field {};
|
||||
struct term {};
|
||||
struct term_base {};
|
||||
|
||||
template <typename FieldT>
|
||||
struct term : term_base {
|
||||
using field_type = FieldT;
|
||||
};
|
||||
|
||||
struct density final : field {
|
||||
struct mass final : term {
|
||||
static constexpr std::string_view name = meta::type_name<density>();
|
||||
|
||||
struct mass final : term<density> { // Note the reading here is mass density (that is this is not in reference to the common FEM usage of mass as a term in a weak form)
|
||||
static constexpr std::string_view name = meta::type_name<mass>();
|
||||
|
||||
using FunctionalSpace = functional_space::DiscreteFunctionalSpace<functional_space::L2, 2, 0>; // second order scalar L2 space
|
||||
using Support = domain::StellarDomains;
|
||||
using Quantity = dimensions::Density;
|
||||
static constexpr std::string_view symbol = "ρ";
|
||||
|
||||
struct value final : value_block_base {};
|
||||
struct residual final : residual_block_base {};
|
||||
};
|
||||
static inline constexpr mass mass_density_term{};
|
||||
|
||||
static inline constexpr mass mass_term{};
|
||||
using Terms = utils::types::TypeList<mass>;
|
||||
};
|
||||
|
||||
struct displacement final : field {
|
||||
struct geometry final : term {
|
||||
static constexpr std::string_view name = meta::type_name<displacement>();
|
||||
|
||||
struct vector final : term<displacement> { // Similarly this is the displacement vector
|
||||
static constexpr std::string_view name = meta::type_name<vector>();
|
||||
|
||||
using FunctionalSpace = functional_space::DiscreteFunctionalSpace<functional_space::H1, 3, 1>; // third order vector H1 space
|
||||
using Support = domain::AllDomains;
|
||||
using Quantity = dimensions::Length;
|
||||
static constexpr std::string_view symbol = "d";
|
||||
|
||||
struct value final : value_block_base {};
|
||||
struct residual final : residual_block_base {};
|
||||
};
|
||||
static inline constexpr vector vector_term{};
|
||||
|
||||
static inline constexpr geometry geometry_term{};
|
||||
using Terms = utils::types::TypeList<vector>;
|
||||
};
|
||||
|
||||
struct surface_deformation final : field {
|
||||
struct parameters final : term {
|
||||
struct surface_deformation final : field { // Surface deformation is a more general field that the others, it is not pointwise defined over the domain. Use concepts to constrain this as a non spatial field.
|
||||
static constexpr std::string_view name = meta::type_name<surface_deformation>();
|
||||
|
||||
struct coefficients final : term<surface_deformation> {
|
||||
static constexpr std::string_view name = meta::type_name<coefficients>();
|
||||
|
||||
struct value final : value_block_base {};
|
||||
};
|
||||
|
||||
struct shape_equilibrium final : term {
|
||||
struct shape_equilibrium final : term<surface_deformation> {
|
||||
static constexpr std::string_view name = meta::type_name<shape_equilibrium>();
|
||||
|
||||
struct residual final : residual_block_base {};
|
||||
};
|
||||
|
||||
static inline constexpr parameters parameters_term{};
|
||||
static inline constexpr coefficients coefficients_term{};
|
||||
static inline constexpr shape_equilibrium shape_equilibrium_term{};
|
||||
|
||||
using Terms = utils::types::TypeList<coefficients, shape_equilibrium>;
|
||||
};
|
||||
|
||||
struct gravity final : field {
|
||||
struct gradient final : term {
|
||||
static constexpr std::string_view name = meta::type_name<gravity>();
|
||||
|
||||
struct gradient final : term<gravity> {
|
||||
static constexpr std::string_view name = meta::type_name<gradient>();
|
||||
|
||||
using FunctionalSpace = functional_space::DiscreteFunctionalSpace<functional_space::Hdiv, 2, 1>;
|
||||
using Support = domain::AllDomains;
|
||||
using Quantity = dimensions::Acceleration;
|
||||
static constexpr std::string_view symbol = "∇φ";
|
||||
|
||||
struct value final : value_block_base {};
|
||||
struct residual final : residual_block_base {};
|
||||
};
|
||||
|
||||
struct potential final : term {
|
||||
struct potential final : term<gravity> {
|
||||
static constexpr std::string_view name = meta::type_name<potential>();
|
||||
|
||||
using FunctionalSpace = functional_space::DiscreteFunctionalSpace<functional_space::L2, 2, 0>;
|
||||
using Support = domain::AllDomains;
|
||||
using Quantity = dimensions::GravitationalPotential;
|
||||
static constexpr std::string_view symbol = "φ";
|
||||
|
||||
struct value final : value_block_base {};
|
||||
struct residual final : residual_block_base {};
|
||||
};
|
||||
|
||||
static inline constexpr potential potential_term{};
|
||||
static inline constexpr gradient gradient_term{};
|
||||
|
||||
using Terms = utils::types::TypeList<potential, gradient>;
|
||||
};
|
||||
|
||||
struct enthalpy final : field {
|
||||
struct specific final : term {
|
||||
static constexpr std::string_view name = meta::type_name<enthalpy>();
|
||||
|
||||
struct specific final : term<enthalpy> {
|
||||
static constexpr std::string_view name = meta::type_name<specific>();
|
||||
|
||||
using FunctionalSpace = functional_space::DiscreteFunctionalSpace<functional_space::H1, 3, 0>;
|
||||
using Support = domain::StellarDomains;
|
||||
using Quantity = dimensions::SpecificEnthalpy;
|
||||
static constexpr std::string_view symbol = "h";
|
||||
|
||||
struct value final : value_block_base { };
|
||||
struct residual final : residual_block_base { };
|
||||
};
|
||||
|
||||
static inline constexpr specific specific_term{};
|
||||
};
|
||||
|
||||
using Terms = utils::types::TypeList<specific>;
|
||||
};
|
||||
}
|
||||
|
||||
43
src/include/serif/discretization/blocks/runtime.hpp
Normal file
43
src/include/serif/discretization/blocks/runtime.hpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include <string_view>
|
||||
#include "serif/discretization/blocks/concepts.hpp"
|
||||
|
||||
namespace serif::discretization::blocks {
|
||||
struct FieldDescriptor final {
|
||||
std::string_view name;
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator==(const FieldDescriptor&, const FieldDescriptor&) noexcept = default;
|
||||
};
|
||||
|
||||
struct TermDescriptor final {
|
||||
FieldDescriptor field;
|
||||
std::string_view name;
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator==(const TermDescriptor&, const TermDescriptor&) noexcept = default;
|
||||
};
|
||||
|
||||
template <Field FieldT>
|
||||
inline constexpr FieldDescriptor field_descriptor{.name = FieldT::name};
|
||||
|
||||
template <Term TermT>
|
||||
inline constexpr TermDescriptor term_descriptor{
|
||||
.field = field_descriptor<typename TermT::field_type>,
|
||||
.name = TermT::name
|
||||
};
|
||||
}
|
||||
|
||||
template <>
|
||||
struct std::formatter<serif::discretization::blocks::FieldDescriptor> : std::formatter<std::string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::blocks::FieldDescriptor& fd, FormatContext& ctx) const {
|
||||
return std::formatter<std::string_view>::format(fd.name, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::formatter<serif::discretization::blocks::TermDescriptor> : std::formatter<std::string> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::blocks::TermDescriptor& td, FormatContext& ctx) const {
|
||||
return std::formatter<std::string>::format(std::format("TermDescriptor(field={}, name={})", td.field, td.name), ctx);
|
||||
}
|
||||
};
|
||||
@@ -1 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "serif/discretization/blocks/concepts.hpp"
|
||||
|
||||
namespace serif::discretization::blocks {
|
||||
template <Term TermT>
|
||||
using FieldOf = std::remove_cvref_t<TermT>::field_type;
|
||||
|
||||
template <HasFunctionalSpace TermT>
|
||||
using FunctionalSpaceOf = std::remove_cvref_t<TermT>::FunctionalSpace;
|
||||
|
||||
template <HasSupport TermT>
|
||||
using SupportOf = std::remove_cvref_t<TermT>::Support;
|
||||
|
||||
template <HasQuantity TermT>
|
||||
using QuantityOf = std::remove_cvref_t<TermT>::Quantity;
|
||||
|
||||
template <typename FieldT>
|
||||
using TermsOf = FieldT::Terms;
|
||||
|
||||
template <SpatialTerm TermT>
|
||||
struct SpatialTermTraits {
|
||||
using FunctionalSpace = TermT::FunctionalSpace;
|
||||
using Support = TermT::Support;
|
||||
|
||||
static constexpr auto family = FunctionalSpace::functionalSpace.family;
|
||||
static constexpr int order = FunctionalSpace::order;
|
||||
static constexpr std::size_t rank = FunctionalSpace::rank;
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
#include <span>
|
||||
#include <array>
|
||||
#include <format>
|
||||
#include <numeric>
|
||||
|
||||
#include "serif/discretization/domain/types.hpp"
|
||||
#include "serif/utils/misc/std_helper/cleaning.hpp"
|
||||
@@ -17,10 +22,33 @@ namespace serif::discretization::domain {
|
||||
concept IsBoundary = AIsBaseClassOfB<T, Boundary>;
|
||||
|
||||
// A DomainSet is several volumes treated as one. StellarDomains
|
||||
// (core + envelope) is the motivating example.
|
||||
// (core + envelope) is a motivating example.
|
||||
template <IsDomain... DomainTs>
|
||||
struct DomainSet {
|
||||
static constexpr std::size_t count = CountVariadicArguments<DomainTs...>();
|
||||
|
||||
static inline const std::array<std::string_view, count> names{
|
||||
DomainTs::name...
|
||||
};
|
||||
};
|
||||
|
||||
// Do not make the constructors of this class explicit. We want to be able to pass a single domain or a domain set to functions that take a DomainOrSet.
|
||||
class DomainOrSet final {
|
||||
public:
|
||||
template <IsDomain DomainT>
|
||||
// ReSharper disable once CppNonExplicitConvertingConstructor
|
||||
constexpr DomainOrSet(DomainT) noexcept : m_domains(&DomainT::name, 1) {}
|
||||
|
||||
template <IsDomain... DomainTs>
|
||||
// ReSharper disable once CppNonExplicitConvertingConstructor
|
||||
constexpr DomainOrSet(DomainSet<DomainTs...>) noexcept : m_domains(DomainSet<DomainTs...>::names) {}
|
||||
|
||||
[[nodiscard]] constexpr std::span<const std::string_view> domains() const noexcept {
|
||||
return m_domains;
|
||||
}
|
||||
|
||||
private:
|
||||
std::span<const std::string_view> m_domains;
|
||||
};
|
||||
|
||||
template <IsBoundary... BoundaryTs>
|
||||
@@ -53,3 +81,25 @@ namespace serif::discretization::domain {
|
||||
template <typename BoundaryT, typename BoundaryIDT>
|
||||
concept BoundaryIsIdentifiedBy = utils::misc::std_helper::SameType<BoundaryT, typename BoundaryIDT::boundary_type>;
|
||||
}
|
||||
|
||||
template <>
|
||||
struct std::formatter<serif::discretization::domain::DomainOrSet> : std::formatter<std::string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::domain::DomainOrSet& dos, FormatContext& ctx) const {
|
||||
auto join = [&](std::span<const std::string_view> strings, std::string_view delimiter) {
|
||||
return std::accumulate(
|
||||
strings.begin(),
|
||||
strings.end(),
|
||||
std::string{},
|
||||
[delimiter](std::string acc, std::string_view s) {
|
||||
if (!acc.empty()) {
|
||||
acc += delimiter;
|
||||
}
|
||||
acc += s;
|
||||
return acc;
|
||||
}
|
||||
);
|
||||
};
|
||||
return std::formatter<std::string_view>::format(std::format("DomainOrSet(domains=[{}])", join(dos.domains(), ", ")), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
10
src/include/serif/discretization/forms/concept.hpp
Normal file
10
src/include/serif/discretization/forms/concept.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include "serif/discretization/forms/forms.hpp"
|
||||
|
||||
namespace serif::discretization::forms {
|
||||
template <typename FormCandidate>
|
||||
concept Form = std::derived_from<std::remove_cvref_t<FormCandidate>, form> && // A form must derive from form
|
||||
requires {
|
||||
typename std::remove_cvref_t<FormCandidate>::operand_types; // a form must define a type list of operand types for the trial and test terms
|
||||
}; // We could consider strengthening this to enforce that the type list (operand_types) contains only operand types and that there is an entry for TrialT and TestT
|
||||
}
|
||||
48
src/include/serif/discretization/forms/form_traits.hpp
Normal file
48
src/include/serif/discretization/forms/form_traits.hpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "serif/discretization/forms/concept.hpp"
|
||||
#include "serif/discretization/forms/forms.hpp"
|
||||
|
||||
namespace serif::discretization::forms {
|
||||
template <Form FormT>
|
||||
struct FormTraits {
|
||||
static constexpr int trial_order_reduction = 0;
|
||||
static constexpr int test_order_reduction = 0;
|
||||
};
|
||||
|
||||
template <blocks::Term TrialT, blocks::Term TestT>
|
||||
struct FormTraits<mass<TrialT, TestT>> {
|
||||
static constexpr int trial_order_reduction = 0;
|
||||
static constexpr int test_order_reduction = 0;
|
||||
};
|
||||
|
||||
template <blocks::Term TrialT, blocks::Term TestT>
|
||||
struct FormTraits<divergence<TrialT, TestT>> {
|
||||
static constexpr int trial_order_reduction = 1;
|
||||
static constexpr int test_order_reduction = 0;
|
||||
};
|
||||
|
||||
template <blocks::Term TrialT, blocks::Term TestT>
|
||||
struct FormTraits<source<TrialT, TestT>> {
|
||||
static constexpr int trial_order_reduction = 0;
|
||||
static constexpr int test_order_reduction = 0;
|
||||
};
|
||||
|
||||
template <blocks::Term TrialT, blocks::Term TestT>
|
||||
struct FormTraits<force<TrialT, TestT>> {
|
||||
static constexpr int trial_order_reduction = 0;
|
||||
static constexpr int test_order_reduction = 0;
|
||||
};
|
||||
|
||||
template <blocks::Term TrialT, blocks::Term TestT>
|
||||
struct FormTraits<boundary_flux<TrialT, TestT>> {
|
||||
static constexpr int trial_order_reduction = 0;
|
||||
static constexpr int test_order_reduction = 0;
|
||||
};
|
||||
|
||||
template <blocks::Term TrialT, blocks::Term TestT>
|
||||
struct FormTraits<projection<TrialT, TestT>> {
|
||||
static constexpr int trial_order_reduction = 0;
|
||||
static constexpr int test_order_reduction = 0;
|
||||
};
|
||||
}
|
||||
73
src/include/serif/discretization/forms/forms.hpp
Normal file
73
src/include/serif/discretization/forms/forms.hpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
#include "serif/discretization/blocks/concepts.hpp"
|
||||
#include "serif/discretization/forms/operands.hpp"
|
||||
#include "serif/utils/types/type_list.hpp"
|
||||
#include "serif/meta/names.hpp"
|
||||
|
||||
namespace serif::discretization::forms {
|
||||
struct form {};
|
||||
|
||||
template <blocks::Term TrialT, blocks::Term TestT>
|
||||
struct mass final : form {
|
||||
static constexpr std::string_view name = meta::template_name<mass>();
|
||||
|
||||
using trial_type = TrialT;
|
||||
using test_type = TestT;
|
||||
|
||||
using operand_types = utils::types::TypeList<Operand<TrialT>, Operand<TestT>>;
|
||||
};
|
||||
|
||||
template <blocks::Term TrialT, blocks::Term TestT>
|
||||
struct divergence final : form {
|
||||
static constexpr std::string_view name = meta::template_name<divergence>();
|
||||
|
||||
using trial_type = TrialT;
|
||||
using test_type = TestT;
|
||||
|
||||
using operand_types = utils::types::TypeList<Operand<TrialT, Operation::Divergence>, Operand<TestT>>;
|
||||
};
|
||||
|
||||
template <blocks::Term TrialT, blocks::Term TestT>
|
||||
struct source final : form {
|
||||
static constexpr std::string_view name = meta::template_name<source>();
|
||||
|
||||
using trial_type = TrialT;
|
||||
using test_type = TestT;
|
||||
|
||||
using operand_types = utils::types::TypeList<Operand<TrialT>, Operand<TestT>>;
|
||||
};
|
||||
|
||||
template <blocks::Term TrialT, blocks::Term TestT>
|
||||
struct force final : form {
|
||||
static constexpr std::string_view name = meta::template_name<force>();
|
||||
|
||||
using trial_type = TrialT;
|
||||
using test_type = TestT;
|
||||
|
||||
using operand_types = utils::types::TypeList<Operand<TrialT>, Operand<TestT>>;
|
||||
};
|
||||
|
||||
template <blocks::Term TrialT, blocks::Term TestT>
|
||||
struct boundary_flux final : form {
|
||||
static constexpr std::string_view name = meta::template_name<boundary_flux>();
|
||||
|
||||
using trial_type = TrialT;
|
||||
using test_type = TestT;
|
||||
|
||||
using operand_types = utils::types::TypeList<Operand<TrialT, Operation::NormalTrace>, Operand<TestT, Operation::NormalTrace>>;
|
||||
};
|
||||
|
||||
template <blocks::Term TrialT, blocks::Term TestT>
|
||||
struct projection final : form {
|
||||
static constexpr std::string_view name = meta::template_name<projection>();
|
||||
|
||||
using trial_type = TrialT;
|
||||
using test_type = TestT;
|
||||
|
||||
using operand_types = utils::types::TypeList<Operand<TrialT>, Operand<TestT>>;
|
||||
};
|
||||
}
|
||||
38
src/include/serif/discretization/forms/operands.hpp
Normal file
38
src/include/serif/discretization/forms/operands.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
#include "serif/discretization/blocks/concepts.hpp"
|
||||
|
||||
namespace serif::discretization::forms {
|
||||
struct Operation {
|
||||
struct Value {};
|
||||
struct Gradient {};
|
||||
struct Divergence {};
|
||||
struct Curl {};
|
||||
struct NormalTrace {};
|
||||
};
|
||||
|
||||
template <typename OperationCandidate>
|
||||
concept IsOperation =
|
||||
std::same_as<std::remove_cvref_t<OperationCandidate>, Operation::Value> ||
|
||||
std::same_as<std::remove_cvref_t<OperationCandidate>, Operation::Gradient> ||
|
||||
std::same_as<std::remove_cvref_t<OperationCandidate>, Operation::Divergence> ||
|
||||
std::same_as<std::remove_cvref_t<OperationCandidate>, Operation::Curl> ||
|
||||
std::same_as<std::remove_cvref_t<OperationCandidate>, Operation::NormalTrace>;
|
||||
|
||||
template <blocks::Term TermT, IsOperation OperationT = Operation::Value> // We choose to default to the value operation because it is the most common.
|
||||
struct Operand final {
|
||||
using term_type = TermT;
|
||||
using operation_type = OperationT;
|
||||
};
|
||||
|
||||
template <typename OperandCandidate>
|
||||
concept IsOperand = requires { // Recall that requires expressions are not evaluated. What this checks is that it is valid syntax given the type to requires the term type and operation type. It does not check that the term type is a valid term or that the operation type is a valid operation.
|
||||
typename std::remove_cvref_t<OperandCandidate>::term_type;
|
||||
typename std::remove_cvref_t<OperandCandidate>::operation_type; } &&
|
||||
blocks::Term<typename std::remove_cvref_t<OperandCandidate>::term_type> && // Then here, now that we know we can safely ask for the term type we validate it is actually a valid term
|
||||
IsOperation<typename std::remove_cvref_t<OperandCandidate>::operation_type>; // And here we validate that the operation type is actually a valid operation
|
||||
|
||||
};
|
||||
34
src/include/serif/discretization/forms/runtime.hpp
Normal file
34
src/include/serif/discretization/forms/runtime.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <format>
|
||||
|
||||
#include "serif/discretization/blocks/runtime.hpp"
|
||||
#include "serif/discretization/forms/concept.hpp"
|
||||
|
||||
namespace serif::discretization::forms {
|
||||
struct FormDescriptor final {
|
||||
std::string_view name;
|
||||
|
||||
blocks::TermDescriptor trial;
|
||||
blocks::TermDescriptor test;
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator==(const FormDescriptor&, const FormDescriptor&) noexcept = default;
|
||||
};
|
||||
|
||||
template <Form FormT>
|
||||
inline constexpr FormDescriptor form_descriptor{
|
||||
.name = FormT::name,
|
||||
.trial = blocks::term_descriptor<typename FormT::trial_type>,
|
||||
.test = blocks::term_descriptor<typename FormT::test_type>
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <>
|
||||
struct std::formatter<serif::discretization::forms::FormDescriptor> : std::formatter<std::string> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::forms::FormDescriptor& fd, FormatContext& ctx) const {
|
||||
return std::formatter<std::string>::format(std::format("FormDescriptor(name={}, trial={}, test={})", fd.name, fd.trial, fd.test), ctx);
|
||||
}
|
||||
};
|
||||
167
src/include/serif/discretization/functional_space/space.hpp
Normal file
167
src/include/serif/discretization/functional_space/space.hpp
Normal file
@@ -0,0 +1,167 @@
|
||||
#pragma once
|
||||
#include <type_traits>
|
||||
#include <format>
|
||||
|
||||
#include "serif/meta/enums.hpp"
|
||||
|
||||
namespace serif::discretization::functional_space {
|
||||
template <std::size_t R>
|
||||
struct Rank {
|
||||
constexpr static std::size_t value = R;
|
||||
};
|
||||
|
||||
enum class Family {
|
||||
l2,
|
||||
h1,
|
||||
hdiv,
|
||||
hcurl
|
||||
};
|
||||
|
||||
inline static auto Family2StringMap = meta::enum_to_enum_string_map<Family>();
|
||||
inline static auto String2FamilyMap = meta::enum_to_string_enum_map<Family>();
|
||||
|
||||
enum class Continuity {
|
||||
discontinuous,
|
||||
continuous,
|
||||
normal_continuous,
|
||||
tangential_continuous
|
||||
};
|
||||
|
||||
inline static auto Continuity2StringMap = meta::enum_to_enum_string_map<Continuity>();
|
||||
inline static auto String2ContinuityMap = meta::enum_to_string_enum_map<Continuity>();
|
||||
|
||||
enum class Trace {
|
||||
none,
|
||||
value,
|
||||
normal,
|
||||
tangential
|
||||
};
|
||||
|
||||
inline static auto Trace2StringMap = meta::enum_to_enum_string_map<Trace>();
|
||||
inline static auto String2TraceMap = meta::enum_to_string_enum_map<Trace>();
|
||||
|
||||
enum class Mapping {
|
||||
identity,
|
||||
contravariant_piola,
|
||||
covariant_piola
|
||||
};
|
||||
|
||||
inline static auto Mapping2StringMap = meta::enum_to_enum_string_map<Mapping>();
|
||||
inline static auto String2MappingMap = meta::enum_to_string_enum_map<Mapping>();
|
||||
|
||||
struct SpaceDescriptor final {
|
||||
const Family family;
|
||||
const Continuity continuity;
|
||||
const Trace trace;
|
||||
const Mapping mapping;
|
||||
};
|
||||
|
||||
inline static constexpr SpaceDescriptor L2{
|
||||
.family = Family::l2,
|
||||
.continuity = Continuity::discontinuous,
|
||||
.trace = Trace::none,
|
||||
.mapping = Mapping::identity
|
||||
};
|
||||
|
||||
inline static constexpr SpaceDescriptor H1{
|
||||
.family = Family::h1,
|
||||
.continuity = Continuity::continuous,
|
||||
.trace = Trace::value,
|
||||
.mapping = Mapping::identity
|
||||
};
|
||||
|
||||
inline static constexpr SpaceDescriptor Hdiv{
|
||||
.family = Family::hdiv,
|
||||
.continuity = Continuity::normal_continuous,
|
||||
.trace = Trace::normal,
|
||||
.mapping = Mapping::contravariant_piola
|
||||
};
|
||||
|
||||
inline static constexpr SpaceDescriptor Hcurl{
|
||||
.family = Family::hcurl,
|
||||
.continuity = Continuity::tangential_continuous,
|
||||
.trace = Trace::tangential,
|
||||
.mapping = Mapping::covariant_piola
|
||||
};
|
||||
|
||||
template <typename SpaceDescriptorCandidate>
|
||||
concept IsSpaceDescriptor = std::is_same_v<SpaceDescriptor, SpaceDescriptorCandidate>;
|
||||
|
||||
template <SpaceDescriptor Space, int Order, std::size_t R>
|
||||
struct DiscreteFunctionalSpace {
|
||||
static constexpr SpaceDescriptor functional_space = Space;
|
||||
static constexpr int order = Order;
|
||||
using rank = Rank<R>;
|
||||
};
|
||||
}
|
||||
|
||||
template <std::size_t R>
|
||||
struct std::formatter<serif::discretization::functional_space::Rank<R>> : std::formatter<std::string> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::functional_space::Rank<R>& /*rank*/, FormatContext& ctx) const {
|
||||
if (R == 0) {
|
||||
return std::formatter<std::string>::format("Scalar", ctx);
|
||||
} else if (R == 1) {
|
||||
return std::formatter<std::string>::format("Vector", ctx);
|
||||
} else if (R >=2) {
|
||||
return std::formatter<std::string>::format("Rank " + std::to_string(R) + " tensor", ctx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::formatter<serif::discretization::functional_space::SpaceDescriptor> : std::formatter<std::string> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::functional_space::SpaceDescriptor& space, FormatContext& ctx) const {
|
||||
return std::formatter<std::string>::format(std::format("SpaceDescriptor(family={}, continuity={}, trace={}, mapping={})",
|
||||
serif::discretization::functional_space::Family2StringMap.at(space.family),
|
||||
serif::discretization::functional_space::Continuity2StringMap.at(space.continuity),
|
||||
serif::discretization::functional_space::Trace2StringMap.at(space.trace),
|
||||
serif::discretization::functional_space::Mapping2StringMap.at(space.mapping)), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
// formatter for DiscreteFunctionalSpace
|
||||
template <serif::discretization::functional_space::SpaceDescriptor Space, int Order, std::size_t R>
|
||||
struct std::formatter<serif::discretization::functional_space::DiscreteFunctionalSpace<Space, Order, R>> : std::formatter<std::string> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::functional_space::DiscreteFunctionalSpace<Space, Order, R>& space, FormatContext& ctx) const {
|
||||
return std::formatter<std::string>::format(std::format("DiscreteFunctionalSpace(functional_space={}, order={}, rank={})",
|
||||
space.functional_space,
|
||||
space.order,
|
||||
space.rank), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
// formatter for each enum
|
||||
template <>
|
||||
struct std::formatter<serif::discretization::functional_space::Family> : std::formatter<std::string> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::functional_space::Family& family, FormatContext& ctx) const {
|
||||
return std::formatter<std::string>::format(serif::discretization::functional_space::Family2StringMap.at(family), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::formatter<serif::discretization::functional_space::Continuity> : std::formatter<std::string> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::functional_space::Continuity& continuity, FormatContext& ctx) const {
|
||||
return std::formatter<std::string>::format(serif::discretization::functional_space::Continuity2StringMap.at(continuity), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::formatter<serif::discretization::functional_space::Trace> : std::formatter<std::string> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::functional_space::Trace& trace, FormatContext& ctx) const {
|
||||
return std::formatter<std::string>::format(serif::discretization::functional_space::Trace2StringMap.at(trace), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::formatter<serif::discretization::functional_space::Mapping> : std::formatter<std::string> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::functional_space::Mapping& mapping, FormatContext& ctx) const {
|
||||
return std::formatter<std::string>::format(serif::discretization::functional_space::Mapping2StringMap.at(mapping), ctx);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "mfem/fem/intrules.hpp"
|
||||
|
||||
namespace serif::discretization::quadrature::backend::mfem {
|
||||
template <typename IntegratorT>
|
||||
concept HasReferenceSetIntegrationRule = requires (IntegratorT &integrator, const ::mfem::IntegrationRule &integration_rule) {
|
||||
integrator.SetIntegrationRule(integration_rule);
|
||||
};
|
||||
|
||||
template <typename IntegratorT>
|
||||
concept HasPointerSetIntegrationRule = requires (IntegratorT &integrator, const ::mfem::IntegrationRule &integration_rule) {
|
||||
integrator.SetIntRule(&integration_rule);
|
||||
};
|
||||
|
||||
template <typename IntegratorT>
|
||||
concept HasSetIntRule = requires (IntegratorT &integrator, const ::mfem::IntegrationRule &integration_rule) {
|
||||
integrator.SetIntRule(integration_rule);
|
||||
};
|
||||
|
||||
template <typename IntegratorT>
|
||||
concept ConfigurableIntegrator = HasReferenceSetIntegrationRule<IntegratorT> ||
|
||||
HasPointerSetIntegrationRule<IntegratorT> ||
|
||||
HasSetIntRule<IntegratorT>;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "mfem/fem/intrules.hpp"
|
||||
|
||||
#include "serif/discretization/quadrature/backend/mfem/concepts.hpp"
|
||||
|
||||
namespace serif::discretization::quadrature::backend::mfem {
|
||||
template <ConfigurableIntegrator IntegratorT>
|
||||
void set_integration_rule(IntegratorT &integrator, const ::mfem::IntegrationRule &integration_rule) {
|
||||
if constexpr (HasReferenceSetIntegrationRule<IntegratorT>) {
|
||||
integrator.SetIntegrationRule(integration_rule);
|
||||
} else if constexpr (HasPointerSetIntegrationRule<IntegratorT>) {
|
||||
integrator.SetIntegrationRule(&integration_rule);
|
||||
} else {
|
||||
integrator.SetIntRule(&integration_rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
#include <span>
|
||||
|
||||
#include "mfem/fem/geom.hpp"
|
||||
#include "mfem/fem/intrules.hpp"
|
||||
|
||||
#include "serif/discretization/blocks/traits.hpp"
|
||||
#include "serif/discretization/quadrature/rules.hpp"
|
||||
|
||||
#include "serif/discretization/quadrature/backend/order.hpp"
|
||||
|
||||
#include "serif/discretization/domain/concepts.hpp"
|
||||
#include "serif/discretization/quadrature/policy.hpp"
|
||||
#include "serif/discretization/quadrature/query.hpp"
|
||||
|
||||
#include "serif/discretization/quadrature/backend/mfem/concepts.hpp"
|
||||
#include "serif/discretization/quadrature/backend/mfem/mfem_static_order.hpp"
|
||||
|
||||
// We may want to consider renaming this file to runtime to stick to convention. The reason
|
||||
// I did not initially is that this is more specific than pure runtime projection of compile
|
||||
// time invariants (which is what most of the other files named runtime.hpp do). Something to think
|
||||
// about
|
||||
|
||||
// Note also that you will see ::mfem throughout. This is an important syntax to use here since MFEM's
|
||||
// code lives in the mfem namespace; however, we also have code living in an mfem namespace.
|
||||
// Outside of translation units that include using namespace serif::discretization::quadrature::backend
|
||||
// and outside of the serif::discretization::quadrature::backend::mfem namespace itself these two things
|
||||
// do not conflict. However, in those two cases there is a degeneracy the compiler cannot
|
||||
// resolve between mfem:: and mfem:: (see what I did there?) Using ::mfem explicitly tells the compiler
|
||||
// that we are looking from the root of the namespace tree not the current namespace. This is important to avoid ambiguity and compiler errors.
|
||||
|
||||
namespace serif::discretization::quadrature::backend::mfem {
|
||||
struct MFEMQuadratureContext final {
|
||||
QuadratureRole role = QuadratureRole::discretization;
|
||||
|
||||
::mfem::Geometry::Type geometry = ::mfem::Geometry::INVALID;
|
||||
int geometry_weight_order = 0;
|
||||
|
||||
std::span<const int> dynamic_orders{};
|
||||
domain::DomainOrSet domain = domain::AllDomains{};
|
||||
|
||||
MappingKind mapping = MappingKind::none;
|
||||
};
|
||||
|
||||
struct MFEMRule final {
|
||||
Resolution resolution;
|
||||
|
||||
const ::mfem::IntegrationRule *integration_rule = nullptr;
|
||||
};
|
||||
|
||||
class MFEMResolver final {
|
||||
public:
|
||||
explicit MFEMResolver (Policy policy) noexcept : m_policy(std::move(policy)) {} // Do we actually want to use move rvalue semantics here?
|
||||
|
||||
template <forms::Form FormT>
|
||||
[[nodiscard]] MFEMRule resolve(const MFEMQuadratureContext& context) const {
|
||||
if (context.geometry_weight_order < 0) {
|
||||
utils::errors::serif_error<std::invalid_argument, utils::errors::SERiFErrorCode::INVALID_QUADRATURE_ORDER>("MFEM geometry weight order must be non-negative. Received an order of {}", context.geometry_weight_order);
|
||||
}
|
||||
|
||||
const int dynamic_order = sum_dynamic_orders(context.dynamic_orders);
|
||||
const int base_order = mfem_form_static_order<FormT> + dynamic_order + context.geometry_weight_order;
|
||||
|
||||
const Query query {
|
||||
.form = forms::form_descriptor<FormT>,
|
||||
.role = context.role,
|
||||
.domain = context.domain,
|
||||
.mapping = context.mapping,
|
||||
.base_order = base_order
|
||||
};
|
||||
|
||||
const Resolution resolution = m_policy.resolve(query);
|
||||
|
||||
const ::mfem::IntegrationRule &integration_rule = ::mfem::IntRules.Get(context.geometry, resolution.order);
|
||||
return MFEMRule{.resolution = resolution, .integration_rule = &integration_rule};
|
||||
}
|
||||
|
||||
template <forms::Form FormT, ConfigurableIntegrator IntegratorT>
|
||||
Resolution configure(IntegratorT &integrator, const MFEMQuadratureContext& context) const {
|
||||
const MFEMRule rule = resolve<FormT>(context);
|
||||
set_integration_rule(integrator, *rule.integration_rule);
|
||||
return rule.resolution;
|
||||
}
|
||||
|
||||
private:
|
||||
Policy m_policy;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "serif/discretization/blocks/concepts.hpp"
|
||||
#include "serif/discretization/forms/operands.hpp"
|
||||
#include "serif/utils/types/type_list.hpp"
|
||||
#include "serif/discretization/quadrature/backend/order.hpp"
|
||||
|
||||
namespace serif::discretization::quadrature::backend::mfem {
|
||||
template <typename OperandListT>
|
||||
struct MFEMOperandListOrder;
|
||||
|
||||
template <forms::IsOperand... OperandTs>
|
||||
struct MFEMOperandListOrder<utils::types::TypeList<OperandTs...>> final {
|
||||
static constexpr int value = (resolve_operand_order<OperandTs>() + ... + 0);
|
||||
};
|
||||
|
||||
template <forms::Form FormT>
|
||||
inline constexpr int mfem_form_static_order = MFEMOperandListOrder<typename FormT::operand_types>::value;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "serif/discretization/functional_space/space.hpp"
|
||||
#include "serif/discretization/blocks/concepts.hpp"
|
||||
#include "serif/discretization/forms/operands.hpp"
|
||||
#include "serif/utils/error/errors.hpp"
|
||||
|
||||
namespace serif::discretization::quadrature::backend::mfem {
|
||||
template <typename FunctionalSpaceT>
|
||||
struct DiscreteFunctionalSpaceTraits;
|
||||
|
||||
template <auto SpaceV, auto OrderV, auto RankV>
|
||||
struct DiscreteFunctionalSpaceTraits<functional_space::DiscreteFunctionalSpace<SpaceV, OrderV, RankV>> final {
|
||||
static constexpr auto space = SpaceV;
|
||||
static constexpr auto order = static_cast<int>(OrderV);
|
||||
static constexpr std::size_t rank = static_cast<std::size_t>(RankV);
|
||||
};
|
||||
|
||||
template <blocks::HasFunctionalSpace TermT>
|
||||
using FunctionalSpaceTraits = DiscreteFunctionalSpaceTraits<typename TermT::FunctionalSpace>;
|
||||
|
||||
template <forms::IsOperand OperandT>
|
||||
[[nodiscard]] consteval int resolve_operand_order() noexcept {
|
||||
using Operand = std::remove_cvref_t<OperandT>;
|
||||
using Term = Operand::term_type;
|
||||
using Operation = Operand::operation_type;
|
||||
|
||||
if constexpr (!blocks::HasFunctionalSpace<Term>) {
|
||||
static_assert(std::same_as<forms::Operation, forms::Operation::Value>, "Non-Spatial terms only support the value operation at the moment.");
|
||||
return 0;
|
||||
} else {
|
||||
using Space = FunctionalSpaceTraits<Term>;
|
||||
|
||||
constexpr auto family = Space::space.family;
|
||||
constexpr int order = Space::order;
|
||||
|
||||
if constexpr (std::same_as<Operation, forms::Operation::Value>) {
|
||||
if constexpr (family == functional_space::Family::hdiv) {
|
||||
return order + 1;
|
||||
} else {
|
||||
return order;
|
||||
}
|
||||
} else if constexpr (std::same_as<Operation, forms::Operation::Gradient>) {
|
||||
static_assert(family == functional_space::Family::h1, "Only H1 spaces support the gradient operation at the moment.");
|
||||
|
||||
return order > 0 ? order - 1 : 0;
|
||||
} else if constexpr (std::same_as<Operation, forms::Operation::Divergence>) {
|
||||
static_assert(family == functional_space::Family::hdiv, "Only H(div) spaces support the divergence operation at the moment.");
|
||||
|
||||
return order;
|
||||
} else if constexpr (std::same_as<Operation, forms::Operation::Curl>) {
|
||||
static_assert(family == functional_space::Family::hcurl, "Only H(curl) spaces support the curl operation at the moment.");
|
||||
|
||||
return order > 0 ? order - 1 : 0;
|
||||
} else if constexpr (std::same_as<Operation, forms::Operation::NormalTrace>) {
|
||||
static_assert(family == functional_space::Family::hdiv, "Only H(div) spaces support the normal trace operation at the moment.");
|
||||
|
||||
return order;
|
||||
} else {
|
||||
static_assert(false, "Unsupported operation type. This indicates that an operation was used which is not currently registered in the operand configuration system. If you are a developer and you are seeing this error, please register your operation in the resolve_operand_order function in discretization/quadrature/detail.hpp. If you are a user and you are seeing this error, please report it to the SERiF developers.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline int sum_dynamic_orders(const std::span<const int> dynamic_orders) {
|
||||
int total = 0;
|
||||
for (const int order : dynamic_orders) {
|
||||
if (order < 0) {
|
||||
utils::errors::serif_error<std::invalid_argument, utils::errors::SERiFErrorCode::INVALID_QUADRATURE_ORDER>("Dynamic quadrature order cannot be negative. Received {}.", order);
|
||||
}
|
||||
|
||||
total += order;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
20
src/include/serif/discretization/quadrature/policy.hpp
Normal file
20
src/include/serif/discretization/quadrature/policy.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "serif/discretization/quadrature/query.hpp"
|
||||
#include "serif/discretization/quadrature/rules.hpp"
|
||||
|
||||
namespace serif::discretization::quadrature {
|
||||
class Policy final {
|
||||
public:
|
||||
explicit Policy(RuleSet ruleSet);
|
||||
|
||||
[[nodiscard]] Resolution resolve(const Query &query) const;
|
||||
|
||||
private:
|
||||
[[nodiscard]] static int compute_base_order(const Query &query);
|
||||
[[nodiscard]] const RuleControl &get_role_control(QuadratureRole role) const;
|
||||
|
||||
private:
|
||||
RuleSet m_ruleSet;
|
||||
};
|
||||
}
|
||||
74
src/include/serif/discretization/quadrature/query.hpp
Normal file
74
src/include/serif/discretization/quadrature/query.hpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <format>
|
||||
#include <string>
|
||||
|
||||
#include "serif/discretization/quadrature/rules.hpp"
|
||||
#include "serif/discretization/domain/physical_domains.hpp"
|
||||
#include "serif/discretization/forms/form_traits.hpp"
|
||||
#include "serif/discretization/forms/runtime.hpp"
|
||||
|
||||
namespace serif::discretization::quadrature {
|
||||
struct Query final {
|
||||
forms::FormDescriptor form;
|
||||
|
||||
QuadratureRole role = QuadratureRole::discretization;
|
||||
domain::DomainOrSet domain = domain::AllDomains{};
|
||||
MappingKind mapping = MappingKind::none;
|
||||
|
||||
int trial_order = 0;
|
||||
int test_order = 0;
|
||||
int coefficient_order = 0;
|
||||
int geometry_weight_order = 0;
|
||||
int trial_order_reduction = 0;
|
||||
int test_order_reduction = 0;
|
||||
|
||||
std::optional<int> base_order;
|
||||
};
|
||||
|
||||
template <forms::Form FormT>
|
||||
[[nodiscard]] constexpr Query make_query(
|
||||
const QuadratureRole role,
|
||||
const domain::DomainOrSet domain,
|
||||
const MappingKind mapping,
|
||||
const int trialOrder,
|
||||
const int testOrder,
|
||||
const int coefficientOrder,
|
||||
const int geometryWeightOrder,
|
||||
const std::optional<int> baseOrder = std::nullopt
|
||||
) noexcept {
|
||||
return Query{
|
||||
.form = forms::form_descriptor<FormT>,
|
||||
.role = role,
|
||||
.domain = domain,
|
||||
.mapping = mapping,
|
||||
.trial_order = trialOrder,
|
||||
.test_order = testOrder,
|
||||
.coefficient_order = coefficientOrder,
|
||||
.geometry_weight_order = geometryWeightOrder,
|
||||
.trial_order_reduction = forms::FormTraits<FormT>::trial_order_reduction,
|
||||
.test_order_reduction = forms::FormTraits<FormT>::test_order_reduction,
|
||||
.base_order = baseOrder
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
struct std::formatter<serif::discretization::quadrature::Query> : std::formatter<std::string> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::quadrature::Query& q, FormatContext& ctx) const {
|
||||
return std::formatter<std::string>::format(std::format("Query(form={}, role={}, domain={}, mapping={}, trial_order={}, test_order={}, coefficient_order={}, geometry_weight_order={}, trial_order_reduction={}, test_order_reduction={}, base_order={})",
|
||||
q.form,
|
||||
q.role,
|
||||
q.domain,
|
||||
q.mapping,
|
||||
q.trial_order,
|
||||
q.test_order,
|
||||
q.coefficient_order,
|
||||
q.geometry_weight_order,
|
||||
q.trial_order_reduction,
|
||||
q.test_order_reduction,
|
||||
(q.base_order.has_value() ? std::to_string(q.base_order.value()) : "nullopt")
|
||||
), ctx);
|
||||
}
|
||||
};
|
||||
342
src/include/serif/discretization/quadrature/rules.hpp
Normal file
342
src/include/serif/discretization/quadrature/rules.hpp
Normal file
@@ -0,0 +1,342 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <format>
|
||||
#include <utility>
|
||||
|
||||
#include "serif/discretization/forms/runtime.hpp"
|
||||
|
||||
namespace serif::discretization::quadrature {
|
||||
enum class QuadratureRole {
|
||||
discretization,
|
||||
preconditioner,
|
||||
diagnostic,
|
||||
projection
|
||||
};
|
||||
|
||||
enum class MappingKind {
|
||||
none,
|
||||
affine,
|
||||
general,
|
||||
kelvin
|
||||
};
|
||||
|
||||
enum class Mode {
|
||||
fast,
|
||||
production,
|
||||
reference,
|
||||
convergence
|
||||
};
|
||||
|
||||
struct RuleControl {
|
||||
std::optional<int> fixed_order;
|
||||
int boost = 0;
|
||||
};
|
||||
|
||||
struct FormRule final {
|
||||
forms::FormDescriptor form;
|
||||
RuleControl control;
|
||||
};
|
||||
|
||||
class RuleSet {
|
||||
public:
|
||||
template <forms::Form FormT>
|
||||
void set_form_control(
|
||||
const RuleControl control
|
||||
) {
|
||||
set_form_control(forms::form_descriptor<FormT>, control);
|
||||
}
|
||||
|
||||
void set_form_control(
|
||||
const forms::FormDescriptor &form,
|
||||
const RuleControl control
|
||||
) {
|
||||
for (auto &[formDescriptor, ruleControl] : m_formRules) {
|
||||
if (formDescriptor == form) {
|
||||
ruleControl = control;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_formRules.push_back(FormRule{.form = form, .control = control});
|
||||
}
|
||||
|
||||
[[nodiscard]] const RuleControl *find_form_control(
|
||||
const forms::FormDescriptor &form
|
||||
) const noexcept {
|
||||
for (const auto &[formDescriptor, ruleControl] : m_formRules) {
|
||||
if (formDescriptor == form) {
|
||||
return &ruleControl;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void set_role_control(
|
||||
const QuadratureRole role,
|
||||
const RuleControl control
|
||||
) noexcept {
|
||||
switch (role) {
|
||||
case QuadratureRole::discretization:
|
||||
m_discretizationControl = control;
|
||||
break;
|
||||
|
||||
case QuadratureRole::preconditioner:
|
||||
m_preconditionerControl = control;
|
||||
break;
|
||||
|
||||
case QuadratureRole::diagnostic:
|
||||
m_diagnosticControl = control;
|
||||
break;
|
||||
|
||||
case QuadratureRole::projection:
|
||||
m_projectionControl = control;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] const RuleControl &role_control(
|
||||
const QuadratureRole role
|
||||
) const noexcept {
|
||||
switch (role) {
|
||||
case QuadratureRole::discretization:
|
||||
return m_discretizationControl;
|
||||
|
||||
case QuadratureRole::preconditioner:
|
||||
return m_preconditionerControl;
|
||||
|
||||
case QuadratureRole::diagnostic:
|
||||
return m_diagnosticControl;
|
||||
|
||||
case QuadratureRole::projection:
|
||||
return m_projectionControl;
|
||||
}
|
||||
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
void set_fallback_control(
|
||||
const RuleControl control
|
||||
) noexcept {
|
||||
m_fallbackControl = control;
|
||||
}
|
||||
|
||||
[[nodiscard]] const RuleControl &fallback_control() const noexcept {
|
||||
return m_fallbackControl;
|
||||
}
|
||||
|
||||
auto begin() {
|
||||
return m_formRules.begin();
|
||||
}
|
||||
|
||||
auto end() {
|
||||
return m_formRules.end();
|
||||
}
|
||||
|
||||
auto begin() const {
|
||||
return m_formRules.begin();
|
||||
}
|
||||
|
||||
auto end() const {
|
||||
return m_formRules.end();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<FormRule> m_formRules;
|
||||
|
||||
RuleControl m_discretizationControl;
|
||||
RuleControl m_preconditionerControl;
|
||||
RuleControl m_diagnosticControl;
|
||||
RuleControl m_projectionControl;
|
||||
|
||||
RuleControl m_fallbackControl;
|
||||
};
|
||||
|
||||
struct RoleControls {
|
||||
RuleControl discretization;
|
||||
RuleControl preconditioner;
|
||||
RuleControl diagnostic;
|
||||
RuleControl projection;
|
||||
};
|
||||
|
||||
|
||||
struct Resolution {
|
||||
int base_order;
|
||||
int boost;
|
||||
int order;
|
||||
bool used_fixed_order;
|
||||
};
|
||||
|
||||
struct QuadratureTermOptions {
|
||||
std::optional<int> fixed_order;
|
||||
int additional_boost = 0;
|
||||
};
|
||||
|
||||
struct QuadratureManifestOptions {
|
||||
bool enabled = false;
|
||||
bool include_repeated_queries = false;
|
||||
std::optional<std::string> output_file;
|
||||
};
|
||||
|
||||
struct QuadratureValidationOptions {
|
||||
bool require_explicit_base_order = false;
|
||||
bool require_explicit_mfem_rule = false;
|
||||
bool reject_negative_boosts = true;
|
||||
bool report_unused_overrides = true;
|
||||
};
|
||||
|
||||
struct QuadratureRoleOptions {
|
||||
QuadratureTermOptions discretization;
|
||||
QuadratureTermOptions preconditioner;
|
||||
QuadratureTermOptions diagnostic;
|
||||
QuadratureTermOptions projection;
|
||||
};
|
||||
|
||||
struct QuadratureOptions {
|
||||
Mode mode = Mode::production;
|
||||
int global_boost = 0;
|
||||
std::optional<int> fallback_fixed_order;
|
||||
|
||||
QuadratureTermOptions gravity_hdiv_mass;
|
||||
QuadratureTermOptions gravity_divergence;
|
||||
QuadratureTermOptions gravity_source;
|
||||
QuadratureTermOptions gravity_force;
|
||||
QuadratureTermOptions gravity_boundary;
|
||||
QuadratureTermOptions centrifugal;
|
||||
QuadratureTermOptions density_projection;
|
||||
QuadratureTermOptions eos_closure;
|
||||
QuadratureTermOptions hydrostatic_equilibrium;
|
||||
QuadratureTermOptions isobaric_surface;
|
||||
QuadratureTermOptions mesh_extension;
|
||||
QuadratureTermOptions mass_conservation;
|
||||
QuadratureTermOptions mass_normalization;
|
||||
QuadratureTermOptions center_of_mass;
|
||||
QuadratureTermOptions quadrupole;
|
||||
QuadratureTermOptions gravitational_energy;
|
||||
QuadratureTermOptions pressure_integral;
|
||||
QuadratureTermOptions pressure_force;
|
||||
QuadratureTermOptions virial;
|
||||
QuadratureTermOptions error_norm;
|
||||
|
||||
QuadratureRoleOptions roles;
|
||||
|
||||
std::vector<int> convergence_boosts = {0, 2, 4};
|
||||
QuadratureManifestOptions manifest;
|
||||
QuadratureValidationOptions validation;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <>
|
||||
struct std::formatter<serif::discretization::quadrature::QuadratureRole> : std::formatter<std::string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::quadrature::QuadratureRole& role, FormatContext& ctx) const {
|
||||
std::string_view roleStr;
|
||||
switch (role) {
|
||||
case serif::discretization::quadrature::QuadratureRole::preconditioner:
|
||||
roleStr = "preconditioner";
|
||||
break;
|
||||
case serif::discretization::quadrature::QuadratureRole::diagnostic:
|
||||
roleStr = "diagnostic";
|
||||
break;
|
||||
case serif::discretization::quadrature::QuadratureRole::projection:
|
||||
roleStr = "projection";
|
||||
break;
|
||||
default:
|
||||
roleStr = "unknown";
|
||||
break;
|
||||
}
|
||||
return std::formatter<std::string_view>::format(roleStr, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::formatter<serif::discretization::quadrature::MappingKind> : std::formatter<std::string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::quadrature::MappingKind& mk, FormatContext& ctx) const {
|
||||
std::string_view mappingKindStr;
|
||||
switch (mk) {
|
||||
case serif::discretization::quadrature::MappingKind::none:
|
||||
mappingKindStr = "none";
|
||||
break;
|
||||
case serif::discretization::quadrature::MappingKind::affine:
|
||||
mappingKindStr = "affine";
|
||||
break;
|
||||
case serif::discretization::quadrature::MappingKind::general:
|
||||
mappingKindStr = "general";
|
||||
break;
|
||||
case serif::discretization::quadrature::MappingKind::kelvin:
|
||||
mappingKindStr = "kelvin";
|
||||
break;
|
||||
default:
|
||||
mappingKindStr = "unknown";
|
||||
break;
|
||||
}
|
||||
return std::formatter<std::string_view>::format(mappingKindStr, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::formatter<serif::discretization::quadrature::Mode> : std::formatter<std::string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::quadrature::Mode& mode, FormatContext& ctx) const {
|
||||
std::string_view modeStr;
|
||||
switch (mode) {
|
||||
case serif::discretization::quadrature::Mode::fast:
|
||||
modeStr = "fast";
|
||||
break;
|
||||
case serif::discretization::quadrature::Mode::production:
|
||||
modeStr = "production";
|
||||
break;
|
||||
case serif::discretization::quadrature::Mode::reference:
|
||||
modeStr = "reference";
|
||||
break;
|
||||
case serif::discretization::quadrature::Mode::convergence:
|
||||
modeStr = "convergence";
|
||||
break;
|
||||
default:
|
||||
modeStr = "unknown";
|
||||
break;
|
||||
}
|
||||
return std::formatter<std::string_view>::format(modeStr, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::formatter<serif::discretization::quadrature::RuleControl> : std::formatter<std::string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::quadrature::RuleControl& rc, FormatContext& ctx) const {
|
||||
return std::formatter<std::string_view>::format(
|
||||
std::format("RuleControl(fixed_order={}, boost={})", rc.fixed_order.has_value() ? std::to_string(rc.fixed_order.value()) : "none", rc.boost),
|
||||
ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::formatter<serif::discretization::quadrature::FormRule> : std::formatter<std::string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::quadrature::FormRule& fr, FormatContext& ctx) const {
|
||||
return std::formatter<std::string_view>::format(
|
||||
std::format("FormRule(form={}, control={})", fr.form, fr.control),
|
||||
ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::formatter<serif::discretization::quadrature::RuleSet> : std::formatter<std::string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const serif::discretization::quadrature::RuleSet& rs, FormatContext& ctx) const {
|
||||
std::string rulesStr;
|
||||
for (const auto& rule : rs) {
|
||||
if (!rulesStr.empty()) {
|
||||
rulesStr += ", ";
|
||||
}
|
||||
rulesStr += std::format("{}", rule);
|
||||
}
|
||||
return std::formatter<std::string_view>::format(
|
||||
std::format("RuleSet(formRules=[{}])", rulesStr),
|
||||
ctx);
|
||||
}
|
||||
};
|
||||
@@ -4,29 +4,9 @@
|
||||
#include <string>
|
||||
|
||||
namespace serif::eos {
|
||||
enum class EOSEvaluationErrorCode : uint8_t {
|
||||
unsupported_relation,
|
||||
unsupported_derivative,
|
||||
wrong_input_count,
|
||||
wrong_input_quantity,
|
||||
nonfinite_input,
|
||||
outside_domain,
|
||||
nonfinite_result,
|
||||
invalid_thermodynamic_input
|
||||
};
|
||||
|
||||
class EOSEvaluationError final : public std::domain_error {
|
||||
public:
|
||||
explicit EOSEvaluationError(
|
||||
const EOSEvaluationErrorCode code,
|
||||
std::string message ):
|
||||
std::domain_error(std::move(message)),
|
||||
m_code(code) {}
|
||||
|
||||
[[nodiscard]] EOSEvaluationErrorCode code() const noexcept {
|
||||
return m_code;
|
||||
}
|
||||
private:
|
||||
EOSEvaluationErrorCode m_code;
|
||||
explicit EOSEvaluationError(std::string message):
|
||||
std::domain_error(std::move(message)){}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#pragma once
|
||||
#include <format>
|
||||
#include "serif/eos/relations/relations.hpp"
|
||||
#include "serif/dimensions/type_alias.hpp"
|
||||
#include "serif/dimensions/partial.hpp"
|
||||
#include "serif/utils/misc/finite.hpp"
|
||||
|
||||
namespace serif::eos::models {
|
||||
struct PolytropeParameters final {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "serif/dimensions/runtime/concepts.hpp"
|
||||
#include "serif/eos/exceptions.hpp"
|
||||
#include "serif/eos/relations/catalog.hpp"
|
||||
#include "serif/utils/error/codes.hpp"
|
||||
|
||||
namespace serif::eos::runtime {
|
||||
template <typename RelationT>
|
||||
@@ -134,32 +135,31 @@ namespace serif::eos::runtime {
|
||||
};
|
||||
};
|
||||
|
||||
[[nodiscard]] inline std::expected<double, EOSEvaluationError> runtimeEvaluationFailure(
|
||||
const EOSEvaluationErrorCode code,
|
||||
std::string message
|
||||
[[nodiscard]] inline std::expected<double, utils::errors::SERiFErrorCode> runtimeEvaluationFailure(
|
||||
const utils::errors::SERiFErrorCode code
|
||||
) {
|
||||
return std::unexpected<EOSEvaluationError>{EOSEvaluationError{code, std::move(message)}};
|
||||
return std::unexpected{code};
|
||||
}
|
||||
|
||||
template <typename EOS, typename OutputT, typename... InputTs>
|
||||
[[nodiscard]] std::expected<double, EOSEvaluationError> evaluateRuntimeRelation(
|
||||
[[nodiscard]] std::expected<double, utils::errors::SERiFErrorCode> evaluateRuntimeRelation(
|
||||
const EOS& eos,
|
||||
relations::Relation<OutputT, InputTs...> /*relation*/,
|
||||
const std::span<const dimensions::runtime::RuntimeQuantityValue> inputValues
|
||||
) {
|
||||
/* Once again we find ourself at a use of folding. I reccomend you read the comment earlier in this file regarding folding, or find the C++ docs on folding.
|
||||
/* Once again we find ourself at a use of folding. I recommend you read the comment earlier in this file regarding folding, or find the C++ docs on folding.
|
||||
*
|
||||
* The general premis here is that we call the evaluation function for the EOS with the given relation and input values. We fold over all indicies using the final ... operator.
|
||||
* The general premise here is that we call the evaluation function for the EOS with the given relation and input values. We fold over all indices using the final ... operator.
|
||||
* This is an implicit loop generated by the compiler over the so called "parameter pack".
|
||||
*/
|
||||
const auto invoke_evaluate = [&]<std::size_t... Indices>(std::index_sequence<Indices...>) {
|
||||
return eos::evaluate<OutputT>(eos, dimensions::QuantityValue<InputTs>{inputValues[Indices].value}...);
|
||||
return eos::evaluate<OutputT>(eos, dimensions::QuantityValue<InputTs>{inputValues[Indices].value}...).value();
|
||||
};
|
||||
|
||||
try {
|
||||
return invoke_evaluate(std::make_index_sequence<sizeof...(InputTs)>{});
|
||||
} catch (const EOSEvaluationError& e) { // We may want to reconsider using a try-catch for this, but this is good enough for now. Realistically if we want to not use a try catch we will need to change the signature of the EOS evaluation function to return a std::expected instead of throwing an exception. This is a larger change that I don't want to make right now.
|
||||
return std::unexpected<EOSEvaluationError>{e};
|
||||
return std::unexpected<utils::errors::SERiFErrorCode>{utils::errors::SERiFErrorCode::EOS_ERROR};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ namespace serif::eos::runtime {
|
||||
relations::Relation<OutputT, InputTs...> /*relation*/,
|
||||
const dimensions::runtime::ThermodynamicQuantityID withRespectTo,
|
||||
const std::span<const dimensions::runtime::RuntimeQuantityValue> inputValues,
|
||||
std::expected<double, EOSEvaluationError>& result
|
||||
std::expected<double, utils::errors::SERiFErrorCode>& result
|
||||
) {
|
||||
if (withRespectTo != dimensions::runtime::thermodynamicQuantityID<InputQuantityT>) {
|
||||
return false;
|
||||
@@ -182,26 +182,25 @@ namespace serif::eos::runtime {
|
||||
|
||||
try {
|
||||
result = invoke_evaluate_partial_derivative(std::index_sequence_for<InputTs...>{});
|
||||
} catch (const EOSEvaluationError& e) {
|
||||
result = std::unexpected<EOSEvaluationError>{e};
|
||||
} catch (const EOSEvaluationError& /*e*/) {
|
||||
result = std::unexpected{utils::errors::SERiFErrorCode::EOS_UNSUPPORTED_DERIVATIVE};
|
||||
}
|
||||
} else {
|
||||
result = runtimeEvaluationFailure(EOSEvaluationErrorCode::unsupported_derivative, "The requested EOS partial derivative is not available in the selected EOS.");
|
||||
result = runtimeEvaluationFailure(utils::errors::SERiFErrorCode::EOS_UNSUPPORTED_DERIVATIVE);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename EOS, typename OutputT, typename... InputTs>
|
||||
[[nodiscard]] std::expected<double, EOSEvaluationError> evaluateRuntimePartialDerivative (
|
||||
[[nodiscard]] std::expected<double, utils::errors::SERiFErrorCode> evaluateRuntimePartialDerivative (
|
||||
const EOS& eos,
|
||||
relations::Relation<OutputT, InputTs...> relation,
|
||||
const dimensions::runtime::ThermodynamicQuantityID withRespectTo,
|
||||
const std::span<const dimensions::runtime::RuntimeQuantityValue> inputValues
|
||||
) {
|
||||
std::expected<double, EOSEvaluationError> result = runtimeEvaluationFailure(
|
||||
EOSEvaluationErrorCode::unsupported_derivative,
|
||||
"The requested quantity is not an input to the EOS relation."
|
||||
std::expected<double, utils::errors::SERiFErrorCode> result = runtimeEvaluationFailure(
|
||||
utils::errors::SERiFErrorCode::EOS_UNSUPPORTED_DERIVATIVE
|
||||
);
|
||||
|
||||
[[maybe_unused]] const bool matched = (tryRuntimePartialDerivative<InputTs>(eos, relation, withRespectTo, inputValues, result) || ...); // Here we fold over all input and try to find one that we can evaluate.
|
||||
@@ -233,7 +232,7 @@ namespace serif::eos::runtime {
|
||||
|
||||
template <typename EOS, typename...RelationTs>
|
||||
struct RuntimeCatalogDispatch<EOS, relations::RelationCatalog<RelationTs...>> {
|
||||
[[nodiscard]] static std::expected<double, EOSEvaluationError> evaluate(
|
||||
[[nodiscard]] static std::expected<double, utils::errors::SERiFErrorCode> evaluate(
|
||||
const void *object,
|
||||
const dimensions::runtime::ThermodynamicQuantityID& outputQuantity,
|
||||
const std::span<const dimensions::runtime::RuntimeQuantityValue> inputValues
|
||||
@@ -241,8 +240,8 @@ namespace serif::eos::runtime {
|
||||
const auto &eos = *static_cast<const EOS*>(object); // Not sure if polymorphic type erasure is the right tool here. It works but we may want to revisit it if it precent compiler optimizations. This does however let us pass any eos model to the runtime view without needing to know the type at compile time (which is therefore helpful when building extension systems in other languages like python). Further, it may make sense to take the performance hit here and use something like a dynamic_cast to ensure that the object is actually of the correct type. This would be a runtime check but it would be a more robust check than just blindly casting to the expected type.
|
||||
|
||||
|
||||
std::expected<double, EOSEvaluationError> result = runtimeEvaluationFailure(
|
||||
EOSEvaluationErrorCode::unsupported_relation, "The requested EOS relation is not available in the current equation of state."
|
||||
std::expected<double, utils::errors::SERiFErrorCode> result = runtimeEvaluationFailure(
|
||||
utils::errors::SERiFErrorCode::EOS_UNSUPPORTED_RELATION
|
||||
);
|
||||
|
||||
/* Fold over all relations in the catalog and check if any of them match the requested output quantity and input quantities. If the match is found then
|
||||
@@ -253,7 +252,7 @@ namespace serif::eos::runtime {
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] static std::expected<double, EOSEvaluationError> partial_derivative(
|
||||
[[nodiscard]] static std::expected<double, utils::errors::SERiFErrorCode> partial_derivative(
|
||||
const void *object,
|
||||
const dimensions::runtime::ThermodynamicQuantityID& outputQuantity,
|
||||
const dimensions::runtime::ThermodynamicQuantityID& withRespectTo,
|
||||
@@ -261,8 +260,8 @@ namespace serif::eos::runtime {
|
||||
) {
|
||||
const auto &eos = *static_cast<const EOS*>(object); // Same Comment as above
|
||||
|
||||
std::expected<double, EOSEvaluationError> result = runtimeEvaluationFailure(
|
||||
EOSEvaluationErrorCode::unsupported_relation, "The requested EOS relation is not available in the current equation of state."
|
||||
std::expected<double, utils::errors::SERiFErrorCode> result = runtimeEvaluationFailure(
|
||||
utils::errors::SERiFErrorCode::EOS_UNSUPPORTED_RELATION
|
||||
);
|
||||
|
||||
[[maybe_unused]] const bool matched = ((runtimeRelationMatches<EOS, RelationTs>(outputQuantity, inputValues) ? (result = evaluateRuntimePartialDerivative(eos, RelationTs{}, withRespectTo, inputValues), true) : false) || ...);
|
||||
|
||||
@@ -7,11 +7,13 @@
|
||||
namespace serif::eos::runtime {
|
||||
class EOSView final {
|
||||
public:
|
||||
template <RuntimeEOSModel EOS>
|
||||
explicit EOSView(EOS& eos) noexcept :
|
||||
template <typename EOS>
|
||||
requires RuntimeEOSModel<std::remove_cvref_t<EOS>>
|
||||
explicit EOSView(const EOS& eos) noexcept :
|
||||
m_object(std::addressof(eos)),
|
||||
m_relations(&RuntimeEOSAdapter<std::remove_cv_t<EOS>>::evaluate),
|
||||
m_partialDerivative(&RuntimeEOSAdapter<std::remove_cv_t<EOS>>::partial_derivative) {}
|
||||
m_relations(runtimeRelationDescriptors<std::remove_cvref_t<EOS>>()),
|
||||
m_evaluate(&RuntimeEOSAdapter<std::remove_cvref_t<EOS>>::evaluate),
|
||||
m_partialDerivative(&RuntimeEOSAdapter<std::remove_cvref_t<EOS>>::partial_derivative) {}
|
||||
|
||||
// TODO: This function can be moved to an implantation file, it may need to be to prevent ODR violations
|
||||
[[nodiscard]] std::span<const dimensions::runtime::RuntimeRelationDescriptor> relations() const noexcept {
|
||||
@@ -26,36 +28,113 @@ namespace serif::eos::runtime {
|
||||
return find_relation(outputQuantity, inputQuantities) != nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::expected<dimensions::runtime::RuntimeQuantityValue, EOSEvaluationError> try_evaluate(
|
||||
[[nodiscard]] std::expected<dimensions::runtime::RuntimeQuantityValue, utils::errors::SERiFErrorCode> try_evaluate(
|
||||
const dimensions::runtime::ThermodynamicQuantityID outputQuantity,
|
||||
const std::span<const dimensions::runtime::RuntimeQuantityValue> inputQuantities
|
||||
) {
|
||||
) const {
|
||||
const auto validation = validate_relation_request(outputQuantity, inputQuantities);
|
||||
|
||||
if (!validation.has_value()) {
|
||||
return std::unexpected<EOSEvaluationError>{validation.error()};
|
||||
return std::unexpected{validation.error()};
|
||||
}
|
||||
|
||||
auto result = m_evaluate(m_object, outputQuantity, inputQuantities);
|
||||
|
||||
if (!result.has_value()) {
|
||||
return std::unexpected<EOSEvaluationError>{result.error()};
|
||||
return std::unexpected{result.error()};
|
||||
}
|
||||
return dimensions::runtime::RuntimeQuantityValue{outputQuantity, *result};
|
||||
}
|
||||
|
||||
// TODO: Still need to translate from the old code the templated version of try_evaluate along with the try_partial_derivative function. The templated version is more user friendly and should be kept, but the non-templated version is needed for the runtime view.
|
||||
template <dimensions::runtime::RuntimeIdentifiedThermodynamicQuantity OutputQuantity, dimensions::ThermodynamicQuantityValueType... InputValues>
|
||||
[[nodiscard]] std::expected<dimensions::QuantityValue<OutputQuantity>, utils::errors::SERiFErrorCode> try_evaluate(
|
||||
const InputValues... inputValues
|
||||
) const {
|
||||
constexpr bool inputsHaveRuntimeIdentifiers = (dimensions::runtime::RuntimeIdentifiedThermodynamicQuantity<dimensions::QuantityOfT<InputValues>> && ...);
|
||||
|
||||
static_assert(inputsHaveRuntimeIdentifiers, "Every runtime EOS input quantity needs a stable identifier.");
|
||||
|
||||
const std::array<dimensions::runtime::RuntimeQuantityValue, sizeof...(InputValues)> runtimeInputs {
|
||||
dimensions::runtime::RuntimeQuantityValue{
|
||||
.id = dimensions::runtime::thermodynamicQuantityID<dimensions::QuantityOfT<InputValues>>, .value = inputValues.value()
|
||||
}...
|
||||
};
|
||||
|
||||
auto result = try_evaluate(
|
||||
dimensions::runtime::thermodynamicQuantityID<OutputQuantity>, std::span<const dimensions::runtime::RuntimeQuantityValue>{runtimeInputs}
|
||||
);
|
||||
|
||||
if (!result.has_value()) {
|
||||
return std::unexpected<utils::errors::SERiFErrorCode>{result.error()};
|
||||
}
|
||||
|
||||
return dimensions::QuantityValue<OutputQuantity>{result->value};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::expected<double, utils::errors::SERiFErrorCode> try_partial_derivative(
|
||||
const dimensions::runtime::ThermodynamicQuantityID outputQuantity,
|
||||
const dimensions::runtime::ThermodynamicQuantityID withRespectTo,
|
||||
const std::span<const dimensions::runtime::RuntimeQuantityValue> inputValues
|
||||
) const {
|
||||
|
||||
const auto validation = validate_relation_request(outputQuantity, inputValues);
|
||||
|
||||
if (!validation.has_value()) {
|
||||
return std::unexpected<utils::errors::SERiFErrorCode>{validation.error()};
|
||||
}
|
||||
|
||||
const dimensions::runtime::RuntimeRelationDescriptor &descriptor = **validation;
|
||||
bool derivativeAvailable = false;
|
||||
|
||||
for (std::size_t index = 0; index < descriptor.inputQuantities.size(); ++index) {
|
||||
if (descriptor.inputQuantities[index] == withRespectTo) {
|
||||
derivativeAvailable = descriptor.hasPartialDerivative(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!derivativeAvailable) {
|
||||
return runtime_failure<double>(utils::errors::SERiFErrorCode::EOS_UNSUPPORTED_DERIVATIVE);
|
||||
}
|
||||
|
||||
return m_partialDerivative(m_object, outputQuantity, withRespectTo, inputValues);
|
||||
}
|
||||
|
||||
template <dimensions::runtime::RuntimeIdentifiedThermodynamicQuantity OutputQuantity, dimensions::runtime::RuntimeIdentifiedThermodynamicQuantity InputQuantity, dimensions::QuantityValueType... InputValues>
|
||||
[[nodiscard]] std::expected<dimensions::PartialDerivative<OutputQuantity, InputQuantity>, EOSEvaluationError> try_partial_derivative(
|
||||
const InputValues... inputValues
|
||||
) const {
|
||||
constexpr bool inputHaveRuntimeIdentifiers = (dimensions::runtime::RuntimeIdentifiedThermodynamicQuantity<dimensions::QuantityOfT<InputValues>> && ...);
|
||||
static_assert(inputHaveRuntimeIdentifiers, "Every runtime EOS input quantity needs a stable identifier.");
|
||||
|
||||
const std::array<dimensions::runtime::RuntimeQuantityValue, sizeof...(InputValues)> runtimeInputs{
|
||||
dimensions::runtime::RuntimeQuantityValue{.id = dimensions::runtime::thermodynamicQuantityID<dimensions::QuantityOfT<InputValues>>, .value = inputValues.value()}...
|
||||
};
|
||||
|
||||
auto result = try_partial_derivative(
|
||||
dimensions::runtime::thermodynamicQuantityID<OutputQuantity>, // output
|
||||
dimensions::runtime::thermodynamicQuantityID<InputQuantity>, // input
|
||||
std::span<const dimensions::runtime::RuntimeQuantityValue>{runtimeInputs} // where to evaluate
|
||||
);
|
||||
|
||||
if (!result.has_value()) {
|
||||
return std::unexpected<EOSEvaluationError>{result.error()};
|
||||
}
|
||||
|
||||
return dimensions::PartialDerivative<OutputQuantity, InputQuantity>{*result};
|
||||
}
|
||||
|
||||
private: // Type aliases
|
||||
using RuntimeEvaluateFunction = std::expected<double, EOSEvaluationError> (*)(
|
||||
using RuntimeEvaluateFunction = std::expected<double, utils::errors::SERiFErrorCode> (*)(
|
||||
const void*,
|
||||
dimensions::runtime::ThermodynamicQuantityID,
|
||||
const dimensions::runtime::ThermodynamicQuantityID&,
|
||||
std::span<const dimensions::runtime::RuntimeQuantityValue>
|
||||
);
|
||||
|
||||
using RuntimePartialDerivativeFunction = std::expected<double, EOSEvaluationError> (*)(
|
||||
using RuntimePartialDerivativeFunction = std::expected<double, utils::errors::SERiFErrorCode> (*)(
|
||||
const void*,
|
||||
dimensions::runtime::ThermodynamicQuantityID,
|
||||
dimensions::runtime::ThermodynamicQuantityID,
|
||||
const dimensions::runtime::ThermodynamicQuantityID&,
|
||||
const dimensions::runtime::ThermodynamicQuantityID&,
|
||||
std::span<const dimensions::runtime::RuntimeQuantityValue>
|
||||
);
|
||||
private: // Private methods
|
||||
@@ -84,7 +163,7 @@ namespace serif::eos::runtime {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::expected<const dimensions::runtime::RuntimeRelationDescriptor *, EOSEvaluationError> validate_relation_request(
|
||||
[[nodiscard]] std::expected<const dimensions::runtime::RuntimeRelationDescriptor *, utils::errors::SERiFErrorCode> validate_relation_request(
|
||||
const dimensions::runtime::ThermodynamicQuantityID outputQuantity,
|
||||
const std::span<const dimensions::runtime::RuntimeQuantityValue> inputValues
|
||||
) const {
|
||||
@@ -114,32 +193,26 @@ namespace serif::eos::runtime {
|
||||
|
||||
if (!outputAvailable) {
|
||||
return runtime_failure<const dimensions::runtime::RuntimeRelationDescriptor *>(
|
||||
EOSEvaluationErrorCode::unsupported_relation,
|
||||
"The requested EOS relation is not available."
|
||||
utils::errors::SERiFErrorCode::EOS_UNSUPPORTED_RELATION
|
||||
);
|
||||
}
|
||||
|
||||
if (!inputCountAvailable) {
|
||||
return runtime_failure<const dimensions::runtime::RuntimeRelationDescriptor *>(
|
||||
EOSEvaluationErrorCode::wrong_input_count,
|
||||
"No EOS relation for given output quantity '" + std::string{outputQuantity.name()} + "' accepts the provided number of inputs."
|
||||
utils::errors::SERiFErrorCode::EOS_WRONG_INPUT_COUNT
|
||||
);
|
||||
}
|
||||
|
||||
return runtime_failure<const dimensions::runtime::RuntimeRelationDescriptor *>(
|
||||
EOSEvaluationErrorCode::wrong_input_quantity,
|
||||
"No EOS relation for given output quantity '" + std::string{outputQuantity.name()} + "' accepts the provided input quantities."
|
||||
utils::errors::SERiFErrorCode::EOS_WRONG_INPUT_QUANTITY
|
||||
);
|
||||
}
|
||||
|
||||
template <typename Value>
|
||||
[[nodiscard]] static std::expected<Value, EOSEvaluationError> runtime_failure(
|
||||
const EOSEvaluationErrorCode code,
|
||||
const std::string message
|
||||
[[nodiscard]] static std::expected<Value, utils::errors::SERiFErrorCode> runtime_failure(
|
||||
const utils::errors::SERiFErrorCode code
|
||||
) {
|
||||
return std::unexpected<EOSEvaluationError>{
|
||||
EOSEvaluationError{code, std::move(message)}
|
||||
};
|
||||
return std::unexpected{code};
|
||||
}
|
||||
|
||||
private: // Private members
|
||||
|
||||
31
src/include/serif/meta/enums.hpp
Normal file
31
src/include/serif/meta/enums.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include <unordered_map>
|
||||
#include <type_traits>
|
||||
|
||||
#include "magic_enum/magic_enum_all.hpp"
|
||||
|
||||
namespace serif::meta {
|
||||
template <typename EnumType>
|
||||
[[nodiscard]] constexpr std::unordered_map<std::string_view, EnumType> enum_to_string_enum_map() noexcept {
|
||||
static_assert(std::is_enum_v<EnumType>, "EnumType must be an enum type");
|
||||
|
||||
std::unordered_map<std::string_view, EnumType> map;
|
||||
|
||||
for (const auto& [value, name] : magic_enum::enum_entries<EnumType>()) {
|
||||
map.emplace(name, value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
template <typename EnumType>
|
||||
[[nodiscard]] constexpr std::unordered_map<EnumType, std::string_view> enum_to_enum_string_map() noexcept {
|
||||
static_assert(std::is_enum_v<EnumType>, "EnumType must be an enum type");
|
||||
|
||||
std::unordered_map<EnumType, std::string_view> map;
|
||||
|
||||
for (const auto& [value, name] : magic_enum::enum_entries<EnumType>()) {
|
||||
map.emplace(value, name);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
25
src/include/serif/meta/names.hpp
Normal file
25
src/include/serif/meta/names.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include <string_view>
|
||||
|
||||
#include "nameof.hpp"
|
||||
|
||||
/* If we ever move to C++26 or above we can replace these with native reflection
|
||||
*/
|
||||
namespace serif::meta {
|
||||
template <typename T>
|
||||
[[nodiscard]] consteval std::string_view type_name() noexcept {
|
||||
return nameof::nameof_short_type<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] consteval std::string_view template_name() noexcept {
|
||||
constexpr std::string_view typeName = type_name<T>();
|
||||
constexpr std::size_t templateStart = typeName.find('<');
|
||||
|
||||
if constexpr (templateStart == std::string_view::npos) {
|
||||
return typeName;
|
||||
}
|
||||
|
||||
return typeName.substr(0, templateStart);
|
||||
}
|
||||
}
|
||||
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...)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
#include "serif/dimensions/runtime/runtime.hpp"
|
||||
|
||||
namespace serif::dimensions::runtime {
|
||||
constexpr ThermodynamicQuantityID::ThermodynamicQuantityID(const std::string_view name) noexcept : m_name(name) {}
|
||||
|
||||
[[nodiscard]] constexpr std::string_view ThermodynamicQuantityID::name() const noexcept {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool RuntimeRelationDescriptor::hasPartialDerivative(const std::size_t inputIndex) const noexcept {
|
||||
return inputIndex < inputQuantities.size() && (partialDerivativeMask & (std::uint64_t{1} << inputIndex)) != 0;
|
||||
}
|
||||
}
|
||||
103
src/lib/serif/discretization/quadrature/policy.cpp
Normal file
103
src/lib/serif/discretization/quadrature/policy.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include <stdexcept>
|
||||
#include <format>
|
||||
#include <print>
|
||||
|
||||
#include "../../../../include/serif/discretization/quadrature/policy.hpp"
|
||||
|
||||
#include "serif/utils/warnings/warning.hpp"
|
||||
#include "serif/utils/error/errors.hpp"
|
||||
|
||||
namespace serif::discretization::quadrature {
|
||||
using namespace serif::utils::warnings;
|
||||
using namespace serif::utils::errors;
|
||||
|
||||
Policy::Policy(RuleSet ruleSet) : m_ruleSet(std::move(ruleSet)) {
|
||||
|
||||
}
|
||||
|
||||
int Policy::compute_base_order(const Query &query) {
|
||||
if (query.base_order.has_value()) {
|
||||
if (*query.base_order < 0) {
|
||||
serif_error<std::invalid_argument, SERiFErrorCode::INVALID_QUADRATURE_ORDER>("Quadrature base_order must be non-negative. Received: {}. This is likely a configuration error", *query.base_order);
|
||||
}
|
||||
|
||||
return *query.base_order;
|
||||
}
|
||||
|
||||
if (query.trial_order < 0 || query.test_order < 0 || query.coefficient_order < 0 || query.geometry_weight_order < 0) {
|
||||
serif_error<std::invalid_argument, SERiFErrorCode::INVALID_QUADRATURE_ORDER>("Quadrature orders must be non-negative. Received: trial_order={}, test_order={}, coefficient_order={}, geometry_weight_order={}. This is likely a configuration error", query.trial_order, query.test_order, query.coefficient_order, query.geometry_weight_order);
|
||||
}
|
||||
|
||||
if (query.trial_order_reduction < 0 || query.test_order_reduction < 0) {
|
||||
serif_error<std::invalid_argument, SERiFErrorCode::INVALID_QUADRATURE_ORDER>("Quadrature order reductions must be non-negative. Received: trial_order_reduction={}, test_order_reduction={}. Note these are reductions, they are subtracted from the corresponding order. You do not need to include a negative sign with them. This is likely a configuration error", query.trial_order_reduction, query.test_order_reduction);
|
||||
}
|
||||
|
||||
const int reduced_trial_order = query.trial_order - query.trial_order_reduction;
|
||||
const int reduced_test_order = query.test_order - query.test_order_reduction;
|
||||
|
||||
if (reduced_trial_order < 0) {
|
||||
serif_warning("Quadrature trial order reduction ({}) is greater than the trial order ({}). Clamping this value to 0", query.trial_order_reduction, query.trial_order);
|
||||
}
|
||||
if (reduced_test_order < 0) {
|
||||
serif_warning("Quadrature test order reduction ({}) is greater than the test order ({}). Clamping this value to 0", query.test_order_reduction, query.test_order);
|
||||
}
|
||||
|
||||
const int trial_order = std::max(0, reduced_trial_order);
|
||||
const int test_order = std::max(0, reduced_test_order);
|
||||
|
||||
return trial_order + test_order + query.coefficient_order + query.geometry_weight_order;
|
||||
}
|
||||
|
||||
const RuleControl &Policy::get_role_control(QuadratureRole role) const {
|
||||
return m_ruleSet.role_control(role);
|
||||
}
|
||||
|
||||
Resolution Policy::resolve(const Query &query) const {
|
||||
const int base_order = compute_base_order(query);
|
||||
const RuleControl *form_control = m_ruleSet.find_form_control(query.form);
|
||||
const RuleControl &role_control = get_role_control(query.role);
|
||||
const RuleControl &fallback_control = m_ruleSet.fallback_control();
|
||||
|
||||
std::optional<int> fixed_order;
|
||||
|
||||
if (form_control != nullptr && form_control->fixed_order.has_value()) {
|
||||
fixed_order = form_control->fixed_order;
|
||||
} else if (role_control.fixed_order.has_value()) {
|
||||
fixed_order = role_control.fixed_order;
|
||||
} else if (fallback_control.fixed_order.has_value()) {
|
||||
fixed_order = fallback_control.fixed_order;
|
||||
}
|
||||
|
||||
if (fixed_order.has_value()) {
|
||||
if (*fixed_order < 0) {
|
||||
serif_error<std::invalid_argument, SERiFErrorCode::INVALID_QUADRATURE_ORDER>("Quadrature fixed_order must be non-negative. Received: {}. This is likely a configuration error", *fixed_order);
|
||||
}
|
||||
|
||||
return Resolution{
|
||||
.base_order = base_order,
|
||||
.boost = 0,
|
||||
.order = *fixed_order,
|
||||
.used_fixed_order = true
|
||||
};
|
||||
}
|
||||
|
||||
int boost = fallback_control.boost + role_control.boost;
|
||||
|
||||
if (form_control != nullptr) {
|
||||
boost += form_control->boost;
|
||||
}
|
||||
|
||||
const int order = base_order + boost;
|
||||
|
||||
if (order < 0) {
|
||||
serif_error<std::invalid_argument, SERiFErrorCode::INVALID_QUADRATURE_ORDER>("Quadrature order must be non-negative. Received: {}. Realistically this should not be able to happen and represents a likely bug in the code. Please contact the SERiF developers.", order);
|
||||
};
|
||||
|
||||
return Resolution{
|
||||
.base_order = base_order,
|
||||
.boost = boost,
|
||||
.order = order,
|
||||
.used_fixed_order = false
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,14 @@
|
||||
#include "serif/utils/misc/finite.hpp"
|
||||
#include "serif/eos/exceptions.hpp"
|
||||
|
||||
#include "serif/utils/error/codes.hpp"
|
||||
|
||||
namespace serif::eos::models {
|
||||
using utils::misc::validate_finite;
|
||||
using utils::misc::validate_greater_than;
|
||||
using utils::misc::validate_less_than;
|
||||
using utils::misc::validate_nonnegativity;
|
||||
using utils::errors::SERiFErrorCode;
|
||||
|
||||
using namespace dimensions;
|
||||
using namespace relations;
|
||||
@@ -17,10 +20,10 @@ namespace serif::eos::models {
|
||||
m_polytropic_index(n),
|
||||
m_polytropic_constant(K),
|
||||
m_enthalpy_scale((n + 1.0) * K) {
|
||||
validate_finite<EOSEvaluationError>(m_polytropic_index, EOSEvaluationErrorCode::outside_domain);
|
||||
validate_greater_than<EOSEvaluationError>(m_polytropic_index, 1.0, EOSEvaluationErrorCode::outside_domain);
|
||||
validate_finite<EOSEvaluationError>(m_polytropic_constant, EOSEvaluationErrorCode::outside_domain);
|
||||
validate_greater_than<EOSEvaluationError>(m_polytropic_constant, 0.0, EOSEvaluationErrorCode::outside_domain);
|
||||
validate_finite<EOSEvaluationError, SERiFErrorCode::EOS_OUTSIDE_DOMAIN>(m_polytropic_index);
|
||||
validate_greater_than<EOSEvaluationError, SERiFErrorCode::EOS_OUTSIDE_DOMAIN>(m_polytropic_index, 1.0);
|
||||
validate_finite<EOSEvaluationError, SERiFErrorCode::EOS_OUTSIDE_DOMAIN>(m_polytropic_constant);
|
||||
validate_greater_than<EOSEvaluationError, SERiFErrorCode::EOS_OUTSIDE_DOMAIN>(m_polytropic_constant, 0.0);
|
||||
}
|
||||
|
||||
[[nodiscard]] double Polytrope::polytropic_index() const noexcept {
|
||||
@@ -36,7 +39,7 @@ namespace serif::eos::models {
|
||||
}
|
||||
|
||||
[[nodiscard]] PressureValue Polytrope::evaluate(PressureFromDensity, const DensityValue density) const {
|
||||
validate_nonnegativity<EOSEvaluationError>(density.value(), EOSEvaluationErrorCode::invalid_thermodynamic_input);
|
||||
validate_nonnegativity<EOSEvaluationError, SERiFErrorCode::EOS_INVALID_THERMODYNAMIC_INPUT>(density.value());
|
||||
|
||||
if (density.value() == 0.0) {
|
||||
// P = 0
|
||||
@@ -50,7 +53,7 @@ namespace serif::eos::models {
|
||||
}
|
||||
|
||||
[[nodiscard]] SpecificEnthalpyValue Polytrope::evaluate(SpecificEnthalpyFromDensity, const DensityValue density) const {
|
||||
validate_nonnegativity<EOSEvaluationError>(density.value(), EOSEvaluationErrorCode::invalid_thermodynamic_input);
|
||||
validate_nonnegativity<EOSEvaluationError, SERiFErrorCode::EOS_INVALID_THERMODYNAMIC_INPUT>(density.value());
|
||||
if (density.value() == 0.0) {
|
||||
// h = 0
|
||||
return SpecificEnthalpyValue{0.0};
|
||||
@@ -63,7 +66,7 @@ namespace serif::eos::models {
|
||||
}
|
||||
|
||||
[[nodiscard]] DensityValue Polytrope::evaluate(DensityFromSpecificEnthalpy, SpecificEnthalpyValue specific_enthalpy) const {
|
||||
validate_finite<EOSEvaluationError>(specific_enthalpy.value(), EOSEvaluationErrorCode::invalid_thermodynamic_input);
|
||||
validate_finite<EOSEvaluationError, SERiFErrorCode::EOS_INVALID_THERMODYNAMIC_INPUT>(specific_enthalpy.value());
|
||||
|
||||
if (specific_enthalpy.value() <= 0.0) {
|
||||
// ρ = 0
|
||||
@@ -86,7 +89,7 @@ namespace serif::eos::models {
|
||||
}
|
||||
|
||||
[[nodiscard]] SpecificEnthalpyValue Polytrope::evaluate(SpecificEnthalpyFromPressure, PressureValue pressure) const {
|
||||
validate_nonnegativity<EOSEvaluationError>(pressure.value(), EOSEvaluationErrorCode::invalid_thermodynamic_input);
|
||||
validate_nonnegativity<EOSEvaluationError, SERiFErrorCode::EOS_INVALID_THERMODYNAMIC_INPUT>(pressure.value());
|
||||
if (pressure.value() == 0.0) {
|
||||
// h = 0
|
||||
return SpecificEnthalpyValue{0.0};
|
||||
@@ -102,7 +105,7 @@ namespace serif::eos::models {
|
||||
}
|
||||
|
||||
[[nodiscard]] PartialDerivative<Density, SpecificEnthalpy> Polytrope::partial_derivative(DensityFromSpecificEnthalpy, WithRespectTo<SpecificEnthalpy>, const SpecificEnthalpyValue specific_enthalpy) const {
|
||||
validate_finite<EOSEvaluationError>(specific_enthalpy.value(), EOSEvaluationErrorCode::invalid_thermodynamic_input);
|
||||
validate_finite<EOSEvaluationError, SERiFErrorCode::EOS_INVALID_THERMODYNAMIC_INPUT>(specific_enthalpy.value());
|
||||
|
||||
if (specific_enthalpy.value() < 0.0) {
|
||||
// dρ/dh = 0
|
||||
@@ -131,7 +134,7 @@ namespace serif::eos::models {
|
||||
}
|
||||
|
||||
[[nodiscard]] PartialDerivative<Pressure, Density> Polytrope::partial_derivative(PressureFromDensity, WithRespectTo<Density>, const DensityValue density) const {
|
||||
validate_nonnegativity<EOSEvaluationError>(density.value(), EOSEvaluationErrorCode::invalid_thermodynamic_input);
|
||||
validate_nonnegativity<EOSEvaluationError, SERiFErrorCode::EOS_INVALID_THERMODYNAMIC_INPUT>(density.value());
|
||||
|
||||
if (density.value() == 0.0) {
|
||||
// dP/dρ = 0
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
include_dir = include_directories('include')
|
||||
|
||||
serif_sources = files(
|
||||
'lib/serif/dimensions/runtime/runtime.cpp',
|
||||
'lib/serif/discretization/domain/mesh/topology.cpp',
|
||||
'lib/serif/discretization/domain/schema/validation/results.cpp',
|
||||
'lib/serif/discretization/quadrature/policy.cpp',
|
||||
'lib/serif/eos/models/polytropic.cpp',
|
||||
)
|
||||
|
||||
@@ -11,12 +11,40 @@ serif_lib = library(
|
||||
'serif',
|
||||
serif_sources,
|
||||
include_directories: include_dir,
|
||||
dependencies: [mfem_dep],
|
||||
dependencies: [mfem_dep, nameof_dep, magic_enum_dep],
|
||||
build_rpath: mfem_runtime_prefix == '' ? '' : mfem_runtime_prefix / 'lib',
|
||||
)
|
||||
|
||||
serif_dep = declare_dependency(
|
||||
include_directories: include_dir,
|
||||
link_with: serif_lib,
|
||||
dependencies: [mfem_dep],
|
||||
dependencies: [mfem_dep, nameof_dep, magic_enum_dep],
|
||||
)
|
||||
|
||||
if get_option('build_checks')
|
||||
checks_srcs = files(
|
||||
'compile_time_checks/serif/discretization/forms/forms.cpp',
|
||||
'compile_time_checks/serif/discretization/forms/operands.cpp',
|
||||
'compile_time_checks/serif/discretization/blocks/fields.cpp',
|
||||
'compile_time_checks/serif/discretization/quadrature/backend/mfem/mfem_resolver.cpp',
|
||||
)
|
||||
|
||||
# We only make this a lib since we really dont need it to link to anything latter. This is just compiled
|
||||
# to exercise the compile time checks of the type invariants in serif
|
||||
|
||||
# Note this will not produce any executable binaries as there is no run time code, we are purley validating
|
||||
# that compilation passes for a series of checks that assert type invariants
|
||||
|
||||
# These checks should be expanded in the future given how much of SERiF is compile time validated.
|
||||
# also note we intentionally do not link to any SERiF library here. Compile time invariants
|
||||
# generally must live in a header file, therfore an important part of the compile time checks
|
||||
# is that they are self contained.
|
||||
# Do not add a dependency on serif_dep or link with serif_lib.
|
||||
# if something seems to break due to lack of linking it means that a bug was introduced.
|
||||
serif_checks_lib = static_library(
|
||||
'serif_checks',
|
||||
checks_srcs,
|
||||
include_directories: [include_dir, nameof_include_dir, magic_enum_include] ,
|
||||
dependencies: [mfem_dep],
|
||||
)
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#include "serif/dimensions/runtime/concepts.hpp"
|
||||
|
||||
int main() {
|
||||
puts("Hello, world!\n");
|
||||
}
|
||||
@@ -6,11 +6,26 @@
|
||||
#include "serif/discretization/domain/schema/validation/results.hpp"
|
||||
#include "stroid/stroid.h"
|
||||
#include "serif/discretization/blocks/base.hpp"
|
||||
#include "serif/dimensions/runtime/runtime.hpp"
|
||||
#include "serif/discretization/forms/forms.hpp"
|
||||
#include "serif/discretization/forms/form_traits.hpp"
|
||||
#include "serif/discretization/blocks/concepts.hpp"
|
||||
#include "serif/discretization/forms/concept.hpp"
|
||||
#include "serif/discretization/forms/runtime.hpp"
|
||||
#include "serif/discretization/blocks/runtime.hpp"
|
||||
#include "serif/discretization/blocks/concepts.hpp"
|
||||
#include "serif/discretization/blocks/traits.hpp"
|
||||
#include "serif/discretization/blocks/concepts.hpp"
|
||||
#include "serif/utils/misc/concepts/enumeration.hpp"
|
||||
#include "serif/discretization/forms/operands.hpp"
|
||||
|
||||
#include "serif/utils/misc/concepts/filter.hpp"
|
||||
|
||||
int main() {
|
||||
using serif::discretization::domain::schema::validation::SchemaValidationResult;
|
||||
using serif::discretization::domain::schema::validation::validate_schema;
|
||||
using serif::discretization::domain::schema::CoreEnvelopeVacuumDomainSchema;
|
||||
using namespace serif::discretization::blocks;
|
||||
|
||||
const auto smesh_result = stroid::IO::LoadStroidMesh("sandbox.smesh");
|
||||
if (not smesh_result.has_value()) {
|
||||
@@ -21,4 +36,18 @@ int main() {
|
||||
|
||||
SchemaValidationResult result = validate_schema<CoreEnvelopeVacuumDomainSchema>(*smesh.mesh);
|
||||
std::println("{}", result);
|
||||
|
||||
std::println("{}", serif::discretization::forms::mass<serif::discretization::blocks::gravity::gradient, serif::discretization::blocks::gravity::gradient>::name);
|
||||
|
||||
using AllTerms = serif::utils::types::TypeList<
|
||||
density::mass,
|
||||
displacement::vector,
|
||||
surface_deformation::coefficients,
|
||||
surface_deformation::shape_equilibrium,
|
||||
gravity::potential,
|
||||
gravity::gradient,
|
||||
enthalpy::specific
|
||||
>;
|
||||
|
||||
using SpatialTerms = serif::utils::misc::concepts::FilterT<AllTerms, IsSpatialTerm>;
|
||||
}
|
||||
@@ -3,9 +3,24 @@
|
||||
#include "serif/eos/models/polytropic.hpp"
|
||||
#include "serif/eos/evaluation.hpp"
|
||||
#include "serif/dimensions/type_alias.hpp"
|
||||
#include "serif/discretization/blocks/base.hpp"
|
||||
#include "serif/discretization/blocks/fields.hpp"
|
||||
#include "serif/discretization/domain/physical_domains.hpp"
|
||||
#include "serif/eos/runtime/details.hpp"
|
||||
#include "serif/eos/runtime/concepts.hpp"
|
||||
#include "serif/eos/runtime/views.hpp"
|
||||
int main() {
|
||||
const serif::eos::models::Polytrope polytrope(1, 0.6);
|
||||
std::println("Density: {}", serif::eos::evaluate<serif::dimensions::Density>(polytrope, serif::dimensions::SpecificEnthalpyValue{1.0}).value());
|
||||
const serif::eos::runtime::EOSView view(polytrope);
|
||||
const auto result = view.try_evaluate<serif::dimensions::Density>(serif::dimensions::SpecificEnthalpyValue{1.0});
|
||||
if (result.has_value()) {
|
||||
std::println("From runtime view: Density: {}", result->value());
|
||||
} else {
|
||||
std::println("From runtime view: EOS view is unavailable.");
|
||||
}
|
||||
|
||||
std::println("{}", serif::discretization::blocks::gravity::name);
|
||||
|
||||
|
||||
}
|
||||
@@ -1,26 +1,17 @@
|
||||
#include "serif/utils/misc/finite.hpp"
|
||||
#include "serif/utils/error/codes.hpp"
|
||||
#include <limits>
|
||||
|
||||
enum class [[maybe_unused]] TestErrorCode {
|
||||
NotFinite
|
||||
};
|
||||
|
||||
namespace {
|
||||
class test_exception : public std::runtime_error {
|
||||
public:
|
||||
test_exception(TestErrorCode error_code, const std::string& message)
|
||||
: std::runtime_error(message), m_error_code(error_code) {}
|
||||
|
||||
TestErrorCode error_code() const noexcept {
|
||||
return m_error_code;
|
||||
}
|
||||
private:
|
||||
TestErrorCode m_error_code;
|
||||
test_exception(const std::string& message)
|
||||
: std::runtime_error(message) {}
|
||||
};
|
||||
}
|
||||
|
||||
int main() {
|
||||
double a = std::numeric_limits<double>::infinity();
|
||||
|
||||
serif::utils::misc::validate_finite<test_exception>(a, TestErrorCode::NotFinite);
|
||||
serif::utils::misc::validate_finite<test_exception, serif::utils::errors::SERiFErrorCode::NON_FINITE_VALUE_ERROR>(a);
|
||||
}
|
||||
@@ -2,13 +2,16 @@ sandboxes = [
|
||||
'discritization_sandbox',
|
||||
'eos_sandbox',
|
||||
'finite_sandbox',
|
||||
'dimensions_sandbox',
|
||||
'quadrature_sandbox',
|
||||
'meta_sandbox',
|
||||
]
|
||||
|
||||
foreach sandbox: sandboxes
|
||||
executable(
|
||||
sandbox,
|
||||
sandbox + '.cpp',
|
||||
dependencies: [serif_dep, stroid_dep],
|
||||
dependencies: [serif_dep, stroid_dep, nameof_dep],
|
||||
build_rpath: mfem_runtime_prefix == '' ? '' : mfem_runtime_prefix / 'lib',
|
||||
)
|
||||
endforeach
|
||||
|
||||
8
tests/sandbox/meta_sandbox.cpp
Normal file
8
tests/sandbox/meta_sandbox.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "serif/meta/names.hpp"
|
||||
#include "serif/utils/error/errors.hpp"
|
||||
|
||||
int main() {
|
||||
using namespace serif::utils::errors;
|
||||
puts("Hello, world!\n");
|
||||
serif_assert<SERiFErrorCode::EOS_ERROR, "This is a message that should be seen at compile time. I am now going to make this very long to make sure that it does not get truncated. If you want to exercise this turn the following bool to false (generally set to true to allow compilation)", true>();
|
||||
}
|
||||
120
tests/sandbox/quadrature_sandbox.cpp
Normal file
120
tests/sandbox/quadrature_sandbox.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include "serif/discretization/quadrature/rules.hpp"
|
||||
#include "serif/discretization/quadrature/query.hpp"
|
||||
|
||||
#include "serif/discretization/forms/concept.hpp"
|
||||
#include "serif/discretization/forms/forms.hpp"
|
||||
#include "serif/discretization/quadrature/rules.hpp"
|
||||
#include "serif/discretization/quadrature/policy.hpp"
|
||||
#include "serif/discretization/quadrature/backend/mfem/mfem_static_order.hpp"
|
||||
#include "serif/discretization/quadrature/backend/order.hpp"
|
||||
#include "serif/discretization/quadrature/backend/mfem/concepts.hpp"
|
||||
#include "serif/discretization/quadrature/backend/mfem/configure.hpp"
|
||||
#include "serif/discretization/quadrature/backend/mfem/mfem_resolver.hpp"
|
||||
|
||||
#include <print>
|
||||
|
||||
int main() {
|
||||
using namespace serif::discretization::quadrature;
|
||||
using namespace serif::discretization::domain;
|
||||
using namespace serif::discretization::forms;
|
||||
|
||||
using GravityMassForm = divergence<serif::discretization::blocks::gravity::gradient, serif::discretization::blocks::gravity::gradient>;
|
||||
|
||||
auto query = make_query<GravityMassForm>(
|
||||
QuadratureRole::discretization,
|
||||
AllDomains{},
|
||||
MappingKind::affine,
|
||||
3,
|
||||
-4,
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
||||
|
||||
std::println("{}", query);
|
||||
|
||||
RuleSet defaultRuleSet;
|
||||
Policy defaultPolicy(defaultRuleSet);
|
||||
|
||||
const auto [base_order, boost, order, used_fixed_order] = defaultPolicy.resolve(query);
|
||||
|
||||
std::println(
|
||||
"Default policy: base_order={}, boost={}, order={}, used_fixed_order={}",
|
||||
base_order,
|
||||
boost,
|
||||
order,
|
||||
used_fixed_order
|
||||
);
|
||||
|
||||
|
||||
RuleSet boostedRuleSet;
|
||||
boostedRuleSet.set_fallback_control(
|
||||
RuleControl{
|
||||
.fixed_order = std::nullopt,
|
||||
.boost = 1
|
||||
}
|
||||
);
|
||||
|
||||
boostedRuleSet.set_role_control(
|
||||
QuadratureRole::discretization,
|
||||
RuleControl{
|
||||
.fixed_order = std::nullopt,
|
||||
.boost = 2
|
||||
}
|
||||
);
|
||||
|
||||
boostedRuleSet.set_form_control<GravityMassForm>(
|
||||
RuleControl{
|
||||
.fixed_order = std::nullopt,
|
||||
.boost = 3
|
||||
}
|
||||
);
|
||||
|
||||
Policy boostedPolicy(boostedRuleSet);
|
||||
|
||||
const Resolution boostedResolution = boostedPolicy.resolve(query);
|
||||
|
||||
std::println(
|
||||
"Boosted policy: base_order={}, boost={}, order={}, used_fixed_order={}",
|
||||
boostedResolution.base_order,
|
||||
boostedResolution.boost,
|
||||
boostedResolution.order,
|
||||
boostedResolution.used_fixed_order
|
||||
);
|
||||
|
||||
RuleSet fixedRuleSet;
|
||||
|
||||
fixedRuleSet.set_fallback_control(
|
||||
RuleControl{
|
||||
.fixed_order = std::nullopt,
|
||||
.boost = 1
|
||||
}
|
||||
);
|
||||
|
||||
fixedRuleSet.set_role_control(
|
||||
QuadratureRole::discretization,
|
||||
RuleControl{
|
||||
.fixed_order = std::nullopt,
|
||||
.boost = 2
|
||||
}
|
||||
);
|
||||
|
||||
fixedRuleSet.set_form_control<GravityMassForm>(
|
||||
RuleControl{
|
||||
.fixed_order = 11,
|
||||
.boost = 3
|
||||
}
|
||||
);
|
||||
|
||||
Policy fixedPolicy(fixedRuleSet);
|
||||
|
||||
const Resolution fixedResolution = fixedPolicy.resolve(query);
|
||||
|
||||
std::println(
|
||||
"Fixed policy: base_order={}, boost={}, order={}, used_fixed_order={}",
|
||||
fixedResolution.base_order,
|
||||
fixedResolution.boost,
|
||||
fixedResolution.order,
|
||||
fixedResolution.used_fixed_order
|
||||
);
|
||||
}
|
||||
@@ -349,6 +349,7 @@ def common_cmake_args(
|
||||
prefix_paths = [os.fspath(args.prefix)]
|
||||
if args.dependency_prefix:
|
||||
prefix_paths.append(args.dependency_prefix)
|
||||
prefix_paths.extend(getattr(args, "openmp_prefixes", []))
|
||||
result = [
|
||||
f"-DCMAKE_BUILD_TYPE={build_type}",
|
||||
f"-DCMAKE_INSTALL_PREFIX={args.prefix}",
|
||||
@@ -453,6 +454,40 @@ def cmake_configure(
|
||||
run(command, env=env)
|
||||
|
||||
|
||||
def check_openmp(
|
||||
args: argparse.Namespace, env: Mapping[str, str], build_type: str
|
||||
) -> None:
|
||||
"""Check both bundle languages before building the expensive dependencies.
|
||||
|
||||
Apple Clang needs a separate runtime. Homebrew's keg-only libomp is
|
||||
discoverable by Meson but is outside CMake's default prefix search.
|
||||
Keep CMake's compiler checks, and use their result for libCEED's Make build.
|
||||
"""
|
||||
args.openmp_prefixes = []
|
||||
if args.host_system == "darwin" and args.allow_system == "true":
|
||||
for prefix in ("/opt/homebrew/opt/libomp", "/usr/local/opt/libomp"):
|
||||
if (Path(prefix) / "include/omp.h").is_file():
|
||||
args.openmp_prefixes.append(prefix)
|
||||
source = args.work_dir / "openmp-check-source"
|
||||
source.mkdir(parents=True, exist_ok=True)
|
||||
write_text_atomic(source / "CMakeLists.txt", """cmake_minimum_required(VERSION 3.18)
|
||||
project(bundle_openmp_check LANGUAGES C CXX)
|
||||
find_package(OpenMP REQUIRED COMPONENTS C CXX)
|
||||
file(WRITE "${CMAKE_BINARY_DIR}/openmp-c.txt"
|
||||
"${OpenMP_C_FLAGS}\\n${OpenMP_C_INCLUDE_DIRS}\\n${OpenMP_C_LIBRARIES}\\n")
|
||||
""")
|
||||
build = args.work_dir / "openmp-check"
|
||||
cmake_configure(
|
||||
args, source, build,
|
||||
common_cmake_args(args, build_type, c_compiler=args.cc, cxx_compiler=args.cxx),
|
||||
env,
|
||||
)
|
||||
flags, includes, libraries = (build / "openmp-c.txt").read_text().splitlines()
|
||||
args.openmp_c_flags = shlex.split(flags)
|
||||
args.openmp_c_flags.extend(f"-I{path}" for path in includes.split(";") if path)
|
||||
args.openmp_c_libraries = [path for path in libraries.split(";") if path]
|
||||
|
||||
|
||||
def cmake_build_install(
|
||||
args: argparse.Namespace,
|
||||
source: Path,
|
||||
@@ -932,6 +967,10 @@ def build_libceed(
|
||||
)
|
||||
)
|
||||
link_flags = [*args.c_link_arg, *args.cxx_link_arg]
|
||||
if features["openmp"]:
|
||||
# libCEED's default clang flag (-fopenmp) does not work with Apple Clang.
|
||||
command.append(f"OMP_FLAG={joined_flags(args.openmp_c_flags)}")
|
||||
link_flags.extend(args.openmp_c_libraries)
|
||||
if args.host_system == "darwin":
|
||||
link_flags.append("-Wl,-headerpad_max_install_names")
|
||||
cuda_root = toolchain_root("nvcc", env) if features["cuda"] else ""
|
||||
@@ -1126,6 +1165,16 @@ def build_algoim(
|
||||
raise RuntimeError("Algoim source archive did not contain src/*.hpp")
|
||||
for header in headers:
|
||||
shutil.copy2(header, include_dir / header.name)
|
||||
# MFEM builds as C++17, but these public headers also reach C++23 consumers.
|
||||
# Preserve the upstream header's non-UTF-8 comments while replacing the
|
||||
# trait removed in C++20 with its C++17-compatible successor.
|
||||
quad_header = include_dir / "algoim_quad.hpp"
|
||||
quad_text = quad_header.read_bytes()
|
||||
old_trait = b"typename std::result_of<F(const TinyVector<Real,N>&)>::type"
|
||||
new_trait = b"std::invoke_result_t<F, const TinyVector<Real,N>&>"
|
||||
if old_trait not in quad_text and new_trait not in quad_text:
|
||||
raise RuntimeError("Algoim's expected quadrature result trait was not found")
|
||||
quad_header.write_bytes(quad_text.replace(old_trait, new_trait))
|
||||
|
||||
|
||||
def prepare_mfem_source(
|
||||
@@ -2373,10 +2422,16 @@ def main() -> int:
|
||||
print("[mfem-bundle] configuration unchanged; reusing private prefix", flush=True)
|
||||
return 0
|
||||
|
||||
pending_file = args.work_dir.parent / "pending-configuration.json"
|
||||
pending = json.loads(pending_file.read_text()) if pending_file.is_file() else {}
|
||||
if pending.get("fingerprint") != fingerprint:
|
||||
remove_tree(args.work_dir)
|
||||
remove_tree(args.prefix)
|
||||
else:
|
||||
print("[mfem-bundle] resuming incomplete build", flush=True)
|
||||
args.work_dir.mkdir(parents=True, exist_ok=True)
|
||||
args.prefix.mkdir(parents=True, exist_ok=True)
|
||||
write_text_atomic(pending_file, json.dumps({"fingerprint": fingerprint}) + "\n")
|
||||
|
||||
env = dict(os.environ)
|
||||
if features["cuda"]:
|
||||
@@ -2418,6 +2473,9 @@ def main() -> int:
|
||||
if args.dependency_prefix and args.vendor_dependency_prefix == "true":
|
||||
copy_prefix(Path(args.dependency_prefix), args.prefix)
|
||||
|
||||
if features["openmp"] or features["legacy_openmp"]:
|
||||
check_openmp(args, env, build_type)
|
||||
|
||||
if features["zlib"] and not bundle_has(args, "include/zlib.h"):
|
||||
build_zlib(args, env, build_type)
|
||||
mpicc, mpicxx = args.cc, args.cxx
|
||||
|
||||
Reference in New Issue
Block a user