feat(mean_field): added dimensions, discritization, and start of eos

The full rewrite of mean_field into something maintainable is progressing. dimensions is mostly done, discritization (domain, blocks, and fields) is done, and eos is progressing quickly
This commit is contained in:
2026-09-15 10:42:00 -04:00
parent 7c99debf2f
commit d1f59d6d70
88 changed files with 324020 additions and 268 deletions

View File

@@ -0,0 +1,32 @@
#pragma once
#include "serif/eos/models/concepts.hpp"
namespace serif::eos {
/*
* I find the syntax that follows to be potentially confusing. Specifically the double use of noexcept. For those
* unfamiliar a brief explanation follows.
*
* The first noexcept is used to mark if the overall function (evaluate) is noexcept. However, noexcept can take a boolean. noexcept(true)
* tells the compiler that the function will not throw an exception (note if an exception is thrown in a noexcept function std::terminate will be called
* and no try-catch block will catch the exception). This allows for the compiler to generate more efficient code. noexcept(false) tells
* the compiler that the function may throw an exception (semantically it is equivalent to not including noexcept). The second noexcept
* (e.g. noexcept(noexcept()) is used to determine what that boolean is. This is a confusing part of C++ syntax where the same
* keyword has different semantic meanings depending on context. In this case the inner noexcept is used
* to determine if the expression inside it is noexcept. That is to say if eos.evaluate(...) is noexcept for the particular
* templates EOS argument then this function will be noexcept. If eos.evaluate(...) is not noexcept then this function will not be noexcept.
*/
template <dimensions::ThermodynamicQuantityType OutputQ, models::EOSModel EOS, dimensions::QuantityValueType... InputValues>
requires models::EOSSupportingRelation<EOS, relations::Relation<OutputQ, dimensions::QuantityOfT<InputValues>...>>
[[nodiscard]] constexpr dimensions::QuantityValue<OutputQ> evaluate(const EOS& eos, const InputValues... inputValues)
noexcept(noexcept(eos.evaluate(relations::Relation<OutputQ, dimensions::QuantityOfT<InputValues>...>{}, inputValues...))) {
return eos.evaluate(relations::Relation<OutputQ, dimensions::QuantityOfT<InputValues>...>{}, inputValues...);
};
template <dimensions::ThermodynamicQuantityType OutputQ, dimensions::ThermodynamicQuantityType InputQ, models::EOSModel EOS, dimensions::QuantityValueType... InputValues>
requires models::EOSSupportingPartialDerivative<EOS, relations::Relation<OutputQ, dimensions::QuantityOfT<InputValues>...>, InputQ>
[[nodiscard]] constexpr dimensions::PartialDerivative<OutputQ, InputQ> partial_derivative(const EOS& eos, const InputValues... inputValues)
noexcept(noexcept(eos.partialDerivative(relations::Relation<OutputQ, dimensions::QuantityOfT<InputValues>...>{}, dimensions::WithRespectTo<InputQ>{}, inputValues...))) {
return eos.partial_derivative(relations::Relation<OutputQ, dimensions::QuantityOfT<InputValues>...>{}, dimensions::WithRespectTo<InputQ>{}, inputValues...);
}
}

View File

@@ -0,0 +1,32 @@
#pragma once
#include <cstdint>
#include <stdexcept>
#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;
};
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include "serif/eos/models/traits.hpp"
namespace serif::eos::models {
template <typename EOSCandidate>
concept EOSModel = is_equation_of_state_model_v<EOSCandidate>;
template <typename EOS, typename RelationT>
concept EOSSupportingRelation = supports_relation_v<EOS, RelationT>;
template <typename EOS, typename RelationT, typename InputQuantity>
concept EOSSupportingPartialDerivative = supports_partial_derivative_v<EOS, RelationT, InputQuantity>;
template <typename EOSCandidate>
concept StructureSeedEOS = EOSModel<EOSCandidate> &&
EOSSupportingRelation<EOSCandidate, relations::SpecificEnthalpyFromDensity>;
template <typename EOSCandidate>
concept BarotropicClosureEOS = EOSModel<EOSCandidate> &&
EOSSupportingRelation<EOSCandidate, relations::DensityFromSpecificEnthalpy> &&
EOSSupportingPartialDerivative<EOSCandidate, relations::DensityFromSpecificEnthalpy, dimensions::SpecificEnthalpy>;
template <typename EOSCandidate>
concept PressureForceEOS = EOSModel<EOSCandidate> &&
EOSSupportingRelation<EOSCandidate, relations::PressureFromSpecificEnthalpy> &&
EOSSupportingPartialDerivative<EOSCandidate, relations::PressureFromSpecificEnthalpy, dimensions::SpecificEnthalpy>;
}

View File

@@ -0,0 +1,67 @@
#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 {
double n;
double K;
};
class Polytrope final {
public:
/* The idea here (and with all EOS) is that we force the developer / implementer to enumerate all the thermodynamic relations
* that the EOS supports explicitly. This is a small amount of boilerplate; however, it enables effectively a fully automatic
* EOS compiler down the line. Much of the EOS system these classes depend on (relations, models, the system in evaluation) are all
* there to allow for the compile-time checking of the EOS model and its relations and to allow the developer cross-section
* of that to be limited to defining this relation catalog. Every EOS implemented should start (at the very top of the class
* with a Relations alias in the public section of the class. Any EOS that does not have this will not compile.
*/
using Relations = relations::RelationCatalog<
relations::PressureFromDensity,
relations::PressureFromSpecificEnthalpy,
relations::SpecificEnthalpyFromDensity,
relations::SpecificEnthalpyFromPressure,
relations::DensityFromSpecificEnthalpy
>;
explicit Polytrope(const PolytropeParameters& parameters);
explicit Polytrope(double n, double K);
[[nodiscard]] double polytropic_index() const noexcept;
[[nodiscard]] double polytropic_constant() const noexcept;
[[nodiscard]] double enthalpy_scale() const noexcept;
[[nodiscard]] dimensions::PressureValue evaluate(relations::PressureFromDensity, dimensions::DensityValue density) const;
[[nodiscard]] dimensions::PressureValue evaluate(relations::PressureFromSpecificEnthalpy, dimensions::SpecificEnthalpyValue specific_enthalpy) const;
[[nodiscard]] dimensions::SpecificEnthalpyValue evaluate(relations::SpecificEnthalpyFromDensity, dimensions::DensityValue density) const;
[[nodiscard]] dimensions::SpecificEnthalpyValue evaluate(relations::SpecificEnthalpyFromPressure, dimensions::PressureValue pressure) const;
[[nodiscard]] dimensions::DensityValue evaluate(relations::DensityFromSpecificEnthalpy, dimensions::SpecificEnthalpyValue specific_enthalpy) const;
[[nodiscard]] dimensions::PartialDerivative<dimensions::Density, dimensions::SpecificEnthalpy> partial_derivative(
relations::DensityFromSpecificEnthalpy,
dimensions::WithRespectTo<dimensions::SpecificEnthalpy>,
dimensions::SpecificEnthalpyValue specific_enthalpy
) const;
[[nodiscard]] dimensions::PartialDerivative<dimensions::Pressure, dimensions::SpecificEnthalpy> partial_derivative(
relations::PressureFromSpecificEnthalpy,
dimensions::WithRespectTo<dimensions::SpecificEnthalpy>,
dimensions::SpecificEnthalpyValue specific_enthalpy
) const;
[[nodiscard]] dimensions::PartialDerivative<dimensions::Pressure, dimensions::Density> partial_derivative(
relations::PressureFromDensity,
dimensions::WithRespectTo<dimensions::Density>,
dimensions::DensityValue density
) const;
private:
double m_polytropic_index;
double m_polytropic_constant;
double m_enthalpy_scale;
};
}

View File

@@ -0,0 +1,56 @@
#pragma once
#include "serif/dimensions/quantities.hpp"
#include "serif/eos/relations/concepts.hpp"
namespace serif::eos::models {
template <typename EOS, typename RelationT>
struct ImplementsRelation_t : std::false_type{};
template <typename EOS, typename Output, typename... Inputs>\
struct ImplementsRelation_t<EOS, relations::Relation<Output, Inputs...>> :
std::bool_constant<
requires(const std::remove_cvref_t<EOS> &eos, dimensions::QuantityValue<Inputs>... inputValues) {
{
eos.evaluate(relations::Relation<Output, Inputs...>{}, inputValues...)
} -> std::same_as<dimensions::QuantityValue<Output>>;}>{}; // Read this as a requirement that when evaluating the templates EOS with the given relation and inputs the returned value is of the correct output type.
template <typename EOS, typename Catalog>
struct ImplementsRelationCatalog_t : std::false_type {};
template <typename EOS, typename... Relations>
struct ImplementsRelationCatalog_t<EOS, relations::RelationCatalog<Relations...>> :
std::bool_constant<
(ImplementsRelation_t<EOS, Relations>::value && ...)>{}; // Fold over all relations in a catalog and ensure the EOS implements all of them.
template <typename EOSCandidate, typename = void>
struct IsEquationOfStateModel_t : std::false_type {};
template <typename EOSCandidate>
struct IsEquationOfStateModel_t<EOSCandidate, std::void_t<typename std::remove_cvref_t<EOSCandidate>::Relations>> :
std::bool_constant<
relations::ValidRelationCatalog<typename std::remove_cvref_t<EOSCandidate>::Relations> && // First ensure the relation catalog is itself valid
ImplementsRelationCatalog_t< // Then ensure that the EOSCandidate implements all relations in the catalog
std::remove_cvref_t<EOSCandidate>,
typename std::remove_cvref_t<EOSCandidate>::Relations>::value>{ };
template <typename EOS, typename RelationT, typename InputQuantity>
struct ImplementsPartialDerivative_t : std::false_type {};
template <typename EOS, typename Output, typename... Inputs, typename InputQuantity>
struct ImplementsPartialDerivative_t<EOS, relations::Relation<Output, Inputs...>, InputQuantity> :
std::bool_constant<
(std::same_as<InputQuantity, Inputs> || ...) &&
requires (const std::remove_cvref_t<EOS> &eos, dimensions::QuantityValue<Inputs>... inputValues) {
{eos.partialDerivative(relations::Relation<Output, Inputs...>{}, dimensions::WithRespectTo<InputQuantity>{}, inputValues...)} -> std::same_as<dimensions::PartialDerivative<Output, Inputs...>>;
}>{};
template <typename EOSCandidate>
inline constexpr bool is_equation_of_state_model_v = IsEquationOfStateModel_t<std::remove_cvref_t<EOSCandidate>>::value;
template <typename EOS, typename RelationT>
inline constexpr bool supports_relation_v = is_equation_of_state_model_v<EOS> && ImplementsRelation_t<std::remove_cvref_t<EOS>, std::remove_cvref_t<RelationT>>::value;
template <typename EOS, typename RelationT, typename InputQuantity>
inline constexpr bool supports_partial_derivative_v = is_equation_of_state_model_v<EOS> && ImplementsPartialDerivative_t<std::remove_cvref_t<EOS>, std::remove_cvref_t<RelationT>, InputQuantity>::value;
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include "serif/utils/misc/concepts/type_uniqueness.hpp"
#include "serif/utils/misc/std_helper/variadic_cleaning.hpp"
#include "serif/dimensions/quantities.hpp"
namespace serif::eos::relations {
using utils::misc::std_helper::CountVariadicArguments;
using utils::misc::concepts::AllTypesAreUnique;
template <typename Output, typename... Inputs>
struct Relation final {
using OutputQuantity = Output;
using InputQuantities = dimensions::QuantityList<Inputs...>;
static constexpr std::size_t inputCount = CountVariadicArguments<Inputs...>();
};
template <typename... Relations>
struct RelationCatalog final {
static constexpr std::size_t size = CountVariadicArguments<Relations...>();
};
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include "serif/eos/relations/traits.hpp"
namespace serif::eos::relations {
template <typename Catalog, typename RelationT>
concept CatalogContainingRelation = relation_catalog_contains_relation_v<Catalog, RelationT>;
template <typename RelationT, typename Quantity>
concept RelationContainingInput = relation_contains_input_v<RelationT, Quantity>;
template <typename RelationCandidate>
concept ValidThermodynamicRelation = is_thermodynamic_relation_v<RelationCandidate>;
template <typename CandidateCatalog>
concept ValidRelationCatalog = is_valid_relation_catalog_v<CandidateCatalog>;
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include "serif/eos/relations/catalog.hpp"
#include "serif/dimensions/dimensions.hpp"
namespace serif::eos::relations {
using PressureFromDensity = Relation<
dimensions::Pressure,
dimensions::Density>;
using PressureFromSpecificEnthalpy = Relation<
dimensions::Pressure,
dimensions::SpecificEnthalpy>;
using SpecificEnthalpyFromDensity = Relation<
dimensions::SpecificEnthalpy,
dimensions::Density>;
using SpecificEnthalpyFromPressure = Relation<
dimensions::SpecificEnthalpy,
dimensions::Pressure>;
using DensityFromSpecificEnthalpy = Relation<
dimensions::Density,
dimensions::SpecificEnthalpy>;
}

View File

@@ -0,0 +1,54 @@
#pragma once
#include "serif/eos/relations/catalog.hpp"
namespace serif::eos::relations {
template <typename Catalog, typename RelationT>
struct CatalogContainsRelation : std::false_type {};
template <typename... Relations, typename RelationT>
struct CatalogContainsRelation<RelationCatalog<Relations...>, RelationT> :
std::bool_constant<(std::same_as<RelationT, Relations> || ...)> {}; // Fold over all Relations and return true type if any of them match RelationT
template <typename RelationT, typename Quantity>
struct RelationContainsInput : std::false_type {};
template <typename Output, typename... Inputs, typename Quantity>
struct RelationContainsInput<Relation<Output, Inputs...>, Quantity> :
std::bool_constant<(std::same_as<Quantity, Inputs> || ...)> {};
template <typename RelationCandidate>
struct IsThermodynamicRelation : std::false_type {};
template <typename Output, typename... Inputs>
struct IsThermodynamicRelation<Relation<Output, Inputs...>> :
std::bool_constant<
dimensions::ThermodynamicQuantityType<Output> && // Ensure the OutputQuantity is a valid thermodynamic quantity
(dimensions::ThermodynamicQuantityType<Inputs> && ...) && // Fold over all InputQuantities to ensure they are valid thermodynamic quantities
AllTypesAreUnique<Inputs...>> {}; // Ensure all InputQuantities are unique types
template <typename CandidateCatalog>
struct IsValidRelationCatalog : std::false_type {};
template <typename... Relations>
struct IsValidRelationCatalog<RelationCatalog<Relations...>> :
std::bool_constant<
(sizeof...(Relations) > 0) && // Ensure the catalog is not empty
(IsThermodynamicRelation<Relations>::value && ...) && // Fold over all Relations and ensure they are all valid
AllTypesAreUnique<Relations...>> {}; // Ensure all Relations are unique types (e.g. no duplicate relations)
template <typename Catalog, typename RelationT>
inline constexpr bool relation_catalog_contains_relation_v = CatalogContainsRelation<std::remove_cv_t<Catalog>, std::remove_cv_t<RelationT>>::value;
template <typename RelationT, typename Quantity>
inline constexpr bool relation_contains_input_v = RelationContainsInput<std::remove_cv_t<RelationT>, std::remove_cv_t<Quantity>>::value;
template <typename RelationCandidate>
constexpr bool is_thermodynamic_relation_v = IsThermodynamicRelation<RelationCandidate>::value;
template <typename CatalogCandidate>
constexpr bool is_valid_relation_catalog_v = IsValidRelationCatalog<CatalogCandidate>::value;
}