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
|
||||
@@ -1,6 +1,7 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
@@ -888,6 +889,282 @@ export namespace mean_field::field {
|
||||
mfem::Array<int> m_trueToReduced;
|
||||
};
|
||||
|
||||
/*
|
||||
* Boundary rows expressed in a field's reduced solver ordering.
|
||||
*
|
||||
* This object is deliberately independent of any particular physical
|
||||
* surface condition. Its template constructor below combines a field,
|
||||
* a semantic boundary, and a domain schema. Consequently the same
|
||||
* topology machinery can be used by any compiled surface formulation;
|
||||
* it is not tied to enthalpy or pressure.
|
||||
*/
|
||||
class FieldBoundaryDofMap final {
|
||||
public:
|
||||
FieldBoundaryDofMap() = default;
|
||||
|
||||
FieldBoundaryDofMap(
|
||||
const int fieldReducedSize,
|
||||
const mfem::Array<int> &boundaryReducedDofs
|
||||
)
|
||||
: m_fieldReducedSize(fieldReducedSize),
|
||||
m_boundaryReducedDofs(boundaryReducedDofs) {
|
||||
if (m_fieldReducedSize < 0) {
|
||||
throw std::invalid_argument("FieldBoundaryDofMap requires a non-negative field size.");
|
||||
}
|
||||
|
||||
m_boundaryReducedDofMarker.SetSize(m_fieldReducedSize);
|
||||
m_boundaryReducedDofMarker = 0;
|
||||
|
||||
int previousReducedDof = -1;
|
||||
for (const int reducedDof : m_boundaryReducedDofs) {
|
||||
if (reducedDof < 0 || reducedDof >= m_fieldReducedSize) {
|
||||
throw std::invalid_argument("FieldBoundaryDofMap contains a DOF outside the reduced field vector.");
|
||||
}
|
||||
if (reducedDof <= previousReducedDof) {
|
||||
throw std::invalid_argument("FieldBoundaryDofMap indices must be strictly increasing and unique.");
|
||||
}
|
||||
|
||||
m_boundaryReducedDofMarker[reducedDof] = 1;
|
||||
previousReducedDof = reducedDof;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] int field_size() const noexcept {
|
||||
return m_fieldReducedSize;
|
||||
}
|
||||
|
||||
[[nodiscard]] int size() const noexcept {
|
||||
return m_boundaryReducedDofs.Size();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Array<int> &reduced_dofs() const noexcept {
|
||||
return m_boundaryReducedDofs;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Array<int> &reduced_dof_marker() const noexcept {
|
||||
return m_boundaryReducedDofMarker;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool contains(const int reducedDof) const {
|
||||
if (reducedDof < 0 || reducedDof >= m_fieldReducedSize) {
|
||||
throw std::out_of_range("Reduced DOF index is outside FieldBoundaryDofMap.");
|
||||
}
|
||||
return m_boundaryReducedDofMarker[reducedDof] != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_fieldReducedSize{0};
|
||||
mfem::Array<int> m_boundaryReducedDofs;
|
||||
mfem::Array<int> m_boundaryReducedDofMarker;
|
||||
};
|
||||
|
||||
/* Point-supported rows in a field's reduced solver ordering. */
|
||||
class FieldPointDofMap final {
|
||||
public:
|
||||
FieldPointDofMap() = default;
|
||||
|
||||
FieldPointDofMap(
|
||||
const int fieldReducedSize,
|
||||
const mfem::Array<int> &pointReducedDofs
|
||||
)
|
||||
: m_selectedDofs(
|
||||
fieldReducedSize,
|
||||
pointReducedDofs
|
||||
) {
|
||||
}
|
||||
|
||||
[[nodiscard]] int field_size() const noexcept {
|
||||
return m_selectedDofs.field_size();
|
||||
}
|
||||
|
||||
[[nodiscard]] int size() const noexcept {
|
||||
return m_selectedDofs.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return m_selectedDofs.empty();
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Array<int> &reduced_dofs() const noexcept {
|
||||
return m_selectedDofs.reduced_dofs();
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Array<int> &reduced_dof_marker() const noexcept {
|
||||
return m_selectedDofs.reduced_dof_marker();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool contains(const int reducedDof) const {
|
||||
return m_selectedDofs.contains(reducedDof);
|
||||
}
|
||||
|
||||
private:
|
||||
FieldBoundaryDofMap m_selectedDofs;
|
||||
};
|
||||
|
||||
template <
|
||||
MfemDomainField FieldT,
|
||||
utils::domain::IsBoundary BoundaryT,
|
||||
utils::domain::IsSchema SchemaT>
|
||||
[[nodiscard]] FieldBoundaryDofMap make_field_boundary_dof_map(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const FieldDofMap &fieldDofMap
|
||||
) {
|
||||
static_assert(
|
||||
SchemaT::template contains_boundary<BoundaryT>(),
|
||||
"The requested boundary is not registered in the supplied DomainSchema."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
!finiteElementSpace.Nonconforming(),
|
||||
"Field boundary true-DOF resolution currently requires a conforming mfem::ParFiniteElementSpace."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
fieldDofMap.full_size() == finiteElementSpace.GetTrueVSize(),
|
||||
"The field map and finite-element space have incompatible true-DOF sizes."
|
||||
);
|
||||
|
||||
const mfem::Mesh *mesh = finiteElementSpace.GetMesh();
|
||||
MFEM_VERIFY(mesh != nullptr, "Field boundary DOF resolution requires an MFEM mesh.");
|
||||
|
||||
mfem::Array<int> boundaryVDofMarker(finiteElementSpace.GetVSize());
|
||||
boundaryVDofMarker = 0;
|
||||
|
||||
mfem::Array<int> boundaryElementVDofs;
|
||||
for (int boundaryElement = 0; boundaryElement < mesh->GetNBE(); ++boundaryElement) {
|
||||
if (!SchemaT::template boundary_attribute_matches<BoundaryT>(mesh->GetBdrAttribute(boundaryElement))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
finiteElementSpace.GetBdrElementVDofs(boundaryElement, boundaryElementVDofs);
|
||||
for (const int encodedVDof : boundaryElementVDofs) {
|
||||
const int vdof = mfem::FiniteElementSpace::DecodeDof(encodedVDof);
|
||||
MFEM_VERIFY(
|
||||
vdof >= 0 && vdof < finiteElementSpace.GetVSize(), "MFEM returned an invalid boundary vector DOF."
|
||||
);
|
||||
boundaryVDofMarker[vdof] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
finiteElementSpace.Synchronize(boundaryVDofMarker);
|
||||
|
||||
mfem::Array<int> boundaryReducedDofMarker(fieldDofMap.reduced_size());
|
||||
boundaryReducedDofMarker = 0;
|
||||
|
||||
for (int vdof = 0; vdof < boundaryVDofMarker.Size(); ++vdof) {
|
||||
if (boundaryVDofMarker[vdof] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int trueDof = finiteElementSpace.GetLocalTDofNumber(vdof);
|
||||
if (trueDof < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::optional<int> reducedDof = fieldDofMap.reduced_dof(trueDof);
|
||||
MFEM_VERIFY(
|
||||
reducedDof.has_value(),
|
||||
"A boundary DOF selected for the field is absent from that field's reduced solver map."
|
||||
);
|
||||
boundaryReducedDofMarker[*reducedDof] = 1;
|
||||
}
|
||||
|
||||
mfem::Array<int> boundaryReducedDofs;
|
||||
mfem::FiniteElementSpace::MarkerToList(boundaryReducedDofMarker, boundaryReducedDofs);
|
||||
return FieldBoundaryDofMap(fieldDofMap.reduced_size(), boundaryReducedDofs);
|
||||
}
|
||||
|
||||
template <MfemDomainField FieldT>
|
||||
[[nodiscard]] FieldPointDofMap make_field_point_dof_map(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const FieldDofMap &fieldDofMap,
|
||||
const mfem::Vector &point,
|
||||
const double tolerance
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
!finiteElementSpace.Nonconforming(),
|
||||
"Field point true-DOF resolution currently requires a conforming mfem::ParFiniteElementSpace."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
fieldDofMap.full_size() == finiteElementSpace.GetTrueVSize(),
|
||||
"The field map and finite-element space have incompatible true-DOF sizes."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(tolerance) && tolerance >= 0.0, "The field point tolerance must be finite and non-negative."
|
||||
);
|
||||
|
||||
const mfem::Mesh *mesh = finiteElementSpace.GetMesh();
|
||||
MFEM_VERIFY(mesh != nullptr, "Field point DOF resolution requires an MFEM mesh.");
|
||||
MFEM_VERIFY(
|
||||
point.Size() == mesh->SpaceDimension(), "The requested field point has the wrong coordinate dimension."
|
||||
);
|
||||
|
||||
mfem::Array<int> pointVDofMarker(finiteElementSpace.GetVSize());
|
||||
pointVDofMarker = 0;
|
||||
|
||||
mfem::Array<int> vertexVDofs;
|
||||
for (int vertex = 0; vertex < mesh->GetNV(); ++vertex) {
|
||||
const mfem::real_t *coordinates = mesh->GetVertex(vertex);
|
||||
double distanceSquared = 0.0;
|
||||
for (int component = 0; component < point.Size(); ++component) {
|
||||
const double difference = coordinates[component] - point(component);
|
||||
distanceSquared += difference * difference;
|
||||
}
|
||||
if (std::sqrt(distanceSquared) > tolerance) {
|
||||
continue;
|
||||
}
|
||||
|
||||
finiteElementSpace.GetVertexVDofs(vertex, vertexVDofs);
|
||||
for (const int encodedVDof : vertexVDofs) {
|
||||
const int vdof = mfem::FiniteElementSpace::DecodeDof(encodedVDof);
|
||||
MFEM_VERIFY(
|
||||
vdof >= 0 && vdof < finiteElementSpace.GetVSize(), "MFEM returned an invalid point vector DOF."
|
||||
);
|
||||
pointVDofMarker[vdof] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
finiteElementSpace.Synchronize(pointVDofMarker);
|
||||
|
||||
mfem::Array<int> pointReducedDofMarker(fieldDofMap.reduced_size());
|
||||
pointReducedDofMarker = 0;
|
||||
for (int vdof = 0; vdof < pointVDofMarker.Size(); ++vdof) {
|
||||
if (pointVDofMarker[vdof] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int trueDof = finiteElementSpace.GetLocalTDofNumber(vdof);
|
||||
if (trueDof < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::optional<int> reducedDof = fieldDofMap.reduced_dof(trueDof);
|
||||
MFEM_VERIFY(
|
||||
reducedDof.has_value(),
|
||||
"A point DOF selected for the field is absent from that field's reduced solver map."
|
||||
);
|
||||
pointReducedDofMarker[*reducedDof] = 1;
|
||||
}
|
||||
|
||||
mfem::Array<int> pointReducedDofs;
|
||||
mfem::FiniteElementSpace::MarkerToList(pointReducedDofMarker, pointReducedDofs);
|
||||
|
||||
const long long localPointDofCount = pointReducedDofs.Size();
|
||||
long long globalPointDofCount = 0;
|
||||
MPI_Allreduce(
|
||||
&localPointDofCount, &globalPointDofCount, 1, MPI_LONG_LONG, MPI_SUM, finiteElementSpace.GetComm()
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
globalPointDofCount == finiteElementSpace.GetVDim(),
|
||||
"The requested geometric point must identify exactly one field vertex globally."
|
||||
);
|
||||
|
||||
return FieldPointDofMap(fieldDofMap.reduced_size(), pointReducedDofs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Canonical adapter between an MFEM GridFunction and a reduced field
|
||||
* vector.
|
||||
@@ -1015,9 +1292,6 @@ export namespace mean_field::field {
|
||||
[[nodiscard]]
|
||||
FieldDofGridFunctionAdapter
|
||||
make_field_dof_grid_function_adapter(const mfem::ParFiniteElementSpace &finiteElementSpace) {
|
||||
return FieldDofGridFunctionAdapter(
|
||||
make_field_dof_map<FieldT, SchemaT>(finiteElementSpace),
|
||||
finiteElementSpace
|
||||
);
|
||||
return FieldDofGridFunctionAdapter(make_field_dof_map<FieldT, SchemaT>(finiteElementSpace), finiteElementSpace);
|
||||
}
|
||||
} // namespace mean_field::field
|
||||
|
||||
@@ -41,7 +41,11 @@ export namespace mean_field::integrators {
|
||||
const mfem::GridFunction &compactification_coordinate,
|
||||
utils::EOS_P<EOS_T> eos
|
||||
)
|
||||
: m_mapping(mapper, displacement, compactification_coordinate),
|
||||
: m_mapping(
|
||||
mapper,
|
||||
displacement,
|
||||
compactification_coordinate
|
||||
),
|
||||
m_eos(std::move(eos)) {
|
||||
}
|
||||
|
||||
|
||||
@@ -8,259 +8,288 @@ import :mapping.compactification;
|
||||
import :utils.user;
|
||||
|
||||
export namespace mean_field::mapping {
|
||||
enum class FaceElementSide : uint8_t { element_1, element_2 };
|
||||
enum class FaceElementSide : uint8_t { element_1, element_2 };
|
||||
|
||||
class ElementDisplacementData {
|
||||
public:
|
||||
ElementDisplacementData(
|
||||
const mfem::FiniteElement &element, const mfem::Vector &displacement_dofs,
|
||||
mfem::Ordering::Type ordering = mfem::Ordering::byNODES);
|
||||
class ElementDisplacementData {
|
||||
public:
|
||||
ElementDisplacementData(
|
||||
const mfem::FiniteElement &element,
|
||||
const mfem::Vector &displacement_dofs,
|
||||
mfem::Ordering::Type ordering = mfem::Ordering::byNODES
|
||||
);
|
||||
|
||||
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
|
||||
[[nodiscard]] const mfem::DenseMatrix &GetDofMatrix() const noexcept;
|
||||
[[nodiscard]] int GetDimension() const noexcept;
|
||||
[[nodiscard]] int GetDofCount() const noexcept;
|
||||
[[nodiscard]] mfem::Ordering::Type GetOrdering() const noexcept;
|
||||
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
|
||||
[[nodiscard]] const mfem::DenseMatrix &GetDofMatrix() const noexcept;
|
||||
[[nodiscard]] int GetDimension() const noexcept;
|
||||
[[nodiscard]] int GetDofCount() const noexcept;
|
||||
[[nodiscard]] mfem::Ordering::Type GetOrdering() const noexcept;
|
||||
|
||||
private:
|
||||
const mfem::FiniteElement *m_element;
|
||||
mfem::DenseMatrix m_dof_matrix;
|
||||
int m_dimension;
|
||||
mfem::Ordering::Type m_ordering;
|
||||
};
|
||||
private:
|
||||
const mfem::FiniteElement *m_element;
|
||||
mfem::DenseMatrix m_dof_matrix;
|
||||
int m_dimension;
|
||||
mfem::Ordering::Type m_ordering;
|
||||
};
|
||||
|
||||
struct CompactificationPointData {
|
||||
double coordinate{0.0};
|
||||
mfem::Vector coordinate_gradient;
|
||||
};
|
||||
struct CompactificationPointData {
|
||||
double coordinate{0.0};
|
||||
mfem::Vector coordinate_gradient;
|
||||
};
|
||||
|
||||
[[nodiscard]] ElementDisplacementData
|
||||
ElementDisplacementDataFromElementVDofs(const mfem::FiniteElement &element,
|
||||
const mfem::Vector &displacement_dofs);
|
||||
[[nodiscard]] ElementDisplacementData ElementDisplacementDataFromElementVDofs(
|
||||
const mfem::FiniteElement &element,
|
||||
const mfem::Vector &displacement_dofs
|
||||
);
|
||||
|
||||
class ElementCompactificationData {
|
||||
public:
|
||||
ElementCompactificationData(const mfem::FiniteElement &element,
|
||||
const mfem::Vector &dofs);
|
||||
class ElementCompactificationData {
|
||||
public:
|
||||
ElementCompactificationData(
|
||||
const mfem::FiniteElement &element,
|
||||
const mfem::Vector &dofs
|
||||
);
|
||||
|
||||
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
|
||||
[[nodiscard]] const mfem::Vector &GetDofs() const noexcept;
|
||||
[[nodiscard]] int GetDofCount() const noexcept;
|
||||
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
|
||||
[[nodiscard]] const mfem::Vector &GetDofs() const noexcept;
|
||||
[[nodiscard]] int GetDofCount() const noexcept;
|
||||
|
||||
private:
|
||||
const mfem::FiniteElement *m_element;
|
||||
mfem::Vector m_dofs;
|
||||
};
|
||||
private:
|
||||
const mfem::FiniteElement *m_element;
|
||||
mfem::Vector m_dofs;
|
||||
};
|
||||
|
||||
struct ElementMappingData {
|
||||
const ElementDisplacementData &displacement;
|
||||
const ElementCompactificationData &compactification;
|
||||
};
|
||||
struct ElementMappingData {
|
||||
const ElementDisplacementData &displacement;
|
||||
const ElementCompactificationData &compactification;
|
||||
};
|
||||
|
||||
class DomainMapper {
|
||||
public:
|
||||
class Workspace {
|
||||
public:
|
||||
explicit Workspace(int dimension = 3);
|
||||
class DomainMapper {
|
||||
public:
|
||||
class Workspace {
|
||||
public:
|
||||
explicit Workspace(int dimension = 3);
|
||||
|
||||
void SetDimension(int dimension);
|
||||
void SetDimension(int dimension);
|
||||
|
||||
[[nodiscard]] int GetDimension() const noexcept;
|
||||
[[nodiscard]] int GetDimension() const noexcept;
|
||||
|
||||
private:
|
||||
friend class DomainMapper;
|
||||
private:
|
||||
friend class DomainMapper;
|
||||
|
||||
int m_dimension;
|
||||
int m_dimension;
|
||||
|
||||
mfem::Vector m_shape;
|
||||
mfem::DenseMatrix m_mesh_dshape;
|
||||
mfem::Vector m_field_value;
|
||||
mfem::DenseMatrix m_field_jacobian;
|
||||
mfem::Vector m_shape;
|
||||
mfem::DenseMatrix m_mesh_dshape;
|
||||
mfem::Vector m_field_value;
|
||||
mfem::DenseMatrix m_field_jacobian;
|
||||
|
||||
mfem::Vector m_compactification_shape;
|
||||
mfem::DenseMatrix m_compactification_dshape;
|
||||
CompactificationPointData m_compactification_point;
|
||||
mfem::Vector m_compactification_shape;
|
||||
mfem::DenseMatrix m_compactification_dshape;
|
||||
CompactificationPointData m_compactification_point;
|
||||
|
||||
mfem::Vector m_reference_normal;
|
||||
mfem::Vector m_mapped_normal;
|
||||
mfem::DenseMatrix m_full_element_jacobian;
|
||||
mfem::Vector m_reference_normal;
|
||||
mfem::Vector m_mapped_normal;
|
||||
mfem::DenseMatrix m_full_element_jacobian;
|
||||
|
||||
mfem::Vector m_vector_temp;
|
||||
mfem::DenseMatrix m_matrix_temp_1;
|
||||
mfem::DenseMatrix m_matrix_temp_2;
|
||||
mfem::Vector m_vector_temp;
|
||||
mfem::DenseMatrix m_matrix_temp_1;
|
||||
mfem::DenseMatrix m_matrix_temp_2;
|
||||
|
||||
compactification::ExteriorMapResult m_exterior_result;
|
||||
compactification::ExteriorMapVariation m_exterior_variation;
|
||||
};
|
||||
compactification::ExteriorMapResult m_exterior_result;
|
||||
compactification::ExteriorMapVariation m_exterior_variation;
|
||||
};
|
||||
|
||||
public:
|
||||
DomainMapper(
|
||||
utils::DomainMapperOptions options,
|
||||
std::unique_ptr<const compactification::ExteriorDomainMap> exterior_map);
|
||||
public:
|
||||
DomainMapper(
|
||||
utils::DomainMapperOptions options,
|
||||
std::unique_ptr<const compactification::ExteriorDomainMap> exterior_map
|
||||
);
|
||||
|
||||
DomainMapper(const DomainMapper &) = delete;
|
||||
DomainMapper &operator=(const DomainMapper &) = delete;
|
||||
DomainMapper(DomainMapper &&) = default;
|
||||
DomainMapper &operator=(DomainMapper &&) = default;
|
||||
DomainMapper(const DomainMapper &) = delete;
|
||||
DomainMapper &operator=(const DomainMapper &) = delete;
|
||||
DomainMapper(DomainMapper &&) = default;
|
||||
DomainMapper &operator=(DomainMapper &&) = default;
|
||||
|
||||
[[nodiscard]] MappingStatus
|
||||
EvaluatePoint(const ElementMappingData &element_data,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace, MappingPointContext &context) const;
|
||||
[[nodiscard]] MappingStatus EvaluatePoint(
|
||||
const ElementMappingData &element_data,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace,
|
||||
MappingPointContext &context
|
||||
) const;
|
||||
|
||||
[[nodiscard]] MappingStatus
|
||||
EvaluateVolume(const ElementMappingData &element_data,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace, VolumeMappingContext &context) const;
|
||||
[[nodiscard]] MappingStatus EvaluateVolume(
|
||||
const ElementMappingData &element_data,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace,
|
||||
VolumeMappingContext &context
|
||||
) const;
|
||||
|
||||
[[nodiscard]] MappingStatus
|
||||
EvaluateFace(const ElementMappingData &element_data,
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
FaceElementSide side,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace, FaceMappingContext &context) const;
|
||||
[[nodiscard]] MappingStatus EvaluateFace(
|
||||
const ElementMappingData &element_data,
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
FaceElementSide side,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace,
|
||||
FaceMappingContext &context
|
||||
) const;
|
||||
|
||||
[[nodiscard]] MappingStatus
|
||||
EvaluatePointVariation(const ElementMappingData &element_data,
|
||||
const ElementDisplacementData &direction,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
const MappingPointContext &base_context,
|
||||
Workspace &workspace,
|
||||
MappingPointVariation &variation) const;
|
||||
[[nodiscard]] MappingStatus EvaluatePointVariation(
|
||||
const ElementMappingData &element_data,
|
||||
const ElementDisplacementData &direction,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
const MappingPointContext &base_context,
|
||||
Workspace &workspace,
|
||||
MappingPointVariation &variation
|
||||
) const;
|
||||
|
||||
[[nodiscard]] MappingStatus
|
||||
EvaluateVolumeVariation(const ElementMappingData &element_data,
|
||||
const ElementDisplacementData &direction,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
const VolumeMappingContext &base_context,
|
||||
Workspace &workspace,
|
||||
VolumeMappingVariation &variation) const;
|
||||
[[nodiscard]] MappingStatus EvaluateVolumeVariation(
|
||||
const ElementMappingData &element_data,
|
||||
const ElementDisplacementData &direction,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
const VolumeMappingContext &base_context,
|
||||
Workspace &workspace,
|
||||
VolumeMappingVariation &variation
|
||||
) const;
|
||||
|
||||
[[nodiscard]] MappingStatus EvaluateFaceVariation(
|
||||
const ElementMappingData &element_data,
|
||||
const ElementDisplacementData &direction,
|
||||
mfem::FaceElementTransformations &transformation, FaceElementSide side,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
const FaceMappingContext &base_context, Workspace &workspace,
|
||||
FaceMappingVariation &variation) const;
|
||||
[[nodiscard]] MappingStatus EvaluateFaceVariation(
|
||||
const ElementMappingData &element_data,
|
||||
const ElementDisplacementData &direction,
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
FaceElementSide side,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
const FaceMappingContext &base_context,
|
||||
Workspace &workspace,
|
||||
FaceMappingVariation &variation
|
||||
) const;
|
||||
|
||||
[[nodiscard]] bool IsCompactifiedElement(
|
||||
const mfem::ElementTransformation &transformation) const noexcept;
|
||||
[[nodiscard]] int GetDimension() const noexcept;
|
||||
[[nodiscard]] const compactification::ExteriorDomainMap &
|
||||
GetExteriorMap() const noexcept;
|
||||
[[nodiscard]] bool IsCompactifiedElement(const mfem::ElementTransformation &transformation) const noexcept;
|
||||
[[nodiscard]] int GetDimension() const noexcept;
|
||||
[[nodiscard]] const compactification::ExteriorDomainMap &GetExteriorMap() const noexcept;
|
||||
|
||||
private:
|
||||
void ValidateElementData(const ElementMappingData &element_data) const;
|
||||
private:
|
||||
void ValidateElementData(const ElementMappingData &element_data) const;
|
||||
|
||||
void EvaluateField(const ElementDisplacementData &field,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace, mfem::Vector &value,
|
||||
mfem::DenseMatrix &jacobian) const;
|
||||
void EvaluateField(
|
||||
const ElementDisplacementData &field,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace,
|
||||
mfem::Vector &value,
|
||||
mfem::DenseMatrix &jacobian
|
||||
) const;
|
||||
|
||||
[[nodiscard]] MappingStatus EvaluateCompactificationCoordinate(
|
||||
const ElementCompactificationData &compactification,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point, Workspace &workspace,
|
||||
CompactificationPointData &point_data) const;
|
||||
[[nodiscard]] MappingStatus EvaluateCompactificationCoordinate(
|
||||
const ElementCompactificationData &compactification,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace,
|
||||
CompactificationPointData &point_data
|
||||
) const;
|
||||
|
||||
[[nodiscard]] static mfem::ElementTransformation &
|
||||
SelectFaceElementTransformation(
|
||||
mfem::FaceElementTransformations &transformation, FaceElementSide side);
|
||||
[[nodiscard]] static mfem::ElementTransformation &SelectFaceElementTransformation(
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
FaceElementSide side
|
||||
);
|
||||
|
||||
[[nodiscard]] static const mfem::IntegrationPoint &
|
||||
SelectFaceElementIntegrationPoint(
|
||||
mfem::FaceElementTransformations &transformation, FaceElementSide side);
|
||||
[[nodiscard]] static const mfem::IntegrationPoint &SelectFaceElementIntegrationPoint(
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
FaceElementSide side
|
||||
);
|
||||
|
||||
utils::DomainMapperOptions m_options;
|
||||
std::unique_ptr<const compactification::ExteriorDomainMap> m_exterior_map;
|
||||
};
|
||||
utils::DomainMapperOptions m_options;
|
||||
std::unique_ptr<const compactification::ExteriorDomainMap> m_exterior_map;
|
||||
};
|
||||
|
||||
class GridFunctionMappingEvaluator {
|
||||
public:
|
||||
/*
|
||||
* The evaluator references the supplied grid functions and caches copies of
|
||||
* their element-local DOFs. Call InvalidateCache() or Refresh() after either
|
||||
* grid function's values are modified. Finite-element-space sequence changes
|
||||
* are detected automatically.
|
||||
*
|
||||
* This object owns mutable workspace and cache state and is not thread-safe.
|
||||
*/
|
||||
GridFunctionMappingEvaluator(
|
||||
const DomainMapper &mapper,
|
||||
const mfem::GridFunction &displacement,
|
||||
const mfem::GridFunction &compactification_coordinate);
|
||||
class GridFunctionMappingEvaluator {
|
||||
public:
|
||||
/*
|
||||
* The evaluator references the supplied grid functions and caches copies of
|
||||
* their element-local DOFs. Call InvalidateCache() or Refresh() after either
|
||||
* grid function's values are modified. Finite-element-space sequence changes
|
||||
* are detected automatically.
|
||||
*
|
||||
* This object owns mutable workspace and cache state and is not thread-safe.
|
||||
*/
|
||||
GridFunctionMappingEvaluator(
|
||||
const DomainMapper &mapper,
|
||||
const mfem::GridFunction &displacement,
|
||||
const mfem::GridFunction &compactification_coordinate
|
||||
);
|
||||
|
||||
/*
|
||||
* Discard all element-local field data. The next evaluation reloads its
|
||||
* requested element lazily. This operation is idempotent.
|
||||
*/
|
||||
void InvalidateCache() noexcept;
|
||||
/*
|
||||
* Discard all element-local field data. The next evaluation reloads its
|
||||
* requested element lazily. This operation is idempotent.
|
||||
*/
|
||||
void InvalidateCache() noexcept;
|
||||
|
||||
/*
|
||||
* Reload the currently cached element immediately. If no element has been
|
||||
* evaluated yet, Refresh() is a validated no-op. If either finite-element
|
||||
* space changed sequence, the old element ID is discarded and the next
|
||||
* evaluation reloads lazily against the updated spaces.
|
||||
*/
|
||||
void Refresh();
|
||||
/*
|
||||
* Reload the currently cached element immediately. If no element has been
|
||||
* evaluated yet, Refresh() is a validated no-op. If either finite-element
|
||||
* space changed sequence, the old element ID is discarded and the next
|
||||
* evaluation reloads lazily against the updated spaces.
|
||||
*/
|
||||
void Refresh();
|
||||
|
||||
[[nodiscard]] MappingStatus
|
||||
EvaluatePoint(mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
MappingPointContext &context);
|
||||
[[nodiscard]] MappingStatus EvaluatePoint(
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
MappingPointContext &context
|
||||
);
|
||||
|
||||
[[nodiscard]] MappingStatus
|
||||
EvaluateVolume(mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
VolumeMappingContext &context);
|
||||
[[nodiscard]] MappingStatus EvaluateVolume(
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
VolumeMappingContext &context
|
||||
);
|
||||
|
||||
[[nodiscard]] MappingStatus
|
||||
EvaluateFace(mfem::FaceElementTransformations &transformation,
|
||||
FaceElementSide side,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
FaceMappingContext &context);
|
||||
[[nodiscard]] MappingStatus EvaluateFace(
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
FaceElementSide side,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
FaceMappingContext &context
|
||||
);
|
||||
|
||||
[[nodiscard]] VolumeQuadratureContext
|
||||
GetQuadratureContext(mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point);
|
||||
[[nodiscard]] VolumeQuadratureContext GetQuadratureContext(
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point
|
||||
);
|
||||
|
||||
[[nodiscard]] FaceQuadratureContext
|
||||
GetFaceQuadratureContext(
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
FaceElementSide side = FaceElementSide::element_1);
|
||||
[[nodiscard]] FaceQuadratureContext GetFaceQuadratureContext(
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
FaceElementSide side = FaceElementSide::element_1
|
||||
);
|
||||
|
||||
void GetPhysicalPoint(mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
mfem::Vector &physical_position);
|
||||
void GetPhysicalPoint(
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
mfem::Vector &physical_position
|
||||
);
|
||||
|
||||
private:
|
||||
void ValidateFieldBindings() const;
|
||||
[[nodiscard]] bool InvalidateForChangedSpaces();
|
||||
void LoadElement(int element_id);
|
||||
private:
|
||||
void ValidateFieldBindings() const;
|
||||
[[nodiscard]] bool InvalidateForChangedSpaces();
|
||||
void LoadElement(int element_id);
|
||||
|
||||
const DomainMapper &m_mapper;
|
||||
const mfem::GridFunction &m_displacement;
|
||||
const mfem::GridFunction &m_compactification_coordinate;
|
||||
const mfem::FiniteElementSpace *m_displacement_space;
|
||||
const mfem::FiniteElementSpace *m_compactification_space;
|
||||
long m_displacement_space_sequence;
|
||||
long m_compactification_space_sequence;
|
||||
DomainMapper::Workspace m_workspace;
|
||||
const DomainMapper &m_mapper;
|
||||
const mfem::GridFunction &m_displacement;
|
||||
const mfem::GridFunction &m_compactification_coordinate;
|
||||
const mfem::FiniteElementSpace *m_displacement_space;
|
||||
const mfem::FiniteElementSpace *m_compactification_space;
|
||||
long m_displacement_space_sequence;
|
||||
long m_compactification_space_sequence;
|
||||
DomainMapper::Workspace m_workspace;
|
||||
|
||||
mfem::Array<int> m_displacement_dofs;
|
||||
mfem::Array<int> m_compactification_dofs;
|
||||
mfem::Vector m_element_displacement;
|
||||
mfem::Vector m_element_compactification;
|
||||
std::unique_ptr<ElementDisplacementData> m_displacement_data;
|
||||
std::unique_ptr<ElementCompactificationData> m_compactification_data;
|
||||
int m_cached_element_id{-1};
|
||||
};
|
||||
mfem::Array<int> m_displacement_dofs;
|
||||
mfem::Array<int> m_compactification_dofs;
|
||||
mfem::Vector m_element_displacement;
|
||||
mfem::Vector m_element_compactification;
|
||||
std::unique_ptr<ElementDisplacementData> m_displacement_data;
|
||||
std::unique_ptr<ElementCompactificationData> m_compactification_data;
|
||||
int m_cached_element_id{-1};
|
||||
};
|
||||
|
||||
} // namespace mean_field::mapping
|
||||
|
||||
@@ -54,10 +54,19 @@ export import :operators.prepared_displacement_residual;
|
||||
export import :model.structure_profile;
|
||||
export import :model.structure.base;
|
||||
export import :model.structure.polytropic;
|
||||
export import :eos.base;
|
||||
export import :eos.quantities;
|
||||
export import :eos.relations;
|
||||
export import :eos.concepts;
|
||||
export import :eos.evaluation;
|
||||
export import :eos.pressure_surface;
|
||||
export import :eos.runtime;
|
||||
export import :eos.polytrope;
|
||||
export import :surface.base;
|
||||
export import :surface.isobaric;
|
||||
export import :surface.constant;
|
||||
export import :surface.dependencies;
|
||||
export import :surface.compiled;
|
||||
export import :surface.compiler;
|
||||
export import :model.stellar;
|
||||
export import :operators.prepared_mass_normalization;
|
||||
export import :operators.prepared_centering_constraint;
|
||||
export import :operators.prepared_surface_constraint;
|
||||
export import :operators.prepared_stellar_equilibrium;
|
||||
|
||||
@@ -7,62 +7,84 @@ module;
|
||||
|
||||
export module mean_field:model.stellar;
|
||||
|
||||
export import :eos.base;
|
||||
export import :eos.runtime;
|
||||
export import :model.structure.base;
|
||||
export import :surface.base;
|
||||
export import :surface.compiler;
|
||||
|
||||
export namespace mean_field::models {
|
||||
template <typename Candidate>
|
||||
concept StructurePrescription =
|
||||
std::derived_from<std::remove_cvref_t<Candidate>, mean_field::models::structure::StructureBase>;
|
||||
namespace detail {
|
||||
template <typename Candidate>
|
||||
concept ConstEquationOfStateReference =
|
||||
std::is_lvalue_reference_v<Candidate> && std::is_const_v<std::remove_reference_t<Candidate>> &&
|
||||
eos::EquationOfStateModel<std::remove_cvref_t<Candidate>>;
|
||||
} // namespace detail
|
||||
|
||||
template <typename Candidate>
|
||||
concept SurfacePrescription = std::derived_from<std::remove_cvref_t<Candidate>, mean_field::surface::SurfaceBase>;
|
||||
concept StructurePrescription = requires(
|
||||
const std::remove_cvref_t<Candidate> &structurePrescription,
|
||||
const structure::StructureSeedRequest &seedRequest
|
||||
) {
|
||||
{ structurePrescription.equationOfState() } noexcept -> detail::ConstEquationOfStateReference;
|
||||
{ structurePrescription.targetMass() } noexcept -> std::same_as<double>;
|
||||
{ structurePrescription.makeInitialSeed(seedRequest) } -> std::same_as<structure::StructureSeed>;
|
||||
{ structurePrescription.validate() } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
/*
|
||||
* Public ownership facade for a physical structure prescription and its
|
||||
* stellar-surface prescription.
|
||||
*
|
||||
* The concrete prescriptions are allocated once at construction. Their
|
||||
* stable addresses allow future prepared operators and contexts to borrow
|
||||
* references without making ownership part of the user-facing API.
|
||||
*/
|
||||
template <StructurePrescription Candidate>
|
||||
using StructureEquationOfStateT =
|
||||
std::remove_cvref_t<decltype(std::declval<const std::remove_cvref_t<Candidate> &>().equationOfState())>;
|
||||
|
||||
template <typename Candidate, typename EquationOfState>
|
||||
concept SurfacePrescription =
|
||||
surface::ConstantPressureSurfaceType<Candidate> &&
|
||||
surface::PressureSurfaceCompilable<surface::BarotropicSurfaceFormulation, std::remove_cvref_t<EquationOfState>>;
|
||||
|
||||
template <StructurePrescription Structure>
|
||||
requires SurfacePrescription<surface::ConstantPressureSurface, StructureEquationOfStateT<Structure>>
|
||||
class StellarModel final {
|
||||
public:
|
||||
template <
|
||||
StructurePrescription StructureType,
|
||||
SurfacePrescription SurfaceType>
|
||||
using StructurePrescriptionType = Structure;
|
||||
using SurfacePrescriptionType = surface::ConstantPressureSurface;
|
||||
using EquationOfStateType = StructureEquationOfStateT<Structure>;
|
||||
using SurfaceConstraintType =
|
||||
surface::CompiledPressureSurfaceConstraintT<surface::BarotropicSurfaceFormulation, EquationOfStateType>;
|
||||
|
||||
template <typename StructureArgument>
|
||||
requires std::same_as<
|
||||
std::remove_cvref_t<StructureArgument>,
|
||||
Structure>
|
||||
explicit StellarModel(
|
||||
StructureType &&structurePrescription,
|
||||
SurfaceType &&surfacePrescription
|
||||
StructureArgument &&structurePrescription,
|
||||
const surface::ConstantPressureSurface surfacePrescription
|
||||
)
|
||||
: StellarModel(
|
||||
std::make_unique<std::remove_cvref_t<StructureType>>(
|
||||
std::forward<StructureType>(structurePrescription)
|
||||
),
|
||||
std::make_unique<std::remove_cvref_t<SurfaceType>>(std::forward<SurfaceType>(surfacePrescription))
|
||||
: m_structurePrescription(
|
||||
std::make_unique<Structure>(std::forward<StructureArgument>(structurePrescription))
|
||||
),
|
||||
m_surfacePrescription(std::make_unique<surface::ConstantPressureSurface>(surfacePrescription)),
|
||||
m_compiledSurfaceConstraint(
|
||||
std::make_unique<SurfaceConstraintType>(validateAndCompileSurface(
|
||||
*m_structurePrescription,
|
||||
*m_surfacePrescription
|
||||
))
|
||||
) {
|
||||
}
|
||||
|
||||
~StellarModel() = default;
|
||||
|
||||
StellarModel(const StellarModel &) = delete;
|
||||
|
||||
StellarModel &operator=(const StellarModel &) = delete;
|
||||
|
||||
StellarModel(StellarModel &&) noexcept = default;
|
||||
|
||||
StellarModel &operator=(StellarModel &&) noexcept = default;
|
||||
|
||||
[[nodiscard]] const mean_field::models::structure::StructureBase &structurePrescription() const noexcept {
|
||||
[[nodiscard]] const Structure &structurePrescription() const noexcept {
|
||||
return *m_structurePrescription;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mean_field::surface::SurfaceBase &surfacePrescription() const noexcept {
|
||||
[[nodiscard]] const surface::ConstantPressureSurface &surfacePrescription() const noexcept {
|
||||
return *m_surfacePrescription;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mean_field::eos::EquationOfState &equationOfState() const noexcept {
|
||||
[[nodiscard]] const EquationOfStateType &equationOfState() const noexcept {
|
||||
return m_structurePrescription->equationOfState();
|
||||
}
|
||||
|
||||
@@ -70,45 +92,99 @@ export namespace mean_field::models {
|
||||
return m_structurePrescription->targetMass();
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::models::structure::StructureSeed
|
||||
makeInitialSeed(const mean_field::models::structure::StructureSeedRequest &request) const {
|
||||
[[nodiscard]] structure::StructureSeed makeInitialSeed(const structure::StructureSeedRequest &request) const {
|
||||
return m_structurePrescription->makeInitialSeed(request);
|
||||
}
|
||||
|
||||
[[nodiscard]] const mean_field::surface::ResolvedSurfaceCondition &resolvedSurfaceCondition() const noexcept {
|
||||
return m_resolvedSurfaceCondition;
|
||||
[[nodiscard]] const SurfaceConstraintType &compiledSurfaceConstraint() const noexcept {
|
||||
return *m_compiledSurfaceConstraint;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit StellarModel(
|
||||
std::unique_ptr<mean_field::models::structure::StructureBase> structurePrescription,
|
||||
std::unique_ptr<mean_field::surface::SurfaceBase> surfacePrescription
|
||||
)
|
||||
: m_structurePrescription(std::move(structurePrescription)),
|
||||
m_surfacePrescription(std::move(surfacePrescription)),
|
||||
m_resolvedSurfaceCondition(validateAndResolve(
|
||||
*m_structurePrescription,
|
||||
*m_surfacePrescription
|
||||
)) {
|
||||
}
|
||||
|
||||
[[nodiscard]] static mean_field::surface::ResolvedSurfaceCondition validateAndResolve(
|
||||
const mean_field::models::structure::StructureBase &structurePrescription,
|
||||
const mean_field::surface::SurfaceBase &surfacePrescription
|
||||
[[nodiscard]] static SurfaceConstraintType validateAndCompileSurface(
|
||||
const Structure &structurePrescription,
|
||||
const surface::ConstantPressureSurface &surfacePrescription
|
||||
) {
|
||||
structurePrescription.validate();
|
||||
|
||||
const mean_field::eos::EquationOfState &equationOfState = structurePrescription.equationOfState();
|
||||
|
||||
surfacePrescription.validate(equationOfState);
|
||||
|
||||
return surfacePrescription.resolve(equationOfState);
|
||||
return surface::compilePressureSurfaceConstraint<surface::BarotropicSurfaceFormulation>(
|
||||
surfacePrescription, structurePrescription.equationOfState()
|
||||
);
|
||||
}
|
||||
|
||||
std::unique_ptr<mean_field::models::structure::StructureBase> m_structurePrescription;
|
||||
std::unique_ptr<Structure> m_structurePrescription;
|
||||
std::unique_ptr<surface::ConstantPressureSurface> m_surfacePrescription;
|
||||
std::unique_ptr<SurfaceConstraintType> m_compiledSurfaceConstraint;
|
||||
};
|
||||
|
||||
std::unique_ptr<mean_field::surface::SurfaceBase> m_surfacePrescription;
|
||||
template <typename Structure>
|
||||
StellarModel(
|
||||
Structure &&,
|
||||
surface::ConstantPressureSurface
|
||||
) -> StellarModel<std::remove_cvref_t<Structure>>;
|
||||
|
||||
mean_field::surface::ResolvedSurfaceCondition m_resolvedSurfaceCondition;
|
||||
namespace detail {
|
||||
template <typename Candidate> struct IsStellarModel : std::false_type { };
|
||||
|
||||
template <typename Structure> struct IsStellarModel<StellarModel<Structure>> : std::true_type { };
|
||||
} // namespace detail
|
||||
|
||||
template <typename Candidate>
|
||||
concept StellarModelType = detail::IsStellarModel<std::remove_cvref_t<Candidate>>::value;
|
||||
|
||||
class StellarModelView final {
|
||||
public:
|
||||
template <typename Model>
|
||||
requires StellarModelType<Model> &&
|
||||
eos::RuntimeEquationOfStateModel<typename std::remove_cvref_t<Model>::EquationOfStateType>
|
||||
explicit StellarModelView(Model &model) noexcept
|
||||
: m_equationOfState(model.equationOfState()),
|
||||
m_structurePrescription(std::addressof(model.structurePrescription())),
|
||||
m_makeInitialSeed(&makeInitialSeedFor<typename std::remove_cvref_t<Model>::StructurePrescriptionType>),
|
||||
m_targetMass(model.targetMass()),
|
||||
m_surfaceCondition(model.compiledSurfaceConstraint().descriptor()),
|
||||
m_surfaceDependencies(model.compiledSurfaceConstraint().runtimeDependencies()) {
|
||||
}
|
||||
|
||||
[[nodiscard]] eos::EquationOfStateView equationOfState() const noexcept {
|
||||
return m_equationOfState;
|
||||
}
|
||||
|
||||
[[nodiscard]] double targetMass() const noexcept {
|
||||
return m_targetMass;
|
||||
}
|
||||
|
||||
[[nodiscard]] structure::StructureSeed makeInitialSeed(const structure::StructureSeedRequest &request) const {
|
||||
return m_makeInitialSeed(m_structurePrescription, request);
|
||||
}
|
||||
|
||||
[[nodiscard]] surface::PressureSurfaceDescriptor surfaceCondition() const noexcept {
|
||||
return m_surfaceCondition;
|
||||
}
|
||||
|
||||
[[nodiscard]] surface::RuntimeSurfaceConstraintDependencies surfaceDependencies() const noexcept {
|
||||
return m_surfaceDependencies;
|
||||
}
|
||||
|
||||
private:
|
||||
using MakeInitialSeedFunction = structure::StructureSeed (*)(
|
||||
const void *,
|
||||
const structure::StructureSeedRequest &
|
||||
);
|
||||
|
||||
template <StructurePrescription Structure>
|
||||
[[nodiscard]] static structure::StructureSeed makeInitialSeedFor(
|
||||
const void *structurePrescription,
|
||||
const structure::StructureSeedRequest &request
|
||||
) {
|
||||
return static_cast<const Structure *>(structurePrescription)->makeInitialSeed(request);
|
||||
}
|
||||
|
||||
eos::EquationOfStateView m_equationOfState;
|
||||
const void *m_structurePrescription;
|
||||
MakeInitialSeedFunction m_makeInitialSeed;
|
||||
double m_targetMass;
|
||||
surface::PressureSurfaceDescriptor m_surfaceCondition;
|
||||
surface::RuntimeSurfaceConstraintDependencies m_surfaceDependencies;
|
||||
};
|
||||
} // namespace mean_field::models
|
||||
|
||||
@@ -12,20 +12,20 @@ export import :model.structure.base;
|
||||
import :utils.misc;
|
||||
|
||||
export namespace mean_field::models::structure {
|
||||
class PolytropicStructure final : public StructureBase {
|
||||
class PolytropicStructure final {
|
||||
public:
|
||||
explicit PolytropicStructure(
|
||||
eos::Polytrope equationOfState,
|
||||
double targetMass
|
||||
);
|
||||
|
||||
[[nodiscard]] const eos::EquationOfState &equationOfState() const noexcept override;
|
||||
[[nodiscard]] const eos::Polytrope &equationOfState() const noexcept;
|
||||
|
||||
[[nodiscard]] double targetMass() const noexcept override;
|
||||
[[nodiscard]] double targetMass() const noexcept;
|
||||
|
||||
[[nodiscard]] StructureSeed makeInitialSeed(const StructureSeedRequest &request) const override;
|
||||
[[nodiscard]] StructureSeed makeInitialSeed(const StructureSeedRequest &request) const;
|
||||
|
||||
void validate() const override;
|
||||
void validate() const;
|
||||
|
||||
private:
|
||||
struct LaneEmdenPoint {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module;
|
||||
#include <mfem.hpp>
|
||||
export module mean_field:model.structure.base;
|
||||
export import :eos.base;
|
||||
export import :eos.runtime;
|
||||
|
||||
export namespace mean_field::models::structure {
|
||||
struct StructureSeed {
|
||||
@@ -23,7 +23,7 @@ export namespace mean_field::models::structure {
|
||||
public:
|
||||
virtual ~StructureBase() = default;
|
||||
|
||||
[[nodiscard]] virtual const eos::EquationOfState &equationOfState() const noexcept = 0;
|
||||
[[nodiscard]] virtual eos::EquationOfStateView equationOfState() const noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual double targetMass() const noexcept = 0;
|
||||
|
||||
@@ -34,4 +34,4 @@ export namespace mean_field::models::structure {
|
||||
protected:
|
||||
StructureBase() = default;
|
||||
};
|
||||
} // namespace mean_field::models::structure
|
||||
} // namespace mean_field::models::structure
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.prepared_centering_constraint;
|
||||
|
||||
export import :field.mfem;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
struct PreparedCenteringConstraintReport final {
|
||||
bool cachedCenterDisplacement{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return cachedCenterDisplacement;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Strong translational gauge: the material point at the computational
|
||||
* origin has zero displacement. The three corresponding displacement
|
||||
* residual rows replace redundant force-balance rows.
|
||||
*/
|
||||
class PreparedCenteringConstraint final {
|
||||
public:
|
||||
explicit PreparedCenteringConstraint(field::FieldPointDofMap centerRows)
|
||||
: m_centerRows(std::move(centerRows)),
|
||||
m_centerDisplacement(m_centerRows.size()) {
|
||||
}
|
||||
|
||||
[[nodiscard]] PreparedCenteringConstraintReport Prepare(
|
||||
const mfem::Vector &displacement,
|
||||
const bool displacementChanged
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
displacement.Size() == m_centerRows.field_size(),
|
||||
"The centering constraint received a displacement vector with the wrong size."
|
||||
);
|
||||
|
||||
PreparedCenteringConstraintReport report;
|
||||
if (!m_isPrepared || displacementChanged) {
|
||||
for (int centerIndex = 0; centerIndex < m_centerRows.size(); ++centerIndex) {
|
||||
const double value = displacement(m_centerRows.reduced_dofs()[centerIndex]);
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(value), "The centering constraint received a non-finite center displacement."
|
||||
);
|
||||
m_centerDisplacement(centerIndex) = value;
|
||||
}
|
||||
report.cachedCenterDisplacement = true;
|
||||
}
|
||||
|
||||
m_isPrepared = true;
|
||||
return report;
|
||||
}
|
||||
|
||||
void ApplyResidualRows(mfem::Vector &displacementResidual) const {
|
||||
VerifyPrepared();
|
||||
MFEM_VERIFY(
|
||||
displacementResidual.Size() == m_centerRows.field_size(),
|
||||
"The centering constraint received a displacement residual with the wrong size."
|
||||
);
|
||||
|
||||
for (int centerIndex = 0; centerIndex < m_centerRows.size(); ++centerIndex) {
|
||||
displacementResidual(m_centerRows.reduced_dofs()[centerIndex]) = m_centerDisplacement(centerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyJacobianRows(
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &displacementAction
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
MFEM_VERIFY(
|
||||
displacementVariation.Size() == m_centerRows.field_size() &&
|
||||
displacementAction.Size() == m_centerRows.field_size(),
|
||||
"The centering constraint received a Jacobian vector with the wrong size."
|
||||
);
|
||||
|
||||
for (const int centerRow : m_centerRows.reduced_dofs()) {
|
||||
displacementAction(centerRow) = displacementVariation(centerRow);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept {
|
||||
return m_isPrepared;
|
||||
}
|
||||
|
||||
[[nodiscard]] const field::FieldPointDofMap &GetCenterRows() const noexcept {
|
||||
return m_centerRows;
|
||||
}
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const {
|
||||
MFEM_VERIFY(m_isPrepared, "The centering constraint must be prepared before row application.");
|
||||
}
|
||||
|
||||
field::FieldPointDofMap m_centerRows;
|
||||
mfem::Vector m_centerDisplacement;
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
@@ -1,7 +1,9 @@
|
||||
module;
|
||||
|
||||
#include <compare>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
@@ -16,9 +18,11 @@ export import :operators.context.gravity_field;
|
||||
export import :operators.gravity_field;
|
||||
export import :operators.gravity_field_jacobian;
|
||||
export import :operators.prepared_barotropic_closure;
|
||||
export import :operators.prepared_centering_constraint;
|
||||
export import :operators.prepared_displacement_residual;
|
||||
export import :operators.prepared_hydrostatic_equilibrium;
|
||||
export import :operators.prepared_mass_normalization;
|
||||
export import :operators.prepared_surface_constraint;
|
||||
export import :physics.rigid_rotation;
|
||||
export import :utils.blocks;
|
||||
|
||||
@@ -50,11 +54,14 @@ export namespace mean_field::operators {
|
||||
PreparedHydrostaticEquilibriumReport hydrostatic;
|
||||
PreparedDisplacementResidualReport displacement;
|
||||
PreparedMassNormalizationReport massNormalization;
|
||||
PreparedSurfaceConstraintReport surfaceConstraint;
|
||||
PreparedCenteringConstraintReport centeringConstraint;
|
||||
bool assembledResidual{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyChildWork() const noexcept {
|
||||
return gravity.DidAnyWork() || barotropicClosure.DidAnyWork() || hydrostatic.DidAnyWork() ||
|
||||
displacement.DidAnyWork() || massNormalization.DidAnyWork();
|
||||
displacement.DidAnyWork() || massNormalization.DidAnyWork() || surfaceConstraint.DidAnyWork() ||
|
||||
centeringConstraint.DidAnyWork();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
@@ -74,19 +81,27 @@ export namespace mean_field::operators {
|
||||
|
||||
class PreparedStellarEquilibriumOperator final : public mfem::Operator {
|
||||
public:
|
||||
template <models::StellarModelType Model>
|
||||
requires std::same_as<
|
||||
typename std::remove_cvref_t<Model>::EquationOfStateType,
|
||||
eos::Polytrope> &&
|
||||
SingleFieldPressureSurfaceConstraintFor<
|
||||
typename std::remove_cvref_t<Model>::SurfaceConstraintType,
|
||||
field::Enthalpy> &&
|
||||
std::is_lvalue_reference_v<Model &&>
|
||||
PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
double targetMass
|
||||
);
|
||||
|
||||
PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
const models::StellarModel &stellarModel
|
||||
);
|
||||
Model &&stellarModel
|
||||
)
|
||||
: PreparedStellarEquilibriumOperator(
|
||||
f,
|
||||
domainMapper,
|
||||
stellarModel.equationOfState(),
|
||||
stellarModel.targetMass(),
|
||||
PressureSurfaceConstraintView{stellarModel.compiledSurfaceConstraint()}
|
||||
) {
|
||||
}
|
||||
|
||||
PreparedStellarEquilibriumOperator(const PreparedStellarEquilibriumOperator &) = delete;
|
||||
PreparedStellarEquilibriumOperator &operator=(const PreparedStellarEquilibriumOperator &) = delete;
|
||||
@@ -122,6 +137,8 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] const PreparedHydrostaticEquilibriumOperator &GetHydrostaticOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedDisplacementResidualOperator &GetDisplacementOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedMassNormalizationOperator &GetMassNormalizationOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedPressureSurfaceConstraint &GetSurfaceConstraintOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedCenteringConstraint &GetCenteringConstraintOperator() const noexcept;
|
||||
|
||||
private:
|
||||
struct ConstructionData;
|
||||
@@ -133,6 +150,15 @@ export namespace mean_field::operators {
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
double targetMass,
|
||||
PressureSurfaceConstraintView surfaceConstraint
|
||||
);
|
||||
|
||||
PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
double targetMass,
|
||||
PressureSurfaceConstraintView surfaceConstraint,
|
||||
ConstructionData constructionData
|
||||
);
|
||||
|
||||
@@ -150,6 +176,8 @@ export namespace mean_field::operators {
|
||||
PreparedHydrostaticEquilibriumOperator m_hydrostaticOperator;
|
||||
PreparedDisplacementResidualOperator m_displacementOperator;
|
||||
PreparedMassNormalizationOperator m_massNormalizationOperator;
|
||||
PreparedPressureSurfaceConstraint m_surfaceConstraintOperator;
|
||||
PreparedCenteringConstraint m_centeringConstraintOperator;
|
||||
|
||||
StellarEquilibriumDependencies m_preparedDependencies;
|
||||
mfem::Vector m_cachedResidual;
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.prepared_surface_constraint;
|
||||
|
||||
export import :field.mfem;
|
||||
export import :surface.compiled;
|
||||
|
||||
namespace mean_field::operators::detail {
|
||||
template <eos::ThermodynamicQuantityType Quantity> struct SingleQuantitySurfaceState final {
|
||||
eos::QuantityValue<Quantity> quantityValue;
|
||||
|
||||
[[nodiscard]] eos::QuantityValue<Quantity> value(Quantity) const noexcept {
|
||||
return quantityValue;
|
||||
}
|
||||
};
|
||||
} // namespace mean_field::operators::detail
|
||||
|
||||
export namespace mean_field::operators {
|
||||
/*
|
||||
* Runtime enforcement currently supports a pointwise pressure constraint
|
||||
* whose row field is also its sole state field. The concept is expressed
|
||||
* entirely in compiled-constraint metadata: no thermodynamic carrier or
|
||||
* concrete field is selected by this prepared layer.
|
||||
*/
|
||||
template <typename Candidate>
|
||||
concept SingleFieldPressureSurfaceConstraint =
|
||||
requires {
|
||||
typename std::remove_cvref_t<Candidate>::PhysicalQuantity;
|
||||
typename std::remove_cvref_t<Candidate>::CarrierQuantity;
|
||||
typename std::remove_cvref_t<Candidate>::CarrierField;
|
||||
typename std::remove_cvref_t<Candidate>::SurfaceDependencies;
|
||||
} && std::same_as<typename std::remove_cvref_t<Candidate>::PhysicalQuantity, eos::quantity::Pressure> &&
|
||||
std::same_as<
|
||||
typename std::remove_cvref_t<Candidate>::SurfaceDependencies::RowField,
|
||||
typename std::remove_cvref_t<Candidate>::CarrierField> &&
|
||||
std::same_as<
|
||||
typename std::remove_cvref_t<Candidate>::SurfaceDependencies::StateFieldTypes,
|
||||
field::TypeList<typename std::remove_cvref_t<Candidate>::CarrierField>>;
|
||||
|
||||
template <typename Candidate, typename Field>
|
||||
concept SingleFieldPressureSurfaceConstraintFor =
|
||||
SingleFieldPressureSurfaceConstraint<Candidate> &&
|
||||
std::same_as<typename std::remove_cvref_t<Candidate>::SurfaceDependencies::RowField, Field>;
|
||||
|
||||
/*
|
||||
* Non-owning runtime bridge for a statically compiled pressure constraint.
|
||||
* There is one function-pointer dispatch per complete row application;
|
||||
* the concrete loop remains templated so EOS operations can be inlined.
|
||||
*/
|
||||
class PressureSurfaceConstraintView final {
|
||||
public:
|
||||
template <SingleFieldPressureSurfaceConstraint Constraint>
|
||||
explicit PressureSurfaceConstraintView(const Constraint &constraint) noexcept
|
||||
: m_constraint(std::addressof(constraint)),
|
||||
m_applyResidualRows(&applyResidualRows<Constraint>),
|
||||
m_applyJacobianRows(&applyJacobianRows<Constraint>),
|
||||
m_descriptor(constraint.descriptor()) {
|
||||
}
|
||||
|
||||
void ApplyResidualRows(
|
||||
const mfem::Vector &surfaceState,
|
||||
const field::FieldBoundaryDofMap &surfaceRows,
|
||||
mfem::Vector &rowResidual
|
||||
) const {
|
||||
m_applyResidualRows(m_constraint, surfaceState, surfaceRows, rowResidual);
|
||||
}
|
||||
|
||||
void ApplyJacobianRows(
|
||||
const mfem::Vector &surfaceState,
|
||||
const field::FieldBoundaryDofMap &surfaceRows,
|
||||
const mfem::Vector &stateVariation,
|
||||
mfem::Vector &rowAction
|
||||
) const {
|
||||
m_applyJacobianRows(m_constraint, surfaceState, surfaceRows, stateVariation, rowAction);
|
||||
}
|
||||
|
||||
[[nodiscard]] surface::PressureSurfaceDescriptor descriptor() const noexcept {
|
||||
return m_descriptor;
|
||||
}
|
||||
|
||||
private:
|
||||
using ApplyResidualRowsFunction = void (*)(
|
||||
const void *,
|
||||
const mfem::Vector &,
|
||||
const field::FieldBoundaryDofMap &,
|
||||
mfem::Vector &
|
||||
);
|
||||
using ApplyJacobianRowsFunction = void (*)(
|
||||
const void *,
|
||||
const mfem::Vector &,
|
||||
const field::FieldBoundaryDofMap &,
|
||||
const mfem::Vector &,
|
||||
mfem::Vector &
|
||||
);
|
||||
|
||||
template <SingleFieldPressureSurfaceConstraint Constraint>
|
||||
static void applyResidualRows(
|
||||
const void *constraint,
|
||||
const mfem::Vector &surfaceState,
|
||||
const field::FieldBoundaryDofMap &surfaceRows,
|
||||
mfem::Vector &rowResidual
|
||||
) {
|
||||
using CarrierQuantity = typename Constraint::CarrierQuantity;
|
||||
|
||||
for (int surfaceIndex = 0; surfaceIndex < surfaceRows.size(); ++surfaceIndex) {
|
||||
const detail::SingleQuantitySurfaceState<CarrierQuantity> state{
|
||||
eos::QuantityValue<CarrierQuantity>{surfaceState(surfaceIndex)}
|
||||
};
|
||||
rowResidual(surfaceRows.reduced_dofs()[surfaceIndex]) =
|
||||
static_cast<const Constraint *>(constraint)->residual(state);
|
||||
}
|
||||
}
|
||||
|
||||
template <SingleFieldPressureSurfaceConstraint Constraint>
|
||||
static void applyJacobianRows(
|
||||
const void *constraint,
|
||||
const mfem::Vector &surfaceState,
|
||||
const field::FieldBoundaryDofMap &surfaceRows,
|
||||
const mfem::Vector &stateVariation,
|
||||
mfem::Vector &rowAction
|
||||
) {
|
||||
using CarrierQuantity = typename Constraint::CarrierQuantity;
|
||||
|
||||
for (int surfaceIndex = 0; surfaceIndex < surfaceRows.size(); ++surfaceIndex) {
|
||||
const int reducedDof = surfaceRows.reduced_dofs()[surfaceIndex];
|
||||
const detail::SingleQuantitySurfaceState<CarrierQuantity> state{
|
||||
eos::QuantityValue<CarrierQuantity>{surfaceState(surfaceIndex)}
|
||||
};
|
||||
const detail::SingleQuantitySurfaceState<CarrierQuantity> variation{
|
||||
eos::QuantityValue<CarrierQuantity>{stateVariation(reducedDof)}
|
||||
};
|
||||
rowAction(reducedDof) = static_cast<const Constraint *>(constraint)->jacobianAction(state, variation);
|
||||
}
|
||||
}
|
||||
|
||||
const void *m_constraint;
|
||||
ApplyResidualRowsFunction m_applyResidualRows;
|
||||
ApplyJacobianRowsFunction m_applyJacobianRows;
|
||||
surface::PressureSurfaceDescriptor m_descriptor;
|
||||
};
|
||||
|
||||
struct PreparedSurfaceConstraintReport final {
|
||||
bool cachedSurfaceState{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return cachedSurfaceState;
|
||||
}
|
||||
};
|
||||
|
||||
class PreparedPressureSurfaceConstraint final {
|
||||
public:
|
||||
PreparedPressureSurfaceConstraint(
|
||||
field::FieldBoundaryDofMap surfaceRows,
|
||||
const PressureSurfaceConstraintView constraint
|
||||
)
|
||||
: m_surfaceRows(std::move(surfaceRows)),
|
||||
m_constraint(constraint),
|
||||
m_surfaceState(m_surfaceRows.size()) {
|
||||
}
|
||||
|
||||
[[nodiscard]] PreparedSurfaceConstraintReport Prepare(
|
||||
const mfem::Vector &reducedState,
|
||||
const bool stateChanged
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
reducedState.Size() == m_surfaceRows.field_size(),
|
||||
"The pressure surface constraint received a state vector with the wrong size."
|
||||
);
|
||||
|
||||
PreparedSurfaceConstraintReport report;
|
||||
if (!m_isPrepared || stateChanged) {
|
||||
for (int surfaceIndex = 0; surfaceIndex < m_surfaceRows.size(); ++surfaceIndex) {
|
||||
const double value = reducedState(m_surfaceRows.reduced_dofs()[surfaceIndex]);
|
||||
MFEM_VERIFY(std::isfinite(value), "The pressure surface constraint received non-finite state.");
|
||||
m_surfaceState(surfaceIndex) = value;
|
||||
}
|
||||
report.cachedSurfaceState = true;
|
||||
}
|
||||
|
||||
m_isPrepared = true;
|
||||
return report;
|
||||
}
|
||||
|
||||
void ApplyResidualRows(mfem::Vector &rowResidual) const {
|
||||
VerifyPrepared();
|
||||
MFEM_VERIFY(
|
||||
rowResidual.Size() == m_surfaceRows.field_size(),
|
||||
"The pressure surface constraint received a residual vector with the wrong size."
|
||||
);
|
||||
m_constraint.ApplyResidualRows(m_surfaceState, m_surfaceRows, rowResidual);
|
||||
}
|
||||
|
||||
void ApplyJacobianRows(
|
||||
const mfem::Vector &stateVariation,
|
||||
mfem::Vector &rowAction
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
MFEM_VERIFY(
|
||||
stateVariation.Size() == m_surfaceRows.field_size() && rowAction.Size() == m_surfaceRows.field_size(),
|
||||
"The pressure surface constraint received a Jacobian vector with the wrong size."
|
||||
);
|
||||
m_constraint.ApplyJacobianRows(m_surfaceState, m_surfaceRows, stateVariation, rowAction);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept {
|
||||
return m_isPrepared;
|
||||
}
|
||||
|
||||
[[nodiscard]] const field::FieldBoundaryDofMap &GetSurfaceRows() const noexcept {
|
||||
return m_surfaceRows;
|
||||
}
|
||||
|
||||
[[nodiscard]] surface::PressureSurfaceDescriptor GetPhysicalCondition() const noexcept {
|
||||
return m_constraint.descriptor();
|
||||
}
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const {
|
||||
MFEM_VERIFY(m_isPrepared, "The pressure surface constraint must be prepared before row application.");
|
||||
}
|
||||
|
||||
field::FieldBoundaryDofMap m_surfaceRows;
|
||||
PressureSurfaceConstraintView m_constraint;
|
||||
mfem::Vector m_surfaceState;
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
66
libmeanfield/interface/surface/compiled.cppm
Normal file
66
libmeanfield/interface/surface/compiled.cppm
Normal file
@@ -0,0 +1,66 @@
|
||||
module;
|
||||
|
||||
export module mean_field:surface.compiled;
|
||||
|
||||
export import :eos.pressure_surface;
|
||||
export import :surface.constant;
|
||||
export import :surface.dependencies;
|
||||
|
||||
export namespace mean_field::surface {
|
||||
template <
|
||||
eos::EquationOfStateModel EquationOfState,
|
||||
SurfaceConstraintFormulationType Formulation,
|
||||
eos::ThermodynamicRelationType SelectedRelation,
|
||||
typename Dependencies>
|
||||
class CompiledPressureSurfaceConstraint final {
|
||||
public:
|
||||
using PhysicalCondition = ConstantPressureSurface;
|
||||
using PhysicalQuantity = eos::quantity::Pressure;
|
||||
using CarrierQuantity = typename Formulation::CarrierQuantity;
|
||||
using CarrierField = typename Formulation::CarrierField;
|
||||
using Relation = SelectedRelation;
|
||||
using SurfaceDependencies = Dependencies;
|
||||
|
||||
CompiledPressureSurfaceConstraint(
|
||||
const ConstantPressureSurface condition,
|
||||
const EquationOfState &equationOfState
|
||||
) noexcept
|
||||
: m_condition(condition),
|
||||
m_resolvedRelation(
|
||||
equationOfState,
|
||||
condition.targetPressure()
|
||||
) {
|
||||
}
|
||||
|
||||
[[nodiscard]] eos::PressureValue targetPressure() const noexcept {
|
||||
return m_condition.targetPressure();
|
||||
}
|
||||
|
||||
[[nodiscard]] PressureSurfaceDescriptor descriptor() const noexcept {
|
||||
return m_condition.descriptor();
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr RuntimeSurfaceConstraintDependencies runtimeDependencies() noexcept {
|
||||
return SurfaceDependencies::runtimeDescription();
|
||||
}
|
||||
|
||||
template <typename SurfaceState> [[nodiscard]] double residual(const SurfaceState &state) const {
|
||||
return state.value(CarrierQuantity{}).value() - m_resolvedRelation.requiredCarrierValue(state).value();
|
||||
}
|
||||
|
||||
template <
|
||||
typename SurfaceState,
|
||||
typename SurfaceVariation>
|
||||
[[nodiscard]] double jacobianAction(
|
||||
const SurfaceState &state,
|
||||
const SurfaceVariation &variation
|
||||
) const {
|
||||
return variation.value(CarrierQuantity{}).value() -
|
||||
m_resolvedRelation.carrierCorrectionJacobianAction(state, variation);
|
||||
}
|
||||
|
||||
private:
|
||||
ConstantPressureSurface m_condition;
|
||||
eos::ResolvedPressureSurfaceRelation<EquationOfState, Relation> m_resolvedRelation;
|
||||
};
|
||||
} // namespace mean_field::surface
|
||||
143
libmeanfield/interface/surface/compiler.cppm
Normal file
143
libmeanfield/interface/surface/compiler.cppm
Normal file
@@ -0,0 +1,143 @@
|
||||
module;
|
||||
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:surface.compiler;
|
||||
|
||||
export import :surface.compiled;
|
||||
|
||||
export namespace mean_field::surface {
|
||||
namespace detail {
|
||||
template <typename RelationType, typename Formulation, typename EquationOfState>
|
||||
struct PressureSurfaceRelationMatches : std::false_type { };
|
||||
|
||||
template <typename OutputQuantity, typename... InputQuantities, typename Formulation, typename EquationOfState>
|
||||
struct PressureSurfaceRelationMatches<
|
||||
eos::Relation<OutputQuantity, InputQuantities...>,
|
||||
Formulation,
|
||||
EquationOfState>
|
||||
: std::bool_constant<
|
||||
std::same_as<OutputQuantity, typename Formulation::CarrierQuantity> &&
|
||||
(std::same_as<eos::quantity::Pressure, InputQuantities> || ...) &&
|
||||
((std::same_as<eos::quantity::Pressure, InputQuantities> ||
|
||||
(surfaceBindingCount<typename Formulation::StateBindings, InputQuantities> == 1 &&
|
||||
eos::SupportsPartialDerivative<
|
||||
EquationOfState,
|
||||
eos::Relation<OutputQuantity, InputQuantities...>,
|
||||
InputQuantities>)) &&
|
||||
...)> { };
|
||||
|
||||
template <typename Catalog, typename Formulation, typename EquationOfState>
|
||||
struct MatchingPressureSurfaceRelations;
|
||||
|
||||
template <typename... Relations, typename Formulation, typename EquationOfState>
|
||||
struct MatchingPressureSurfaceRelations<eos::RelationCatalog<Relations...>, Formulation, EquationOfState> {
|
||||
using Tuple = decltype(std::tuple_cat(
|
||||
std::conditional_t<
|
||||
PressureSurfaceRelationMatches<Relations, Formulation, EquationOfState>::value,
|
||||
std::tuple<Relations>,
|
||||
std::tuple<>>{}...
|
||||
));
|
||||
|
||||
static constexpr std::size_t count = std::tuple_size_v<Tuple>;
|
||||
};
|
||||
|
||||
template <std::size_t Count, typename Tuple> struct UniquePressureSurfaceRelation {
|
||||
using Type = void;
|
||||
};
|
||||
|
||||
template <typename Tuple> struct UniquePressureSurfaceRelation<1, Tuple> {
|
||||
using Type = std::tuple_element_t<0, Tuple>;
|
||||
};
|
||||
|
||||
template <typename Dependencies, typename Field> struct AppendSurfaceDependency;
|
||||
|
||||
template <typename RowField, typename... StateFields, typename Field>
|
||||
struct AppendSurfaceDependency<SurfaceConstraintDependencies<RowField, StateFields...>, Field> {
|
||||
using Type = SurfaceConstraintDependencies<RowField, StateFields..., Field>;
|
||||
};
|
||||
|
||||
template <typename Dependencies, typename InputQuantity, typename Bindings>
|
||||
struct AppendPressureSurfaceInputDependency {
|
||||
using Type =
|
||||
typename AppendSurfaceDependency<Dependencies, SurfaceFieldForQuantityT<Bindings, InputQuantity>>::Type;
|
||||
};
|
||||
|
||||
template <typename Dependencies, typename Bindings>
|
||||
struct AppendPressureSurfaceInputDependency<Dependencies, eos::quantity::Pressure, Bindings> {
|
||||
using Type = Dependencies;
|
||||
};
|
||||
|
||||
template <typename Dependencies, typename Bindings, typename... InputQuantities>
|
||||
struct AppendPressureSurfaceInputDependencies;
|
||||
|
||||
template <typename Dependencies, typename Bindings>
|
||||
struct AppendPressureSurfaceInputDependencies<Dependencies, Bindings> {
|
||||
using Type = Dependencies;
|
||||
};
|
||||
|
||||
template <typename Dependencies, typename Bindings, typename FirstInput, typename... RemainingInputs>
|
||||
struct AppendPressureSurfaceInputDependencies<Dependencies, Bindings, FirstInput, RemainingInputs...> {
|
||||
using WithFirst = typename AppendPressureSurfaceInputDependency<Dependencies, FirstInput, Bindings>::Type;
|
||||
using Type = typename AppendPressureSurfaceInputDependencies<WithFirst, Bindings, RemainingInputs...>::Type;
|
||||
};
|
||||
|
||||
template <typename RelationType, typename Formulation> struct PressureSurfaceDependenciesForRelation;
|
||||
|
||||
template <typename OutputQuantity, typename... InputQuantities, typename Formulation>
|
||||
struct PressureSurfaceDependenciesForRelation<eos::Relation<OutputQuantity, InputQuantities...>, Formulation> {
|
||||
using InitialDependencies =
|
||||
SurfaceConstraintDependencies<typename Formulation::CarrierField, typename Formulation::CarrierField>;
|
||||
using Type = typename AppendPressureSurfaceInputDependencies<
|
||||
InitialDependencies,
|
||||
typename Formulation::StateBindings,
|
||||
InputQuantities...>::Type;
|
||||
};
|
||||
|
||||
template <SurfaceConstraintFormulationType Formulation, eos::EquationOfStateModel EquationOfState>
|
||||
struct PressureSurfaceCompilation {
|
||||
using Matches =
|
||||
MatchingPressureSurfaceRelations<typename EquationOfState::Relations, Formulation, EquationOfState>;
|
||||
using Relation = typename UniquePressureSurfaceRelation<Matches::count, typename Matches::Tuple>::Type;
|
||||
};
|
||||
|
||||
template <SurfaceConstraintFormulationType Formulation, eos::EquationOfStateModel EquationOfState>
|
||||
requires(PressureSurfaceCompilation<Formulation, EquationOfState>::Matches::count == 1)
|
||||
struct CompiledPressureSurfaceConstraintType {
|
||||
using Compilation = PressureSurfaceCompilation<Formulation, EquationOfState>;
|
||||
using Relation = typename Compilation::Relation;
|
||||
using Dependencies = typename PressureSurfaceDependenciesForRelation<Relation, Formulation>::Type;
|
||||
using Type = CompiledPressureSurfaceConstraint<EquationOfState, Formulation, Relation, Dependencies>;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename Formulation, typename EquationOfState>
|
||||
concept PressureSurfaceCompilable =
|
||||
SurfaceConstraintFormulationType<Formulation> && eos::EquationOfStateModel<EquationOfState> &&
|
||||
(detail::PressureSurfaceCompilation<std::remove_cvref_t<Formulation>, std::remove_cvref_t<EquationOfState>>::
|
||||
Matches::count == 1);
|
||||
|
||||
template <SurfaceConstraintFormulationType Formulation, eos::EquationOfStateModel EquationOfState>
|
||||
requires PressureSurfaceCompilable<Formulation, EquationOfState>
|
||||
using CompiledPressureSurfaceConstraintT = typename detail::CompiledPressureSurfaceConstraintType<
|
||||
std::remove_cvref_t<Formulation>,
|
||||
std::remove_cvref_t<EquationOfState>>::Type;
|
||||
|
||||
template <
|
||||
SurfaceConstraintFormulationType Formulation,
|
||||
eos::EquationOfStateModel EquationOfState>
|
||||
requires PressureSurfaceCompilable<
|
||||
Formulation,
|
||||
EquationOfState>
|
||||
[[nodiscard]] CompiledPressureSurfaceConstraintT<
|
||||
Formulation,
|
||||
EquationOfState>
|
||||
compilePressureSurfaceConstraint(
|
||||
const ConstantPressureSurface condition,
|
||||
const EquationOfState &equationOfState
|
||||
) noexcept {
|
||||
return CompiledPressureSurfaceConstraintT<Formulation, EquationOfState>{condition, equationOfState};
|
||||
}
|
||||
} // namespace mean_field::surface
|
||||
65
libmeanfield/interface/surface/constant.cppm
Normal file
65
libmeanfield/interface/surface/constant.cppm
Normal file
@@ -0,0 +1,65 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <format>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:surface.constant;
|
||||
|
||||
export import :eos.quantities;
|
||||
|
||||
export namespace mean_field::surface {
|
||||
struct PressureSurfaceDescriptor final {
|
||||
double targetPressure;
|
||||
};
|
||||
|
||||
/*
|
||||
* The only physical surface prescription currently supported by
|
||||
* MeanField. It says nothing about which thermodynamic variable appears
|
||||
* in a nonlinear state vector; resolving pressure into that representation
|
||||
* is an EOS responsibility.
|
||||
*/
|
||||
class ConstantPressureSurface final {
|
||||
public:
|
||||
using PhysicalQuantity = eos::quantity::Pressure;
|
||||
using TargetValue = eos::PressureValue;
|
||||
|
||||
explicit ConstantPressureSurface(const TargetValue targetPressure) : m_targetPressure(targetPressure) {
|
||||
if (!std::isfinite(targetPressure.value())) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"The target surface pressure must be finite. Instead P = {} was provided.",
|
||||
targetPressure.value()
|
||||
)
|
||||
);
|
||||
}
|
||||
if (targetPressure.value() < 0.0) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"The target surface pressure must be non-negative. Instead P = {} was provided.",
|
||||
targetPressure.value()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] TargetValue targetPressure() const noexcept {
|
||||
return m_targetPressure;
|
||||
}
|
||||
|
||||
[[nodiscard]] PressureSurfaceDescriptor descriptor() const noexcept {
|
||||
return PressureSurfaceDescriptor{.targetPressure = m_targetPressure.value()};
|
||||
}
|
||||
|
||||
private:
|
||||
TargetValue m_targetPressure;
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
concept ConstantPressureSurfaceType = std::same_as<std::remove_cvref_t<Candidate>, ConstantPressureSurface>;
|
||||
|
||||
// Familiar physical terminology retained as a synonym, not as a second
|
||||
// surface-condition type.
|
||||
using Isobaric = ConstantPressureSurface;
|
||||
} // namespace mean_field::surface
|
||||
186
libmeanfield/interface/surface/dependencies.cppm
Normal file
186
libmeanfield/interface/surface/dependencies.cppm
Normal file
@@ -0,0 +1,186 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:surface.dependencies;
|
||||
|
||||
export import :eos.relations;
|
||||
export import :field.registry;
|
||||
|
||||
export namespace mean_field::surface {
|
||||
template <typename Candidate>
|
||||
concept SurfaceFieldType = requires {
|
||||
{ Candidate::name } -> std::convertible_to<std::string_view>;
|
||||
} && (std::string_view{Candidate::name}.size() > 0);
|
||||
|
||||
class SurfaceFieldId final {
|
||||
public:
|
||||
explicit constexpr SurfaceFieldId(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 SurfaceFieldId &,
|
||||
const SurfaceFieldId &
|
||||
) noexcept = default;
|
||||
|
||||
private:
|
||||
std::string_view m_name;
|
||||
};
|
||||
|
||||
template <SurfaceFieldType Field> inline constexpr SurfaceFieldId surfaceFieldId{std::string_view{Field::name}};
|
||||
|
||||
template <eos::ThermodynamicQuantityType ThermodynamicQuantity, SurfaceFieldType Field>
|
||||
struct SurfaceStateBinding final {
|
||||
using Quantity = ThermodynamicQuantity;
|
||||
using FieldType = Field;
|
||||
};
|
||||
|
||||
template <typename... Bindings> struct SurfaceStateBindings final { };
|
||||
|
||||
namespace detail {
|
||||
template <typename... Types> struct SurfaceTypesAreUnique : std::true_type { };
|
||||
|
||||
template <typename First, typename... Remaining>
|
||||
struct SurfaceTypesAreUnique<First, Remaining...>
|
||||
: std::bool_constant<
|
||||
(!std::same_as<First, Remaining> && ...) && SurfaceTypesAreUnique<Remaining...>::value> { };
|
||||
|
||||
template <typename Bindings> struct SurfaceBindingsAreValid : std::false_type { };
|
||||
|
||||
template <typename... Bindings>
|
||||
struct SurfaceBindingsAreValid<SurfaceStateBindings<Bindings...>>
|
||||
: std::bool_constant<
|
||||
(sizeof...(Bindings) > 0) &&
|
||||
(requires {
|
||||
typename Bindings::Quantity;
|
||||
typename Bindings::FieldType;
|
||||
} && ...) &&
|
||||
(eos::ThermodynamicQuantityType<
|
||||
typename Bindings::Quantity> && ...) &&
|
||||
(SurfaceFieldType<typename Bindings::FieldType> && ...) &&
|
||||
SurfaceTypesAreUnique<
|
||||
typename Bindings::Quantity...>::value> { };
|
||||
|
||||
template <typename Bindings, typename Quantity> struct SurfaceBindingCount;
|
||||
|
||||
template <typename Quantity, typename... Bindings>
|
||||
struct SurfaceBindingCount<SurfaceStateBindings<Bindings...>, Quantity>
|
||||
: std::integral_constant<
|
||||
std::size_t,
|
||||
(std::size_t{0} + ... +
|
||||
(std::same_as<Quantity, typename Bindings::Quantity> ? std::size_t{1} : std::size_t{0}))> {
|
||||
};
|
||||
|
||||
template <typename Bindings, typename Quantity> struct SurfaceFieldForQuantity;
|
||||
|
||||
template <typename Quantity, typename First, typename... Remaining>
|
||||
struct SurfaceFieldForQuantity<SurfaceStateBindings<First, Remaining...>, Quantity>
|
||||
: std::conditional_t<
|
||||
std::same_as<Quantity, typename First::Quantity>,
|
||||
std::type_identity<typename First::FieldType>,
|
||||
SurfaceFieldForQuantity<SurfaceStateBindings<Remaining...>, Quantity>> { };
|
||||
|
||||
template <typename Candidate, std::size_t CarrierBindingCount>
|
||||
struct CarrierFieldMatchesSurfaceBinding : std::false_type { };
|
||||
|
||||
template <typename Candidate>
|
||||
struct CarrierFieldMatchesSurfaceBinding<Candidate, 1>
|
||||
: std::bool_constant<std::same_as<
|
||||
typename SurfaceFieldForQuantity<
|
||||
typename Candidate::StateBindings,
|
||||
typename Candidate::CarrierQuantity>::type,
|
||||
typename Candidate::CarrierField>> { };
|
||||
|
||||
template <
|
||||
typename Candidate,
|
||||
bool BindingsAreValid = SurfaceBindingsAreValid<typename Candidate::StateBindings>::value>
|
||||
struct FormulationBindingsMatchCarrier : std::false_type { };
|
||||
|
||||
template <typename Candidate>
|
||||
struct FormulationBindingsMatchCarrier<Candidate, true>
|
||||
: CarrierFieldMatchesSurfaceBinding<
|
||||
Candidate,
|
||||
SurfaceBindingCount<
|
||||
typename Candidate::StateBindings,
|
||||
typename Candidate::CarrierQuantity>::value> { };
|
||||
|
||||
template <typename Candidate, typename = void>
|
||||
struct IsSurfaceConstraintFormulation : std::false_type { };
|
||||
|
||||
template <typename Candidate>
|
||||
struct IsSurfaceConstraintFormulation<
|
||||
Candidate,
|
||||
std::void_t<
|
||||
typename Candidate::CarrierQuantity,
|
||||
typename Candidate::CarrierField,
|
||||
typename Candidate::StateBindings>>
|
||||
: std::bool_constant<
|
||||
eos::ThermodynamicQuantityType<typename Candidate::CarrierQuantity> &&
|
||||
SurfaceFieldType<typename Candidate::CarrierField> &&
|
||||
FormulationBindingsMatchCarrier<Candidate>::value> { };
|
||||
} // namespace detail
|
||||
|
||||
template <typename Candidate>
|
||||
concept ValidSurfaceStateBindings = detail::SurfaceBindingsAreValid<std::remove_cv_t<Candidate>>::value;
|
||||
|
||||
template <ValidSurfaceStateBindings Bindings, typename Quantity>
|
||||
inline constexpr std::size_t surfaceBindingCount = detail::SurfaceBindingCount<Bindings, Quantity>::value;
|
||||
|
||||
template <ValidSurfaceStateBindings Bindings, typename Quantity>
|
||||
requires(surfaceBindingCount<Bindings, Quantity> == 1)
|
||||
using SurfaceFieldForQuantityT = typename detail::SurfaceFieldForQuantity<Bindings, Quantity>::type;
|
||||
|
||||
template <
|
||||
eos::ThermodynamicQuantityType CarrierThermodynamicQuantity,
|
||||
SurfaceFieldType CarrierFieldType,
|
||||
ValidSurfaceStateBindings Bindings>
|
||||
requires(
|
||||
surfaceBindingCount<Bindings, CarrierThermodynamicQuantity> == 1 &&
|
||||
std::same_as<SurfaceFieldForQuantityT<Bindings, CarrierThermodynamicQuantity>, CarrierFieldType>
|
||||
)
|
||||
struct SurfaceConstraintFormulation final {
|
||||
using CarrierQuantity = CarrierThermodynamicQuantity;
|
||||
using CarrierField = CarrierFieldType;
|
||||
using StateBindings = Bindings;
|
||||
};
|
||||
|
||||
using BarotropicSurfaceFormulation = SurfaceConstraintFormulation<
|
||||
eos::quantity::SpecificEnthalpy,
|
||||
field::Enthalpy,
|
||||
SurfaceStateBindings<SurfaceStateBinding<eos::quantity::SpecificEnthalpy, field::Enthalpy>>>;
|
||||
|
||||
template <typename Candidate>
|
||||
concept SurfaceConstraintFormulationType =
|
||||
detail::IsSurfaceConstraintFormulation<std::remove_cv_t<Candidate>>::value;
|
||||
|
||||
struct RuntimeSurfaceConstraintDependencies final {
|
||||
SurfaceFieldId residualRowField;
|
||||
std::span<const SurfaceFieldId> stateFields;
|
||||
};
|
||||
|
||||
template <SurfaceFieldType ResidualField, SurfaceFieldType... StateFields>
|
||||
struct SurfaceConstraintDependencies final {
|
||||
using RowField = ResidualField;
|
||||
using StateFieldTypes = field::TypeList<StateFields...>;
|
||||
|
||||
inline static constexpr std::array<SurfaceFieldId, sizeof...(StateFields)> runtimeStateFields{
|
||||
surfaceFieldId<StateFields>...
|
||||
};
|
||||
|
||||
[[nodiscard]] static constexpr RuntimeSurfaceConstraintDependencies runtimeDescription() noexcept {
|
||||
return RuntimeSurfaceConstraintDependencies{
|
||||
.residualRowField = surfaceFieldId<ResidualField>,
|
||||
.stateFields = std::span<const SurfaceFieldId>{runtimeStateFields}
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace mean_field::surface
|
||||
@@ -1,64 +0,0 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <format>
|
||||
#include <stdexcept>
|
||||
|
||||
export module mean_field:surface.isobaric;
|
||||
|
||||
export import :surface.base;
|
||||
|
||||
export namespace mean_field::surface {
|
||||
class Isobaric final : public SurfaceBase {
|
||||
public:
|
||||
explicit Isobaric(const double targetPressure = 0.0) : m_targetPressure(targetPressure) {
|
||||
validateTargetPressure();
|
||||
}
|
||||
|
||||
[[nodiscard]] double targetPressure() const noexcept {
|
||||
return m_targetPressure;
|
||||
}
|
||||
|
||||
[[nodiscard]] ResolvedSurfaceCondition
|
||||
resolve(const mean_field::eos::EquationOfState &equationOfState) const override {
|
||||
return ResolvedSurfaceCondition{resolveTargetEnthalpy(equationOfState)};
|
||||
}
|
||||
|
||||
void validate(const mean_field::eos::EquationOfState &equationOfState) const override {
|
||||
static_cast<void>(resolveTargetEnthalpy(equationOfState));
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] double resolveTargetEnthalpy(const mean_field::eos::EquationOfState &equationOfState) const {
|
||||
validateTargetPressure();
|
||||
|
||||
const double targetEnthalpy = equationOfState.enthalpy_from_pressure(m_targetPressure);
|
||||
|
||||
if (!std::isfinite(targetEnthalpy) || targetEnthalpy < 0.0) {
|
||||
throw std::domain_error(
|
||||
std::format(
|
||||
"The equation of state resolved the isobaric "
|
||||
"target P = {} to the invalid enthalpy h = {}.",
|
||||
m_targetPressure, targetEnthalpy
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return targetEnthalpy;
|
||||
}
|
||||
|
||||
void validateTargetPressure() const {
|
||||
if (!std::isfinite(m_targetPressure) || m_targetPressure < 0.0) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"The target surface pressure must be finite and "
|
||||
"non-negative. Instead P = {} was provided.",
|
||||
m_targetPressure
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
double m_targetPressure;
|
||||
};
|
||||
} // namespace mean_field::surface
|
||||
@@ -1,53 +0,0 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
|
||||
export module mean_field:surface.base;
|
||||
|
||||
export import :eos.base;
|
||||
|
||||
export namespace mean_field::surface {
|
||||
struct ResolvedSurfaceCondition final {
|
||||
double targetEnthalpy{0.0};
|
||||
|
||||
explicit ResolvedSurfaceCondition(const double requestedTargetEnthalpy)
|
||||
: targetEnthalpy(requestedTargetEnthalpy) {
|
||||
if (!std::isfinite(targetEnthalpy) || targetEnthalpy < 0.0) {
|
||||
throw std::invalid_argument(
|
||||
"A resolved surface enthalpy must be finite and "
|
||||
"non-negative."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] double residual(const double enthalpy) const {
|
||||
if (!std::isfinite(enthalpy)) {
|
||||
throw std::invalid_argument("A surface enthalpy value must be finite.");
|
||||
}
|
||||
|
||||
return enthalpy - targetEnthalpy;
|
||||
}
|
||||
|
||||
[[nodiscard]] static double jacobianAction(const double enthalpyVariation) {
|
||||
if (!std::isfinite(enthalpyVariation)) {
|
||||
throw std::invalid_argument("A surface enthalpy variation must be finite.");
|
||||
}
|
||||
|
||||
return enthalpyVariation;
|
||||
}
|
||||
};
|
||||
|
||||
class SurfaceBase {
|
||||
public:
|
||||
virtual ~SurfaceBase() = default;
|
||||
|
||||
[[nodiscard]] virtual ResolvedSurfaceCondition
|
||||
resolve(const mean_field::eos::EquationOfState &equationOfState) const = 0;
|
||||
|
||||
virtual void validate(const mean_field::eos::EquationOfState &equationOfState) const = 0;
|
||||
|
||||
protected:
|
||||
SurfaceBase() = default;
|
||||
};
|
||||
} // namespace mean_field::surface
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,82 +11,90 @@ export module mean_field:utils.misc;
|
||||
import :utils.domain;
|
||||
|
||||
export namespace mean_field::utils {
|
||||
constexpr double APPROX_MAX_ACCEPTABLE_POTENTIAL_ERROR_SI_BURNING = 1e-4;
|
||||
constexpr double APPROX_MAX_ACCEPTABLE_POTENTIAL_ERROR_SI_BURNING = 1e-4;
|
||||
|
||||
bool is_vacuum(const mfem::ElementTransformation &Tr,
|
||||
mfem::Array<mfem::Vector *> elvec) {
|
||||
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
|
||||
bool is_vacuum(
|
||||
const mfem::ElementTransformation &Tr,
|
||||
mfem::Array<mfem::Vector *> elvec
|
||||
) {
|
||||
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
if (Schema::template attribute_belongs_to<domain::Vacuum>(Tr.Attribute)) {
|
||||
const int size_elvec = elvec.Size();
|
||||
for (int i = 0; i < size_elvec; i++) {
|
||||
if (elvec[i]) {
|
||||
*elvec[i] = 0.0;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_vacuum(const mfem::ElementTransformation &Tr,
|
||||
const mfem::Array2D<mfem::DenseMatrix *> &elmats) {
|
||||
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
if (Schema::template attribute_belongs_to<domain::Vacuum>(Tr.Attribute)) {
|
||||
const int cols = elmats.NumCols();
|
||||
const int rows = elmats.NumRows();
|
||||
for (int rowID = 0; rowID < rows; rowID++) {
|
||||
for (int colID = 0; colID < cols; colID++) {
|
||||
if (elmats(rowID, colID)) {
|
||||
*elmats(rowID, colID) = 0.0;
|
||||
if (Schema::template attribute_belongs_to<domain::Vacuum>(Tr.Attribute)) {
|
||||
const int size_elvec = elvec.Size();
|
||||
for (int i = 0; i < size_elvec; i++) {
|
||||
if (elvec[i]) {
|
||||
*elvec[i] = 0.0;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr std::string_view ANSI_GREEN = "\033[32m";
|
||||
constexpr std::string_view ANSI_RED = "\033[31m";
|
||||
constexpr std::string_view ANSI_YELLOW = "\033[33m";
|
||||
constexpr std::string_view ANSI_BLUE = "\033[34m";
|
||||
constexpr std::string_view ANSI_MAGENTA = "\033[35m";
|
||||
constexpr std::string_view ANSI_CYAN = "\033[36m";
|
||||
constexpr std::string_view ANSI_RESET = "\033[0m";
|
||||
constexpr std::string_view ANSI_BCYAN = "\033[1;36m";
|
||||
bool is_vacuum(
|
||||
const mfem::ElementTransformation &Tr,
|
||||
const mfem::Array2D<mfem::DenseMatrix *> &elmats
|
||||
) {
|
||||
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
constexpr double G = 1.0;
|
||||
constexpr double MASS = 1.0;
|
||||
constexpr double RADIUS = 1.0;
|
||||
if (Schema::template attribute_belongs_to<domain::Vacuum>(Tr.Attribute)) {
|
||||
const int cols = elmats.NumCols();
|
||||
const int rows = elmats.NumRows();
|
||||
for (int rowID = 0; rowID < rows; rowID++) {
|
||||
for (int colID = 0; colID < cols; colID++) {
|
||||
if (elmats(rowID, colID)) {
|
||||
*elmats(rowID, colID) = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[[maybe_unused]] constexpr char HOST[10] = "localhost";
|
||||
[[maybe_unused]] constexpr int PORT = 19916;
|
||||
constexpr std::string_view ANSI_GREEN = "\033[32m";
|
||||
constexpr std::string_view ANSI_RED = "\033[31m";
|
||||
constexpr std::string_view ANSI_YELLOW = "\033[33m";
|
||||
constexpr std::string_view ANSI_BLUE = "\033[34m";
|
||||
constexpr std::string_view ANSI_MAGENTA = "\033[35m";
|
||||
constexpr std::string_view ANSI_CYAN = "\033[36m";
|
||||
constexpr std::string_view ANSI_RESET = "\033[0m";
|
||||
constexpr std::string_view ANSI_BCYAN = "\033[1;36m";
|
||||
|
||||
template <typename T>
|
||||
concept is_xad = std::is_same_v<T, xad::AReal<long double>> ||
|
||||
std::is_same_v<T, xad::AReal<double>> ||
|
||||
std::is_same_v<T, xad::AReal<float>>;
|
||||
constexpr double G = 1.0;
|
||||
constexpr double MASS = 1.0;
|
||||
constexpr double RADIUS = 1.0;
|
||||
|
||||
template <typename T>
|
||||
concept is_real = std::is_floating_point_v<T> || is_xad<T>;
|
||||
[[maybe_unused]] constexpr char HOST[10] = "localhost";
|
||||
[[maybe_unused]] constexpr int PORT = 19916;
|
||||
|
||||
template <is_real T>
|
||||
using EOS_P = std::function<T(const T &rho, const T &temp)>;
|
||||
template <typename T>
|
||||
concept is_xad = std::is_same_v<T, xad::AReal<long double>> || std::is_same_v<T, xad::AReal<double>> ||
|
||||
std::is_same_v<T, xad::AReal<float>>;
|
||||
|
||||
enum class DOMAINS : uint8_t {
|
||||
CORE = 1 << 0,
|
||||
ENVELOPE = 1 << 1,
|
||||
VACUUM = 1 << 2,
|
||||
STELLAR = CORE | ENVELOPE,
|
||||
ALL = CORE | ENVELOPE | VACUUM
|
||||
};
|
||||
template <typename T>
|
||||
concept is_real = std::is_floating_point_v<T> || is_xad<T>;
|
||||
|
||||
DOMAINS operator|(DOMAINS lhs, DOMAINS rhs);
|
||||
template <is_real T> using EOS_P = std::function<T(const T &rho, const T &temp)>;
|
||||
|
||||
DOMAINS operator&(DOMAINS lhs, DOMAINS rhs);
|
||||
enum class DOMAINS : uint8_t {
|
||||
CORE = 1 << 0,
|
||||
ENVELOPE = 1 << 1,
|
||||
VACUUM = 1 << 2,
|
||||
STELLAR = CORE | ENVELOPE,
|
||||
ALL = CORE | ENVELOPE | VACUUM
|
||||
};
|
||||
|
||||
int get_mesh_order(const mfem::Mesh &mesh);
|
||||
DOMAINS operator|(
|
||||
DOMAINS lhs,
|
||||
DOMAINS rhs
|
||||
);
|
||||
|
||||
DOMAINS operator&(
|
||||
DOMAINS lhs,
|
||||
DOMAINS rhs
|
||||
);
|
||||
|
||||
int get_mesh_order(const mfem::Mesh &mesh);
|
||||
|
||||
} // namespace mean_field::utils
|
||||
|
||||
Reference in New Issue
Block a user