feat(surface): major work on implementing surface constraints in a presciption agnostic manner
This commit is contained in:
100
libmeanfield/interface/eos/concepts.cppm
Normal file
100
libmeanfield/interface/eos/concepts.cppm
Normal file
@@ -0,0 +1,100 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:eos.concepts;
|
||||
export import :eos.relations;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
namespace detail {
|
||||
template <typename EquationOfState, typename RelationType> struct ImplementsRelation : std::false_type { };
|
||||
|
||||
template <
|
||||
typename EquationOfState,
|
||||
typename Output,
|
||||
typename... Inputs>
|
||||
struct ImplementsRelation<
|
||||
EquationOfState,
|
||||
Relation<
|
||||
Output,
|
||||
Inputs...>> : std::bool_constant <
|
||||
requires(
|
||||
const std::remove_cvref_t<EquationOfState> &equationOfState,
|
||||
QuantityValue<Inputs>... inputValues
|
||||
) {
|
||||
{equationOfState.evaluate(Relation<Output, Inputs...>{}, inputValues...)}
|
||||
->std::same_as<QuantityValue<Output>>;
|
||||
}>{};
|
||||
|
||||
template <typename EquationOfState, typename Catalog> struct ImplementsRelationCatalog : std::false_type { };
|
||||
|
||||
template <typename EquationOfState, typename... Relations>
|
||||
struct ImplementsRelationCatalog<EquationOfState, RelationCatalog<Relations...>>
|
||||
: std::bool_constant<(ImplementsRelation<EquationOfState, Relations>::value && ...)> { };
|
||||
|
||||
template <typename Candidate, typename = void> struct IsEquationOfStateModel : std::false_type { };
|
||||
|
||||
template <typename Candidate>
|
||||
struct IsEquationOfStateModel<Candidate, std::void_t<typename std::remove_cvref_t<Candidate>::Relations>>
|
||||
: std::bool_constant<
|
||||
ValidRelationCatalog<typename std::remove_cvref_t<Candidate>::Relations> &&
|
||||
ImplementsRelationCatalog<
|
||||
std::remove_cvref_t<Candidate>,
|
||||
typename std::remove_cvref_t<Candidate>::Relations>::value> { };
|
||||
|
||||
template <typename EquationOfState, typename RelationType, typename InputQuantity>
|
||||
struct ImplementsPartialDerivative : std::false_type { };
|
||||
|
||||
template <
|
||||
typename EquationOfState,
|
||||
typename Output,
|
||||
typename... Inputs,
|
||||
typename InputQuantity>
|
||||
struct ImplementsPartialDerivative<
|
||||
EquationOfState,
|
||||
Relation<
|
||||
Output,
|
||||
Inputs...>,
|
||||
InputQuantity> : std::bool_constant <
|
||||
(std::same_as<
|
||||
InputQuantity,
|
||||
Inputs> ||
|
||||
...) &&
|
||||
requires(
|
||||
const std::remove_cvref_t<EquationOfState> &equationOfState,
|
||||
QuantityValue<Inputs>... inputValues
|
||||
) {
|
||||
{equationOfState
|
||||
.partialDerivative(Relation<Output, Inputs...>{}, WithRespectTo<InputQuantity>{}, inputValues...)}
|
||||
->std::same_as<PartialDerivative<Output, InputQuantity>>;
|
||||
}>{};
|
||||
} // namespace detail
|
||||
|
||||
template <typename Candidate>
|
||||
concept EquationOfStateModel = detail::IsEquationOfStateModel<Candidate>::value;
|
||||
|
||||
template <typename EquationOfState, typename RelationType>
|
||||
concept SupportsRelation =
|
||||
EquationOfStateModel<EquationOfState> && ThermodynamicRelationType<RelationType> &&
|
||||
relationCatalogContains<typename std::remove_cvref_t<EquationOfState>::Relations, RelationType>;
|
||||
|
||||
template <typename EquationOfState, typename RelationType, typename InputQuantity>
|
||||
concept SupportsPartialDerivative =
|
||||
SupportsRelation<EquationOfState, RelationType> && ThermodynamicQuantityType<InputQuantity> &&
|
||||
detail::ImplementsPartialDerivative<EquationOfState, RelationType, InputQuantity>::value;
|
||||
|
||||
template <typename Candidate>
|
||||
concept StructureSeedEquationOfState =
|
||||
EquationOfStateModel<Candidate> && SupportsRelation<Candidate, SpecificEnthalpyFromDensity>;
|
||||
|
||||
template <typename Candidate>
|
||||
concept BarotropicClosureEquationOfState =
|
||||
EquationOfStateModel<Candidate> && SupportsRelation<Candidate, DensityFromSpecificEnthalpy> &&
|
||||
SupportsPartialDerivative<Candidate, DensityFromSpecificEnthalpy, quantity::SpecificEnthalpy>;
|
||||
|
||||
template <typename Candidate>
|
||||
concept PressureForceEquationOfState =
|
||||
EquationOfStateModel<Candidate> && SupportsRelation<Candidate, PressureFromSpecificEnthalpy> &&
|
||||
SupportsPartialDerivative<Candidate, PressureFromSpecificEnthalpy, quantity::SpecificEnthalpy>;
|
||||
} // namespace mean_field::eos
|
||||
@@ -1,16 +0,0 @@
|
||||
export module mean_field:eos.base;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
class EquationOfState {
|
||||
public:
|
||||
virtual ~EquationOfState() = default;
|
||||
[[nodiscard]] virtual double pressure_from_density(double density) const = 0;
|
||||
[[nodiscard]] virtual double pressure_from_enthalpy(double enthalpy) const = 0;
|
||||
[[nodiscard]] virtual double enthalpy_from_density(double density) const = 0;
|
||||
[[nodiscard]] virtual double enthalpy_from_pressure(double pressure) const = 0;
|
||||
[[nodiscard]] virtual double density_from_enthalpy(double enthalpy) const = 0;
|
||||
[[nodiscard]] virtual double density_derivative_from_enthalpy(double enthalpy) const = 0;
|
||||
[[nodiscard]] virtual double pressure_derivative_from_enthalpy(double enthalpy) const = 0;
|
||||
[[nodiscard]] virtual double pressure_derivative_from_density(double density) const = 0;
|
||||
};
|
||||
} // namespace mean_field::eos
|
||||
90
libmeanfield/interface/eos/evaluation.cppm
Normal file
90
libmeanfield/interface/eos/evaluation.cppm
Normal file
@@ -0,0 +1,90 @@
|
||||
module;
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
export module mean_field:eos.evaluation;
|
||||
export import :eos.concepts;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
enum class EvaluationErrorCode {
|
||||
unsupported_relation,
|
||||
unsupported_derivative,
|
||||
wrong_input_count,
|
||||
wrong_input_quantity,
|
||||
nonfinite_input,
|
||||
outside_domain,
|
||||
nonfinite_result
|
||||
};
|
||||
|
||||
class EvaluationError final : public std::domain_error {
|
||||
public:
|
||||
explicit EvaluationError(
|
||||
const EvaluationErrorCode code,
|
||||
std::string message
|
||||
)
|
||||
: std::domain_error(std::move(message)),
|
||||
m_code(code) {
|
||||
}
|
||||
|
||||
[[nodiscard]] EvaluationErrorCode code() const noexcept {
|
||||
return m_code;
|
||||
}
|
||||
|
||||
private:
|
||||
EvaluationErrorCode m_code;
|
||||
};
|
||||
|
||||
template <
|
||||
ThermodynamicQuantityType OutputQuantity,
|
||||
EquationOfStateModel EquationOfState,
|
||||
QuantityValueType... InputValues>
|
||||
requires SupportsRelation<
|
||||
EquationOfState,
|
||||
Relation<
|
||||
OutputQuantity,
|
||||
QuantityOfT<InputValues>...>>
|
||||
[[nodiscard]] constexpr QuantityValue<OutputQuantity> evaluate(
|
||||
const EquationOfState &equationOfState,
|
||||
const InputValues... inputValues
|
||||
) noexcept(noexcept(equationOfState
|
||||
.evaluate(
|
||||
Relation<
|
||||
OutputQuantity,
|
||||
QuantityOfT<InputValues>...>{},
|
||||
inputValues...
|
||||
))) {
|
||||
return equationOfState.evaluate(Relation<OutputQuantity, QuantityOfT<InputValues>...>{}, inputValues...);
|
||||
}
|
||||
|
||||
template <
|
||||
ThermodynamicQuantityType OutputQuantity,
|
||||
ThermodynamicQuantityType InputQuantity,
|
||||
EquationOfStateModel EquationOfState,
|
||||
QuantityValueType... InputValues>
|
||||
requires SupportsPartialDerivative<
|
||||
EquationOfState,
|
||||
Relation<
|
||||
OutputQuantity,
|
||||
QuantityOfT<InputValues>...>,
|
||||
InputQuantity>
|
||||
[[nodiscard]] constexpr PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity>
|
||||
partialDerivative(
|
||||
const EquationOfState &equationOfState,
|
||||
const InputValues... inputValues
|
||||
) noexcept(noexcept(equationOfState
|
||||
.partialDerivative(
|
||||
Relation<
|
||||
OutputQuantity,
|
||||
QuantityOfT<InputValues>...>{},
|
||||
WithRespectTo<InputQuantity>{},
|
||||
inputValues...
|
||||
))) {
|
||||
return equationOfState.partialDerivative(
|
||||
Relation<OutputQuantity, QuantityOfT<InputValues>...>{}, WithRespectTo<InputQuantity>{}, inputValues...
|
||||
);
|
||||
}
|
||||
} // namespace mean_field::eos
|
||||
@@ -3,11 +3,18 @@ module;
|
||||
#include <format>
|
||||
#include <stdexcept>
|
||||
export module mean_field:eos.polytrope;
|
||||
export import :eos.base;
|
||||
export import :eos.evaluation;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
class Polytrope final : public EquationOfState {
|
||||
class Polytrope final {
|
||||
public:
|
||||
using Relations = RelationCatalog<
|
||||
PressureFromDensity,
|
||||
PressureFromSpecificEnthalpy,
|
||||
SpecificEnthalpyFromDensity,
|
||||
SpecificEnthalpyFromPressure,
|
||||
DensityFromSpecificEnthalpy>;
|
||||
|
||||
Polytrope(
|
||||
const double polytropic_index,
|
||||
const double polytropic_constant
|
||||
@@ -49,82 +56,128 @@ export namespace mean_field::eos {
|
||||
return m_enthalpy_scale;
|
||||
}
|
||||
|
||||
[[nodiscard]] double pressure_from_density(const double density) const override {
|
||||
validate_nonnegativity(density, "density");
|
||||
if (density == 0.0) {
|
||||
return 0.0;
|
||||
[[nodiscard]] PressureValue evaluate(
|
||||
PressureFromDensity,
|
||||
const DensityValue density
|
||||
) const {
|
||||
validate_nonnegativity(density.value(), "density");
|
||||
if (density.value() == 0.0) {
|
||||
return PressureValue{0.0};
|
||||
}
|
||||
|
||||
return m_polytropic_constant * std::pow(density, 1.0 + 1.0 / m_polytropic_index);
|
||||
return PressureValue{m_polytropic_constant * std::pow(density.value(), 1.0 + 1.0 / m_polytropic_index)};
|
||||
}
|
||||
|
||||
[[nodiscard]] double enthalpy_from_density(const double density) const override {
|
||||
validate_nonnegativity(density, "density");
|
||||
if (density == 0.0) {
|
||||
return 0.0;
|
||||
[[nodiscard]] SpecificEnthalpyValue evaluate(
|
||||
SpecificEnthalpyFromDensity,
|
||||
const DensityValue density
|
||||
) const {
|
||||
validate_nonnegativity(density.value(), "density");
|
||||
if (density.value() == 0.0) {
|
||||
return SpecificEnthalpyValue{0.0};
|
||||
}
|
||||
|
||||
return m_enthalpy_scale * std::pow(density, 1.0 / m_polytropic_index);
|
||||
return SpecificEnthalpyValue{m_enthalpy_scale * std::pow(density.value(), 1.0 / m_polytropic_index)};
|
||||
}
|
||||
|
||||
[[nodiscard]] double density_from_enthalpy(const double enthalpy) const override {
|
||||
validate_finite(enthalpy, "enthalpy");
|
||||
[[nodiscard]] DensityValue evaluate(
|
||||
DensityFromSpecificEnthalpy,
|
||||
const SpecificEnthalpyValue specificEnthalpy
|
||||
) const {
|
||||
validate_finite(specificEnthalpy.value(), "specific enthalpy");
|
||||
|
||||
if (enthalpy <= 0.0) {
|
||||
return 0.0;
|
||||
if (specificEnthalpy.value() <= 0.0) {
|
||||
return DensityValue{0.0};
|
||||
}
|
||||
|
||||
return std::pow(enthalpy / m_enthalpy_scale, m_polytropic_index);
|
||||
return DensityValue{std::pow(specificEnthalpy.value() / m_enthalpy_scale, m_polytropic_index)};
|
||||
}
|
||||
|
||||
[[nodiscard]] double pressure_from_enthalpy(const double enthalpy) const override {
|
||||
validate_finite(enthalpy, "enthalpy");
|
||||
[[nodiscard]] PressureValue evaluate(
|
||||
PressureFromSpecificEnthalpy,
|
||||
const SpecificEnthalpyValue specificEnthalpy
|
||||
) const {
|
||||
const DensityValue density = evaluate(DensityFromSpecificEnthalpy{}, specificEnthalpy);
|
||||
|
||||
if (enthalpy <= 0.0) {
|
||||
return 0.0;
|
||||
if (specificEnthalpy.value() <= 0.0) {
|
||||
return PressureValue{0.0};
|
||||
}
|
||||
|
||||
return density_from_enthalpy(enthalpy) * enthalpy / (m_polytropic_index + 1.0);
|
||||
return PressureValue{density.value() * specificEnthalpy.value() / (m_polytropic_index + 1.0)};
|
||||
}
|
||||
|
||||
[[nodiscard]] double density_derivative_from_enthalpy(const double enthalpy) const override {
|
||||
validate_finite(enthalpy, "enthalpy");
|
||||
if (enthalpy < 0.0) {
|
||||
return 0.0;
|
||||
[[nodiscard]] SpecificEnthalpyValue evaluate(
|
||||
SpecificEnthalpyFromPressure,
|
||||
const PressureValue pressure
|
||||
) const {
|
||||
validate_nonnegativity(pressure.value(), "pressure");
|
||||
if (pressure.value() == 0.0) {
|
||||
return SpecificEnthalpyValue{0.0};
|
||||
}
|
||||
|
||||
if (enthalpy == 0.0) {
|
||||
return m_polytropic_index == 1.0 ? 1.0 / m_enthalpy_scale : 0.0;
|
||||
}
|
||||
const double indexPlusOne = m_polytropic_index + 1.0;
|
||||
|
||||
return m_polytropic_index / m_enthalpy_scale *
|
||||
std::pow(enthalpy / m_enthalpy_scale, m_polytropic_index - 1.0);
|
||||
return SpecificEnthalpyValue{
|
||||
indexPlusOne * std::pow(m_polytropic_constant, m_polytropic_index / indexPlusOne) *
|
||||
std::pow(pressure.value(), 1.0 / indexPlusOne)
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] double pressure_derivative_from_enthalpy(const double enthalpy) const override {
|
||||
validate_finite(enthalpy, "enthalpy");
|
||||
|
||||
if (enthalpy <= 0.0) {
|
||||
return 0.0;
|
||||
[[nodiscard]] PartialDerivative<
|
||||
quantity::Density,
|
||||
quantity::SpecificEnthalpy>
|
||||
partialDerivative(
|
||||
DensityFromSpecificEnthalpy,
|
||||
WithRespectTo<quantity::SpecificEnthalpy>,
|
||||
const SpecificEnthalpyValue specificEnthalpy
|
||||
) const {
|
||||
validate_finite(specificEnthalpy.value(), "specific enthalpy");
|
||||
if (specificEnthalpy.value() < 0.0) {
|
||||
return PartialDerivative<quantity::Density, quantity::SpecificEnthalpy>{0.0};
|
||||
}
|
||||
|
||||
return density_from_enthalpy(enthalpy);
|
||||
}
|
||||
|
||||
[[nodiscard]] double pressure_derivative_from_density(const double density) const override {
|
||||
validate_nonnegativity(density, "density");
|
||||
if (density == 0.0) {
|
||||
return 0.0;
|
||||
if (specificEnthalpy.value() == 0.0) {
|
||||
return PartialDerivative<quantity::Density, quantity::SpecificEnthalpy>{
|
||||
m_polytropic_index == 1.0 ? 1.0 / m_enthalpy_scale : 0.0
|
||||
};
|
||||
}
|
||||
|
||||
return m_polytropic_constant * (1.0 + 1.0 / m_polytropic_index) *
|
||||
std::pow(density, 1.0 / m_polytropic_index);
|
||||
return PartialDerivative<quantity::Density, quantity::SpecificEnthalpy>{
|
||||
m_polytropic_index / m_enthalpy_scale *
|
||||
std::pow(specificEnthalpy.value() / m_enthalpy_scale, m_polytropic_index - 1.0)
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] double enthalpy_from_pressure(const double pressure) const override {
|
||||
validate_nonnegativity(pressure, "pressure");
|
||||
const double np1 = m_polytropic_index + 1;
|
||||
return np1 * std::pow(m_polytropic_constant, m_polytropic_index / np1) * std::pow(pressure, 1.0 / np1);
|
||||
[[nodiscard]] PartialDerivative<
|
||||
quantity::Pressure,
|
||||
quantity::SpecificEnthalpy>
|
||||
partialDerivative(
|
||||
PressureFromSpecificEnthalpy,
|
||||
WithRespectTo<quantity::SpecificEnthalpy>,
|
||||
const SpecificEnthalpyValue specificEnthalpy
|
||||
) const {
|
||||
const DensityValue density = evaluate(DensityFromSpecificEnthalpy{}, specificEnthalpy);
|
||||
|
||||
return PartialDerivative<quantity::Pressure, quantity::SpecificEnthalpy>{density.value()};
|
||||
}
|
||||
|
||||
[[nodiscard]] PartialDerivative<
|
||||
quantity::Pressure,
|
||||
quantity::Density>
|
||||
partialDerivative(
|
||||
PressureFromDensity,
|
||||
WithRespectTo<quantity::Density>,
|
||||
const DensityValue density
|
||||
) const {
|
||||
validate_nonnegativity(density.value(), "density");
|
||||
if (density.value() == 0.0) {
|
||||
return PartialDerivative<quantity::Pressure, quantity::Density>{0.0};
|
||||
}
|
||||
|
||||
return PartialDerivative<quantity::Pressure, quantity::Density>{
|
||||
m_polytropic_constant * (1.0 + 1.0 / m_polytropic_index) *
|
||||
std::pow(density.value(), 1.0 / m_polytropic_index)
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -133,12 +186,12 @@ export namespace mean_field::eos {
|
||||
const char *quantity
|
||||
) {
|
||||
if (!std::isfinite(value)) {
|
||||
throw std::domain_error(
|
||||
std::format(
|
||||
"The {} must be finite. Instead a value of {} has been "
|
||||
"provided",
|
||||
quantity, value
|
||||
)
|
||||
throw EvaluationError(
|
||||
EvaluationErrorCode::nonfinite_input, std::format(
|
||||
"The {} must be finite. Instead a value of {} has been "
|
||||
"provided",
|
||||
quantity, value
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -149,13 +202,13 @@ export namespace mean_field::eos {
|
||||
) {
|
||||
validate_finite(value, quantity);
|
||||
if (value < 0.0) {
|
||||
throw std::domain_error(
|
||||
std::format(
|
||||
"The {} must be non-negative. Instead a value of {} "
|
||||
"has been "
|
||||
"provided",
|
||||
quantity, value
|
||||
)
|
||||
throw EvaluationError(
|
||||
EvaluationErrorCode::outside_domain, std::format(
|
||||
"The {} must be non-negative. Instead a value of {} "
|
||||
"has been "
|
||||
"provided",
|
||||
quantity, value
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
128
libmeanfield/interface/eos/pressure_surface.cppm
Normal file
128
libmeanfield/interface/eos/pressure_surface.cppm
Normal file
@@ -0,0 +1,128 @@
|
||||
module;
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:eos.pressure_surface;
|
||||
|
||||
export import :eos.evaluation;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
namespace detail {
|
||||
template <
|
||||
ThermodynamicQuantityType InputQuantity,
|
||||
typename SurfaceState>
|
||||
[[nodiscard]] constexpr auto pressureSurfaceRelationInput(
|
||||
const PressureValue targetPressure,
|
||||
const SurfaceState &state
|
||||
) {
|
||||
if constexpr (std::same_as<InputQuantity, quantity::Pressure>) {
|
||||
return targetPressure;
|
||||
} else {
|
||||
return state.value(InputQuantity{});
|
||||
}
|
||||
}
|
||||
|
||||
template <typename RelationType> struct PressureSurfaceRelationOperations;
|
||||
|
||||
template <typename CarrierQuantity, typename... InputQuantities>
|
||||
struct PressureSurfaceRelationOperations<Relation<CarrierQuantity, InputQuantities...>> {
|
||||
template <
|
||||
typename EquationOfState,
|
||||
typename SurfaceState>
|
||||
[[nodiscard]] static QuantityValue<CarrierQuantity> requiredCarrierValue(
|
||||
const EquationOfState &equationOfState,
|
||||
const PressureValue targetPressure,
|
||||
const SurfaceState &state
|
||||
) {
|
||||
return evaluate<CarrierQuantity>(
|
||||
equationOfState, pressureSurfaceRelationInput<InputQuantities>(targetPressure, state)...
|
||||
);
|
||||
}
|
||||
|
||||
template <
|
||||
typename InputQuantity,
|
||||
typename EquationOfState,
|
||||
typename SurfaceState,
|
||||
typename SurfaceVariation>
|
||||
[[nodiscard]] static double inputJacobianContribution(
|
||||
const EquationOfState &equationOfState,
|
||||
const PressureValue targetPressure,
|
||||
const SurfaceState &state,
|
||||
const SurfaceVariation &variation
|
||||
) {
|
||||
if constexpr (std::same_as<InputQuantity, quantity::Pressure>) {
|
||||
return 0.0;
|
||||
} else {
|
||||
const auto derivative = partialDerivative<CarrierQuantity, InputQuantity>(
|
||||
equationOfState, pressureSurfaceRelationInput<InputQuantities>(targetPressure, state)...
|
||||
);
|
||||
return derivative.value() * variation.value(InputQuantity{}).value();
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename EquationOfState,
|
||||
typename SurfaceState,
|
||||
typename SurfaceVariation>
|
||||
[[nodiscard]] static double carrierCorrectionJacobianAction(
|
||||
const EquationOfState &equationOfState,
|
||||
const PressureValue targetPressure,
|
||||
const SurfaceState &state,
|
||||
const SurfaceVariation &variation
|
||||
) {
|
||||
return (
|
||||
0.0 + ... +
|
||||
inputJacobianContribution<InputQuantities>(equationOfState, targetPressure, state, variation)
|
||||
);
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/*
|
||||
* EOS-owned resolution of a constant-pressure condition into the carrier
|
||||
* quantity used by an equation formulation. No field or solver concepts
|
||||
* enter this type.
|
||||
*/
|
||||
template <EquationOfStateModel EquationOfState, ThermodynamicRelationType SelectedRelation>
|
||||
class ResolvedPressureSurfaceRelation final {
|
||||
public:
|
||||
using RelationType = SelectedRelation;
|
||||
using CarrierQuantity = RelationOutputT<RelationType>;
|
||||
|
||||
ResolvedPressureSurfaceRelation(
|
||||
const EquationOfState &equationOfState,
|
||||
const PressureValue targetPressure
|
||||
) noexcept
|
||||
: m_equationOfState(std::addressof(equationOfState)),
|
||||
m_targetPressure(targetPressure) {
|
||||
}
|
||||
|
||||
[[nodiscard]] PressureValue targetPressure() const noexcept {
|
||||
return m_targetPressure;
|
||||
}
|
||||
|
||||
template <typename SurfaceState>
|
||||
[[nodiscard]] QuantityValue<CarrierQuantity> requiredCarrierValue(const SurfaceState &state) const {
|
||||
return detail::PressureSurfaceRelationOperations<RelationType>::requiredCarrierValue(
|
||||
*m_equationOfState, m_targetPressure, state
|
||||
);
|
||||
}
|
||||
|
||||
template <
|
||||
typename SurfaceState,
|
||||
typename SurfaceVariation>
|
||||
[[nodiscard]] double carrierCorrectionJacobianAction(
|
||||
const SurfaceState &state,
|
||||
const SurfaceVariation &variation
|
||||
) const {
|
||||
return detail::PressureSurfaceRelationOperations<RelationType>::carrierCorrectionJacobianAction(
|
||||
*m_equationOfState, m_targetPressure, state, variation
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
const EquationOfState *m_equationOfState;
|
||||
PressureValue m_targetPressure;
|
||||
};
|
||||
} // namespace mean_field::eos
|
||||
235
libmeanfield/interface/eos/quantities.cppm
Normal file
235
libmeanfield/interface/eos/quantities.cppm
Normal file
@@ -0,0 +1,235 @@
|
||||
module;
|
||||
|
||||
#include <compare>
|
||||
#include <concepts>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:eos.quantities;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
struct ThermodynamicQuantity { };
|
||||
|
||||
template <typename Candidate>
|
||||
concept ThermodynamicQuantityType =
|
||||
std::same_as<Candidate, std::remove_cv_t<Candidate>> && std::derived_from<Candidate, ThermodynamicQuantity>;
|
||||
|
||||
namespace quantity {
|
||||
struct Density final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "density";
|
||||
};
|
||||
|
||||
struct Pressure final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "pressure";
|
||||
};
|
||||
|
||||
struct SpecificEnthalpy final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "specific_enthalpy";
|
||||
};
|
||||
} // namespace quantity
|
||||
|
||||
template <typename T>
|
||||
concept Numeric = std::integral<T> || std::floating_point<T>;
|
||||
|
||||
template <ThermodynamicQuantityType Quantity> class QuantityValue final {
|
||||
public:
|
||||
explicit constexpr QuantityValue(const double value) noexcept : m_value(value) {
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr double value() const noexcept {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator==(
|
||||
const QuantityValue &,
|
||||
const QuantityValue &
|
||||
) noexcept = default;
|
||||
|
||||
friend constexpr QuantityValue<Quantity> operator+(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
const QuantityValue<Quantity> &rhs
|
||||
) noexcept {
|
||||
return QuantityValue<Quantity>{lhs.m_value + rhs.m_value};
|
||||
}
|
||||
|
||||
friend constexpr QuantityValue<Quantity> operator-(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
const QuantityValue<Quantity> &rhs
|
||||
) noexcept {
|
||||
return QuantityValue<Quantity>{lhs.m_value - rhs.m_value};
|
||||
}
|
||||
|
||||
template <Numeric rhsT>
|
||||
friend constexpr QuantityValue<Quantity> operator*(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
rhsT rhs
|
||||
) noexcept {
|
||||
return QuantityValue<Quantity>{lhs.m_value * static_cast<double>(rhs)};
|
||||
}
|
||||
|
||||
template <Numeric lhsT>
|
||||
friend constexpr QuantityValue<Quantity> operator*(
|
||||
lhsT lhs,
|
||||
const QuantityValue<Quantity> &rhs
|
||||
) noexcept {
|
||||
return QuantityValue<Quantity>{static_cast<double>(lhs) * rhs.m_value};
|
||||
}
|
||||
|
||||
template <Numeric rhsT>
|
||||
friend constexpr QuantityValue<Quantity> operator/(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
rhsT rhs
|
||||
) noexcept {
|
||||
return QuantityValue<Quantity>{lhs.m_value / static_cast<double>(rhs)};
|
||||
}
|
||||
|
||||
template <Numeric compT>
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
compT rhs
|
||||
) noexcept {
|
||||
return lhs.m_value <=> static_cast<double>(rhs);
|
||||
}
|
||||
|
||||
template <Numeric compT>
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
compT lhs,
|
||||
const QuantityValue<Quantity> &rhs
|
||||
) noexcept {
|
||||
return static_cast<double>(lhs) <=> rhs.m_value;
|
||||
}
|
||||
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
const QuantityValue<Quantity> &rhs
|
||||
) noexcept {
|
||||
return lhs.m_value <=> rhs.m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
double m_value;
|
||||
};
|
||||
|
||||
using DensityValue = QuantityValue<quantity::Density>;
|
||||
using PressureValue = QuantityValue<quantity::Pressure>;
|
||||
using SpecificEnthalpyValue = QuantityValue<quantity::SpecificEnthalpy>;
|
||||
|
||||
template <typename Candidate> struct IsQuantityValue : std::false_type { };
|
||||
|
||||
template <ThermodynamicQuantityType Quantity> struct IsQuantityValue<QuantityValue<Quantity>> : std::true_type { };
|
||||
|
||||
template <typename Candidate>
|
||||
concept QuantityValueType = IsQuantityValue<std::remove_cvref_t<Candidate>>::value;
|
||||
|
||||
template <typename Candidate> struct QuantityOf;
|
||||
|
||||
template <ThermodynamicQuantityType Quantity> struct QuantityOf<QuantityValue<Quantity>> {
|
||||
using Type = Quantity;
|
||||
};
|
||||
|
||||
template <QuantityValueType Value> using QuantityOfT = typename QuantityOf<std::remove_cvref_t<Value>>::Type;
|
||||
|
||||
template <ThermodynamicQuantityType OutputQuantity, ThermodynamicQuantityType InputQuantity>
|
||||
class PartialDerivative final {
|
||||
public:
|
||||
explicit constexpr PartialDerivative(const double value) noexcept : m_value(value) {
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr double value() const noexcept {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
friend constexpr PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity>
|
||||
operator+(
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &lhs,
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &rhs
|
||||
) noexcept;
|
||||
|
||||
friend constexpr PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity>
|
||||
operator-(
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &lhs,
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &rhs
|
||||
) noexcept;
|
||||
|
||||
template <Numeric rhsT>
|
||||
friend constexpr PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity>
|
||||
operator*(
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &,
|
||||
rhsT
|
||||
) noexcept;
|
||||
|
||||
template <Numeric lhsT>
|
||||
friend constexpr PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity>
|
||||
operator*(
|
||||
lhsT,
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &
|
||||
) noexcept;
|
||||
|
||||
template <Numeric rhsT>
|
||||
friend constexpr PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity>
|
||||
operator/(
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &,
|
||||
rhsT
|
||||
) noexcept;
|
||||
|
||||
template <Numeric cmpT>
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &lhs,
|
||||
cmpT rhs
|
||||
) noexcept {
|
||||
return lhs.m_value <=> static_cast<double>(rhs);
|
||||
}
|
||||
|
||||
template <Numeric cmpT>
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
cmpT lhs,
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &rhs
|
||||
) noexcept {
|
||||
return static_cast<double>(lhs) <=> rhs.m_value;
|
||||
}
|
||||
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &lhs,
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &rhs
|
||||
) noexcept {
|
||||
return lhs.m_value <=> rhs.m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
double m_value;
|
||||
};
|
||||
|
||||
template <ThermodynamicQuantityType Quantity> struct WithRespectTo final { };
|
||||
} // namespace mean_field::eos
|
||||
93
libmeanfield/interface/eos/relations.cppm
Normal file
93
libmeanfield/interface/eos/relations.cppm
Normal file
@@ -0,0 +1,93 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:eos.relations;
|
||||
export import :eos.quantities;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
template <typename... Quantities> struct QuantityList final { };
|
||||
|
||||
template <typename Output, typename... Inputs> struct Relation final {
|
||||
using OutputQuantity = Output;
|
||||
using InputQuantities = QuantityList<Inputs...>;
|
||||
|
||||
static constexpr std::size_t inputCount = sizeof...(Inputs);
|
||||
};
|
||||
|
||||
template <typename... Relations> struct RelationCatalog final {
|
||||
static constexpr std::size_t size = sizeof...(Relations);
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename... Types> struct TypesAreUnique;
|
||||
|
||||
template <typename Candidate> struct IsThermodynamicRelation : std::false_type { };
|
||||
|
||||
template <typename Output, typename... Inputs>
|
||||
struct IsThermodynamicRelation<Relation<Output, Inputs...>>
|
||||
: std::bool_constant<
|
||||
ThermodynamicQuantityType<Output> && (ThermodynamicQuantityType<Inputs> && ...) &&
|
||||
TypesAreUnique<Inputs...>::value> { };
|
||||
|
||||
template <typename... Types> struct TypesAreUnique : std::true_type { };
|
||||
|
||||
template <typename First, typename... Remaining>
|
||||
struct TypesAreUnique<First, Remaining...>
|
||||
: std::bool_constant<(!std::same_as<First, Remaining> && ...) && TypesAreUnique<Remaining...>::value> { };
|
||||
|
||||
template <typename Candidate> struct IsValidRelationCatalog : std::false_type { };
|
||||
|
||||
template <typename... Relations>
|
||||
struct IsValidRelationCatalog<RelationCatalog<Relations...>>
|
||||
: std::bool_constant<
|
||||
(sizeof...(Relations) > 0) && (IsThermodynamicRelation<Relations>::value && ...) &&
|
||||
TypesAreUnique<Relations...>::value> { };
|
||||
|
||||
template <typename Catalog, typename RelationType> struct CatalogContainsRelation : std::false_type { };
|
||||
|
||||
template <typename... Relations, typename RelationType>
|
||||
struct CatalogContainsRelation<RelationCatalog<Relations...>, RelationType>
|
||||
: std::bool_constant<(std::same_as<RelationType, Relations> || ...)> { };
|
||||
|
||||
template <typename RelationType, 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 <std::size_t Index, typename Quantities> struct QuantityAt;
|
||||
|
||||
template <std::size_t Index, typename... Quantities> struct QuantityAt<Index, QuantityList<Quantities...>> {
|
||||
using Type = std::tuple_element_t<Index, std::tuple<Quantities...>>;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename Candidate>
|
||||
concept ThermodynamicRelationType = detail::IsThermodynamicRelation<std::remove_cv_t<Candidate>>::value;
|
||||
|
||||
template <typename Candidate>
|
||||
concept ValidRelationCatalog = detail::IsValidRelationCatalog<std::remove_cv_t<Candidate>>::value;
|
||||
|
||||
template <typename Catalog, typename RelationType>
|
||||
inline constexpr bool relationCatalogContains =
|
||||
detail::CatalogContainsRelation<std::remove_cv_t<Catalog>, std::remove_cv_t<RelationType>>::value;
|
||||
|
||||
template <typename RelationType, typename Quantity>
|
||||
inline constexpr bool relationContainsInput =
|
||||
detail::RelationContainsInput<std::remove_cv_t<RelationType>, std::remove_cv_t<Quantity>>::value;
|
||||
|
||||
template <ThermodynamicRelationType RelationType> using RelationOutputT = typename RelationType::OutputQuantity;
|
||||
|
||||
template <std::size_t Index, ThermodynamicRelationType RelationType>
|
||||
using RelationInputT = typename detail::QuantityAt<Index, typename RelationType::InputQuantities>::Type;
|
||||
|
||||
using PressureFromDensity = Relation<quantity::Pressure, quantity::Density>;
|
||||
using PressureFromSpecificEnthalpy = Relation<quantity::Pressure, quantity::SpecificEnthalpy>;
|
||||
using SpecificEnthalpyFromDensity = Relation<quantity::SpecificEnthalpy, quantity::Density>;
|
||||
using SpecificEnthalpyFromPressure = Relation<quantity::SpecificEnthalpy, quantity::Pressure>;
|
||||
using DensityFromSpecificEnthalpy = Relation<quantity::Density, quantity::SpecificEnthalpy>;
|
||||
} // namespace mean_field::eos
|
||||
645
libmeanfield/interface/eos/runtime.cppm
Normal file
645
libmeanfield/interface/eos/runtime.cppm
Normal file
@@ -0,0 +1,645 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
export module mean_field:eos.runtime;
|
||||
export import :eos.evaluation;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
class ThermodynamicQuantityId final {
|
||||
public:
|
||||
explicit constexpr ThermodynamicQuantityId(const std::string_view name) noexcept : m_name(name) {
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::string_view name() const noexcept {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator==(
|
||||
const ThermodynamicQuantityId &,
|
||||
const ThermodynamicQuantityId &
|
||||
) noexcept = default;
|
||||
|
||||
private:
|
||||
std::string_view m_name;
|
||||
};
|
||||
|
||||
template <typename Quantity>
|
||||
concept RuntimeIdentifiedThermodynamicQuantity = ThermodynamicQuantityType<Quantity> && requires {
|
||||
{ Quantity::identifier } -> std::convertible_to<std::string_view>;
|
||||
} && (std::string_view{Quantity::identifier}.size() > 0);
|
||||
|
||||
template <RuntimeIdentifiedThermodynamicQuantity Quantity>
|
||||
inline constexpr ThermodynamicQuantityId thermodynamicQuantityId{std::string_view{Quantity::identifier}};
|
||||
|
||||
struct RuntimeQuantityValue final {
|
||||
ThermodynamicQuantityId quantity;
|
||||
double value;
|
||||
};
|
||||
|
||||
struct RuntimeRelationDescriptor final {
|
||||
ThermodynamicQuantityId outputQuantity;
|
||||
std::span<const ThermodynamicQuantityId> inputQuantities;
|
||||
std::uint64_t partialDerivativeMask;
|
||||
|
||||
[[nodiscard]] constexpr bool hasPartialDerivative(const std::size_t inputIndex) const noexcept {
|
||||
return inputIndex < inputQuantities.size() &&
|
||||
(partialDerivativeMask & (std::uint64_t{1} << inputIndex)) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename RelationType> struct HasRuntimeQuantityIdentifiers : std::false_type { };
|
||||
|
||||
template <typename Output, typename... Inputs>
|
||||
struct HasRuntimeQuantityIdentifiers<Relation<Output, Inputs...>>
|
||||
: std::bool_constant<
|
||||
RuntimeIdentifiedThermodynamicQuantity<Output> &&
|
||||
(RuntimeIdentifiedThermodynamicQuantity<Inputs> && ...)> { };
|
||||
|
||||
template <typename RelationType> struct RuntimeRelationQuantities;
|
||||
|
||||
template <typename Output, typename... Inputs> struct RuntimeRelationQuantities<Relation<Output, Inputs...>> {
|
||||
using Type = std::tuple<Output, Inputs...>;
|
||||
};
|
||||
|
||||
template <typename... Relations>
|
||||
using RuntimeCatalogQuantityTuple =
|
||||
decltype(std::tuple_cat(std::declval<typename RuntimeRelationQuantities<Relations>::Type>()...));
|
||||
|
||||
template <
|
||||
typename FirstQuantity,
|
||||
typename SecondQuantity>
|
||||
[[nodiscard]] consteval bool runtimeQuantityIdentifiersAreCompatible() {
|
||||
if constexpr (std::same_as<FirstQuantity, SecondQuantity>) {
|
||||
return true;
|
||||
} else {
|
||||
return thermodynamicQuantityId<FirstQuantity> != thermodynamicQuantityId<SecondQuantity>;
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename QuantityTuple,
|
||||
std::size_t First,
|
||||
std::size_t... Offsets>
|
||||
[[nodiscard]] consteval bool runtimeQuantityIdentifierIsUnambiguous(std::index_sequence<Offsets...>) {
|
||||
return (
|
||||
runtimeQuantityIdentifiersAreCompatible<
|
||||
std::tuple_element_t<First, QuantityTuple>,
|
||||
std::tuple_element_t<First + 1 + Offsets, QuantityTuple>>() &&
|
||||
...
|
||||
);
|
||||
}
|
||||
|
||||
template <
|
||||
typename QuantityTuple,
|
||||
std::size_t... Indices>
|
||||
[[nodiscard]] consteval bool runtimeQuantityIdentifiersAreUnambiguous(std::index_sequence<Indices...>) {
|
||||
return (
|
||||
runtimeQuantityIdentifierIsUnambiguous<QuantityTuple, Indices>(
|
||||
std::make_index_sequence<std::tuple_size_v<QuantityTuple> - Indices - 1>{}
|
||||
) &&
|
||||
...
|
||||
);
|
||||
}
|
||||
|
||||
template <bool QuantitiesAreIdentified, typename... Relations>
|
||||
struct RuntimeRelationsAreSupported : std::false_type { };
|
||||
|
||||
template <typename... Relations>
|
||||
struct RuntimeRelationsAreSupported<true, Relations...>
|
||||
: std::bool_constant<runtimeQuantityIdentifiersAreUnambiguous<RuntimeCatalogQuantityTuple<Relations...>>(
|
||||
std::make_index_sequence<std::tuple_size_v<RuntimeCatalogQuantityTuple<Relations...>>>{}
|
||||
)> { };
|
||||
|
||||
template <typename Catalog> struct RuntimeCatalogIsSupported : std::false_type { };
|
||||
|
||||
template <typename... Relations>
|
||||
struct RuntimeCatalogIsSupported<RelationCatalog<Relations...>>
|
||||
: RuntimeRelationsAreSupported<(HasRuntimeQuantityIdentifiers<Relations>::value && ...), Relations...> { };
|
||||
} // namespace detail
|
||||
|
||||
template <typename Candidate>
|
||||
concept RuntimeEquationOfStateModel =
|
||||
EquationOfStateModel<Candidate> &&
|
||||
detail::RuntimeCatalogIsSupported<typename std::remove_cvref_t<Candidate>::Relations>::value;
|
||||
|
||||
namespace detail {
|
||||
template <typename EquationOfState, typename RelationType> struct RuntimeRelationStorage;
|
||||
|
||||
template <typename EquationOfState, typename Output, typename... Inputs>
|
||||
struct RuntimeRelationStorage<EquationOfState, Relation<Output, Inputs...>> {
|
||||
using RelationType = Relation<Output, Inputs...>;
|
||||
|
||||
static_assert(
|
||||
sizeof...(Inputs) <= 64,
|
||||
"Runtime EOS relation descriptors support at most 64 inputs."
|
||||
);
|
||||
|
||||
inline static constexpr std::array<ThermodynamicQuantityId, sizeof...(Inputs)> inputQuantityIds{
|
||||
thermodynamicQuantityId<Inputs>...
|
||||
};
|
||||
|
||||
template <std::size_t... Indices>
|
||||
[[nodiscard]] static consteval std::uint64_t makePartialDerivativeMask(std::index_sequence<Indices...>) {
|
||||
using InputTuple = std::tuple<Inputs...>;
|
||||
|
||||
return (
|
||||
std::uint64_t{0} | ... |
|
||||
(SupportsPartialDerivative<EquationOfState, RelationType, std::tuple_element_t<Indices, InputTuple>>
|
||||
? (std::uint64_t{1} << Indices)
|
||||
: std::uint64_t{0})
|
||||
);
|
||||
}
|
||||
|
||||
inline static constexpr std::uint64_t partialDerivativeMask =
|
||||
makePartialDerivativeMask(std::index_sequence_for<Inputs...>{});
|
||||
|
||||
inline static constexpr RuntimeRelationDescriptor descriptor{
|
||||
thermodynamicQuantityId<Output>, std::span<const ThermodynamicQuantityId>{inputQuantityIds},
|
||||
partialDerivativeMask
|
||||
};
|
||||
};
|
||||
|
||||
template <typename EquationOfState, typename Catalog> struct RuntimeCatalogStorage;
|
||||
|
||||
template <typename EquationOfState, typename... Relations>
|
||||
struct RuntimeCatalogStorage<EquationOfState, RelationCatalog<Relations...>> {
|
||||
inline static constexpr std::array descriptors{
|
||||
RuntimeRelationStorage<EquationOfState, Relations>::descriptor...
|
||||
};
|
||||
};
|
||||
|
||||
[[nodiscard]] inline std::expected<
|
||||
double,
|
||||
EvaluationError>
|
||||
runtimeEvaluationFailure(
|
||||
const EvaluationErrorCode code,
|
||||
std::string message
|
||||
) {
|
||||
return std::unexpected<EvaluationError>{EvaluationError{code, std::move(message)}};
|
||||
}
|
||||
|
||||
template <
|
||||
typename EquationOfState,
|
||||
typename Output,
|
||||
typename... Inputs>
|
||||
[[nodiscard]] std::expected<
|
||||
double,
|
||||
EvaluationError>
|
||||
evaluateRuntimeRelation(
|
||||
const EquationOfState &equationOfState,
|
||||
Relation<
|
||||
Output,
|
||||
Inputs...>,
|
||||
const std::span<const RuntimeQuantityValue> inputValues
|
||||
) {
|
||||
const auto invoke = [&]<std::size_t... Indices>(std::index_sequence<Indices...>) {
|
||||
return eos::evaluate<Output>(equationOfState, QuantityValue<Inputs>{inputValues[Indices].value}...)
|
||||
.value();
|
||||
};
|
||||
|
||||
try {
|
||||
return invoke(std::index_sequence_for<Inputs...>{});
|
||||
} catch (const EvaluationError &error) {
|
||||
return std::unexpected<EvaluationError>{error};
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename InputQuantity,
|
||||
typename EquationOfState,
|
||||
typename Output,
|
||||
typename... Inputs>
|
||||
[[nodiscard]] bool tryRuntimePartialDerivative(
|
||||
const EquationOfState &equationOfState,
|
||||
Relation<
|
||||
Output,
|
||||
Inputs...> relation,
|
||||
const ThermodynamicQuantityId withRespectTo,
|
||||
const std::span<const RuntimeQuantityValue> inputValues,
|
||||
std::expected<
|
||||
double,
|
||||
EvaluationError> &result
|
||||
) {
|
||||
if (withRespectTo != thermodynamicQuantityId<InputQuantity>) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if constexpr (SupportsPartialDerivative<EquationOfState, Relation<Output, Inputs...>, InputQuantity>) {
|
||||
const auto invoke = [&]<std::size_t... Indices>(std::index_sequence<Indices...>) {
|
||||
return eos::partialDerivative<Output, InputQuantity>(
|
||||
equationOfState, QuantityValue<Inputs>{inputValues[Indices].value}...
|
||||
)
|
||||
.value();
|
||||
};
|
||||
|
||||
try {
|
||||
result = invoke(std::index_sequence_for<Inputs...>{});
|
||||
} catch (const EvaluationError &error) {
|
||||
result = std::unexpected<EvaluationError>{error};
|
||||
}
|
||||
} else {
|
||||
result = runtimeEvaluationFailure(
|
||||
EvaluationErrorCode::unsupported_derivative,
|
||||
"The requested EOS partial derivative is not available."
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <
|
||||
typename EquationOfState,
|
||||
typename Output,
|
||||
typename... Inputs>
|
||||
[[nodiscard]] std::expected<
|
||||
double,
|
||||
EvaluationError>
|
||||
evaluateRuntimePartialDerivative(
|
||||
const EquationOfState &equationOfState,
|
||||
Relation<
|
||||
Output,
|
||||
Inputs...> relation,
|
||||
const ThermodynamicQuantityId withRespectTo,
|
||||
const std::span<const RuntimeQuantityValue> inputValues
|
||||
) {
|
||||
std::expected<double, EvaluationError> result = runtimeEvaluationFailure(
|
||||
EvaluationErrorCode::unsupported_derivative,
|
||||
"The requested quantity is not an input to the EOS relation."
|
||||
);
|
||||
|
||||
const bool matched =
|
||||
(tryRuntimePartialDerivative<Inputs>(equationOfState, relation, withRespectTo, inputValues, result) ||
|
||||
...);
|
||||
|
||||
static_cast<void>(matched);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <
|
||||
typename EquationOfState,
|
||||
typename RelationType>
|
||||
[[nodiscard]] bool runtimeRelationMatches(
|
||||
const ThermodynamicQuantityId outputQuantity,
|
||||
const std::span<const RuntimeQuantityValue> inputValues
|
||||
) {
|
||||
const RuntimeRelationDescriptor &descriptor =
|
||||
RuntimeRelationStorage<EquationOfState, RelationType>::descriptor;
|
||||
|
||||
if (descriptor.outputQuantity != outputQuantity ||
|
||||
descriptor.inputQuantities.size() != inputValues.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < inputValues.size(); ++index) {
|
||||
if (descriptor.inputQuantities[index] != inputValues[index].quantity) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename EquationOfState, typename Catalog> struct RuntimeCatalogDispatch;
|
||||
|
||||
template <typename EquationOfState, typename... Relations>
|
||||
struct RuntimeCatalogDispatch<EquationOfState, RelationCatalog<Relations...>> {
|
||||
[[nodiscard]] static std::expected<
|
||||
double,
|
||||
EvaluationError>
|
||||
evaluate(
|
||||
const void *object,
|
||||
const ThermodynamicQuantityId outputQuantity,
|
||||
const std::span<const RuntimeQuantityValue> inputValues
|
||||
) {
|
||||
const auto &equationOfState = *static_cast<const EquationOfState *>(object);
|
||||
|
||||
std::expected<double, EvaluationError> result = runtimeEvaluationFailure(
|
||||
EvaluationErrorCode::unsupported_relation, "The requested EOS relation is not available."
|
||||
);
|
||||
|
||||
const bool matched =
|
||||
((runtimeRelationMatches<EquationOfState, Relations>(outputQuantity, inputValues)
|
||||
? (result = evaluateRuntimeRelation(equationOfState, Relations{}, inputValues), true)
|
||||
: false) ||
|
||||
...);
|
||||
|
||||
static_cast<void>(matched);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] static std::expected<
|
||||
double,
|
||||
EvaluationError>
|
||||
partialDerivative(
|
||||
const void *object,
|
||||
const ThermodynamicQuantityId outputQuantity,
|
||||
const ThermodynamicQuantityId withRespectTo,
|
||||
const std::span<const RuntimeQuantityValue> inputValues
|
||||
) {
|
||||
const auto &equationOfState = *static_cast<const EquationOfState *>(object);
|
||||
|
||||
std::expected<double, EvaluationError> result = runtimeEvaluationFailure(
|
||||
EvaluationErrorCode::unsupported_relation, "The requested EOS relation is not available."
|
||||
);
|
||||
|
||||
const bool matched =
|
||||
((runtimeRelationMatches<EquationOfState, Relations>(outputQuantity, inputValues)
|
||||
? (result = evaluateRuntimePartialDerivative(
|
||||
equationOfState, Relations{}, withRespectTo, inputValues
|
||||
),
|
||||
true)
|
||||
: false) ||
|
||||
...);
|
||||
|
||||
static_cast<void>(matched);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <RuntimeEquationOfStateModel EquationOfState>
|
||||
using RuntimeAdapter = RuntimeCatalogDispatch<EquationOfState, typename EquationOfState::Relations>;
|
||||
|
||||
template <RuntimeEquationOfStateModel EquationOfState>
|
||||
[[nodiscard]] constexpr std::span<const RuntimeRelationDescriptor> runtimeRelationDescriptors() noexcept {
|
||||
return RuntimeCatalogStorage<EquationOfState, typename EquationOfState::Relations>::descriptors;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
class EquationOfStateView final {
|
||||
public:
|
||||
template <RuntimeEquationOfStateModel EquationOfState>
|
||||
explicit EquationOfStateView(EquationOfState &equationOfState) noexcept
|
||||
: m_object(std::addressof(equationOfState)),
|
||||
m_relations(detail::runtimeRelationDescriptors<std::remove_cv_t<EquationOfState>>()),
|
||||
m_evaluate(&detail::RuntimeAdapter<std::remove_cv_t<EquationOfState>>::evaluate),
|
||||
m_partialDerivative(&detail::RuntimeAdapter<std::remove_cv_t<EquationOfState>>::partialDerivative) {
|
||||
}
|
||||
|
||||
[[nodiscard]] std::span<const RuntimeRelationDescriptor> relations() const noexcept {
|
||||
return m_relations;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool supports(
|
||||
const ThermodynamicQuantityId outputQuantity,
|
||||
const std::span<const ThermodynamicQuantityId> inputQuantities
|
||||
) const noexcept {
|
||||
return findRelation(outputQuantity, inputQuantities) != nullptr;
|
||||
}
|
||||
|
||||
template <
|
||||
RuntimeIdentifiedThermodynamicQuantity OutputQuantity,
|
||||
RuntimeIdentifiedThermodynamicQuantity... InputQuantities>
|
||||
[[nodiscard]] bool supports() const noexcept {
|
||||
constexpr std::array<ThermodynamicQuantityId, sizeof...(InputQuantities)> inputs{
|
||||
thermodynamicQuantityId<InputQuantities>...
|
||||
};
|
||||
|
||||
return supports(thermodynamicQuantityId<OutputQuantity>, std::span<const ThermodynamicQuantityId>{inputs});
|
||||
}
|
||||
|
||||
[[nodiscard]] std::expected<
|
||||
RuntimeQuantityValue,
|
||||
EvaluationError>
|
||||
tryEvaluate(
|
||||
const ThermodynamicQuantityId outputQuantity,
|
||||
const std::span<const RuntimeQuantityValue> inputValues
|
||||
) const {
|
||||
const auto validation = validateRelationRequest(outputQuantity, inputValues);
|
||||
|
||||
if (!validation.has_value()) {
|
||||
return std::unexpected<EvaluationError>{validation.error()};
|
||||
}
|
||||
|
||||
auto result = m_evaluate(m_object, outputQuantity, inputValues);
|
||||
if (!result.has_value()) {
|
||||
return std::unexpected<EvaluationError>{result.error()};
|
||||
}
|
||||
|
||||
return RuntimeQuantityValue{outputQuantity, *result};
|
||||
}
|
||||
|
||||
template <
|
||||
RuntimeIdentifiedThermodynamicQuantity OutputQuantity,
|
||||
QuantityValueType... InputValues>
|
||||
[[nodiscard]] std::expected<
|
||||
QuantityValue<OutputQuantity>,
|
||||
EvaluationError>
|
||||
tryEvaluate(const InputValues... inputValues) const {
|
||||
constexpr bool inputsHaveRuntimeIdentifiers =
|
||||
(RuntimeIdentifiedThermodynamicQuantity<QuantityOfT<InputValues>> && ...);
|
||||
|
||||
static_assert(inputsHaveRuntimeIdentifiers, "Every runtime EOS input quantity needs a stable identifier.");
|
||||
|
||||
const std::array<RuntimeQuantityValue, sizeof...(InputValues)> runtimeInputs{
|
||||
RuntimeQuantityValue{thermodynamicQuantityId<QuantityOfT<InputValues>>, inputValues.value()}...
|
||||
};
|
||||
|
||||
auto result = tryEvaluate(
|
||||
thermodynamicQuantityId<OutputQuantity>, std::span<const RuntimeQuantityValue>{runtimeInputs}
|
||||
);
|
||||
|
||||
if (!result.has_value()) {
|
||||
return std::unexpected<EvaluationError>{result.error()};
|
||||
}
|
||||
|
||||
return QuantityValue<OutputQuantity>{result->value};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::expected<
|
||||
double,
|
||||
EvaluationError>
|
||||
tryPartialDerivative(
|
||||
const ThermodynamicQuantityId outputQuantity,
|
||||
const ThermodynamicQuantityId withRespectTo,
|
||||
const std::span<const RuntimeQuantityValue> inputValues
|
||||
) const {
|
||||
const auto validation = validateRelationRequest(outputQuantity, inputValues);
|
||||
|
||||
if (!validation.has_value()) {
|
||||
return std::unexpected<EvaluationError>{validation.error()};
|
||||
}
|
||||
|
||||
const 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 runtimeFailure<double>(
|
||||
EvaluationErrorCode::unsupported_derivative,
|
||||
"The requested EOS partial derivative is not available."
|
||||
);
|
||||
}
|
||||
|
||||
return m_partialDerivative(m_object, outputQuantity, withRespectTo, inputValues);
|
||||
}
|
||||
|
||||
template <
|
||||
RuntimeIdentifiedThermodynamicQuantity OutputQuantity,
|
||||
RuntimeIdentifiedThermodynamicQuantity InputQuantity,
|
||||
QuantityValueType... InputValues>
|
||||
[[nodiscard]] std::expected<
|
||||
PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity>,
|
||||
EvaluationError>
|
||||
tryPartialDerivative(const InputValues... inputValues) const {
|
||||
constexpr bool inputsHaveRuntimeIdentifiers =
|
||||
(RuntimeIdentifiedThermodynamicQuantity<QuantityOfT<InputValues>> && ...);
|
||||
|
||||
static_assert(inputsHaveRuntimeIdentifiers, "Every runtime EOS input quantity needs a stable identifier.");
|
||||
|
||||
const std::array<RuntimeQuantityValue, sizeof...(InputValues)> runtimeInputs{
|
||||
RuntimeQuantityValue{thermodynamicQuantityId<QuantityOfT<InputValues>>, inputValues.value()}...
|
||||
};
|
||||
|
||||
auto result = tryPartialDerivative(
|
||||
thermodynamicQuantityId<OutputQuantity>, thermodynamicQuantityId<InputQuantity>,
|
||||
std::span<const RuntimeQuantityValue>{runtimeInputs}
|
||||
);
|
||||
|
||||
if (!result.has_value()) {
|
||||
return std::unexpected<EvaluationError>{result.error()};
|
||||
}
|
||||
|
||||
return PartialDerivative<OutputQuantity, InputQuantity>{*result};
|
||||
}
|
||||
|
||||
private:
|
||||
using RuntimeEvaluateFunction = std::expected<
|
||||
double,
|
||||
EvaluationError> (*)(
|
||||
const void *,
|
||||
ThermodynamicQuantityId,
|
||||
std::span<const RuntimeQuantityValue>
|
||||
);
|
||||
|
||||
using RuntimePartialDerivativeFunction = std::expected<
|
||||
double,
|
||||
EvaluationError> (*)(
|
||||
const void *,
|
||||
ThermodynamicQuantityId,
|
||||
ThermodynamicQuantityId,
|
||||
std::span<const RuntimeQuantityValue>
|
||||
);
|
||||
|
||||
[[nodiscard]] const RuntimeRelationDescriptor *findRelation(
|
||||
const ThermodynamicQuantityId outputQuantity,
|
||||
const std::span<const ThermodynamicQuantityId> inputQuantities
|
||||
) const noexcept {
|
||||
for (const RuntimeRelationDescriptor &descriptor : m_relations) {
|
||||
if (descriptor.outputQuantity != outputQuantity ||
|
||||
descriptor.inputQuantities.size() != inputQuantities.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool matches = true;
|
||||
for (std::size_t index = 0; index < inputQuantities.size(); ++index) {
|
||||
if (descriptor.inputQuantities[index] != inputQuantities[index]) {
|
||||
matches = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
return std::addressof(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::expected<
|
||||
const RuntimeRelationDescriptor *,
|
||||
EvaluationError>
|
||||
validateRelationRequest(
|
||||
const ThermodynamicQuantityId outputQuantity,
|
||||
const std::span<const RuntimeQuantityValue> inputValues
|
||||
) const {
|
||||
bool outputAvailable = false;
|
||||
bool inputCountAvailable = false;
|
||||
|
||||
for (const RuntimeRelationDescriptor &descriptor : m_relations) {
|
||||
if (descriptor.outputQuantity != outputQuantity) {
|
||||
continue;
|
||||
}
|
||||
|
||||
outputAvailable = true;
|
||||
|
||||
if (descriptor.inputQuantities.size() != inputValues.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
inputCountAvailable = true;
|
||||
bool matches = true;
|
||||
|
||||
for (std::size_t index = 0; index < inputValues.size(); ++index) {
|
||||
if (descriptor.inputQuantities[index] != inputValues[index].quantity) {
|
||||
matches = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
return std::addressof(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
if (!outputAvailable) {
|
||||
return runtimeFailure<const RuntimeRelationDescriptor *>(
|
||||
EvaluationErrorCode::unsupported_relation,
|
||||
"The EOS does not provide a relation for output quantity '" + std::string{outputQuantity.name()} +
|
||||
"'."
|
||||
);
|
||||
}
|
||||
|
||||
if (!inputCountAvailable) {
|
||||
return runtimeFailure<const RuntimeRelationDescriptor *>(
|
||||
EvaluationErrorCode::wrong_input_count, "No EOS relation for output quantity '" +
|
||||
std::string{outputQuantity.name()} +
|
||||
"' accepts the supplied number of inputs."
|
||||
);
|
||||
}
|
||||
|
||||
return runtimeFailure<const RuntimeRelationDescriptor *>(
|
||||
EvaluationErrorCode::wrong_input_quantity, "No EOS relation for output quantity '" +
|
||||
std::string{outputQuantity.name()} +
|
||||
"' accepts the supplied input quantities."
|
||||
);
|
||||
}
|
||||
|
||||
template <typename Value>
|
||||
[[nodiscard]] static std::expected<
|
||||
Value,
|
||||
EvaluationError>
|
||||
runtimeFailure(
|
||||
const EvaluationErrorCode code,
|
||||
std::string message
|
||||
) {
|
||||
return std::unexpected<EvaluationError>{EvaluationError{code, std::move(message)}};
|
||||
}
|
||||
|
||||
const void *m_object;
|
||||
std::span<const RuntimeRelationDescriptor> m_relations;
|
||||
RuntimeEvaluateFunction m_evaluate;
|
||||
RuntimePartialDerivativeFunction m_partialDerivative;
|
||||
};
|
||||
} // namespace mean_field::eos
|
||||
Reference in New Issue
Block a user