test(compile_time_checks): added robust compile time checking library. Note that the compile time checks were developed with the assistance of a large language model. All LLM generated code has been manually reviewed
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
#include "serif/backend/mfem/discretization/quadrature/mfem_resolver.hpp"
|
||||
#include "serif/backend/mfem/discretization/quadrature/mfem_static_order.hpp"
|
||||
|
||||
#include "serif/utils/error/errors.hpp"
|
||||
#include "serif/utils/types/strings.hpp"
|
||||
|
||||
namespace {
|
||||
using namespace serif::discretization;
|
||||
using namespace serif::utils::errors;
|
||||
using namespace quadrature::backend::mfem;
|
||||
using namespace serif::utils::types;
|
||||
|
||||
using DensityProjectionForm = forms::projection<blocks::density::mass, blocks::density::mass>;
|
||||
|
||||
|
||||
// Note when using serif_assert (since it is a consteval function) it must be within
|
||||
// a function (C++ does not allow arbitrary calls to consteval functions outside of a function context). This is why we have this function here. It is not called, but it is used to force the compiler to evaluate the consteval function and thus trigger the compile time checks.
|
||||
consteval void compile_checks() {
|
||||
constexpr auto density_projection_error_msg = ct_format<"Density Projection Form should have a static, compile time, order of 6, instead got {}">(mfem_form_static_order<DensityProjectionForm>);
|
||||
serif::utils::errors::serif_ensure<SERiFErrorCode::FAILED_TYPE_INVARIANT, mfem_form_static_order<DensityProjectionForm> == 6, density_projection_error_msg>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "serif/backend/mfem/discretization/quadrature/mfem_static_order.hpp"
|
||||
#include "serif/discretization/forms/forms.hpp"
|
||||
|
||||
using namespace serif::discretization;
|
||||
using namespace serif::discretization::quadrature::backend::mfem;
|
||||
|
||||
static_assert(resolve_operand_order<forms::Operand<blocks::density::mass>>() == 3);
|
||||
static_assert(resolve_operand_order<forms::Operand<blocks::displacement::vector, forms::Operation::Divergence>>() == 2);
|
||||
static_assert(resolve_operand_order<forms::Operand<blocks::displacement::vector, forms::Operation::Gradient>>() == 2);
|
||||
static_assert(resolve_operand_order<forms::Operand<blocks::surface_deformation::coefficients>>() == 0, "Non-spatial values contribute no polynomial order");
|
||||
static_assert(mfem_form_static_order<forms::mass<blocks::displacement::vector, blocks::displacement::vector>> == 6);
|
||||
13
src/compile_time_checks/serif/backend_independent.cpp
Normal file
13
src/compile_time_checks/serif/backend_independent.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "serif/model/model.hpp"
|
||||
#include "serif/model/configured_discretization.hpp"
|
||||
#include "serif/solver/nonlinear/newton.hpp"
|
||||
#include "serif/solver/continuation/continuation.hpp"
|
||||
#include "serif/solver/preconditioner/default_preconditioners.hpp"
|
||||
#include "serif/discretization/domain/schema/validation/all.hpp"
|
||||
#include "serif/utils/projection/radial.hpp"
|
||||
#include "serif/utils/checks/finite.hpp"
|
||||
#include "serif/utils/serialize/serialize.hpp"
|
||||
|
||||
static_assert(serif::discretization::blocks::ScalarTraceTerm<serif::discretization::blocks::density::mass>);
|
||||
static_assert(std::same_as<decltype(serif::utils::serialize::pack(serif::solver::SolverOptions<>{})), serif::utils::serialize::Pack>);
|
||||
static_assert(std::same_as<decltype(serif::utils::serialize::decode<serif::solver::SolverOptions<>>(std::declval<const serif::utils::serialize::Node&>())), serif::utils::serialize::Result<serif::solver::SolverOptions<>>>);
|
||||
@@ -0,0 +1,59 @@
|
||||
#include <concepts>
|
||||
|
||||
#include "serif/dimensions/type_alias.hpp"
|
||||
|
||||
namespace serif::compile_time_checks::dimensions_meanfield
|
||||
{
|
||||
template <typename Left, typename Right>
|
||||
concept Addable = requires(Left left, Right right)
|
||||
{
|
||||
left + right;
|
||||
};
|
||||
|
||||
template <typename Left, typename Right>
|
||||
concept EqualityComparable = requires(Left left, Right right)
|
||||
{
|
||||
{ left == right } -> std::convertible_to<bool>;
|
||||
};
|
||||
|
||||
static_assert(!Addable<dimensions::MassValue, dimensions::LengthValue>);
|
||||
static_assert(!EqualityComparable<dimensions::MassValue, dimensions::LengthValue>);
|
||||
static_assert(dimensions::MassValue{2.0} > dimensions::MassValue{0.5});
|
||||
|
||||
static_assert(dimensions::QuantityValueType<dimensions::DimensionlessValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::MassValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::LengthValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::TimeValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::AreaValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::VolumeValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::DensityValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::NumberDensityValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::PressureValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::TemperatureValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::EntropyValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::SpecificEntropyValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::ChemicalPotentialValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::EnergyValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::InternalEnergyValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::SpecificEnergyValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::SpecificInternalEnergyValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::SpecificEnthalpyValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::EnergyDensityValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::GravitationalPotentialValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::VelocityValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::AccelerationValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::FrequencyValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::AngularVelocityValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::MomentumValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::AngularMomentumValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::MomentOfInertiaValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::ForceValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::TorqueValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::PowerValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::LuminosityValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::MassFlowRateValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::OpacityValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::DynamicViscosityValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::KinematicViscosityValue>);
|
||||
static_assert(dimensions::QuantityValueType<dimensions::MagneticFluxDensityValue>);
|
||||
} // namespace serif::compile_time_checks::dimensions_meanfield
|
||||
22
src/compile_time_checks/serif/dimensions/quantities.cpp
Normal file
22
src/compile_time_checks/serif/dimensions/quantities.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "serif/dimensions/quantities.hpp"
|
||||
#include "serif/dimensions/type_alias.hpp"
|
||||
#include "serif/dimensions/partial.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
using namespace serif::dimensions;
|
||||
|
||||
static_assert(PhysicalQuantityType<Mass>);
|
||||
static_assert(ThermodynamicQuantityType<MassDensity>);
|
||||
static_assert(!ThermodynamicQuantityType<Mass>);
|
||||
static_assert(QuantityValueType<MassValue>);
|
||||
static_assert(std::same_as<QuantityOfT<MassValue>, Mass>);
|
||||
static_assert(std::constructible_from<MassValue, double>);
|
||||
static_assert(!std::convertible_to<double, MassValue>);
|
||||
static_assert(!std::convertible_to<MassValue, double>);
|
||||
static_assert(!std::constructible_from<MassValue, LengthValue>);
|
||||
static_assert((MassValue{2.0} + MassValue{0.5}).value() == 2.5);
|
||||
static_assert((MassValue{2.0} - MassValue{0.5}).value() == 1.5);
|
||||
static_assert((3.0 * MassValue{2.0}).value() == 6.0);
|
||||
static_assert((MassValue{2.0} / 4.0).value() == 0.5);
|
||||
static_assert(!std::convertible_to<double, PartialDerivative<Pressure, MassDensity>>);
|
||||
@@ -19,10 +19,19 @@ namespace {
|
||||
|
||||
using DensitySpace = blocks::FunctionalSpaceOf<Density>;
|
||||
|
||||
static_assert(DensitySpace::functional_space.family == functional_space::Family::l2, "Density should be in an L2 space");
|
||||
static_assert(DensitySpace::functional_space.family == functional_space::Family::h1, "Density should be in an H1 space");
|
||||
|
||||
static_assert(DensitySpace::order == 2, "Density should be in a second order space");
|
||||
static_assert(DensitySpace::order == 3, "Density should be in a third order space");
|
||||
static_assert(DensitySpace::rank::value == 0, "Density should be a scalar field");
|
||||
static_assert(std::same_as<blocks::SupportOf<Density>, domain::StellarDomains>, "Density should be supported on stellar domains");
|
||||
static_assert(std::same_as<blocks::QuantityOf<Density>, serif::dimensions::MassDensity>, "Density should have a quantity of type Density");
|
||||
static_assert(blocks::SpatialTermTraits<Density>::family == functional_space::Family::h1, "Density should be in an H1 space");
|
||||
static_assert(blocks::SpatialTermTraits<Density>::order == 3, "Density should be in a third order space");
|
||||
static_assert(blocks::SpatialTermTraits<Density>::rank == 0, "Density should be a scalar field");
|
||||
static_assert(blocks::SpatialTermTraits<Displacement>::family == functional_space::Family::h1, "Displacement should be in an H1 space");
|
||||
static_assert(blocks::SpatialTermTraits<Displacement>::order == 3, "Displacement should be in a third order space");
|
||||
static_assert(blocks::SpatialTermTraits<Displacement>::rank == 1, "Displacement should be a vector field");
|
||||
static_assert(blocks::SpatialTermTraits<blocks::gravity::potential>::family == functional_space::Family::h1, "Gravity potential should be in an H1 space");
|
||||
static_assert(blocks::SpatialTermTraits<blocks::gravity::potential>::order == 4, "Gravity potential should be in a fourth order space");
|
||||
static_assert(blocks::SpatialTermTraits<blocks::gravity::potential>::rank == 0, "Gravity potential should be a scalar field");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
// MeanField field/base/registry/block contracts expressed through SERiF's production descriptors.
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "serif/discretization/blocks/blocks.hpp"
|
||||
#include "serif/discretization/forms/runtime.hpp"
|
||||
#include "serif/backend/mfem/discretization/quadrature/order.hpp"
|
||||
#include "serif/equations/point.hpp"
|
||||
#include "serif/utils/dependencies/dependency_context.hpp"
|
||||
|
||||
namespace serif::compile_time_checks::fields_meanfield
|
||||
{
|
||||
namespace blocks = discretization::blocks;
|
||||
namespace spaces = discretization::functional_space;
|
||||
namespace forms = discretization::forms;
|
||||
namespace domain = discretization::domain;
|
||||
namespace dependencies = utils::dependencies;
|
||||
|
||||
using Density = blocks::density::mass;
|
||||
using Potential = blocks::gravity::potential;
|
||||
using Displacement = blocks::displacement::vector;
|
||||
using Constant = blocks::barotropic_constant::mass_normalization;
|
||||
using AngularVelocity = blocks::rotation::angular_velocity;
|
||||
|
||||
static_assert(blocks::Field<blocks::density> && blocks::Field<blocks::gravity> && blocks::Field<blocks::displacement> && blocks::Field<blocks::barotropic_constant> && blocks::Field<blocks::rotation>);
|
||||
static_assert(blocks::SpatialTerm<Density> && blocks::SpatialTerm<Potential> && blocks::SpatialTerm<Displacement>);
|
||||
static_assert(blocks::GlobalScalarTerm<Constant> && blocks::GlobalScalarTerm<AngularVelocity>);
|
||||
static_assert(!blocks::SpatialTerm<Constant> && !blocks::HasFunctionalSpace<Constant>);
|
||||
static_assert(!blocks::SpatialTerm<AngularVelocity> && !blocks::HasFunctionalSpace<AngularVelocity>);
|
||||
static_assert(!blocks::Term<int> && !blocks::SpatialTerm<int> && !blocks::GlobalScalarTerm<int>);
|
||||
static_assert(std::same_as<blocks::SupportOf<Density>, domain::StellarDomains>);
|
||||
static_assert(std::same_as<blocks::SupportOf<Potential>, domain::AllDomains>);
|
||||
static_assert(std::same_as<blocks::SupportOf<Displacement>, domain::AllDomains>);
|
||||
static_assert(Constant::value::static_block_size == 1 && Constant::residual::static_block_size == 1);
|
||||
static_assert(AngularVelocity::value::static_block_size == 1 && AngularVelocity::residual::static_block_size == 1);
|
||||
static_assert(Density::value::static_block_size == blocks::DYNAMIC_BLOCK_SIZE);
|
||||
static_assert(std::derived_from<Density::value, blocks::value_block_base> && std::derived_from<Density::residual, blocks::residual_block_base>);
|
||||
static_assert(!std::derived_from<Density::value, blocks::residual_block_base>);
|
||||
static_assert(std::derived_from<blocks::value_block_base, blocks::block> && std::derived_from<blocks::residual_block_base, blocks::block>);
|
||||
static_assert(static_cast<int>(blocks::value_block<2>{}) == 2);
|
||||
static_assert(static_cast<int>(blocks::residual_block<3>{}) == 3);
|
||||
|
||||
using ScalarL2 = spaces::DiscreteFunctionalSpace<spaces::L2, 2, 0>;
|
||||
using VectorH1 = spaces::DiscreteFunctionalSpace<spaces::H1, 3, 1>;
|
||||
using VectorRT = spaces::DiscreteFunctionalSpace<spaces::Hdiv, 2, 1>;
|
||||
using VectorND = spaces::DiscreteFunctionalSpace<spaces::Hcurl, 2, 1>;
|
||||
static_assert(ScalarL2::rank::value == 0 && ScalarL2::order == 2 && ScalarL2::functional_space.family == spaces::Family::l2);
|
||||
static_assert(VectorH1::rank::value == 1 && VectorH1::order == 3 && VectorH1::functional_space.trace == spaces::Trace::value);
|
||||
static_assert(VectorRT::rank::value == 1 && VectorRT::functional_space.mapping == spaces::Mapping::contravariant_piola);
|
||||
static_assert(VectorND::rank::value == 1 && VectorND::functional_space.mapping == spaces::Mapping::covariant_piola);
|
||||
static_assert(blocks::SpatialTermTraits<Density>::order == 3 && blocks::SpatialTermTraits<Potential>::order == 4);
|
||||
static_assert(blocks::SpatialTermTraits<Displacement>::family == spaces::Family::h1 && blocks::SpatialTermTraits<Potential>::family == spaces::Family::h1);
|
||||
|
||||
using GradientOperand = forms::Operand<Density, forms::Operation::Gradient>;
|
||||
using DivergenceOperand = forms::Operand<Displacement, forms::Operation::Divergence>;
|
||||
static_assert(forms::IsOperation<forms::Operation::Value> && forms::IsOperation<forms::Operation::Gradient> && forms::IsOperation<forms::Operation::Divergence> && forms::IsOperation<forms::Operation::Curl> && forms::IsOperation<forms::Operation::NormalTrace>);
|
||||
static_assert(!forms::IsOperation<int> && !forms::IsOperand<int>);
|
||||
static_assert(std::same_as<GradientOperand::term_type, Density> && std::same_as<GradientOperand::operation_type, forms::Operation::Gradient>);
|
||||
static_assert(std::same_as<DivergenceOperand::term_type, Displacement>);
|
||||
static_assert(discretization::quadrature::backend::mfem::resolve_operand_order<forms::Operand<Displacement>>() == 3);
|
||||
static_assert(discretization::quadrature::backend::mfem::resolve_operand_order<DivergenceOperand>() == 2);
|
||||
static_assert(discretization::quadrature::backend::mfem::resolve_operand_order<GradientOperand>() == 2);
|
||||
static_assert(discretization::quadrature::backend::mfem::resolve_operand_order<forms::Operand<Constant>>() == 0);
|
||||
static_assert(forms::form_descriptor<forms::mass<Potential, Potential>> != forms::form_descriptor<forms::mass<Density, Density>>);
|
||||
static_assert(forms::form_descriptor<forms::source<Density, Potential>>.trial == blocks::term_descriptor<Density>);
|
||||
static_assert(forms::form_descriptor<forms::source<Density, Potential>>.test == blocks::term_descriptor<Potential>);
|
||||
|
||||
using Keys = dependencies::Bindings<dependencies::Binding<int, int>, dependencies::Binding<double, double>, dependencies::Binding<char, char>>;
|
||||
static_assert(dependencies::SatisfiesKeyList<Keys, dependencies::KeyList<int, double, char>>);
|
||||
static_assert(!dependencies::SatisfiesKeyList<Keys, dependencies::KeyList<float>>);
|
||||
static_assert(!dependencies::SatisfiesKeyList<dependencies::Bindings<>, dependencies::KeyList<int>>);
|
||||
static_assert(std::same_as<dependencies::bound_type_t<Keys, double>, double>);
|
||||
|
||||
// Ordered physical operands retain positional identity without reintroducing a general typelist indexing API.
|
||||
using DensityOperand = forms::Operand<Density>;
|
||||
using DisplacementOperand = forms::Operand<Displacement>;
|
||||
using DivergenceSample = forms::Operand<Displacement, forms::Operation::Divergence>;
|
||||
using PotentialOperand = forms::Operand<Potential>;
|
||||
using OrderedSamples = equations::OperandValues<utils::types::TypeList<DensityOperand, DisplacementOperand, DivergenceSample, PotentialOperand>>;
|
||||
static_assert(OrderedSamples::count == 4);
|
||||
static_assert(OrderedSamples::index<DensityOperand>() == 0);
|
||||
static_assert(OrderedSamples::index<DisplacementOperand>() == 1);
|
||||
static_assert(OrderedSamples::index<DivergenceSample>() == 2);
|
||||
static_assert(OrderedSamples::index<PotentialOperand>() == 3);
|
||||
static_assert(OrderedSamples::index<Density>() == 0 && OrderedSamples::index<Potential>() == 3);
|
||||
static_assert(std::same_as<decltype(std::declval<OrderedSamples&>().get<DensityOperand>()), double&>);
|
||||
static_assert(std::same_as<decltype(std::declval<const OrderedSamples&>().get<DisplacementOperand>()), const forms::VectorValue&>);
|
||||
static_assert(std::same_as<std::tuple_element_t<1, OrderedSamples::Storage>, forms::VectorValue>);
|
||||
static_assert(std::same_as<std::tuple_element_t<2, OrderedSamples::Storage>, double>);
|
||||
static_assert(std::same_as<std::tuple_element_t<3, OrderedSamples::Storage>, double>);
|
||||
|
||||
} // namespace serif::compile_time_checks::fields_meanfield
|
||||
@@ -12,15 +12,15 @@ namespace {
|
||||
// Note we define custom versions of forms that likely exist in the codebase. We are not here testing that the
|
||||
// forms have a particular structure; rather, we are testing that given some known structure
|
||||
// the form system behaves as we expect it to.
|
||||
using HypotheticalTestingGravityMass = mass<gravity::gradient, gravity::gradient>;
|
||||
using HypotheticalTestingGravityDivergence = divergence<gravity::gradient, gravity::potential>;
|
||||
using HypotheticalTestingDisplacementMass = mass<displacement::vector, displacement::vector>;
|
||||
using HypotheticalTestingDisplacementDivergence = divergence<displacement::vector, gravity::potential>;
|
||||
|
||||
static_assert(Form<HypotheticalTestingGravityMass>, "GravityMass should satisfy the Form concept. A failure here indicates that the mass form is not properly defined for the gravity field. Please check out the forms submodule");
|
||||
static_assert(Form<HypotheticalTestingGravityDivergence>, "GravityDivergence should satisfy the Form concept. A failure here indicates that the divergence form is not properly defined for the gravity field. Please check out the forms submodule");
|
||||
static_assert(Form<HypotheticalTestingDisplacementMass>, "DisplacementMass should satisfy the Form concept. A failure here indicates that the mass form is not properly defined for a vector field. Please check out the forms submodule");
|
||||
static_assert(Form<HypotheticalTestingDisplacementDivergence>, "DisplacementDivergence should satisfy the Form concept. A failure here indicates that the divergence form is not properly defined for a vector field. Please check out the forms submodule");
|
||||
|
||||
static_assert(std::same_as<HypotheticalTestingGravityMass::test_type, gravity::gradient>, "GravityMass::test_type should be gravity::gradient. A failure here indicates that the mass form is not properly defined for the gravity field. Please check out the forms submodule");
|
||||
static_assert(std::same_as<HypotheticalTestingGravityMass::trial_type, gravity::gradient>, "GravityMass::trial_type should be gravity::gradient. A failure here indicates that the mass form is not properly defined for the gravity field. Please check out the forms submodule");
|
||||
static_assert(std::same_as<HypotheticalTestingDisplacementMass::test_type, displacement::vector>, "DisplacementMass::test_type should be displacement::vector. A failure here indicates that the mass form is not properly defined for a vector field. Please check out the forms submodule");
|
||||
static_assert(std::same_as<HypotheticalTestingDisplacementMass::trial_type, displacement::vector>, "DisplacementMass::trial_type should be displacement::vector. A failure here indicates that the mass form is not properly defined for a vector field. Please check out the forms submodule");
|
||||
|
||||
static_assert(std::same_as<HypotheticalTestingGravityDivergence::trial_type, gravity::gradient>, "GravityDivergence::trial_type should be gravity::gradient. A failure here indicates that the divergence form is not properly defined for the gravity field. Please check out the forms submodule");
|
||||
static_assert(std::same_as<HypotheticalTestingGravityDivergence::test_type, gravity::potential>, "GravityDivergence::test_type should be gravity::potential. A failure here indicates that the divergence form is not properly defined for the gravity field. Please check out the forms submodule");
|
||||
static_assert(std::same_as<HypotheticalTestingDisplacementDivergence::trial_type, displacement::vector>, "DisplacementDivergence::trial_type should be displacement::vector. A failure here indicates that the divergence form is not properly defined for a vector field. Please check out the forms submodule");
|
||||
static_assert(std::same_as<HypotheticalTestingDisplacementDivergence::test_type, gravity::potential>, "DisplacementDivergence::test_type should be gravity::potential. A failure here indicates that the divergence form is not properly defined for a vector field. Please check out the forms submodule");
|
||||
}
|
||||
@@ -7,17 +7,17 @@ namespace {
|
||||
using namespace serif::discretization;
|
||||
|
||||
using DensityValue = forms::Operand<blocks::density::mass>;
|
||||
using GravityValue = forms::Operand<blocks::gravity::gradient>;
|
||||
using GravityDivergence = forms::Operand<blocks::gravity::gradient, forms::Operation::Divergence>;
|
||||
using GravityNormalTrace = forms::Operand<blocks::gravity::gradient, forms::Operation::NormalTrace>;
|
||||
using DisplacementValue = forms::Operand<blocks::displacement::vector>;
|
||||
using DisplacementDivergence = forms::Operand<blocks::displacement::vector, forms::Operation::Divergence>;
|
||||
using DisplacementNormalTrace = forms::Operand<blocks::displacement::vector, forms::Operation::NormalTrace>;
|
||||
using DisplacementGradient = forms::Operand<blocks::displacement::vector, forms::Operation::Gradient>;
|
||||
|
||||
static_assert(forms::IsOperand<DensityValue>, "DensityValue should be a valid operand");
|
||||
static_assert(forms::IsOperand<GravityValue>, "GravityValue should be a valid operand");
|
||||
static_assert(forms::IsOperand<GravityDivergence>, "GravityDivergence should be a valid operand");
|
||||
static_assert(forms::IsOperand<GravityNormalTrace>, "GravityNormalTrace should be a valid operand");
|
||||
static_assert(forms::IsOperand<DisplacementValue>, "DisplacementValue should be a valid operand");
|
||||
static_assert(forms::IsOperand<DisplacementDivergence>, "DisplacementDivergence should be a valid operand");
|
||||
static_assert(forms::IsOperand<DisplacementNormalTrace>, "DisplacementNormalTrace should be a valid operand");
|
||||
static_assert(forms::IsOperand<DisplacementGradient>, "DisplacementGradient should be a valid operand");
|
||||
|
||||
static_assert(std::same_as<GravityDivergence::term_type, blocks::gravity::gradient>, "GravityDivergence should have the correct term type");
|
||||
static_assert(std::same_as<GravityDivergence::operation_type, forms::Operation::Divergence>, "GravityDivergence should have the correct operation type");
|
||||
static_assert(std::same_as<DisplacementDivergence::term_type, blocks::displacement::vector>, "DisplacementDivergence should have the correct term type");
|
||||
static_assert(std::same_as<DisplacementDivergence::operation_type, forms::Operation::Divergence>, "DisplacementDivergence should have the correct operation type");
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#include "serif/discretization/quadrature/backend/mfem/mfem_resolver.hpp"
|
||||
#include "serif/discretization/quadrature/backend/mfem/mfem_static_order.hpp"
|
||||
|
||||
#include "serif/utils/error/errors.hpp"
|
||||
|
||||
namespace {
|
||||
using namespace serif::discretization;
|
||||
using namespace serif::utils::errors;
|
||||
using namespace quadrature::backend::mfem;
|
||||
|
||||
using GravityMassForm = forms::mass<blocks::gravity::gradient, blocks::gravity::gradient>;
|
||||
using GravityDivergenceForm = forms::divergence<blocks::gravity::gradient, blocks::gravity::potential>;
|
||||
using GravityBoundaryForm = forms::boundary_flux<blocks::gravity::gradient, blocks::gravity::gradient>;
|
||||
|
||||
using DensityProjectionForm = forms::projection<blocks::density::mass, blocks::density::mass>;
|
||||
|
||||
|
||||
// Note when using serif_assert (since it is a consteval function) it must be within
|
||||
// a function (C++ does not allow arbitrary calls to consteval functions outside of a function context). This is why we have this function here. It is not called, but it is used to force the compiler to evaluate the consteval function and thus trigger the compile time checks.
|
||||
consteval void compile_checks() {
|
||||
serif::utils::errors::serif_assert<SERiFErrorCode::FAILED_TYPE_INVARIANT, "Gravity Mass Form should have a static, compile time, order of 6", mfem_form_static_order<GravityMassForm> == 6>();
|
||||
serif::utils::errors::serif_assert<SERiFErrorCode::FAILED_TYPE_INVARIANT, "Gravity Divergence Form should have a static, compile time, order of 4", mfem_form_static_order<GravityDivergenceForm> == 4>();
|
||||
serif::utils::errors::serif_assert<SERiFErrorCode::FAILED_TYPE_INVARIANT, "Gravity Boundary Form should have a static, compile time, order of 4", mfem_form_static_order<GravityBoundaryForm> == 4>();
|
||||
serif::utils::errors::serif_assert<SERiFErrorCode::FAILED_TYPE_INVARIANT, "Density Projection Form should have a static, compile time, order of 4", mfem_form_static_order<DensityProjectionForm> == 4>();
|
||||
}
|
||||
}
|
||||
41
src/compile_time_checks/serif/eos/consumers_meanfield.cpp
Normal file
41
src/compile_time_checks/serif/eos/consumers_meanfield.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "serif/eos/models/concepts.hpp"
|
||||
#include "serif/eos/models/polytropic.hpp"
|
||||
|
||||
namespace serif::compile_time_checks::eos_meanfield::consumer_contracts
|
||||
{
|
||||
/** @brief Describe exactly one EOS relation; unrelated consumer capabilities remain absent. */
|
||||
template <typename Output, typename Input, bool HasDerivative>
|
||||
struct SingleRelationEOS
|
||||
{
|
||||
using Relation = eos::relations::Relation<Output, Input>;
|
||||
using Relations = eos::relations::RelationCatalog<Relation>;
|
||||
|
||||
/** @brief Preserve strong physical input/output types in the minimal consumer probe. */
|
||||
[[nodiscard]] constexpr dimensions::QuantityValue<Output> evaluate(Relation, dimensions::QuantityValue<Input> value) const
|
||||
{
|
||||
return dimensions::QuantityValue<Output>{value.value()};
|
||||
}
|
||||
|
||||
/** @brief Make derivative availability independent of value evaluation. */
|
||||
[[nodiscard]] constexpr dimensions::PartialDerivative<Output, Input> partial_derivative(Relation, dimensions::WithRespectTo<Input>, dimensions::QuantityValue<Input>) const requires HasDerivative
|
||||
{
|
||||
return dimensions::PartialDerivative<Output, Input>{1.0};
|
||||
}
|
||||
};
|
||||
|
||||
using DensityClosure = SingleRelationEOS<dimensions::MassDensity, dimensions::SpecificEnthalpy, true>;
|
||||
using DensityWithoutDerivative = SingleRelationEOS<dimensions::MassDensity, dimensions::SpecificEnthalpy, false>;
|
||||
using EnthalpyPressure = SingleRelationEOS<dimensions::Pressure, dimensions::SpecificEnthalpy, true>;
|
||||
using DensitySeed = SingleRelationEOS<dimensions::SpecificEnthalpy, dimensions::MassDensity, false>;
|
||||
using GeneralEOS = SingleRelationEOS<dimensions::SpecificEnthalpy, dimensions::Pressure, false>;
|
||||
|
||||
using DensityFromEnthalpy = eos::relations::DensityFromSpecificEnthalpy;
|
||||
static_assert(eos::models::EOSSupportingPartialDerivative<DensityClosure, DensityFromEnthalpy, dimensions::SpecificEnthalpy>);
|
||||
static_assert(eos::models::EOSModel<DensityWithoutDerivative> && eos::models::EOSSupportingRelation<DensityWithoutDerivative, DensityFromEnthalpy> &&
|
||||
!eos::models::EOSSupportingPartialDerivative<DensityWithoutDerivative, DensityFromEnthalpy, dimensions::SpecificEnthalpy>);
|
||||
static_assert(!eos::models::EOSSupportingRelation<EnthalpyPressure, DensityFromEnthalpy>);
|
||||
static_assert(eos::models::StructureSeedEOS<eos::models::Polytrope> && eos::models::StructureSeedEOS<DensitySeed>);
|
||||
static_assert(!eos::models::StructureSeedEOS<DensityClosure>);
|
||||
static_assert(eos::models::EOSModel<GeneralEOS>);
|
||||
static_assert(!eos::models::StructureSeedEOS<GeneralEOS> && !eos::models::EOSSupportingRelation<GeneralEOS, DensityFromEnthalpy>);
|
||||
} // namespace serif::compile_time_checks::eos_meanfield::consumer_contracts
|
||||
21
src/compile_time_checks/serif/eos/evaluation.cpp
Normal file
21
src/compile_time_checks/serif/eos/evaluation.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "serif/eos/evaluation.hpp"
|
||||
#include "serif/eos/models/polytropic.hpp"
|
||||
#include "serif/model/eos_reference.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
using namespace serif::eos;
|
||||
using namespace serif::dimensions;
|
||||
|
||||
static_assert(std::same_as<decltype(evaluate<Pressure>(std::declval<const models::Polytrope&>(), DensityValue{1.0})), PressureValue>);
|
||||
static_assert(std::same_as<decltype(partial_derivative<MassDensity, SpecificEnthalpy>(std::declval<const models::Polytrope&>(), SpecificEnthalpyValue{1.0})), PartialDerivative<MassDensity, SpecificEnthalpy>>);
|
||||
static_assert(std::same_as<decltype(partial_derivative<Pressure, MassDensity>(std::declval<const models::Polytrope&>(), DensityValue{1.0})), PartialDerivative<Pressure, MassDensity>>);
|
||||
static_assert(!noexcept(evaluate<Pressure>(std::declval<const models::Polytrope&>(), DensityValue{1.0})), "Invalid thermodynamic inputs must be allowed to throw");
|
||||
static_assert(std::same_as<decltype(evaluate<MassDensity>(std::declval<const models::Polytrope&>(), PressureValue{1.0})), DensityValue>);
|
||||
static_assert(std::same_as<decltype(second_partial_derivative<Pressure, MassDensity, MassDensity>(
|
||||
std::declval<const models::Polytrope&>(), DensityValue{1.0})), SecondPartialDerivative<Pressure, MassDensity, MassDensity>>);
|
||||
static_assert(models::EOSSupportingSecondPartialDerivative<serif::model::EosReference<models::Polytrope>, relations::PressureFromDensity, MassDensity, MassDensity>);
|
||||
static_assert(!models::EOSSupportingSecondPartialDerivative<models::Polytrope, relations::PressureFromDensity, SpecificEnthalpy, MassDensity>);
|
||||
static_assert(!models::EOSSupportingSecondPartialDerivative<models::Polytrope, relations::PressureFromSpecificEnthalpy, SpecificEnthalpy, SpecificEnthalpy>);
|
||||
static_assert(!models::EOSSupportingSecondPartialDerivative<int, relations::PressureFromDensity, MassDensity, MassDensity>);
|
||||
23
src/compile_time_checks/serif/eos/models/traits.cpp
Normal file
23
src/compile_time_checks/serif/eos/models/traits.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "serif/eos/models/traits.hpp"
|
||||
#include "serif/eos/models/concepts.hpp"
|
||||
#include "serif/eos/models/polytropic.hpp"
|
||||
|
||||
using namespace serif::eos;
|
||||
using namespace serif::dimensions;
|
||||
|
||||
static_assert(models::EOSModel<models::Polytrope>, "Polytrope should satisfy the EOS model contract");
|
||||
static_assert(models::StructureSeedEOS<models::Polytrope>, "Polytrope should support structure seeds");
|
||||
static_assert(models::EOSSupportingPartialDerivative<models::Polytrope, relations::DensityFromSpecificEnthalpy, SpecificEnthalpy>, "Polytrope should support density from enthalpy and its derivative");
|
||||
|
||||
static_assert(models::EOSSupportingRelation<models::Polytrope, relations::PressureFromDensity>, "Polytrope should support pressure from density");
|
||||
static_assert(models::EOSSupportingRelation<models::Polytrope, relations::PressureFromSpecificEnthalpy>, "Polytrope should support pressure from specific enthalpy");
|
||||
static_assert(models::EOSSupportingRelation<models::Polytrope, relations::SpecificEnthalpyFromDensity>, "Polytrope should support specific enthalpy from density");
|
||||
static_assert(models::EOSSupportingRelation<models::Polytrope, relations::SpecificEnthalpyFromPressure>, "Polytrope should support specific enthalpy from pressure");
|
||||
static_assert(models::EOSSupportingRelation<models::Polytrope, relations::DensityFromSpecificEnthalpy>, "Polytrope should support density from specific enthalpy");
|
||||
|
||||
static_assert(models::EOSSupportingPartialDerivative<models::Polytrope, relations::PressureFromDensity, MassDensity>, "Polytrope should support partial derivative of pressure with respect to density");
|
||||
static_assert(models::EOSSupportingPartialDerivative<models::Polytrope, relations::PressureFromSpecificEnthalpy, SpecificEnthalpy>, "Polytrope should support partial derivative of pressure with respect to specific enthalpy");
|
||||
static_assert(models::EOSSupportingPartialDerivative<models::Polytrope, relations::DensityFromSpecificEnthalpy, SpecificEnthalpy>, "Polytrope should support partial derivative of density with respect to specific enthalpy");
|
||||
static_assert(!models::EOSSupportingPartialDerivative<models::Polytrope, relations::SpecificEnthalpyFromDensity, MassDensity>, "Polytrope should not support partial derivative of specific enthalpy with respect to density");
|
||||
static_assert(!models::EOSSupportingPartialDerivative<models::Polytrope, relations::PressureFromDensity, Temperature>, "Polytrope should not support partial derivative of pressure with respect to temperature");
|
||||
static_assert(!models::EOSModel<double>, "double should not satisfy the EOS model contract");
|
||||
136
src/compile_time_checks/serif/geometry/deformation.cpp
Normal file
136
src/compile_time_checks/serif/geometry/deformation.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include <concepts>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "serif/geometry/deformation/actions.hpp"
|
||||
#include "serif/geometry/deformation/composition.hpp"
|
||||
#include "serif/geometry/deformation/linearization.hpp"
|
||||
#include "serif/geometry/deformation/radial_extension.hpp"
|
||||
#include "serif/geometry/deformation/surface_embedding.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace deformation = serif::geometry::deformation;
|
||||
namespace spaces = deformation::spaces;
|
||||
|
||||
/** @brief Check nonlinear capabilities without linking or running numerical implementations. */
|
||||
struct NonlinearSurface
|
||||
{
|
||||
using InputSpace = spaces::ShapeParameters;
|
||||
using OutputSpace = spaces::SurfaceDisplacement;
|
||||
|
||||
struct Workspace
|
||||
{
|
||||
};
|
||||
|
||||
/// Declare the shape coordinate count needed by action validation.
|
||||
[[nodiscard]] std::size_t input_size() const;
|
||||
/// Declare the output trace count needed by composition validation.
|
||||
[[nodiscard]] std::size_t output_size() const;
|
||||
/// Require explicit caller-owned scratch construction.
|
||||
[[nodiscard]] Workspace make_workspace() const;
|
||||
/// Publish the primal map capability.
|
||||
[[nodiscard]] deformation::DeformationStatus build_displacement(std::span<const double>, std::span<double>, Workspace&) const;
|
||||
/// Publish the exact tangent capability.
|
||||
[[nodiscard]] deformation::DeformationStatus apply_jacobian(std::span<const double>, std::span<const double>, std::span<double>, Workspace&) const;
|
||||
/// Publish the work-conjugate transpose capability.
|
||||
[[nodiscard]] deformation::DeformationStatus apply_jacobian_transpose(std::span<const double>, std::span<const double>, std::span<double>, Workspace&) const;
|
||||
/// Require the geometric second-derivative term instead of assuming that it vanishes.
|
||||
[[nodiscard]] deformation::DeformationStatus apply_pullback_derivative(std::span<const double>, std::span<const double>, std::span<const double>, std::span<double>, Workspace&) const;
|
||||
};
|
||||
|
||||
/** @brief Linear declarations may retain the full differentiable interface. */
|
||||
struct DeclaredLinearSurface : NonlinearSurface
|
||||
{
|
||||
static constexpr bool is_linear = true;
|
||||
};
|
||||
|
||||
/** @brief Forward actions cannot stand in for the required work-conjugate transpose. */
|
||||
struct IncompleteLinearSurface
|
||||
{
|
||||
using InputSpace = spaces::ShapeParameters;
|
||||
using OutputSpace = spaces::SurfaceDisplacement;
|
||||
using Workspace = deformation::EmptyDeformationWorkspace;
|
||||
static constexpr bool is_linear = true;
|
||||
|
||||
/// Declare the native shape parameter count.
|
||||
[[nodiscard]] std::size_t input_size() const;
|
||||
/// Declare the surface displacement count.
|
||||
[[nodiscard]] std::size_t output_size() const;
|
||||
/// Deliberately omit the transpose contract.
|
||||
[[nodiscard]] deformation::DeformationStatus apply(std::span<const double>, std::span<double>, Workspace&) const;
|
||||
};
|
||||
|
||||
struct MissingPullback : NonlinearSurface
|
||||
{
|
||||
/// Hide the inherited capability to model an incomplete nonlinear interface.
|
||||
deformation::DeformationStatus apply_pullback_derivative(std::span<const double>, std::span<const double>, std::span<const double>, std::span<double>, Workspace&) const = delete;
|
||||
};
|
||||
|
||||
struct MissingTranspose : NonlinearSurface
|
||||
{
|
||||
/// A forward tangent alone cannot establish virtual-work preservation.
|
||||
deformation::DeformationStatus apply_jacobian_transpose(std::span<const double>, std::span<const double>, std::span<double>, Workspace&) const = delete;
|
||||
};
|
||||
|
||||
struct WrongTrace : NonlinearSurface
|
||||
{
|
||||
using OutputSpace = spaces::VolumeDisplacement;
|
||||
};
|
||||
|
||||
struct MissingSemanticSpace : NonlinearSurface
|
||||
{
|
||||
private:
|
||||
using OutputSpace = spaces::SurfaceDisplacement;
|
||||
};
|
||||
|
||||
/** @brief Equal vector lengths cannot substitute for matching physical meanings at a composition boundary. */
|
||||
template <typename Surface, typename Extension>
|
||||
concept CanCompose = requires(std::shared_ptr<const Surface> surface, std::shared_ptr<const Extension> extension)
|
||||
{
|
||||
deformation::compose_deformation(surface, extension);
|
||||
};
|
||||
|
||||
static_assert(deformation::LinearDeformationMap<deformation::SurfaceEmbedding>);
|
||||
static_assert(deformation::LinearDeformationMap<deformation::RadialExtension>);
|
||||
static_assert(!std::default_initializable<deformation::SurfaceEmbedding>);
|
||||
static_assert(!std::default_initializable<deformation::RadialExtension>);
|
||||
static_assert(deformation::DeformationMap<NonlinearSurface>);
|
||||
static_assert(deformation::DifferentiableDeformationMap<NonlinearSurface>);
|
||||
static_assert(!deformation::LinearDeformationMap<NonlinearSurface>);
|
||||
static_assert(!deformation::is_linear_deformation<NonlinearSurface>);
|
||||
static_assert(!deformation::DeformationMap<MissingPullback>);
|
||||
static_assert(!deformation::DeformationMap<MissingTranspose>);
|
||||
static_assert(!deformation::DeformationMap<MissingSemanticSpace>);
|
||||
static_assert(!deformation::DeformationMap<int>);
|
||||
static_assert(!deformation::DeformationMap<IncompleteLinearSurface>);
|
||||
static_assert(!std::default_initializable<deformation::RadialInterpolation>);
|
||||
static_assert(!std::is_aggregate_v<deformation::RadialInterpolation>);
|
||||
static_assert(!std::constructible_from<deformation::RadialInterpolation, deformation::RadialInterpolationData>);
|
||||
static_assert(std::same_as<decltype(std::declval<deformation::RadialInterpolation&>().data()), const deformation::RadialInterpolationData&>);
|
||||
static_assert(CanCompose<deformation::SurfaceEmbedding, deformation::RadialExtension>);
|
||||
static_assert(CanCompose<NonlinearSurface, deformation::RadialExtension>);
|
||||
static_assert(!CanCompose<WrongTrace, deformation::RadialExtension>);
|
||||
static_assert(!CanCompose<deformation::RadialExtension, deformation::SurfaceEmbedding>);
|
||||
|
||||
using LinearDomain = deformation::ComposedDeformation<deformation::SurfaceEmbedding, deformation::RadialExtension>;
|
||||
using NonlinearDomain = deformation::ComposedDeformation<NonlinearSurface, deformation::RadialExtension>;
|
||||
using DeclaredLinearDomain = deformation::ComposedDeformation<DeclaredLinearSurface, deformation::RadialExtension>;
|
||||
static_assert(deformation::LinearDeformationMap<LinearDomain>);
|
||||
static_assert(!deformation::DifferentiableDeformationMap<LinearDomain>);
|
||||
static_assert(std::same_as<decltype(LinearDomain::Workspace::curvature), deformation::EmptyDeformationWorkspace>);
|
||||
static_assert(deformation::DeformationMap<DeclaredLinearDomain>);
|
||||
static_assert(deformation::is_linear_deformation<DeclaredLinearDomain>);
|
||||
static_assert(!deformation::LinearDeformationMap<DeclaredLinearDomain>);
|
||||
static_assert(!std::default_initializable<LinearDomain>);
|
||||
static_assert(deformation::DeformationMap<NonlinearDomain>);
|
||||
static_assert(deformation::is_linear_deformation<LinearDomain>);
|
||||
static_assert(!deformation::is_linear_deformation<NonlinearDomain>);
|
||||
static_assert(std::same_as<LinearDomain::InputSpace, spaces::ShapeParameters>);
|
||||
static_assert(std::same_as<LinearDomain::OutputSpace, spaces::VolumeDisplacement>);
|
||||
static_assert(std::same_as<deformation::DeformationWorkspace<deformation::SurfaceEmbedding>, deformation::EmptyDeformationWorkspace>);
|
||||
static_assert(std::same_as<deformation::DeformationWorkspace<NonlinearSurface>, NonlinearSurface::Workspace>);
|
||||
static_assert(std::same_as<deformation::DeformationWorkspace<LinearDomain>, LinearDomain::Workspace>);
|
||||
} // namespace
|
||||
@@ -0,0 +1,90 @@
|
||||
#include <array>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "serif/geometry/deformation/actions.hpp"
|
||||
#include "serif/backend/mfem/geometry/deformation/inputs.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace deformation = serif::geometry::deformation;
|
||||
namespace spaces = deformation::spaces;
|
||||
namespace backend = deformation::backend::mfem;
|
||||
namespace domain = serif::discretization::domain;
|
||||
using Schema = domain::schema::CoreEnvelopeVacuumDomainSchema;
|
||||
using AlternateBoundarySchema = domain::schema::DomainSchema<typename Schema::domain_ids_type, domain::ids::lists::BoundaryIDList<domain::ids::BoundaryID<domain::StellarSurfaceBoundary, 2>, domain::ids::BoundaryID<domain::InfinitySurfaceBoundary, 1>>, domain::relation::lists::RelationList<>>;
|
||||
using MissingSurfaceSchema = domain::schema::DomainSchema<typename Schema::domain_ids_type, domain::ids::lists::BoundaryIDList<domain::ids::BoundaryID<domain::InfinitySurfaceBoundary, 2>>, domain::relation::lists::RelationList<>>;
|
||||
|
||||
/** @brief Missing physical boundary names must fail the public discretization factory's constraints. */
|
||||
template <typename Candidate>
|
||||
concept CanResolveDeformation = requires(const mfem::FiniteElementSpace& scalar, const mfem::FiniteElementSpace& vector, mfem::Mesh& logical)
|
||||
{
|
||||
backend::deformation_discretization<Candidate>(scalar, vector, logical);
|
||||
};
|
||||
|
||||
static_assert(CanResolveDeformation<Schema>);
|
||||
static_assert(CanResolveDeformation<AlternateBoundarySchema>);
|
||||
static_assert(!CanResolveDeformation<MissingSurfaceSchema>);
|
||||
|
||||
|
||||
/** @brief Input views may be temporary, but their owning containers may not be. */
|
||||
template <typename Coefficients>
|
||||
concept CanBindShape = requires(const deformation::DomainDeformation<deformation::SphericalHarmonicSurface>& model,
|
||||
const backend::ReferenceDiscretization& reference, Coefficients&& coefficients) {
|
||||
backend::make_deformation_inputs(model, reference, std::forward<Coefficients>(coefficients));
|
||||
};
|
||||
|
||||
static_assert(CanBindShape<std::span<const double>>);
|
||||
static_assert(CanBindShape<std::span<double>>);
|
||||
static_assert(CanBindShape<std::span<const double, 4>>);
|
||||
static_assert(CanBindShape<std::vector<double>&>);
|
||||
static_assert(CanBindShape<const std::vector<double>&>);
|
||||
static_assert(CanBindShape<std::array<double, 4>&>);
|
||||
static_assert(!CanBindShape<std::vector<double>>);
|
||||
static_assert(!CanBindShape<const std::vector<double>>);
|
||||
static_assert(!CanBindShape<std::array<double, 4>>);
|
||||
static_assert(!CanBindShape<const std::array<double, 4>>);
|
||||
|
||||
/** @brief Remove one capability at a time without providing any numerical implementation. */
|
||||
template <typename Input, typename Output, bool Build = true, bool Jacobian = true, bool Transpose = true, bool Curvature = true>
|
||||
struct MapCapabilities
|
||||
{
|
||||
using InputSpace = Input;
|
||||
using OutputSpace = Output;
|
||||
using Workspace = deformation::EmptyDeformationWorkspace;
|
||||
|
||||
/** @brief Advertise the native physical input count. */
|
||||
[[nodiscard]] std::size_t input_size() const;
|
||||
/** @brief Advertise the native physical output count. */
|
||||
[[nodiscard]] std::size_t output_size() const;
|
||||
/** @brief Require an explicit scratch construction contract even for an empty workspace. */
|
||||
[[nodiscard]] Workspace make_workspace() const;
|
||||
/** @brief Conditionally advertise the forward deformation. */
|
||||
[[nodiscard]] deformation::DeformationStatus build_displacement(std::span<const double>, std::span<double>, Workspace&) const requires Build;
|
||||
/** @brief Conditionally advertise its tangent. */
|
||||
[[nodiscard]] deformation::DeformationStatus apply_jacobian(std::span<const double>, std::span<const double>, std::span<double>, Workspace&) const requires Jacobian;
|
||||
/** @brief Conditionally advertise the work-conjugate transpose. */
|
||||
[[nodiscard]] deformation::DeformationStatus apply_jacobian_transpose(std::span<const double>, std::span<const double>, std::span<double>, Workspace&) const requires Transpose;
|
||||
/** @brief Conditionally advertise the geometric part of the Newton force derivative. */
|
||||
[[nodiscard]] deformation::DeformationStatus apply_pullback_derivative(std::span<const double>, std::span<const double>, std::span<const double>, std::span<double>, Workspace&) const requires Curvature;
|
||||
};
|
||||
|
||||
/** @brief Enforce the old complete contract through the common surface, extension and composed-map concept. */
|
||||
template <typename Input, typename Output>
|
||||
consteval bool complete_contract_is_required()
|
||||
{
|
||||
static_assert(deformation::DeformationMap<MapCapabilities<Input, Output>>);
|
||||
static_assert(!deformation::DeformationMap<MapCapabilities<Input, Output, false>>);
|
||||
static_assert(!deformation::DeformationMap<MapCapabilities<Input, Output, true, false>>);
|
||||
static_assert(!deformation::DeformationMap<MapCapabilities<Input, Output, true, true, false>>);
|
||||
static_assert(!deformation::DeformationMap<MapCapabilities<Input, Output, true, true, true, false>>);
|
||||
return true;
|
||||
}
|
||||
static_assert(complete_contract_is_required<spaces::ShapeParameters, spaces::SurfaceDisplacement>());
|
||||
// Interior and vacuum behavior now share one extension map with the same complete derivative contract.
|
||||
static_assert(complete_contract_is_required<spaces::SurfaceDisplacement, spaces::VolumeDisplacement>());
|
||||
static_assert(complete_contract_is_required<spaces::ShapeParameters, spaces::VolumeDisplacement>());
|
||||
} // namespace
|
||||
188
src/compile_time_checks/serif/geometry/surface.cpp
Normal file
188
src/compile_time_checks/serif/geometry/surface.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
#include <type_traits>
|
||||
|
||||
#include "serif/eos/models/polytropic.hpp"
|
||||
#include "serif/geometry/surface/surface.hpp"
|
||||
#include "serif/utils/dependencies/computation_cache.hpp"
|
||||
|
||||
namespace {
|
||||
namespace surface = serif::geometry::surface;
|
||||
namespace dependencies = serif::utils::dependencies;
|
||||
namespace quantities = serif::dimensions;
|
||||
namespace eos = serif::eos;
|
||||
namespace blocks = serif::discretization::blocks;
|
||||
|
||||
/** @brief A scalar H1 enthalpy-like carrier; surface selection depends only on its declared quantity and space. */
|
||||
struct Enthalpy : blocks::term<blocks::density> {
|
||||
using FunctionalSpace = serif::discretization::functional_space::DiscreteFunctionalSpace<serif::discretization::functional_space::H1, 3, 0>;
|
||||
using Support = serif::discretization::domain::StellarDomains;
|
||||
using Quantity = quantities::SpecificEnthalpy;
|
||||
struct value final : blocks::value_block_base {};
|
||||
struct residual final : blocks::residual_block_base {};
|
||||
};
|
||||
using Density = blocks::density::mass;
|
||||
using Barotropic = dependencies::Bindings<dependencies::Binding<quantities::SpecificEnthalpy, Enthalpy>>;
|
||||
using DensityState = dependencies::Bindings<dependencies::Binding<quantities::MassDensity, Density>>;
|
||||
using BothCarriers = dependencies::Bindings<dependencies::Binding<quantities::SpecificEnthalpy, Enthalpy>, dependencies::Binding<quantities::MassDensity, Density>>;
|
||||
|
||||
using DensityFromPressure = eos::relations::Relation<quantities::MassDensity, quantities::Pressure>;
|
||||
using DensityFromPressureAndEnthalpy = eos::relations::Relation<quantities::MassDensity, quantities::SpecificEnthalpy, quantities::Pressure>;
|
||||
|
||||
struct TwoCarriers {
|
||||
using Relations = eos::relations::RelationCatalog<DensityFromPressure, eos::relations::SpecificEnthalpyFromPressure>;
|
||||
/// Only declarations are needed to prove EOS capabilities; this check library has no runtime implementation.
|
||||
quantities::DensityValue evaluate(DensityFromPressure, quantities::PressureValue) const;
|
||||
/// The second candidate prevents accidental selection by catalog order.
|
||||
quantities::SpecificEnthalpyValue evaluate(eos::relations::SpecificEnthalpyFromPressure, quantities::PressureValue) const;
|
||||
};
|
||||
|
||||
struct ReversedCarriers : TwoCarriers {
|
||||
using Relations = eos::relations::RelationCatalog<eos::relations::SpecificEnthalpyFromPressure, DensityFromPressure>;
|
||||
};
|
||||
|
||||
struct GeneralEOS {
|
||||
using Relations = eos::relations::RelationCatalog<DensityFromPressureAndEnthalpy>;
|
||||
/// Pressure deliberately appears after the state input in the relation.
|
||||
quantities::DensityValue evaluate(DensityFromPressureAndEnthalpy, quantities::SpecificEnthalpyValue, quantities::PressureValue) const;
|
||||
/// Only state partials are required; no pressure partial is declared.
|
||||
quantities::PartialDerivative<quantities::MassDensity, quantities::SpecificEnthalpy> partial_derivative(DensityFromPressureAndEnthalpy, quantities::WithRespectTo<quantities::SpecificEnthalpy>, quantities::SpecificEnthalpyValue, quantities::PressureValue) const;
|
||||
};
|
||||
|
||||
struct MissingPartial {
|
||||
using Relations = eos::relations::RelationCatalog<DensityFromPressureAndEnthalpy>;
|
||||
/// Relation support alone is insufficient for a Newton linearization.
|
||||
quantities::DensityValue evaluate(DensityFromPressureAndEnthalpy, quantities::SpecificEnthalpyValue, quantities::PressureValue) const;
|
||||
};
|
||||
|
||||
struct AmbiguousEOS : GeneralEOS {
|
||||
using Relations = eos::relations::RelationCatalog<DensityFromPressureAndEnthalpy, DensityFromPressure>;
|
||||
using GeneralEOS::evaluate;
|
||||
/// Even explicitly choosing density leaves these two relations ambiguous.
|
||||
quantities::DensityValue evaluate(DensityFromPressure, quantities::PressureValue) const;
|
||||
};
|
||||
|
||||
struct ForwardOnly {
|
||||
using Relations = eos::relations::RelationCatalog<eos::relations::PressureFromDensity>;
|
||||
/// A forward relation must not be silently inverted.
|
||||
quantities::PressureValue evaluate(eos::relations::PressureFromDensity, quantities::DensityValue) const;
|
||||
/// The direct-pressure formulation requires this derivative instead.
|
||||
quantities::PartialDerivative<quantities::Pressure, quantities::MassDensity> partial_derivative(eos::relations::PressureFromDensity, quantities::WithRespectTo<quantities::MassDensity>, quantities::DensityValue) const;
|
||||
};
|
||||
|
||||
using TwoStateRelation = eos::relations::Relation<quantities::SpecificEnthalpy, quantities::Pressure, quantities::Entropy, quantities::Temperature>;
|
||||
using TwoStateBindings = dependencies::Bindings<dependencies::Binding<quantities::SpecificEnthalpy, Enthalpy>, dependencies::Binding<quantities::Entropy, Density>, dependencies::Binding<quantities::Temperature, Density>>;
|
||||
|
||||
struct MissingSecondPartial {
|
||||
using Relations = eos::relations::RelationCatalog<TwoStateRelation>;
|
||||
/// MeanField's general EOS contract requires every additional state input, not just one of them.
|
||||
quantities::SpecificEnthalpyValue evaluate(TwoStateRelation, quantities::PressureValue, quantities::EntropyValue, quantities::TemperatureValue) const;
|
||||
/// Supplying the entropy partial does not compensate for the missing temperature partial.
|
||||
quantities::PartialDerivative<quantities::SpecificEnthalpy, quantities::Entropy> partial_derivative(TwoStateRelation, quantities::WithRespectTo<quantities::Entropy>, quantities::PressureValue, quantities::EntropyValue, quantities::TemperatureValue) const;
|
||||
};
|
||||
|
||||
struct CompletePartials : MissingSecondPartial {
|
||||
using MissingSecondPartial::partial_derivative;
|
||||
/// Adding precisely the missing partial makes this relation compilable.
|
||||
quantities::PartialDerivative<quantities::SpecificEnthalpy, quantities::Temperature> partial_derivative(TwoStateRelation, quantities::WithRespectTo<quantities::Temperature>, quantities::PressureValue, quantities::EntropyValue, quantities::TemperatureValue) const;
|
||||
};
|
||||
|
||||
struct SampleOnlyTerm : blocks::term<blocks::density> {
|
||||
using FunctionalSpace = Enthalpy::FunctionalSpace;
|
||||
using Support = Enthalpy::Support;
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
concept SelectsCarrier = requires {
|
||||
typename Candidate::CarrierQuantity;
|
||||
};
|
||||
|
||||
template <typename EOS, typename Bindings>
|
||||
concept HasSurfaceRelation = requires {
|
||||
typename surface::pressure_relation_t<EOS, Bindings>;
|
||||
};
|
||||
|
||||
static_assert(std::same_as<surface::IsobaricSurface::PhysicalQuantity, quantities::Pressure>);
|
||||
static_assert(std::same_as<surface::Isobaric, surface::IsobaricSurface>);
|
||||
static_assert(std::constructible_from<surface::IsobaricSurface, quantities::PressureValue>);
|
||||
static_assert(!std::constructible_from<surface::IsobaricSurface, quantities::SpecificEnthalpyValue>);
|
||||
static_assert(!std::constructible_from<surface::IsobaricSurface, double>);
|
||||
static_assert(!SelectsCarrier<surface::IsobaricSurface>);
|
||||
static_assert(std::is_trivially_copyable_v<surface::IsobaricSurface>);
|
||||
static_assert(std::is_trivially_copyable_v<surface::PressureSurfaceDescriptor>);
|
||||
|
||||
using DuplicateQuantities = dependencies::Bindings<dependencies::Binding<quantities::SpecificEnthalpy, Enthalpy>, dependencies::Binding<quantities::SpecificEnthalpy, Density>>;
|
||||
using SharedTarget = dependencies::Bindings<dependencies::Binding<quantities::SpecificEnthalpy, Enthalpy>, dependencies::Binding<quantities::MassDensity, Enthalpy>>;
|
||||
using VectorCarrier = dependencies::Bindings<dependencies::Binding<quantities::SpecificEnthalpy, blocks::displacement::vector>>;
|
||||
using InvalidQuantity = dependencies::Bindings<dependencies::Binding<int, Enthalpy>>;
|
||||
|
||||
static_assert(surface::SurfaceStateBindings<Barotropic>);
|
||||
static_assert(surface::SurfaceStateBindings<const BothCarriers&>);
|
||||
static_assert(surface::SurfaceStateBindings<SharedTarget>);
|
||||
static_assert(!surface::SurfaceStateBindings<DuplicateQuantities>);
|
||||
static_assert(!surface::SurfaceStateBindings<VectorCarrier>);
|
||||
static_assert(!surface::SurfaceStateBindings<InvalidQuantity>);
|
||||
static_assert(!surface::SurfaceStateBindings<dependencies::Bindings<>>);
|
||||
static_assert(!surface::SurfaceStateBindings<int>);
|
||||
static_assert(surface::SurfaceScalarTerm<SampleOnlyTerm>);
|
||||
static_assert(!surface::PressureSurfaceCompilable<TwoCarriers, dependencies::Bindings<dependencies::Binding<quantities::SpecificEnthalpy, SampleOnlyTerm>>>);
|
||||
|
||||
static_assert(surface::PressureSurfaceCompilable<eos::models::Polytrope, Barotropic>);
|
||||
static_assert(surface::PressureSurfaceCompilable<const eos::models::Polytrope&, const Barotropic&>);
|
||||
static_assert(!surface::PressureSurfaceCompilable<ForwardOnly, DensityState>);
|
||||
static_assert(!surface::PressureSurfaceCompilable<int, Barotropic>);
|
||||
static_assert(!surface::PressureSurfaceCompilable<TwoCarriers, int>);
|
||||
static_assert(!surface::PressureSurfaceCompilable<TwoCarriers, DuplicateQuantities>);
|
||||
static_assert(!surface::PressureSurfaceCompilable<TwoCarriers, VectorCarrier>);
|
||||
static_assert(!HasSurfaceRelation<TwoCarriers, int>);
|
||||
static_assert(!HasSurfaceRelation<TwoCarriers, BothCarriers>);
|
||||
static_assert(!HasSurfaceRelation<int, Barotropic>);
|
||||
|
||||
static_assert(surface::PressureSurfaceCompilable<TwoCarriers, DensityState>);
|
||||
static_assert(surface::PressureSurfaceCompilable<TwoCarriers, Barotropic>);
|
||||
static_assert(!surface::PressureSurfaceCompilable<TwoCarriers, BothCarriers>);
|
||||
static_assert(!surface::PressureSurfaceCompilable<ReversedCarriers, BothCarriers>);
|
||||
static_assert(surface::PressureSurfaceCompilable<TwoCarriers, BothCarriers, quantities::MassDensity>);
|
||||
static_assert(surface::PressureSurfaceCompilable<TwoCarriers, BothCarriers, quantities::SpecificEnthalpy>);
|
||||
static_assert(!surface::PressureSurfaceCompilable<TwoCarriers, BothCarriers, quantities::Temperature>);
|
||||
static_assert(surface::PressureSurfaceCompilable<GeneralEOS, BothCarriers>);
|
||||
static_assert(!surface::PressureSurfaceCompilable<GeneralEOS, DensityState>);
|
||||
static_assert(!surface::PressureSurfaceCompilable<MissingPartial, BothCarriers>);
|
||||
static_assert(!surface::PressureSurfaceCompilable<MissingSecondPartial, TwoStateBindings>);
|
||||
static_assert(surface::PressureSurfaceCompilable<CompletePartials, TwoStateBindings>);
|
||||
static_assert(!surface::PressureSurfaceCompilable<AmbiguousEOS, BothCarriers, quantities::MassDensity>);
|
||||
|
||||
using General = surface::PressureSurfaceFormulation<GeneralEOS, BothCarriers>;
|
||||
using Shared = surface::PressureSurfaceFormulation<GeneralEOS, SharedTarget>;
|
||||
static_assert(std::same_as<General::CarrierQuantity, quantities::MassDensity>);
|
||||
static_assert(std::same_as<General::CarrierTerm, Density>);
|
||||
static_assert(std::same_as<General::StateQuantities, dependencies::KeyList<quantities::MassDensity, quantities::SpecificEnthalpy>>);
|
||||
static_assert(std::same_as<General::StateTerms, dependencies::KeyList<Density, Enthalpy>>);
|
||||
static_assert(std::same_as<General::EOSStateTerms, dependencies::KeyList<Enthalpy>>);
|
||||
static_assert(std::same_as<Shared::StateQuantities, General::StateQuantities>);
|
||||
static_assert(std::same_as<Shared::StateTerms, dependencies::KeyList<Enthalpy>>);
|
||||
|
||||
static_assert(surface::DirectPressureSurfaceCompilable<ForwardOnly, DensityState, quantities::MassDensity, eos::relations::PressureFromDensity>);
|
||||
static_assert(!surface::DirectPressureSurfaceCompilable<ForwardOnly, DensityState, quantities::Temperature, eos::relations::PressureFromDensity>);
|
||||
static_assert(!surface::DirectPressureSurfaceCompilable<ForwardOnly, DensityState, quantities::MassDensity, DensityFromPressure>);
|
||||
static_assert(!surface::DirectPressureSurfaceCompilable<ForwardOnly, DensityState, quantities::MassDensity, int>);
|
||||
static_assert(!surface::DirectPressureSurfaceCompilable<int, DensityState, quantities::MassDensity, eos::relations::PressureFromDensity>);
|
||||
|
||||
using Direct = surface::DirectPressureSurfaceFormulation<ForwardOnly, DensityState, quantities::MassDensity, eos::relations::PressureFromDensity>;
|
||||
static_assert(std::same_as<Direct::ResidualQuantity, quantities::Pressure>);
|
||||
static_assert(std::same_as<Direct::CarrierTerm, Density>);
|
||||
static_assert(!Direct::resolves_carrier);
|
||||
static_assert(surface::PressureSurfaceFormulationType<Direct>);
|
||||
static_assert(!surface::PressureSurfaceFormulationType<int>);
|
||||
|
||||
using Graph = decltype(surface::make_pressure_surface_graph<eos::models::Polytrope, Barotropic>());
|
||||
using EOSContext = decltype(dependencies::make_dependency_context(dependencies::borrow<surface::keys::EquationOfState>(std::declval<eos::models::Polytrope&>())));
|
||||
static_assert(!dependencies::CanPrepare<Graph, EOSContext, surface::SurfaceFailure>);
|
||||
static_assert(!std::copy_constructible<decltype(dependencies::make_computation_cache<surface::SurfaceFailure>(
|
||||
surface::make_pressure_surface_graph<eos::models::Polytrope, Barotropic>(),
|
||||
dependencies::make_dependency_context(
|
||||
dependencies::borrow<surface::keys::EquationOfState>(std::declval<eos::models::Polytrope&>(), dependencies::track_by([](const auto& model) {
|
||||
return std::pair{model.polytropic_index(), model.polytropic_constant()};
|
||||
})),
|
||||
surface::pressure_condition(std::declval<surface::IsobaricSurface>()),
|
||||
surface::boundary_values<Enthalpy>({0.4})
|
||||
)))>);
|
||||
}
|
||||
273
src/compile_time_checks/serif/geometry/surface_boundary.cpp
Normal file
273
src/compile_time_checks/serif/geometry/surface_boundary.cpp
Normal file
@@ -0,0 +1,273 @@
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "serif/discretization/domain/schema/schemas.hpp"
|
||||
#include "serif/backend/mfem/geometry/surface/boundary_computations.hpp"
|
||||
#include "serif/geometry/surface/automatic.hpp"
|
||||
#include "serif/eos/models/polytropic.hpp"
|
||||
#include "serif/utils/dependencies/computation_cache.hpp"
|
||||
|
||||
namespace serif::compile_time_checks::surface_boundary
|
||||
{
|
||||
namespace surface = serif::geometry::surface;
|
||||
namespace backend = surface::backend::mfem;
|
||||
namespace discretization = serif::discretization;
|
||||
namespace finite_elements = discretization::backend::mfem;
|
||||
namespace coefficients = serif::operators::backend::mfem;
|
||||
namespace blocks = discretization::blocks;
|
||||
namespace spaces = discretization::functional_space;
|
||||
namespace quantities = serif::dimensions;
|
||||
namespace dependencies = serif::utils::dependencies;
|
||||
namespace eos = serif::eos;
|
||||
|
||||
template <typename QuantityT, typename Space>
|
||||
struct ScalarTerm : blocks::term<blocks::density>
|
||||
{
|
||||
using FunctionalSpace = Space;
|
||||
using Support = discretization::domain::StellarDomains;
|
||||
using Quantity = QuantityT;
|
||||
|
||||
struct value : blocks::value_block_base
|
||||
{
|
||||
};
|
||||
|
||||
struct residual : blocks::residual_block_base
|
||||
{
|
||||
};
|
||||
};
|
||||
|
||||
struct UniformTemperature : blocks::term<blocks::density>, blocks::global_scalar
|
||||
{
|
||||
using Support = discretization::domain::StellarDomains;
|
||||
using Quantity = quantities::Temperature;
|
||||
static constexpr bool thermodynamic_state = true;
|
||||
};
|
||||
|
||||
using Density = ScalarTerm<quantities::MassDensity, spaces::DiscreteFunctionalSpace<spaces::H1, 1, 0>>;
|
||||
using DiscontinuousDensity = ScalarTerm<quantities::MassDensity, spaces::DiscreteFunctionalSpace<spaces::L2, 2, 0>>;
|
||||
using Temperature = ScalarTerm<quantities::Temperature, spaces::DiscreteFunctionalSpace<spaces::H1, 2, 0>>;
|
||||
using DiscontinuousTemperature = ScalarTerm<quantities::Temperature, spaces::DiscreteFunctionalSpace<spaces::L2, 2, 0>>;
|
||||
using DensityRelation = eos::relations::Relation<quantities::MassDensity, quantities::Pressure, quantities::Temperature>;
|
||||
using PressureRelation = eos::relations::Relation<quantities::Pressure, quantities::MassDensity, quantities::Temperature>;
|
||||
|
||||
struct Gas
|
||||
{
|
||||
using Relations = eos::relations::RelationCatalog<DensityRelation, PressureRelation>;
|
||||
|
||||
/** @brief Declare a density-carried pressure condition without enthalpy. */
|
||||
quantities::DensityValue evaluate(DensityRelation, quantities::PressureValue, quantities::TemperatureValue) const;
|
||||
/** @brief Declare the independent forward-pressure relation. */
|
||||
quantities::PressureValue evaluate(PressureRelation, quantities::DensityValue, quantities::TemperatureValue) const;
|
||||
/** @brief Require the non-carrier derivative in the inverse relation. */
|
||||
quantities::PartialDerivative<quantities::MassDensity, quantities::Temperature> partial_derivative(DensityRelation, quantities::WithRespectTo<quantities::Temperature>, quantities::PressureValue, quantities::TemperatureValue) const;
|
||||
/** @brief Require the density column in the forward relation. */
|
||||
quantities::PartialDerivative<quantities::Pressure, quantities::MassDensity> partial_derivative(PressureRelation, quantities::WithRespectTo<quantities::MassDensity>, quantities::DensityValue, quantities::TemperatureValue) const;
|
||||
/** @brief Require the temperature column in the forward relation. */
|
||||
quantities::PartialDerivative<quantities::Pressure, quantities::Temperature> partial_derivative(PressureRelation, quantities::WithRespectTo<quantities::Temperature>, quantities::DensityValue, quantities::TemperatureValue) const;
|
||||
/** @brief This declaration-only EOS has no mutable configuration. */
|
||||
bool operator==(const Gas&) const = default;
|
||||
};
|
||||
|
||||
template <typename DensityTerm = Density, typename TemperatureTerm = Temperature>
|
||||
using State = dependencies::Bindings<dependencies::Binding<quantities::MassDensity, DensityTerm>, dependencies::Binding<quantities::Temperature, TemperatureTerm>>;
|
||||
|
||||
using Formulation = surface::PressureSurfaceFormulation<Gas, State<>>;
|
||||
using GlobalInput = surface::PressureSurfaceFormulation<Gas, State<Density, UniformTemperature>>;
|
||||
using DiscontinuousInput = surface::PressureSurfaceFormulation<Gas, State<Density, DiscontinuousTemperature>>;
|
||||
using DiscontinuousRow = surface::PressureSurfaceFormulation<Gas, State<DiscontinuousDensity>>;
|
||||
using Direct = surface::DirectPressureSurfaceFormulation<Gas, State<>, quantities::Temperature, PressureRelation>;
|
||||
using GlobalRow = surface::DirectPressureSurfaceFormulation<Gas, State<Density, UniformTemperature>, quantities::Temperature, PressureRelation>;
|
||||
|
||||
static_assert(finite_elements::BoundaryTraceTerm<Density>);
|
||||
static_assert(finite_elements::BoundaryTraceTerm<Temperature>);
|
||||
static_assert(finite_elements::BoundaryTraceTerm<UniformTemperature>);
|
||||
static_assert(!finite_elements::BoundaryTraceTerm<DiscontinuousTemperature>);
|
||||
static_assert(!finite_elements::BoundaryTraceTerm<blocks::displacement::vector>);
|
||||
static_assert(!finite_elements::BoundaryTraceTerm<int>);
|
||||
static_assert(backend::PressureBoundaryFormulation<Formulation>);
|
||||
static_assert(backend::PressureBoundaryFormulation<GlobalInput>);
|
||||
static_assert(backend::PressureBoundaryFormulation<Direct>);
|
||||
static_assert(std::same_as<Direct::Test, Temperature>);
|
||||
static_assert(std::same_as<Direct::ResidualQuantity, quantities::Pressure>);
|
||||
static_assert(!backend::PressureBoundaryFormulation<DiscontinuousInput>);
|
||||
static_assert(!backend::PressureBoundaryFormulation<DiscontinuousRow>);
|
||||
static_assert(!backend::PressureBoundaryFormulation<GlobalRow>);
|
||||
static_assert(!backend::PressureBoundaryFormulation<int>);
|
||||
|
||||
using Schema = discretization::domain::schema::CoreEnvelopeVacuumDomainSchema;
|
||||
template <typename Term>
|
||||
using View = coefficients::CoefficientView<finite_elements::TermBinding<Term, Schema>>;
|
||||
|
||||
template <typename Term>
|
||||
concept BuildsTrace = requires(std::shared_ptr<const finite_elements::DofSelection> rows, const finite_elements::TermBinding<Term, Schema>& binding)
|
||||
{
|
||||
finite_elements::make_boundary_trace(rows, binding);
|
||||
};
|
||||
|
||||
static_assert(BuildsTrace<Density>);
|
||||
static_assert(BuildsTrace<Temperature>);
|
||||
static_assert(BuildsTrace<UniformTemperature>);
|
||||
static_assert(!BuildsTrace<DiscontinuousTemperature>);
|
||||
static_assert(!BuildsTrace<blocks::displacement::vector>);
|
||||
|
||||
template <typename Binding>
|
||||
concept BuildsTemporaryTrace = requires(std::shared_ptr<const finite_elements::DofSelection> rows)
|
||||
{
|
||||
finite_elements::make_boundary_trace(rows, std::declval<Binding>());
|
||||
};
|
||||
|
||||
static_assert(!BuildsTemporaryTrace<finite_elements::TermBinding<Density, Schema>>);
|
||||
static_assert(!BuildsTemporaryTrace<const finite_elements::TermBinding<Density, Schema>>);
|
||||
static_assert(!BuildsTemporaryTrace<finite_elements::TermBinding<UniformTemperature, Schema>>);
|
||||
|
||||
using Coefficients = decltype(coefficients::make_term_coefficients(std::declval<View<Density>>(), std::declval<View<Temperature>>()));
|
||||
using MissingColumn = decltype(coefficients::make_term_coefficients(std::declval<View<Density>>()));
|
||||
|
||||
template <typename Context>
|
||||
concept BuildsBoundary = requires(const Context& context)
|
||||
{
|
||||
backend::make_pressure_boundary<Formulation>(context);
|
||||
};
|
||||
|
||||
static_assert(BuildsBoundary<Coefficients>);
|
||||
static_assert(!BuildsBoundary<MissingColumn>);
|
||||
using Boundary = decltype(backend::make_pressure_boundary<Formulation>(std::declval<const Coefficients&>()));
|
||||
using EOSResource = decltype(dependencies::own<surface::keys::EquationOfState>(Gas{}));
|
||||
using WrongEOS = decltype(dependencies::own<surface::keys::EquationOfState>(0));
|
||||
using WrongEOSKey = decltype(dependencies::own<Gas>(Gas{}));
|
||||
|
||||
template <typename Resource, typename Context>
|
||||
concept BuildsInputs = requires(Boundary boundary, Resource equation_of_state, surface::IsobaricSurface condition, const Context& context)
|
||||
{
|
||||
backend::make_pressure_boundary_inputs(boundary, equation_of_state, condition, context);
|
||||
};
|
||||
|
||||
static_assert(BuildsInputs<EOSResource, Coefficients>);
|
||||
static_assert(!BuildsInputs<WrongEOS, Coefficients>);
|
||||
static_assert(!BuildsInputs<WrongEOSKey, Coefficients>);
|
||||
static_assert(!BuildsInputs<EOSResource, MissingColumn>);
|
||||
|
||||
using Inputs = decltype(backend::make_pressure_boundary_inputs(std::declval<Boundary>(), std::declval<EOSResource>(), std::declval<surface::IsobaricSurface>(), std::declval<const Coefficients&>()));
|
||||
using Graph = decltype(backend::make_pressure_boundary_graph<Formulation>());
|
||||
using MissingEOS = decltype(std::declval<const Inputs&>().select(dependencies::KeyList<backend::keys::BoundaryDiscretization, surface::keys::PressureCondition, Density, Temperature>{}));
|
||||
using MissingState = decltype(std::declval<const Inputs&>().select(dependencies::KeyList<backend::keys::BoundaryDiscretization, surface::keys::EquationOfState, surface::keys::PressureCondition, Density>{}));
|
||||
|
||||
static_assert(dependencies::CanPrepare<Graph, Inputs, surface::SurfaceFailure>);
|
||||
static_assert(!dependencies::CanPrepare<Graph, MissingEOS, surface::SurfaceFailure>);
|
||||
static_assert(!dependencies::CanPrepare<Graph, MissingState, surface::SurfaceFailure>);
|
||||
static_assert(!dependencies::CanPrepare<Graph, Inputs, int>);
|
||||
static_assert(std::same_as<dependencies::bound_type_t<typename Inputs::Bindings, Density>, View<Density>>);
|
||||
static_assert(std::same_as<dependencies::bound_type_t<typename Inputs::Bindings, Temperature>, View<Temperature>>);
|
||||
|
||||
using GlobalCoefficients = decltype(coefficients::make_term_coefficients(std::declval<View<Density>>(), std::declval<View<UniformTemperature>>()));
|
||||
using GlobalBoundary = decltype(backend::make_pressure_boundary<GlobalInput>(std::declval<const GlobalCoefficients&>()));
|
||||
using GlobalInputs = decltype(backend::make_pressure_boundary_inputs(std::declval<GlobalBoundary>(), std::declval<EOSResource>(), std::declval<surface::IsobaricSurface>(), std::declval<const GlobalCoefficients&>()));
|
||||
using DirectBoundary = decltype(backend::make_pressure_boundary<Direct>(std::declval<const Coefficients&>()));
|
||||
using DirectInputs = decltype(backend::make_pressure_boundary_inputs(std::declval<DirectBoundary>(), std::declval<EOSResource>(), std::declval<surface::IsobaricSurface>(), std::declval<const Coefficients&>()));
|
||||
static_assert(dependencies::CanPrepare<decltype(backend::make_pressure_boundary_graph<GlobalInput>()), GlobalInputs, surface::SurfaceFailure>);
|
||||
static_assert(dependencies::CanPrepare<decltype(backend::make_pressure_boundary_graph<Direct>()), DirectInputs, surface::SurfaceFailure>);
|
||||
|
||||
using DensitySampling = Graph::ComputationAt<Graph::index<backend::keys::BoundarySamples<Density>>()>;
|
||||
using TemperatureSampling = Graph::ComputationAt<Graph::index<backend::keys::BoundarySamples<Temperature>>()>;
|
||||
static_assert(std::same_as<typename DensitySampling::Inputs, dependencies::InputList<backend::keys::BoundaryDiscretization, Density>>);
|
||||
static_assert(std::same_as<typename TemperatureSampling::Inputs, dependencies::InputList<backend::keys::BoundaryDiscretization, Temperature>>);
|
||||
static_assert(!std::same_as<backend::keys::BoundarySamples<Density>, Density>);
|
||||
static_assert(Graph::size == 4);
|
||||
|
||||
/** @brief An enthalpy-dimensioned global multiplier must not be mistaken for the enthalpy carrier. */
|
||||
struct CentralMultiplier : blocks::term<blocks::density>, blocks::global_scalar
|
||||
{
|
||||
using Support = discretization::domain::StellarDomains;
|
||||
using Quantity = quantities::SpecificEnthalpy;
|
||||
};
|
||||
|
||||
using Enthalpy = ScalarTerm<quantities::SpecificEnthalpy, spaces::DiscreteFunctionalSpace<spaces::H1, 3, 0>>;
|
||||
using Pressure = ScalarTerm<quantities::Pressure, spaces::DiscreteFunctionalSpace<spaces::H1, 1, 0>>;
|
||||
using BernoulliConstant = blocks::barotropic_constant::mass_normalization;
|
||||
using GasFields = dependencies::KeyList<Density, Temperature>;
|
||||
using GasRows = dependencies::KeyList<Density>;
|
||||
using GasSelection = surface::AutomaticPressureSurface<Gas, GasFields, GasRows>;
|
||||
static_assert(GasSelection::status == surface::PressureSurfaceSelection::supported);
|
||||
static_assert(GasSelection::Formulation::resolves_carrier);
|
||||
static_assert(std::same_as<typename GasSelection::Formulation::Test, Density>);
|
||||
static_assert(!surface::AutomaticPressureSurfaceCompilable<Gas, GasFields, dependencies::KeyList<>>);
|
||||
static_assert(!surface::AutomaticPressureSurfaceCompilable<Gas, GasFields, GasRows, dependencies::KeyList<Temperature>>);
|
||||
static_assert(!surface::AutomaticPressureSurfaceCompilable<Gas, dependencies::KeyList<Density>, GasRows>);
|
||||
static_assert(!surface::AutomaticPressureSurfaceCompilable<Gas, dependencies::KeyList<Density, DiscontinuousTemperature>, GasRows>);
|
||||
static_assert(surface::AutomaticPressureSurfaceCompilable<Gas, dependencies::KeyList<Density, UniformTemperature>, GasRows>);
|
||||
|
||||
struct ForwardOnly : Gas
|
||||
{
|
||||
using Relations = eos::relations::RelationCatalog<PressureRelation>;
|
||||
};
|
||||
|
||||
struct MissingPartials
|
||||
{
|
||||
using Relations = eos::relations::RelationCatalog<PressureRelation>;
|
||||
/** @brief Value evaluation alone cannot supply an analytic boundary Jacobian. */
|
||||
quantities::PressureValue evaluate(PressureRelation, quantities::DensityValue, quantities::TemperatureValue) const;
|
||||
};
|
||||
|
||||
struct MissingInversePartial : Gas
|
||||
{
|
||||
using Gas::partial_derivative;
|
||||
|
||||
/** @brief An unusable inverse must not hide the fully differentiable forward relation. */
|
||||
quantities::PartialDerivative<quantities::MassDensity, quantities::Temperature> partial_derivative(DensityRelation, quantities::WithRespectTo<quantities::Temperature>, quantities::PressureValue, quantities::TemperatureValue) const = delete;
|
||||
};
|
||||
|
||||
using ForwardFallback = surface::AutomaticPressureSurface<MissingInversePartial, GasFields, GasRows>;
|
||||
static_assert(ForwardFallback::status == surface::PressureSurfaceSelection::supported);
|
||||
static_assert(!ForwardFallback::Formulation::resolves_carrier);
|
||||
|
||||
struct OtherTemperature : Temperature
|
||||
{
|
||||
};
|
||||
|
||||
struct OtherDensity : Density
|
||||
{
|
||||
};
|
||||
|
||||
using DensityFromPressure = eos::relations::Relation<quantities::MassDensity, quantities::Pressure>;
|
||||
|
||||
struct TwoInverseRelations : Gas
|
||||
{
|
||||
using Relations = eos::relations::RelationCatalog<DensityFromPressure, DensityRelation, PressureRelation>;
|
||||
using Gas::evaluate;
|
||||
|
||||
/** @brief A second admissible inverse must not silently win by catalog order. */
|
||||
quantities::DensityValue evaluate(DensityFromPressure, quantities::PressureValue) const;
|
||||
};
|
||||
|
||||
struct ReversedInverseRelations : TwoInverseRelations
|
||||
{
|
||||
using Relations = eos::relations::RelationCatalog<PressureRelation, DensityRelation, DensityFromPressure>;
|
||||
};
|
||||
|
||||
using DirectSelection = surface::AutomaticPressureSurface<ForwardOnly, GasFields, GasRows>;
|
||||
static_assert(!DirectSelection::Formulation::resolves_carrier);
|
||||
static_assert(!surface::AutomaticPressureSurfaceCompilable<MissingPartials, GasFields, GasRows>);
|
||||
static_assert(surface::AutomaticPressureSurface<ForwardOnly, GasFields, GasFields>::status == surface::PressureSurfaceSelection::ambiguous);
|
||||
static_assert(surface::AutomaticPressureSurfaceCompilable<ForwardOnly, GasFields, GasFields, GasFields, Temperature>);
|
||||
static_assert(surface::AutomaticPressureSurface<TwoInverseRelations, GasFields, GasRows>::status == surface::PressureSurfaceSelection::ambiguous);
|
||||
static_assert(surface::AutomaticPressureSurface<ReversedInverseRelations, GasFields, GasRows>::status == surface::PressureSurfaceSelection::ambiguous);
|
||||
static_assert(!surface::AutomaticPressureSurfaceCompilable<Gas, dependencies::KeyList<Density, Temperature, OtherTemperature>, GasRows>);
|
||||
static_assert(surface::AutomaticPressureSurface<Gas, dependencies::KeyList<Density, OtherDensity, Temperature>, dependencies::KeyList<Density, OtherDensity>>::status == surface::PressureSurfaceSelection::ambiguous);
|
||||
|
||||
using PolytropicFields = dependencies::KeyList<Enthalpy, blocks::density::mass, BernoulliConstant, CentralMultiplier>;
|
||||
using PolytropicSelection = surface::AutomaticPressureSurface<eos::models::Polytrope, PolytropicFields, dependencies::KeyList<Enthalpy, BernoulliConstant>>;
|
||||
static_assert(std::same_as<typename PolytropicSelection::Formulation::CarrierTerm, Enthalpy>);
|
||||
static_assert(!surface::AutomaticPressureSurfaceCompilable<eos::models::Polytrope, dependencies::KeyList<BernoulliConstant, CentralMultiplier>, dependencies::KeyList<BernoulliConstant>>);
|
||||
static_assert(surface::AutomaticPressureSurfaceCompilable<eos::models::Polytrope, dependencies::KeyList<blocks::density::mass>, dependencies::KeyList<blocks::density::mass>>);
|
||||
|
||||
struct SymbolicEOS
|
||||
{
|
||||
};
|
||||
|
||||
using PressureFields = dependencies::KeyList<Pressure, Density, Temperature>;
|
||||
using PressureSelection = surface::AutomaticPressureSurface<SymbolicEOS, PressureFields, dependencies::KeyList<Pressure>>;
|
||||
static_assert(!PressureSelection::Formulation::uses_eos);
|
||||
static_assert(std::same_as<typename surface::AutomaticPressureSurface<Gas, PressureFields, PressureFields>::Formulation::CarrierTerm, Pressure>);
|
||||
static_assert(backend::PressureBoundaryFormulation<typename PressureSelection::Formulation>);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include <type_traits>
|
||||
|
||||
#include "serif/geometry/deformation/models.hpp"
|
||||
|
||||
namespace serif::geometry::deformation
|
||||
{
|
||||
// MeanField's move tests guarded heap-owned center vectors. The current prescriptions store their coordinates inline.
|
||||
static_assert(std::is_trivially_copyable_v<NodalRadialSurface>);
|
||||
static_assert(std::is_trivially_copyable_v<SphericalHarmonicSurface>);
|
||||
static_assert(std::is_nothrow_move_constructible_v<NodalRadialSurface>);
|
||||
static_assert(std::is_nothrow_move_assignable_v<NodalRadialSurface>);
|
||||
static_assert(std::is_nothrow_move_constructible_v<SphericalHarmonicSurface>);
|
||||
static_assert(std::is_nothrow_move_assignable_v<SphericalHarmonicSurface>);
|
||||
}
|
||||
67
src/compile_time_checks/serif/model/barotropic_density.cpp
Normal file
67
src/compile_time_checks/serif/model/barotropic_density.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "serif/equations/angular_momentum.hpp"
|
||||
#include "serif/equations/barotropic_density.hpp"
|
||||
#include "serif/equations/mass_normalization.hpp"
|
||||
#include "serif/equations/primal_gravity.hpp"
|
||||
#include "serif/equations/surface_potential.hpp"
|
||||
#include "serif/eos/models/polytropic.hpp"
|
||||
#include "serif/model/eos_reference.hpp"
|
||||
#include "serif/model/execution_validation.hpp"
|
||||
#include "serif/model/normalization/plan.hpp"
|
||||
#include "serif/solver/normalization/computations.hpp"
|
||||
|
||||
namespace serif::compile_time_checks::barotropic_density {
|
||||
namespace blocks = discretization::blocks;
|
||||
namespace dependencies = utils::dependencies;
|
||||
namespace normalization = solver::normalization;
|
||||
|
||||
/** @brief Declare only the mechanical EOS capabilities; no enthalpy, polytropic parameters, or curvature is available. */
|
||||
struct MechanicalBarotrope final {
|
||||
using Relations = eos::relations::RelationCatalog<eos::relations::PressureFromDensity,
|
||||
eos::relations::DensityFromPressure, eos::relations::PressurePotentialFromDensity>;
|
||||
/** @brief Declare pressure evaluation for capability checking without a runtime implementation. */
|
||||
dimensions::PressureValue evaluate(eos::relations::PressureFromDensity, dimensions::DensityValue) const;
|
||||
/** @brief Declare the prescribed-pressure surface density. */
|
||||
dimensions::DensityValue evaluate(eos::relations::DensityFromPressure, dimensions::PressureValue) const;
|
||||
/** @brief Declare the mechanical primitive independently of thermodynamic enthalpy. */
|
||||
dimensions::BarotropicPressurePotentialValue evaluate(eos::relations::PressurePotentialFromDensity, dimensions::DensityValue) const;
|
||||
/** @brief Declare the sole pressure derivative required by the local balance. */
|
||||
dimensions::PartialDerivative<dimensions::Pressure, dimensions::MassDensity> partial_derivative(
|
||||
eos::relations::PressureFromDensity, dimensions::WithRespectTo<dimensions::MassDensity>, dimensions::DensityValue) const;
|
||||
};
|
||||
|
||||
using Balance = equations::BarotropicDensityBalance<MechanicalBarotrope>;
|
||||
using BorrowedBalance = equations::BarotropicDensityBalance<model::EosReference<MechanicalBarotrope>>;
|
||||
using Registry = equations::EquationRegistry<Balance, equations::PrimalPoissonBalance,
|
||||
equations::MassNormalization, equations::AngularMomentumConstraint>;
|
||||
using Description = model::EquilibriumEquations<Registry, equations::SurfacePotentialBalance, geometry::surface::Isobaric>;
|
||||
struct BoundDescription {
|
||||
using Description = barotropic_density::Description;
|
||||
using Plan = model::EquilibriumPlan<MechanicalBarotrope, Description>;
|
||||
};
|
||||
using Plan = BoundDescription::Plan;
|
||||
using Rules = model::normalization::PlanFor<normalization::AutomaticPlan, normalization::Dimensionless, BoundDescription>;
|
||||
|
||||
static_assert(dimensions::ThermodynamicQuantityType<dimensions::BarotropicPressurePotential>);
|
||||
static_assert(!std::same_as<dimensions::BarotropicPressurePotential, dimensions::SpecificEnthalpy>);
|
||||
static_assert(eos::models::EOSSupportingRelation<eos::models::Polytrope, eos::relations::PressurePotentialFromDensity>);
|
||||
static_assert(eos::models::EOSModel<MechanicalBarotrope>);
|
||||
static_assert(!eos::models::EOSSupportingRelation<MechanicalBarotrope, eos::relations::SpecificEnthalpyFromDensity>);
|
||||
static_assert(!eos::models::EOSSupportingSecondPartialDerivative<MechanicalBarotrope,
|
||||
eos::relations::PressureFromDensity, dimensions::MassDensity, dimensions::MassDensity>);
|
||||
static_assert(operators::VolumeEquation<Balance>);
|
||||
static_assert(operators::VolumeEquation<BorrowedBalance>);
|
||||
static_assert(std::same_as<Balance::TestOperand, discretization::forms::Operand<blocks::density::mass>>);
|
||||
static_assert(std::same_as<Balance::ResidualQuantity, dimensions::BarotropicPressurePotential>);
|
||||
static_assert(model::ExecutableEquilibriumDescription<Description>);
|
||||
static_assert(Plan::has_shape && Plan::has_primal_gravity);
|
||||
static_assert(dependencies::key_count_v<Plan::Values> == 5);
|
||||
static_assert(dependencies::key_count_v<Plan::Residuals> == 5);
|
||||
static_assert(std::same_as<Plan::Surface::Formulation::CarrierTerm, blocks::density::mass>);
|
||||
static_assert(normalization::NormalizationPreparable<Rules, Plan::Layout>);
|
||||
static_assert(normalization::CompleteNormalizationFor<Rules, Plan::Values, Plan::Residuals>);
|
||||
static_assert(std::same_as<dependencies::bound_type_t<Rules::Residuals, blocks::density::mass>,
|
||||
normalization::WithPointRows<normalization::DimensionlessRieszCoordinate<dimensions::BarotropicPressurePotential,
|
||||
normalization::ReferenceVolumeMetric<blocks::density::mass>>, dimensions::MassDensity, model::normalization::SurfaceRows>>);
|
||||
static_assert(std::same_as<dependencies::bound_type_t<Rules::Values, blocks::density::mass>,
|
||||
normalization::DimensionlessRieszCoordinate<dimensions::MassDensity, normalization::ReferenceVolumeMetric<blocks::density::mass>>>);
|
||||
}
|
||||
43
src/compile_time_checks/serif/model/density_primal.cpp
Normal file
43
src/compile_time_checks/serif/model/density_primal.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "serif/equations/barotropic_density.hpp"
|
||||
#include "serif/equations/primal_gravity.hpp"
|
||||
#include "serif/equations/surface_potential.hpp"
|
||||
#include "serif/equations/mass_normalization.hpp"
|
||||
#include "serif/equations/angular_momentum.hpp"
|
||||
#include "serif/eos/models/polytropic.hpp"
|
||||
#include "serif/model/execution_validation.hpp"
|
||||
#include "serif/model/normalization/plan.hpp"
|
||||
#include "serif/solver/normalization/computations.hpp"
|
||||
|
||||
namespace serif::compile_time_checks::density_primal {
|
||||
namespace blocks = discretization::blocks;
|
||||
namespace dependencies = utils::dependencies;
|
||||
namespace normalization = solver::normalization;
|
||||
using EOS = eos::models::Polytrope;
|
||||
using Balance = equations::BarotropicDensityBalance<EOS>;
|
||||
using Registry = equations::EquationRegistry<Balance, equations::PrimalPoissonBalance,
|
||||
equations::MassNormalization, equations::AngularMomentumConstraint>;
|
||||
using Description = model::EquilibriumEquations<Registry, equations::SurfacePotentialBalance, geometry::surface::Isobaric>;
|
||||
struct BoundDescription {
|
||||
using Description = density_primal::Description;
|
||||
using Plan = model::EquilibriumPlan<EOS, Description>;
|
||||
};
|
||||
using Plan = BoundDescription::Plan;
|
||||
using Rules = model::normalization::PlanFor<normalization::AutomaticPlan, normalization::Dimensionless, BoundDescription>;
|
||||
static_assert(operators::VolumeEquation<Balance>);
|
||||
static_assert(operators::VolumeEquation<equations::PrimalPoissonBalance>);
|
||||
static_assert(model::ExecutableEquilibriumDescription<Description>);
|
||||
static_assert(Plan::has_shape && Plan::has_primal_gravity);
|
||||
static_assert(std::same_as<Plan::Shape::Coefficients, dependencies::KeyList<blocks::gravity::potential,
|
||||
blocks::barotropic_constant::mass_normalization, blocks::rotation::angular_velocity>>);
|
||||
static_assert(dependencies::key_count_v<Plan::Values> == 5);
|
||||
static_assert(dependencies::key_count_v<Plan::Residuals> == 5);
|
||||
static_assert(std::same_as<Plan::Surface::Formulation::CarrierTerm, blocks::density::mass>);
|
||||
static_assert(normalization::NormalizationPreparable<Rules, Plan::Layout>);
|
||||
static_assert(normalization::CompleteNormalizationFor<Rules, Plan::Values, Plan::Residuals>);
|
||||
static_assert(std::same_as<dependencies::bound_type_t<Rules::Residuals, blocks::surface_deformation::shape_equilibrium>,
|
||||
normalization::DimensionlessRieszCoordinate<dimensions::GravitationalPotential, normalization::UnitMetric>>);
|
||||
static_assert(std::same_as<dependencies::bound_type_t<Rules::Residuals, blocks::gravity::potential>,
|
||||
normalization::WithPointRows<normalization::DimensionlessRieszCoordinate<dimensions::InverseTimeSquared,
|
||||
normalization::ReferenceVolumeMetric<blocks::gravity::potential>>, dimensions::GravitationalPotential,
|
||||
model::normalization::InfinityRows>>);
|
||||
}
|
||||
157
src/compile_time_checks/serif/model/description.cpp
Normal file
157
src/compile_time_checks/serif/model/description.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
#include <concepts>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
#include "serif/model/description.hpp"
|
||||
#include "serif/model/eos_reference.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace model = serif::model;
|
||||
namespace dimensions = serif::dimensions;
|
||||
|
||||
struct SymbolicEOS {};
|
||||
struct OtherSymbolicEOS {};
|
||||
using Equations = model::EquilibriumEquations<int>;
|
||||
|
||||
struct Boundary
|
||||
{
|
||||
using ModelDefinition = model::SpecificationDefinition<Boundary, "Boundary", model::SpecificationRole::boundary_condition>;
|
||||
};
|
||||
|
||||
struct OtherBoundary
|
||||
{
|
||||
using ModelDefinition = model::SpecificationDefinition<OtherBoundary, "OtherBoundary", model::SpecificationRole::boundary_condition>;
|
||||
};
|
||||
|
||||
struct SemanticCollision
|
||||
{
|
||||
using ModelDefinition = model::SpecificationDefinition<SemanticCollision, "FixedTotalMass", model::SpecificationRole::invariant,
|
||||
model::GeneratedStateKind::physical_coordinate>;
|
||||
};
|
||||
|
||||
struct SameNameDifferentRole
|
||||
{
|
||||
using ModelDefinition = model::SpecificationDefinition<SameNameDifferentRole, "FixedTotalMass", model::SpecificationRole::boundary_condition>;
|
||||
};
|
||||
|
||||
template <model::SpecificationRole Role, model::GeneratedStateKind Kind>
|
||||
struct RoleChoice
|
||||
{
|
||||
using ModelDefinition = model::SpecificationDefinition<RoleChoice, "role choice", Role, Kind>;
|
||||
};
|
||||
|
||||
/** @brief Retain the historical role/state admission matrix through the public specification concept. */
|
||||
template <model::SpecificationRole Role>
|
||||
consteval std::array<bool, 4> admitted_states()
|
||||
{
|
||||
using Kind = model::GeneratedStateKind;
|
||||
return {model::ModelSpecification<RoleChoice<Role, Kind::none>>, model::ModelSpecification<RoleChoice<Role, Kind::multiplier>>,
|
||||
model::ModelSpecification<RoleChoice<Role, Kind::physical_coordinate>>, model::ModelSpecification<RoleChoice<Role, Kind::solver_border>>};
|
||||
}
|
||||
|
||||
struct InvalidRole
|
||||
{
|
||||
using ModelDefinition = model::SpecificationDefinition<InvalidRole, "invalid", model::SpecificationRole::invariant>;
|
||||
};
|
||||
|
||||
struct WrongOwner
|
||||
{
|
||||
using ModelDefinition = model::SpecificationDefinition<Boundary, "wrong", model::SpecificationRole::boundary_condition>;
|
||||
};
|
||||
|
||||
struct Malformed
|
||||
{
|
||||
using ModelDefinition = int;
|
||||
};
|
||||
|
||||
struct IndependentUnits
|
||||
{
|
||||
using ScalarDescription = model::ScalarConstraint<dimensions::SpecificEnergy, dimensions::Dimensionless, dimensions::Mass,
|
||||
"custom.coordinate", "x", "custom.residual", "F">;
|
||||
using ModelDefinition = model::SpecificationDefinition<IndependentUnits, "independent", model::SpecificationRole::invariant,
|
||||
model::GeneratedStateKind::physical_coordinate, ScalarDescription>;
|
||||
/** @brief Supply the target units independently of generated coordinate and residual units. */
|
||||
ScalarDescription::TargetValue target() const;
|
||||
};
|
||||
|
||||
struct IncorrectTarget
|
||||
{
|
||||
using ModelDefinition = model::SpecificationDefinition<IncorrectTarget, "wrong units", model::SpecificationRole::invariant,
|
||||
model::GeneratedStateKind::physical_coordinate, IndependentUnits::ScalarDescription>;
|
||||
/** @brief Deliberately mismatch the typed target to exercise detection. */
|
||||
dimensions::MassValue target() const;
|
||||
};
|
||||
|
||||
using First = decltype(model::StellarModel(std::declval<SymbolicEOS>(), std::declval<Equations>(), std::declval<model::FixedTotalMass>(), std::declval<model::FixedAngularMomentum>()));
|
||||
using Reordered = decltype(model::StellarModel(std::declval<SymbolicEOS>(), std::declval<Equations>(), std::declval<model::FixedAngularMomentum>(), std::declval<model::FixedTotalMass>()));
|
||||
using Qualified = decltype(model::StellarModel(std::declval<const SymbolicEOS&>(), std::declval<const Equations&>(), std::declval<const model::FixedTotalMass&>(), std::declval<const model::FixedAngularMomentum&>()));
|
||||
using TwoBoundaries = decltype(model::StellarModel(SymbolicEOS{}, Equations{1}, Boundary{}, OtherBoundary{}));
|
||||
using NoBoundary = decltype(model::StellarModel(SymbolicEOS{}, Equations{1}));
|
||||
|
||||
template <typename Candidate>
|
||||
concept CanResolveTemporary = requires { model::resolve_equilibrium_equations(std::declval<Candidate>()); };
|
||||
|
||||
template <typename Candidate>
|
||||
concept CanResolveDescription = requires(const Candidate& candidate) { model::resolve_equilibrium_equations(candidate); };
|
||||
|
||||
template <typename Candidate>
|
||||
concept CanReadUniqueBoundary = requires(const Candidate& candidate) { candidate.template specification_for_role<model::SpecificationRole::boundary_condition>(); };
|
||||
|
||||
static_assert(!CanResolveTemporary<First>);
|
||||
static_assert(!CanResolveDescription<int>);
|
||||
using NotEquations = decltype(model::StellarModel(SymbolicEOS{}, 5));
|
||||
static_assert(!CanResolveDescription<NotEquations>);
|
||||
|
||||
static_assert(std::same_as<First, Reordered> && std::same_as<First, Qualified>);
|
||||
static_assert(model::StellarModelType<NoBoundary> && model::StellarModelType<TwoBoundaries>);
|
||||
static_assert(model::specification_role_count<model::SpecificationRole::boundary_condition, TwoBoundaries> == 2);
|
||||
static_assert(!model::HasUniqueSpecificationForRole<model::SpecificationRole::boundary_condition, TwoBoundaries>);
|
||||
static_assert(!CanReadUniqueBoundary<TwoBoundaries> && !CanReadUniqueBoundary<NoBoundary>);
|
||||
static_assert(CanReadUniqueBoundary<decltype(model::StellarModel(SymbolicEOS{}, Equations{1}, Boundary{}))>);
|
||||
static_assert(std::same_as<decltype(std::declval<const TwoBoundaries&>().specifications_for_role<model::SpecificationRole::boundary_condition>()), std::tuple<const Boundary&, const OtherBoundary&>>);
|
||||
static_assert(model::specification_role_count<model::SpecificationRole::boundary_condition, int> == 0);
|
||||
static_assert(!model::StellarModelType<model::StellarModel<int, int, int>>);
|
||||
static_assert(!model::ModelSpecification<int> && !model::ModelSpecification<WrongOwner> && !model::ModelSpecification<Malformed>);
|
||||
static_assert(!model::ModelSpecification<InvalidRole>);
|
||||
static_assert(!model::ValidSpecificationPack<model::FixedTotalMass, SemanticCollision>);
|
||||
static_assert(model::ValidSpecificationPack<model::FixedTotalMass, SameNameDifferentRole>);
|
||||
static_assert(!model::ValidSpecificationPack<model::FixedTotalMass, model::FixedTotalMass>);
|
||||
static_assert(!model::ValidSpecificationPack<int>);
|
||||
static_assert(model::DimensionalScalarSpecification<IndependentUnits>);
|
||||
static_assert(!model::DimensionalScalarSpecification<IncorrectTarget>);
|
||||
static_assert(std::same_as<model::GeneratedCoordinateQuantity<IndependentUnits>, dimensions::Dimensionless>);
|
||||
static_assert(std::same_as<model::ConstraintQuantity<IndependentUnits>, dimensions::Mass>);
|
||||
static_assert(std::same_as<model::GeneratedCoordinateQuantity<model::FixedAngularMomentum>, dimensions::AngularVelocity>);
|
||||
static_assert(!std::same_as<model::GeneratedCoordinateOf<model::FixedTotalMass>, model::GeneratedCoordinateOf<model::FixedAngularMomentum>>);
|
||||
static_assert(std::same_as<decltype(std::declval<const First&>().specification<model::FixedTotalMass>()), const model::FixedTotalMass&>);
|
||||
static_assert(std::same_as<model::SpecificationsForRole<model::SpecificationRole::constitutive_law, First>, serif::utils::types::TypeList<SymbolicEOS>>);
|
||||
|
||||
using Role = model::SpecificationRole;
|
||||
static_assert(admitted_states<Role::constitutive_law>() == std::array{true, false, false, false});
|
||||
static_assert(admitted_states<Role::boundary_condition>() == std::array{true, false, false, false});
|
||||
static_assert(admitted_states<Role::invariant>() == std::array{false, true, true, false});
|
||||
static_assert(admitted_states<Role::phase_condition>() == std::array{false, false, false, true});
|
||||
static_assert(admitted_states<Role::gauge_choice>() == std::array{false, false, false, true});
|
||||
static_assert(admitted_states<Role::rotation_law>() == std::array{true, false, false, false});
|
||||
static_assert(!model::ModelSpecification<RoleChoice<static_cast<Role>(1000), model::GeneratedStateKind::none>>);
|
||||
static_assert(!model::ModelSpecification<RoleChoice<Role::constitutive_law, static_cast<model::GeneratedStateKind>(1000)>>);
|
||||
|
||||
/** @brief Exercise 2 EOS x 2 boundaries x 4 constraint packs without a numerical EOS. */
|
||||
template <typename EOS, typename Surface>
|
||||
consteval bool independent_constraint_choices()
|
||||
{
|
||||
using Definition = model::EquilibriumEquations<int, model::NoShapeForce, Surface>;
|
||||
using Mass = model::FixedTotalMass;
|
||||
using Momentum = model::FixedAngularMomentum;
|
||||
return model::StellarModelType<model::StellarModel<EOS, Definition, serif::utils::types::TypeList<>>> &&
|
||||
model::StellarModelType<model::StellarModel<EOS, Definition, serif::utils::types::TypeList<Mass>>> &&
|
||||
model::StellarModelType<model::StellarModel<EOS, Definition, serif::utils::types::TypeList<Momentum>>> &&
|
||||
model::StellarModelType<model::StellarModel<EOS, Definition, serif::utils::types::TypeList<Mass, Momentum>>>;
|
||||
}
|
||||
|
||||
static_assert(independent_constraint_choices<SymbolicEOS, Boundary>());
|
||||
static_assert(independent_constraint_choices<SymbolicEOS, OtherBoundary>());
|
||||
static_assert(independent_constraint_choices<OtherSymbolicEOS, Boundary>());
|
||||
static_assert(independent_constraint_choices<OtherSymbolicEOS, OtherBoundary>());
|
||||
}
|
||||
169
src/compile_time_checks/serif/model/execution_validation.cpp
Normal file
169
src/compile_time_checks/serif/model/execution_validation.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
#include <string_view>
|
||||
|
||||
#include "serif/equations/barycenter.hpp"
|
||||
#include "serif/equations/angular_momentum.hpp"
|
||||
#include "serif/eos/models/polytropic.hpp"
|
||||
#include "serif/equations/barotropic_density.hpp"
|
||||
#include "serif/equations/mass_normalization.hpp"
|
||||
#include "serif/equations/primal_gravity.hpp"
|
||||
#include "serif/equations/surface_potential.hpp"
|
||||
#include "serif/model/execution_validation.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace model = serif::model;
|
||||
namespace blocks = serif::discretization::blocks;
|
||||
namespace dependencies = serif::utils::dependencies;
|
||||
namespace equations = serif::equations;
|
||||
using EOS = serif::eos::models::Polytrope;
|
||||
using Registry = equations::EquationRegistry<equations::BarotropicDensityBalance<EOS>, equations::PrimalPoissonBalance,
|
||||
equations::MassNormalization, equations::AngularMomentumConstraint>;
|
||||
using Description = model::EquilibriumEquations<Registry, equations::SurfacePotentialBalance>;
|
||||
using Plan = model::EquilibriumPlan<EOS, Description>;
|
||||
using Model = model::StellarModel<EOS, Description, serif::utils::types::TypeList<model::FixedTotalMass, model::FixedAngularMomentum>>;
|
||||
static_assert(model::ExecutableEquilibriumDescription<Description>);
|
||||
static_assert(model::executable_model_topology<Model, Description, Plan>());
|
||||
static_assert(!model::ExecutableEquilibriumDescription<int>);
|
||||
static_assert(!model::ExecutableEquilibriumDescription<model::EquilibriumEquations<int>>);
|
||||
static_assert(!model::ExecutableEquilibriumDescription<model::EquilibriumEquations<Registry, int>>);
|
||||
static_assert(!model::ExecutableEquilibriumDescription<model::EquilibriumEquations<Registry, equations::PrimalPoissonBalance>>);
|
||||
static_assert(!model::ExecutableEquilibriumDescription<model::EquilibriumEquations<Registry, model::NoShapeForce, int>>);
|
||||
static_assert(!model::executable_model_topology<int, Description, Plan>());
|
||||
static_assert(!model::executable_model_topology<Model, int, Plan>());
|
||||
static_assert(!model::executable_model_topology<Model, Description, int>());
|
||||
|
||||
using IsobaricDescription = model::EquilibriumEquations<Registry, equations::SurfacePotentialBalance, serif::geometry::surface::Isobaric>;
|
||||
using IsobaricPlan = model::EquilibriumPlan<EOS, IsobaricDescription>;
|
||||
static_assert(model::ExecutableEquilibriumDescription<IsobaricDescription>);
|
||||
static_assert(model::executable_model_topology<Model, IsobaricDescription, IsobaricPlan>());
|
||||
static_assert(IsobaricPlan::Surface::Formulation::resolves_carrier);
|
||||
static_assert(std::same_as<typename IsobaricPlan::Surface::Formulation::CarrierTerm, blocks::density::mass>);
|
||||
using PrescribedCarrier = model::EquilibriumPlan<EOS, IsobaricDescription, dependencies::KeyList<blocks::density::mass>>;
|
||||
static_assert(!PrescribedCarrier::Surface::supported);
|
||||
static_assert(!model::executable_model_topology<Model, IsobaricDescription, PrescribedCarrier>());
|
||||
|
||||
struct FirstSurfaceHint
|
||||
{
|
||||
using IsobaricSurfaceField = blocks::gravity::potential;
|
||||
};
|
||||
|
||||
struct SecondSurfaceHint
|
||||
{
|
||||
using IsobaricSurfaceField = blocks::density::mass;
|
||||
};
|
||||
|
||||
// Surface hints are irrelevant when the physical model has no surface condition.
|
||||
using UnusedSurfaceHints = model::detail::SurfacePlan<model::NoSurfaceCondition, EOS, dependencies::KeyList<>, dependencies::KeyList<>,
|
||||
dependencies::KeyList<>, serif::utils::types::TypeList<FirstSurfaceHint, SecondSurfaceHint>>;
|
||||
static_assert(UnusedSurfaceHints::supported);
|
||||
|
||||
using MissingMassClosure = model::EquilibriumEquations<Registry>;
|
||||
static_assert(!model::executable_model_topology<Model, MissingMassClosure, model::EquilibriumPlan<EOS, MissingMassClosure>>());
|
||||
|
||||
// Fixed geometry has its own mass outcome; the remaining rotation prescription still requires a mechanical row.
|
||||
using FixedRegistry = equations::EquationRegistry<equations::BarotropicDensityBalance<EOS>, equations::PrimalPoissonBalance,
|
||||
equations::AngularMomentumConstraint>;
|
||||
using WithoutShape = model::EquilibriumEquations<FixedRegistry>;
|
||||
using FixedModel = model::StellarModel<EOS, WithoutShape, serif::utils::types::TypeList<model::FixedAngularMomentum>>;
|
||||
using FixedGeometry = model::EquilibriumPlan<EOS, WithoutShape>;
|
||||
static_assert(model::executable_model_topology<FixedModel, WithoutShape, FixedGeometry>());
|
||||
static_assert(dependencies::ContainsKey<blocks::surface_deformation::coefficients, FixedGeometry::PhysicalInputs>);
|
||||
static_assert(!dependencies::ContainsKey<blocks::surface_deformation::coefficients, FixedGeometry::Values>);
|
||||
static_assert(!dependencies::ContainsKey<blocks::surface_deformation::shape_equilibrium, FixedGeometry::Residuals>);
|
||||
using PrescribedShape = model::EquilibriumPlan<EOS, WithoutShape, dependencies::KeyList<blocks::surface_deformation::coefficients>>;
|
||||
static_assert(model::executable_model_topology<FixedModel, WithoutShape, PrescribedShape>());
|
||||
using PrescribedDensity = model::EquilibriumPlan<EOS, WithoutShape, dependencies::KeyList<blocks::density::mass>>;
|
||||
static_assert(model::executable_model_topology<FixedModel, WithoutShape, PrescribedDensity>());
|
||||
static_assert(!dependencies::ContainsKey<blocks::density::mass, PrescribedDensity::Values>);
|
||||
using PrescribedOmega = model::EquilibriumPlan<EOS, WithoutShape, dependencies::KeyList<blocks::rotation::angular_velocity>>;
|
||||
static_assert(!model::executable_model_topology<FixedModel, WithoutShape, PrescribedOmega>());
|
||||
|
||||
// Fixed geometry removes only its own equation; every remaining physical force must still be authored.
|
||||
using WithoutHydrostatic = model::EquilibriumEquations<equations::EquationRegistry<equations::PrimalPoissonBalance,
|
||||
equations::MassNormalization, equations::AngularMomentumConstraint>, equations::SurfacePotentialBalance>;
|
||||
using GlobalConstraints = serif::utils::types::TypeList<model::FixedTotalMass, model::FixedAngularMomentum>;
|
||||
using GlobalModel = model::StellarModel<EOS, WithoutHydrostatic, GlobalConstraints>;
|
||||
static_assert(!model::executable_model_topology<GlobalModel, WithoutHydrostatic, model::EquilibriumPlan<EOS, WithoutHydrostatic>>());
|
||||
using WithoutAngularMomentum = model::EquilibriumEquations<equations::EquationRegistry<equations::BarotropicDensityBalance<EOS>,
|
||||
equations::PrimalPoissonBalance, equations::MassNormalization>, equations::SurfacePotentialBalance>;
|
||||
using RotationModel = model::StellarModel<EOS, WithoutAngularMomentum, GlobalConstraints>;
|
||||
static_assert(!model::executable_model_topology<RotationModel, WithoutAngularMomentum, model::EquilibriumPlan<EOS, WithoutAngularMomentum>>());
|
||||
|
||||
struct NonrotatingHydrostatic
|
||||
{
|
||||
static constexpr std::string_view name = "nonrotating hydrostatic equilibrium";
|
||||
using Test = blocks::density::mass;
|
||||
using Operands = serif::utils::types::TypeList<serif::discretization::forms::Operand<Test>, serif::discretization::forms::Operand<blocks::gravity::potential>>;
|
||||
/** @brief Declare a closed row deliberately lacking an angular-velocity effect. */
|
||||
equations::PointLinearization<Operands> evaluate(const equations::EquationPoint<Operands>&) const;
|
||||
/** @brief Admit scalar-value integration independently of the omitted rotation operand. */
|
||||
template <typename Operand>
|
||||
int additional_order(Operand, int) const;
|
||||
};
|
||||
using WithoutRotationForce = model::EquilibriumEquations<equations::EquationRegistry<NonrotatingHydrostatic, equations::PrimalPoissonBalance,
|
||||
equations::MassNormalization, equations::AngularMomentumConstraint>, equations::SurfacePotentialBalance>;
|
||||
using NoRotationForcePlan = model::EquilibriumPlan<EOS, WithoutRotationForce>;
|
||||
static_assert(!model::executable_model_topology<GlobalModel, WithoutRotationForce, NoRotationForcePlan>());
|
||||
using ExplicitRotationZero = dependencies::KeyList<model::Derivative<blocks::density::mass, blocks::rotation::angular_velocity>>;
|
||||
static_assert(model::executable_model_topology<GlobalModel, WithoutRotationForce, NoRotationForcePlan, model::DefaultSpecificationBindings, ExplicitRotationZero>());
|
||||
using Actual = model::ActualEquilibriumCouplings<Description, Plan>;
|
||||
static_assert(dependencies::ContainsKey<model::Derivative<blocks::surface_deformation::shape_equilibrium, blocks::rotation::angular_velocity>, Actual>);
|
||||
static_assert(dependencies::ContainsKey<model::Derivative<blocks::barotropic_constant::mass_normalization, blocks::surface_deformation::coefficients>, Actual>);
|
||||
static_assert(dependencies::ContainsKey<model::Derivative<blocks::density::mass, blocks::gravity::potential>, Actual>);
|
||||
|
||||
struct CustomConstraint
|
||||
{
|
||||
using ScalarDescription = model::FixedTotalMass::ScalarDescription;
|
||||
using ModelDefinition = model::SpecificationDefinition<CustomConstraint, "custom missing force", model::SpecificationRole::invariant,
|
||||
model::GeneratedStateKind::multiplier, ScalarDescription, model::Reads<model::state::Density>, model::Changes<model::equation::HydrostaticBalance>>;
|
||||
/** @brief Describe a custom target without pretending that metadata implements its force. */
|
||||
serif::dimensions::MassValue target() const;
|
||||
};
|
||||
|
||||
using CustomModel = model::StellarModel<EOS, Description, serif::utils::types::TypeList<CustomConstraint>>;
|
||||
using Coordinate = blocks::frame_acceleration::component<1>;
|
||||
using Bindings = model::SpecificationBindings<dependencies::Binding<model::GeneratedCoordinateOf<CustomConstraint>, Coordinate>,
|
||||
dependencies::Binding<model::ConstraintOf<CustomConstraint>, blocks::barotropic_constant::mass_normalization>>;
|
||||
// The complete field layout can name lambda while the equation list supplies no lambda force.
|
||||
using CustomPlan = model::ModelPlan<Registry, dependencies::KeyList<>, dependencies::KeyList<Coordinate>>;
|
||||
using InteriorOnly = model::EquilibriumEquations<Registry>;
|
||||
static_assert(!model::executable_model_topology<CustomModel, InteriorOnly, CustomPlan, Bindings>());
|
||||
using ExplicitForceZero = dependencies::KeyList<model::Derivative<blocks::density::mass, Coordinate>>;
|
||||
static_assert(model::executable_model_topology<CustomModel, InteriorOnly, CustomPlan, Bindings, ExplicitForceZero>());
|
||||
|
||||
using AliasedModel = model::StellarModel<EOS, Description, serif::utils::types::TypeList<model::FixedTotalMass, CustomConstraint>>;
|
||||
using AliasedBindings = model::SpecificationBindings<dependencies::Binding<model::GeneratedCoordinateOf<CustomConstraint>, blocks::barotropic_constant::mass_normalization>,
|
||||
dependencies::Binding<model::ConstraintOf<CustomConstraint>, blocks::barotropic_constant::mass_normalization>>;
|
||||
static_assert(!model::executable_model_topology<AliasedModel, Description, Plan, AliasedBindings>());
|
||||
|
||||
struct InvalidScalarTarget
|
||||
{
|
||||
using ModelDefinition = model::SpecificationDefinition<InvalidScalarTarget, "invalid scalar units", model::SpecificationRole::invariant,
|
||||
model::GeneratedStateKind::multiplier, model::FixedTotalMass::ScalarDescription, model::Reads<model::state::Density>, model::Changes<model::equation::HydrostaticBalance>>;
|
||||
/** @brief Deliberately violate the declared mass target to exercise clean metadata rejection. */
|
||||
serif::dimensions::DensityValue target() const;
|
||||
};
|
||||
using InvalidScalarModel = model::StellarModel<EOS, Description, serif::utils::types::TypeList<InvalidScalarTarget>>;
|
||||
static_assert(model::StellarModelType<InvalidScalarModel>);
|
||||
static_assert(!model::SpecificationTopologyValid<InvalidScalarModel, Plan::Values, Plan::Residuals, Actual>);
|
||||
static_assert(!model::executable_model_topology<InvalidScalarModel, Description, Plan>());
|
||||
|
||||
struct InvalidUngeneratedReads
|
||||
{
|
||||
using ModelDefinition = model::SpecificationDefinition<InvalidUngeneratedReads, "ungenerated dependencies", model::SpecificationRole::boundary_condition,
|
||||
model::GeneratedStateKind::none, void, model::Reads<model::state::Density>>;
|
||||
};
|
||||
using InvalidUngeneratedModel = model::StellarModel<EOS, Description, serif::utils::types::TypeList<InvalidUngeneratedReads>>;
|
||||
static_assert(model::StellarModelType<InvalidUngeneratedModel>);
|
||||
static_assert(!model::SpecificationTopologyValid<InvalidUngeneratedModel, Plan::Values, Plan::Residuals, Actual>);
|
||||
static_assert(!model::executable_model_topology<InvalidUngeneratedModel, Description, Plan>());
|
||||
}
|
||||
|
||||
namespace {
|
||||
using IncompatibleEndpoint = dependencies::Bindings<dependencies::Binding<model::GeneratedCoordinateOf<model::FixedTotalMass>, blocks::frame_acceleration::component<1>>>;
|
||||
static_assert(!model::executable_model_topology<Model, Description, Plan, IncompatibleEndpoint>());
|
||||
|
||||
template <int Axis>
|
||||
constexpr bool barycenter_admitted = serif::operators::VolumeEquation<equations::BarycenterConstraint<Axis>>;
|
||||
static_assert(barycenter_admitted<0> && barycenter_admitted<1> && barycenter_admitted<2>);
|
||||
}
|
||||
24
src/compile_time_checks/serif/model/finite_elements.cpp
Normal file
24
src/compile_time_checks/serif/model/finite_elements.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "serif/model/finite_elements.hpp"
|
||||
#include "serif/eos/models/polytropic.hpp"
|
||||
#include "serif/equations/primal_gravity.hpp"
|
||||
|
||||
namespace {
|
||||
namespace model = serif::model;
|
||||
namespace equations = serif::equations;
|
||||
namespace blocks = serif::discretization::blocks;
|
||||
namespace dependencies = serif::utils::dependencies;
|
||||
using Physics = decltype(model::make_stellar_model(serif::eos::models::Polytrope{1.0, 1.0},
|
||||
model::EquilibriumEquations{.interior_equations = equations::EquationRegistry{
|
||||
equations::PrimalPoissonBalance{1.0}}}));
|
||||
using FiniteElements = decltype(model::make_stellar_discretization(std::declval<const Physics&>(), {},
|
||||
serif::geometry::deformation::DomainDeformation<serif::geometry::deformation::SphericalHarmonicSurface>{}));
|
||||
using Keys = typename std::remove_cvref_t<decltype(std::declval<FiniteElements>().term_bindings)>::Keys;
|
||||
|
||||
static_assert(dependencies::ContainsKey<blocks::gravity::potential, Keys>);
|
||||
static_assert(dependencies::ContainsKey<blocks::density::mass, Keys>);
|
||||
static_assert(dependencies::ContainsKey<blocks::displacement::vector, Keys>);
|
||||
static_assert(!dependencies::ContainsKey<blocks::rotation::angular_velocity, Keys>);
|
||||
}
|
||||
148
src/compile_time_checks/serif/model/normalization.cpp
Normal file
148
src/compile_time_checks/serif/model/normalization.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
#include <concepts>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "serif/equations/angular_momentum.hpp"
|
||||
#include "serif/eos/models/polytropic.hpp"
|
||||
#include "serif/equations/barotropic_density.hpp"
|
||||
#include "serif/equations/surface_potential.hpp"
|
||||
#include "serif/equations/mass_normalization.hpp"
|
||||
#include "serif/equations/primal_gravity.hpp"
|
||||
#include "serif/geometry/surface/isobaric.hpp"
|
||||
#include "serif/model/configured_discretization.hpp"
|
||||
#include "serif/backend/mfem/model/computations/solver_layout.hpp"
|
||||
#include "serif/model/normalization/plan.hpp"
|
||||
#include "serif/solver/normalization/computations.hpp"
|
||||
#include "serif/solver/preconditioner/recipes/block.hpp"
|
||||
#include "serif/solver/preconditioner/strategies/inverse/diagonal.hpp"
|
||||
|
||||
namespace {
|
||||
namespace model = serif::model;
|
||||
namespace normalization = serif::solver::normalization;
|
||||
namespace dimensions = serif::dimensions;
|
||||
namespace dependencies = serif::utils::dependencies;
|
||||
namespace blocks = serif::discretization::blocks;
|
||||
namespace equations = serif::equations;
|
||||
using EOS = serif::eos::models::Polytrope;
|
||||
using Registry = equations::EquationRegistry<equations::BarotropicDensityBalance<EOS>, equations::PrimalPoissonBalance,
|
||||
equations::MassNormalization, equations::AngularMomentumConstraint>;
|
||||
using Definition = model::EquilibriumEquations<Registry, equations::SurfacePotentialBalance, serif::geometry::surface::Isobaric>;
|
||||
using Model = model::StellarModel<EOS, Definition, serif::utils::types::TypeList<model::FixedTotalMass, model::FixedAngularMomentum>>;
|
||||
using WithoutMass = model::StellarModel<EOS, Definition, serif::utils::types::TypeList<model::FixedAngularMomentum>>;
|
||||
using ExplicitScheme = normalization::LaneEmdenApproximation<dimensions::MassValue, dimensions::DensityValue>;
|
||||
using ExplicitOptions = normalization::NormalizationOptions<ExplicitScheme>;
|
||||
// No model prescribes central density, so the reference always names it; the mass may still come from the model.
|
||||
using ModelMassScheme = normalization::LaneEmdenApproximation<normalization::FromModel, dimensions::DensityValue>;
|
||||
using ModelMassOptions = normalization::NormalizationOptions<ModelMassScheme>;
|
||||
|
||||
static_assert(!model::normalization::ReferenceResolvable<normalization::NormalizationOptions<>, Model>);
|
||||
static_assert(model::normalization::ReferenceResolvable<ModelMassOptions, Model>);
|
||||
static_assert(!model::normalization::ReferenceResolvable<ModelMassOptions, WithoutMass>);
|
||||
static_assert(model::normalization::ReferenceResolvable<ExplicitOptions, Model>);
|
||||
static_assert(model::normalization::ReferenceResolvable<ExplicitOptions, WithoutMass>);
|
||||
static_assert(std::same_as<decltype(normalization::make_normalization()), normalization::NormalizationOptions<>>);
|
||||
static_assert(std::same_as<decltype(model::normalization::resolve_reference(std::declval<ModelMassScheme>(), std::declval<const Model&>())), ExplicitScheme>);
|
||||
|
||||
/** @brief Make invalid factory configurations detectable without entering numerical preparation. */
|
||||
template <typename Options>
|
||||
concept CanConfigureDiscretization = requires(Options options) {
|
||||
model::make_discretization(std::move(options));
|
||||
};
|
||||
static_assert(CanConfigureDiscretization<model::DiscretizationOptions<Model, int, ModelMassOptions>>);
|
||||
static_assert(!CanConfigureDiscretization<model::DiscretizationOptions<Model, int>>);
|
||||
static_assert(!CanConfigureDiscretization<model::DiscretizationOptions<WithoutMass, int, ModelMassOptions>>);
|
||||
static_assert(CanConfigureDiscretization<model::DiscretizationOptions<WithoutMass, int, ExplicitOptions>>);
|
||||
using ResolvedConfiguration = decltype(model::make_discretization(
|
||||
std::declval<model::DiscretizationOptions<Model, int, ModelMassOptions>>()));
|
||||
static_assert(std::same_as<ResolvedConfiguration, model::DiscretizationOptions<Model, int, ExplicitOptions>>);
|
||||
|
||||
using SelectedInverse = decltype(serif::solver::preconditioner::make_preconditioner<blocks::density::mass>(
|
||||
serif::solver::preconditioner::strategies::inverse::Diagonal{}));
|
||||
using PreconditionedConfiguration = model::DiscretizationOptions<Model, int, ModelMassOptions, SelectedInverse>;
|
||||
using ResolvedPreconditionedConfiguration = decltype(model::make_discretization(std::declval<PreconditionedConfiguration>()));
|
||||
static_assert(CanConfigureDiscretization<PreconditionedConfiguration>);
|
||||
static_assert(std::same_as<ResolvedPreconditionedConfiguration,
|
||||
model::DiscretizationOptions<Model, int, ExplicitOptions, SelectedInverse>>);
|
||||
static_assert(std::same_as<decltype(std::declval<ResolvedPreconditionedConfiguration>().preconditioner), SelectedInverse>);
|
||||
|
||||
|
||||
/** @brief Keep unsupported coordinate policies and wrongly typed reference values out of user configurations. */
|
||||
template <typename Policy>
|
||||
concept CoordinatePolicy = requires {
|
||||
typename normalization::NormalizationOptions<ExplicitScheme, Policy>;
|
||||
};
|
||||
template <typename Mass, typename Density>
|
||||
concept LaneEmdenInputs = requires {
|
||||
typename normalization::LaneEmdenApproximation<Mass, Density>;
|
||||
};
|
||||
static_assert(CoordinatePolicy<normalization::Dimensionless>);
|
||||
static_assert(!CoordinatePolicy<bool>);
|
||||
static_assert(!LaneEmdenInputs<double, double>);
|
||||
static_assert(!LaneEmdenInputs<dimensions::DensityValue, dimensions::MassValue>);
|
||||
|
||||
struct CustomReference final {};
|
||||
|
||||
/** @brief Future strategies share the reference-scale result through ordinary argument-dependent lookup. */
|
||||
[[nodiscard]] std::expected<normalization::StellarReferenceScales, std::string> prepare_reference_scales(
|
||||
const CustomReference&, dimensions::QuantityValue<dimensions::GravitationalConstant> gravity) {
|
||||
return normalization::prepare_stellar_reference_scales(dimensions::MassValue{2.0}, dimensions::LengthValue{3.0}, gravity);
|
||||
}
|
||||
|
||||
static_assert(normalization::ReferenceScheme<CustomReference>);
|
||||
static_assert(!normalization::ReferenceScheme<int>);
|
||||
using CustomOptions = normalization::NormalizationOptions<CustomReference>;
|
||||
static_assert(model::normalization::ReferenceResolvable<CustomOptions, WithoutMass>);
|
||||
static_assert(CanConfigureDiscretization<model::DiscretizationOptions<WithoutMass, int, CustomOptions>>);
|
||||
static_assert(std::same_as<decltype(model::normalization::resolve_reference(CustomReference{}, std::declval<const Model&>())), CustomReference>);
|
||||
|
||||
/** @brief Use the real equilibrium plan without requiring any runtime mesh resources. */
|
||||
struct BoundDescription {
|
||||
using Description = Definition;
|
||||
using Plan = model::EquilibriumPlan<EOS, Definition, dependencies::KeyList<blocks::displacement::vector>>;
|
||||
};
|
||||
using Plan = model::normalization::PlanFor<normalization::AutomaticPlan, normalization::Dimensionless, BoundDescription>;
|
||||
using Layout = BoundDescription::Plan::Layout;
|
||||
using LayoutComputations = decltype(model::computations::make_solver_layout_computations(std::declval<const Layout&>()));
|
||||
static_assert(std::tuple_size_v<LayoutComputations> == 1);
|
||||
static_assert(std::same_as<typename std::tuple_element_t<0, LayoutComputations>::Key, blocks::keys::Layout>);
|
||||
static_assert(normalization::NormalizationPreparable<Plan, Layout>);
|
||||
static_assert(normalization::CompleteNormalizationFor<Plan, BoundDescription::Plan::Values, BoundDescription::Plan::Residuals>);
|
||||
|
||||
using Density = blocks::density::mass;
|
||||
using Potential = blocks::gravity::potential;
|
||||
using MassConstant = blocks::barotropic_constant::mass_normalization;
|
||||
using Shape = blocks::surface_deformation::coefficients;
|
||||
using SurfaceMetric = serif::geometry::deformation::keys::SurfaceMetric;
|
||||
using DensityMethod = normalization::DimensionlessRieszCoordinate<dimensions::BarotropicPressurePotential, normalization::ReferenceVolumeMetric<Density>>;
|
||||
static_assert(std::same_as<dependencies::bound_type_t<Plan::Residuals, Density>,
|
||||
normalization::WithPointRows<DensityMethod, dimensions::MassDensity, model::normalization::SurfaceRows>>);
|
||||
static_assert(std::same_as<dependencies::bound_type_t<Plan::Residuals, Potential>,
|
||||
normalization::WithPointRows<normalization::DimensionlessRieszCoordinate<dimensions::InverseTimeSquared,
|
||||
normalization::ReferenceVolumeMetric<Potential>>, dimensions::GravitationalPotential, model::normalization::InfinityRows>>);
|
||||
static_assert(std::same_as<dependencies::bound_type_t<Plan::Values, Potential>::Quantity, dimensions::GravitationalPotential>);
|
||||
static_assert(std::same_as<dependencies::bound_type_t<Plan::Residuals, MassConstant>::Quantity, dimensions::Mass>);
|
||||
static_assert(std::same_as<dependencies::bound_type_t<Plan::Values, Shape>, normalization::DimensionlessRieszCoordinate<dimensions::Length, SurfaceMetric>>);
|
||||
static_assert(dependencies::ContainsKey<SurfaceMetric, model::normalization::MetricsFor<Plan>>);
|
||||
static_assert(std::same_as<model::normalization::PlanFor<Plan, normalization::Dimensionless, BoundDescription>, Plan>);
|
||||
|
||||
/** @brief A surface EOS with only a forward pressure relation must retain pressure residual units. */
|
||||
struct ForwardPressureEOS {
|
||||
using Relations = serif::eos::relations::RelationCatalog<serif::eos::relations::PressureFromDensity>;
|
||||
/** @brief Declare the physical relation needed for surface selection without an inverse density relation. */
|
||||
dimensions::PressureValue evaluate(serif::eos::relations::PressureFromDensity, dimensions::DensityValue) const;
|
||||
/** @brief Declare its field derivative, as required by the direct-pressure surface formulation. */
|
||||
dimensions::PartialDerivative<dimensions::Pressure, dimensions::MassDensity> partial_derivative(
|
||||
serif::eos::relations::PressureFromDensity, dimensions::WithRespectTo<dimensions::MassDensity>, dimensions::DensityValue) const;
|
||||
};
|
||||
struct DirectPressureDescription {
|
||||
using Description = Definition;
|
||||
using Plan = model::EquilibriumPlan<ForwardPressureEOS, Definition, dependencies::KeyList<blocks::displacement::vector>>;
|
||||
};
|
||||
using DirectPressurePlan = model::normalization::PlanFor<normalization::AutomaticPlan, normalization::Dimensionless, DirectPressureDescription>;
|
||||
static_assert(normalization::NormalizationPreparable<DirectPressurePlan, DirectPressureDescription::Plan::Layout>);
|
||||
static_assert(std::same_as<dependencies::bound_type_t<DirectPressurePlan::Residuals, Density>,
|
||||
normalization::WithPointRows<DensityMethod, dimensions::Pressure, model::normalization::SurfaceRows>>);
|
||||
|
||||
}
|
||||
25
src/compile_time_checks/serif/model/scalar_row.cpp
Normal file
25
src/compile_time_checks/serif/model/scalar_row.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "serif/equations/mass_normalization.hpp"
|
||||
#include "serif/backend/mfem/model/computations/scalar_row.hpp"
|
||||
|
||||
namespace {
|
||||
namespace blocks = serif::discretization::blocks;
|
||||
namespace computations = serif::model::computations;
|
||||
namespace dependencies = serif::utils::dependencies;
|
||||
using Density = blocks::density::mass;
|
||||
using MassRow = blocks::barotropic_constant::mass_normalization;
|
||||
using Shape = blocks::surface_deformation::coefficients;
|
||||
using Volume = serif::operators::backend::mfem::volume_keys::Scoped<MassRow>::Linearization;
|
||||
|
||||
/** @brief Spatial equations must retain their ordinary volume representation. */
|
||||
template <typename Row>
|
||||
concept CacheableRow = requires { typename computations::ScalarResidualRow<Row>; };
|
||||
|
||||
static_assert(CacheableRow<MassRow>);
|
||||
static_assert(!CacheableRow<Density>);
|
||||
using FixedLayout = blocks::SystemLayout<dependencies::KeyList<Density, MassRow>, dependencies::KeyList<MassRow>>;
|
||||
using MovingLayout = blocks::SystemLayout<dependencies::KeyList<Density, Shape, MassRow>, dependencies::KeyList<MassRow>>;
|
||||
using Fixed = decltype(computations::scalar_residual_row<serif::equations::MassNormalization>(std::declval<FixedLayout>()));
|
||||
using Moving = decltype(computations::scalar_residual_row<serif::equations::MassNormalization>(std::declval<MovingLayout>()));
|
||||
static_assert(std::same_as<Fixed::Inputs, dependencies::KeyList<Volume>>);
|
||||
static_assert(std::same_as<Moving::Inputs, dependencies::KeyList<Volume, computations::ShapeDerivatives>>);
|
||||
}
|
||||
161
src/compile_time_checks/serif/model/state.cpp
Normal file
161
src/compile_time_checks/serif/model/state.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
#include <concepts>
|
||||
#include <span>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "serif/backend/mfem/model/equilibrium.hpp"
|
||||
#include "serif/model/state.hpp"
|
||||
|
||||
namespace serif::model::checks {
|
||||
namespace blocks = discretization::blocks;
|
||||
namespace dependencies = utils::dependencies;
|
||||
namespace execution = backend::mfem;
|
||||
using Density = blocks::density::mass;
|
||||
using Rotation = blocks::rotation::angular_velocity;
|
||||
using Shape = blocks::surface_deformation::coefficients;
|
||||
using ShapeRow = blocks::surface_deformation::shape_equilibrium;
|
||||
using Values = dependencies::KeyList<Density, Shape>;
|
||||
using Rows = dependencies::KeyList<ShapeRow, Density>;
|
||||
using Layout = blocks::SystemLayout<Values, Rows>;
|
||||
using State = StellarState<Layout>;
|
||||
using EmptyInputs = dependencies::DependencyContext<>;
|
||||
|
||||
/** @brief Check field availability and owner lifetime without instantiating a getter body. */
|
||||
template <typename Owner, typename Term>
|
||||
concept ReadsField = requires(Owner&& state) {
|
||||
std::forward<Owner>(state).template get<Term>();
|
||||
};
|
||||
|
||||
/** @brief Packed views must remain tied to an lvalue owning state. */
|
||||
template <typename Owner>
|
||||
concept ReadsValues = requires(Owner&& state) {
|
||||
std::forward<Owner>(state).values();
|
||||
};
|
||||
|
||||
/** @brief Layout references cannot survive destruction of a temporary owning state. */
|
||||
template <typename Owner>
|
||||
concept ReadsLayout = requires(Owner&& state) {
|
||||
std::forward<Owner>(state).layout();
|
||||
};
|
||||
|
||||
static_assert(std::copyable<State>);
|
||||
static_assert(std::same_as<typename StellarState<const Layout&>::Layout, Layout>);
|
||||
static_assert(std::constructible_from<StellarState<const Layout&>, Layout>);
|
||||
static_assert(std::same_as<decltype(StellarState(std::declval<const Layout&>())), State>);
|
||||
static_assert(std::same_as<decltype(StellarState(std::declval<Layout>(), std::declval<std::vector<double>>())), State>);
|
||||
static_assert(std::same_as<decltype(std::declval<State&>().get<Density>()), std::span<double>>);
|
||||
static_assert(std::same_as<decltype(std::declval<const State&>().get<Density>()), std::span<const double>>);
|
||||
static_assert(std::same_as<decltype(std::declval<State&>().values()), std::span<double>>);
|
||||
static_assert(std::same_as<decltype(std::declval<const State&>().values()), std::span<const double>>);
|
||||
static_assert(std::same_as<decltype(std::declval<State&>().layout()), const Layout&>);
|
||||
static_assert(ReadsField<State&, Density> && ReadsField<const State&, Shape>);
|
||||
static_assert(!ReadsField<State&, ShapeRow> && !ReadsField<State&, Rotation>);
|
||||
static_assert(!ReadsField<State, Density> && !ReadsField<const State, Density>);
|
||||
static_assert(ReadsValues<State&> && ReadsValues<const State&>);
|
||||
static_assert(!ReadsValues<State> && !ReadsValues<const State>);
|
||||
static_assert(ReadsLayout<State&> && ReadsLayout<const State&>);
|
||||
static_assert(!ReadsLayout<State> && !ReadsLayout<const State>);
|
||||
|
||||
/** @brief Only the binding's physical identity is needed to check public overload constraints. */
|
||||
template <typename TermT>
|
||||
struct Binding {
|
||||
using Term = TermT;
|
||||
};
|
||||
|
||||
template <typename Key, typename Term = Key>
|
||||
using CoefficientSource = decltype(dependencies::own<Key>(std::declval<operators::backend::mfem::CoefficientView<Binding<Term>>>()));
|
||||
using RotationInputs = dependencies::DependencyContext<CoefficientSource<Rotation>>;
|
||||
using WrongIdentity = dependencies::DependencyContext<CoefficientSource<Rotation, Density>>;
|
||||
using WrongValue = decltype(dependencies::make_dependency_context(dependencies::own<Rotation>(1.0)));
|
||||
using Overlap = dependencies::DependencyContext<CoefficientSource<Density>, CoefficientSource<Rotation>>;
|
||||
using ShapeOverlap = dependencies::DependencyContext<CoefficientSource<Shape>, CoefficientSource<Rotation>>;
|
||||
using CompleteFields = dependencies::DependencyContext<CoefficientSource<Density>, CoefficientSource<Rotation>>;
|
||||
|
||||
struct MovingPlan {
|
||||
using Values = checks::Values;
|
||||
using Coefficients = dependencies::KeyList<Density, Rotation>;
|
||||
using Layout = checks::Layout;
|
||||
struct Surface {
|
||||
using Formulation = void;
|
||||
};
|
||||
static constexpr bool has_shape = true;
|
||||
};
|
||||
|
||||
struct FixedPlan : MovingPlan {
|
||||
using Values = dependencies::KeyList<Density>;
|
||||
using Layout = blocks::SystemLayout<Values, Values>;
|
||||
static constexpr bool has_shape = false;
|
||||
};
|
||||
|
||||
template <typename PlanT>
|
||||
struct Bound {
|
||||
using Plan = PlanT;
|
||||
};
|
||||
|
||||
// A constant graph checks overload participation without constructing finite-element resources.
|
||||
using Graph = decltype(dependencies::make_computation_graph(
|
||||
dependencies::make_computation<keys::PhysicalSystem, dependencies::InputList<>>([]() {
|
||||
return 0;
|
||||
})));
|
||||
using MovingEvaluator = execution::EquilibriumEvaluator<Bound<MovingPlan>, Graph, EmptyInputs>;
|
||||
using FixedEvaluator = execution::EquilibriumEvaluator<Bound<FixedPlan>, Graph, EmptyInputs>;
|
||||
using PreparedGraph = decltype(dependencies::make_computation_graph(
|
||||
dependencies::make_computation<keys::PhysicalSystem, dependencies::InputList<>>([]() {
|
||||
return 0;
|
||||
}), dependencies::make_computation<preconditioning::PreparedSystem, dependencies::InputList<keys::PhysicalSystem>>([](int value) {
|
||||
return value;
|
||||
})));
|
||||
using MovingPreparedEvaluator = execution::EquilibriumEvaluator<Bound<MovingPlan>, PreparedGraph, EmptyInputs>;
|
||||
using FixedPreparedEvaluator = execution::EquilibriumEvaluator<Bound<FixedPlan>, PreparedGraph, EmptyInputs>;
|
||||
using FixedState = StellarState<FixedPlan::Layout>;
|
||||
using OtherState = StellarState<blocks::SystemLayout<dependencies::KeyList<Shape, Density>, Rows>>;
|
||||
|
||||
/** @brief Unsupported state and prescribed-field combinations must fail during overload selection. */
|
||||
template <typename Evaluator, typename... Arguments>
|
||||
concept CanEvaluate = requires(Evaluator& evaluator, const Arguments&... arguments) {
|
||||
evaluator.evaluate(arguments...);
|
||||
};
|
||||
|
||||
/** @brief Linear preparation needs both a selected recipe and a valid candidate binding. */
|
||||
template <typename Evaluator, typename... Arguments>
|
||||
concept CanPrepareLinearSystem = requires(Evaluator& evaluator, const Arguments&... arguments) {
|
||||
evaluator.prepare_linear_system(arguments...);
|
||||
};
|
||||
|
||||
static_assert(execution::detail::PrescribedInputsFor<MovingPlan, RotationInputs>);
|
||||
static_assert(!execution::detail::PrescribedInputsFor<MovingPlan, EmptyInputs>);
|
||||
static_assert(!execution::detail::PrescribedInputsFor<MovingPlan, WrongIdentity>);
|
||||
static_assert(!execution::detail::PrescribedInputsFor<MovingPlan, WrongValue>);
|
||||
static_assert(!execution::detail::PrescribedInputsFor<MovingPlan, Overlap>);
|
||||
static_assert(!execution::detail::PrescribedInputsFor<MovingPlan, ShapeOverlap>);
|
||||
static_assert(CanEvaluate<MovingEvaluator, State, RotationInputs>);
|
||||
static_assert(!CanEvaluate<MovingEvaluator, State>);
|
||||
static_assert(!CanEvaluate<MovingEvaluator, State, EmptyInputs>);
|
||||
static_assert(!CanEvaluate<MovingEvaluator, State, WrongIdentity>);
|
||||
static_assert(!CanEvaluate<MovingEvaluator, State, WrongValue>);
|
||||
static_assert(!CanEvaluate<MovingEvaluator, State, Overlap>);
|
||||
static_assert(!CanEvaluate<MovingEvaluator, OtherState, RotationInputs>);
|
||||
static_assert(!CanEvaluate<MovingEvaluator, State, dependencies::PreparationMode>);
|
||||
static_assert(CanEvaluate<MovingEvaluator, CompleteFields, std::span<const double>>);
|
||||
static_assert(!CanEvaluate<MovingEvaluator, RotationInputs, std::span<const double>>);
|
||||
static_assert(CanEvaluate<FixedEvaluator, FixedState, ::mfem::Vector, RotationInputs>);
|
||||
static_assert(!CanEvaluate<FixedEvaluator, FixedState, ::mfem::Vector>);
|
||||
static_assert(!CanEvaluate<FixedEvaluator, FixedState, ::mfem::Vector, Overlap>);
|
||||
static_assert(!CanEvaluate<FixedEvaluator, State, ::mfem::Vector, RotationInputs>);
|
||||
static_assert(!CanPrepareLinearSystem<MovingEvaluator, State, RotationInputs>);
|
||||
static_assert(!CanPrepareLinearSystem<FixedEvaluator, FixedState, ::mfem::Vector, RotationInputs>);
|
||||
static_assert(CanPrepareLinearSystem<MovingPreparedEvaluator, State, RotationInputs>);
|
||||
static_assert(CanPrepareLinearSystem<MovingPreparedEvaluator, State, RotationInputs, dependencies::PreparationMode>);
|
||||
static_assert(CanPrepareLinearSystem<MovingPreparedEvaluator, CompleteFields, std::span<const double>>);
|
||||
static_assert(!CanPrepareLinearSystem<MovingPreparedEvaluator, State>);
|
||||
static_assert(!CanPrepareLinearSystem<MovingPreparedEvaluator, State, EmptyInputs>);
|
||||
static_assert(!CanPrepareLinearSystem<MovingPreparedEvaluator, State, WrongIdentity>);
|
||||
static_assert(!CanPrepareLinearSystem<MovingPreparedEvaluator, State, WrongValue>);
|
||||
static_assert(!CanPrepareLinearSystem<MovingPreparedEvaluator, State, Overlap>);
|
||||
static_assert(!CanPrepareLinearSystem<MovingPreparedEvaluator, OtherState, RotationInputs>);
|
||||
static_assert(!CanPrepareLinearSystem<MovingPreparedEvaluator, RotationInputs, std::span<const double>>);
|
||||
static_assert(CanPrepareLinearSystem<FixedPreparedEvaluator, FixedState, ::mfem::Vector, RotationInputs>);
|
||||
static_assert(!CanPrepareLinearSystem<FixedPreparedEvaluator, FixedState, ::mfem::Vector>);
|
||||
static_assert(!CanPrepareLinearSystem<FixedPreparedEvaluator, State, ::mfem::Vector, RotationInputs>);
|
||||
}
|
||||
177
src/compile_time_checks/serif/model/validation.cpp
Normal file
177
src/compile_time_checks/serif/model/validation.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
#include "serif/eos/models/polytropic.hpp"
|
||||
#include "serif/equations/barotropic_density.hpp"
|
||||
#include "serif/equations/primal_gravity.hpp"
|
||||
#include "serif/model/validation.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace model = serif::model;
|
||||
namespace dimensions = serif::dimensions;
|
||||
namespace blocks = serif::discretization::blocks;
|
||||
namespace dependencies = serif::utils::dependencies;
|
||||
|
||||
struct SymbolicEOS
|
||||
{
|
||||
};
|
||||
struct CustomCoordinate
|
||||
{
|
||||
};
|
||||
struct CustomResidual
|
||||
{
|
||||
};
|
||||
struct CustomState
|
||||
{
|
||||
};
|
||||
struct CustomBalance
|
||||
{
|
||||
};
|
||||
|
||||
struct CustomConstraint
|
||||
{
|
||||
using ScalarDescription = model::ScalarConstraint<dimensions::Mass, dimensions::SpecificEnthalpy, dimensions::Mass, "custom.value", "C", "custom.row", "R">;
|
||||
using ModelDefinition = model::SpecificationDefinition<CustomConstraint, "custom", model::SpecificationRole::invariant, model::GeneratedStateKind::multiplier,
|
||||
ScalarDescription, model::Reads<CustomState>, model::Changes<CustomBalance>>;
|
||||
/** @brief Declare independently typed target metadata without requiring a numerical provider. */
|
||||
dimensions::MassValue target() const;
|
||||
};
|
||||
|
||||
struct ForeignConstraint
|
||||
{
|
||||
using ModelDefinition = model::SpecificationDefinition<ForeignConstraint, "foreign", model::SpecificationRole::invariant, model::GeneratedStateKind::multiplier,
|
||||
CustomConstraint::ScalarDescription, model::Reads<model::GeneratedCoordinateOf<CustomConstraint>>, model::Changes<model::equation::HydrostaticBalance>>;
|
||||
/** @brief Supply correct units while deliberately depending on another owning prescription. */
|
||||
dimensions::MassValue target() const;
|
||||
};
|
||||
|
||||
// Any spatial unknown can carry a custom physical state and its balance; potential is merely a convenient registered term.
|
||||
using Field = blocks::gravity::potential;
|
||||
using Description = model::EquilibriumEquations<int>;
|
||||
using CustomModel = model::StellarModel<SymbolicEOS, Description, serif::utils::types::TypeList<CustomConstraint>>;
|
||||
using Values = dependencies::KeyList<Field, CustomCoordinate>;
|
||||
using Rows = dependencies::KeyList<CustomResidual, Field>;
|
||||
using Bindings = model::SpecificationBindings<dependencies::Binding<model::GeneratedCoordinateOf<CustomConstraint>, CustomCoordinate>,
|
||||
dependencies::Binding<model::ConstraintOf<CustomConstraint>, CustomResidual>,
|
||||
dependencies::Binding<CustomState, Field>, dependencies::Binding<CustomBalance, Field>>;
|
||||
using ConstraintDerivative = model::Derivative<CustomResidual, Field>;
|
||||
using MultiplierDerivative = model::Derivative<Field, CustomCoordinate>;
|
||||
using StateDerivative = model::Derivative<Field, Field>;
|
||||
using Declared = dependencies::KeyList<ConstraintDerivative, MultiplierDerivative, StateDerivative>;
|
||||
using MissingStateDerivative = dependencies::KeyList<ConstraintDerivative, MultiplierDerivative>;
|
||||
|
||||
static_assert(model::StellarModelType<CustomModel>);
|
||||
static_assert(model::SpecificationTopologyValid<CustomModel, Values, Rows, Declared, Bindings>);
|
||||
static_assert(!model::SpecificationTopologyValid<CustomModel, Values, Rows, Declared>);
|
||||
static_assert(!model::SpecificationTopologyValid<CustomModel, Values, Rows, MissingStateDerivative, Bindings>);
|
||||
static_assert(!model::SpecificationTopologyValid<CustomModel, dependencies::KeyList<Field>, Rows, Declared, Bindings>);
|
||||
static_assert(!model::SpecificationTopologyValid<CustomModel, Values, dependencies::KeyList<Field>, Declared, Bindings>);
|
||||
static_assert(!model::SpecificationTopologyValid<int, Values, Rows, Declared, Bindings>);
|
||||
static_assert(!model::SpecificationTopologyValid<CustomModel, int, Rows, Declared, Bindings>);
|
||||
static_assert(!model::SpecificationTopologyValid<CustomModel, Values, Rows, int, Bindings>);
|
||||
static_assert(!model::SpecificationTopologyValid<CustomModel, Values, Rows, Declared, int>);
|
||||
static_assert(model::SpecificationEndpointsValid<CustomModel, Values, Values, Rows, dependencies::KeyList<>, Bindings>);
|
||||
static_assert(model::SpecificationEndpointsValid<CustomModel, dependencies::KeyList<CustomCoordinate>, Values, Rows, dependencies::KeyList<>, Bindings>);
|
||||
static_assert(!model::SpecificationEndpointsValid<CustomModel, dependencies::KeyList<CustomCoordinate>, dependencies::KeyList<CustomCoordinate>, Rows, dependencies::KeyList<>, Bindings>);
|
||||
static_assert(!model::SpecificationBackendAvailable<CustomModel, Values, Rows, Declared, MissingStateDerivative, Bindings>);
|
||||
// Registering an explicit zero is an implementation decision, never inferred from an absent method.
|
||||
using WithExplicitStateZero = dependencies::CombinedKeyList<MissingStateDerivative, dependencies::KeyList<StateDerivative>>;
|
||||
static_assert(model::SpecificationBackendAvailable<CustomModel, Values, Rows, Declared, WithExplicitStateZero, Bindings>);
|
||||
|
||||
using ForeignModel = model::StellarModel<SymbolicEOS, Description, serif::utils::types::TypeList<ForeignConstraint>>;
|
||||
using ForeignBindings = model::SpecificationBindings<dependencies::Binding<model::GeneratedCoordinateOf<ForeignConstraint>, CustomCoordinate>,
|
||||
dependencies::Binding<model::ConstraintOf<ForeignConstraint>, CustomResidual>, dependencies::Binding<model::GeneratedCoordinateOf<CustomConstraint>, Field>>;
|
||||
static_assert(!model::SpecificationTopologyValid<ForeignModel, Values, Rows, Declared, ForeignBindings>);
|
||||
static_assert(!model::SpecificationEndpointsValid<ForeignModel, Values, Values, Rows, dependencies::KeyList<>, ForeignBindings>);
|
||||
|
||||
using MassModel = model::StellarModel<SymbolicEOS, Description, serif::utils::types::TypeList<model::FixedTotalMass>>;
|
||||
using MassCoordinate = blocks::barotropic_constant::mass_normalization;
|
||||
using MassValues = dependencies::KeyList<MassCoordinate, Field, blocks::density::mass>;
|
||||
using MassRows = dependencies::KeyList<MassCoordinate, Field>;
|
||||
using MassCouplings = dependencies::KeyList<model::Derivative<MassCoordinate, blocks::density::mass>, model::Derivative<Field, MassCoordinate>, model::Derivative<Field, blocks::density::mass>>;
|
||||
static_assert(!model::SpecificationTopologyValid<MassModel, MassValues, MassRows, MassCouplings>);
|
||||
|
||||
using MassShape = blocks::surface_deformation::coefficients;
|
||||
using MassShapeRow = blocks::surface_deformation::shape_equilibrium;
|
||||
using SurfaceMassValues = dependencies::KeyList<MassCoordinate, blocks::density::mass, MassShape>;
|
||||
using SurfaceMassRows = dependencies::KeyList<MassCoordinate, MassShapeRow>;
|
||||
using SurfaceMassCouplings = dependencies::KeyList<
|
||||
model::Derivative<MassCoordinate, blocks::density::mass>, model::Derivative<MassCoordinate, MassShape>,
|
||||
model::Derivative<MassShapeRow, MassCoordinate>, model::Derivative<MassShapeRow, blocks::density::mass>,
|
||||
model::Derivative<MassShapeRow, MassShape>>;
|
||||
static_assert(model::SpecificationTopologyValid<MassModel, SurfaceMassValues, SurfaceMassRows, SurfaceMassCouplings>);
|
||||
static_assert(!model::SpecificationTopologyValid<MassModel, SurfaceMassValues, MassRows, SurfaceMassCouplings>);
|
||||
static_assert(std::same_as<model::GeneratedCoordinateQuantity<model::FixedTotalMass>, dimensions::GravitationalPotential>);
|
||||
static_assert(std::same_as<model::ConstraintQuantity<model::FixedTotalMass>, dimensions::Mass>);
|
||||
static_assert(model::FixedTotalMass::ScalarDescription::coordinate_id == "fixed_total_mass.multiplier");
|
||||
static_assert(model::FixedTotalMass::ScalarDescription::coordinate_symbol == "Us");
|
||||
|
||||
using Balance = serif::equations::BarotropicDensityBalance<serif::eos::models::Polytrope>;
|
||||
using Registry = serif::equations::EquationRegistry<Balance, serif::equations::PrimalPoissonBalance>;
|
||||
using Derived = model::DeclaredEquationCouplings<Registry>;
|
||||
static_assert(dependencies::ContainsKey<model::Derivative<blocks::density::mass, blocks::gravity::potential>, Derived>);
|
||||
static_assert(dependencies::ContainsKey<model::Derivative<blocks::density::mass, blocks::rotation::angular_velocity>, Derived>);
|
||||
static_assert(!dependencies::ContainsKey<model::Derivative<blocks::gravity::potential, blocks::rotation::angular_velocity>, Derived>);
|
||||
|
||||
struct CrossAlpha;
|
||||
struct CrossBeta;
|
||||
using AlphaCoordinate = model::GeneratedCoordinateOf<CrossAlpha>;
|
||||
using BetaCoordinate = model::GeneratedCoordinateOf<CrossBeta>;
|
||||
using AlphaResidual = model::ConstraintOf<CrossAlpha>;
|
||||
using BetaResidual = model::ConstraintOf<CrossBeta>;
|
||||
|
||||
// Identity tags must be usable before either owner is complete, including as binding keys.
|
||||
static_assert(AlphaCoordinate::global_scalar && BetaResidual::global_scalar);
|
||||
using CrossBindings = model::SpecificationBindings<dependencies::Binding<AlphaCoordinate, AlphaCoordinate>, dependencies::Binding<AlphaResidual, AlphaResidual>,
|
||||
dependencies::Binding<BetaCoordinate, BetaCoordinate>, dependencies::Binding<BetaResidual, BetaResidual>>;
|
||||
|
||||
struct CrossAlpha
|
||||
{
|
||||
using ModelDefinition = model::SpecificationDefinition<CrossAlpha, "CrossAlpha", model::SpecificationRole::invariant, model::GeneratedStateKind::multiplier,
|
||||
CustomConstraint::ScalarDescription, model::Reads<BetaCoordinate>, model::Changes<BetaResidual>>;
|
||||
/** @brief Preserve separate owner identity even when the scalar units and descriptive IDs match. */
|
||||
dimensions::MassValue target() const;
|
||||
};
|
||||
|
||||
struct CrossBeta
|
||||
{
|
||||
using ModelDefinition = model::SpecificationDefinition<CrossBeta, "CrossBeta", model::SpecificationRole::invariant, model::GeneratedStateKind::multiplier,
|
||||
CustomConstraint::ScalarDescription, model::Reads<model::state::Density>, model::Changes<model::equation::SurfaceShapeBalance>>;
|
||||
/** @brief Declare a second independently owned constraint with the same scalar dimensions. */
|
||||
dimensions::MassValue target() const;
|
||||
};
|
||||
|
||||
using CrossModel = decltype(model::StellarModel(SymbolicEOS{}, Description{1}, CrossAlpha{}, CrossBeta{}));
|
||||
using PermutedCrossModel = decltype(model::StellarModel(SymbolicEOS{}, Description{1}, CrossBeta{}, CrossAlpha{}));
|
||||
using ShapeRow = blocks::surface_deformation::shape_equilibrium;
|
||||
using CrossValues = dependencies::KeyList<AlphaCoordinate, BetaCoordinate, blocks::density::mass>;
|
||||
using CrossRows = dependencies::KeyList<AlphaResidual, BetaResidual, ShapeRow>;
|
||||
using CrossBaseCouplings = dependencies::KeyList<model::Derivative<AlphaResidual, BetaCoordinate>, model::Derivative<BetaResidual, AlphaCoordinate>,
|
||||
model::Derivative<BetaResidual, blocks::density::mass>, model::Derivative<ShapeRow, BetaCoordinate>, model::Derivative<ShapeRow, blocks::density::mass>>;
|
||||
using CrossCouplings = dependencies::CombinedKeyList<CrossBaseCouplings, dependencies::KeyList<model::Derivative<BetaResidual, BetaCoordinate>>>;
|
||||
static_assert(std::same_as<CrossModel, PermutedCrossModel>);
|
||||
static_assert(!std::same_as<AlphaCoordinate, BetaCoordinate> && !std::same_as<AlphaResidual, BetaResidual>);
|
||||
static_assert(std::same_as<model::GeneratedCoordinateQuantity<CrossAlpha>, model::GeneratedCoordinateQuantity<CrossBeta>>);
|
||||
static_assert(model::SpecificationTopologyValid<CrossModel, CrossValues, CrossRows, CrossCouplings, CrossBindings>);
|
||||
static_assert(!model::SpecificationTopologyValid<CrossModel, CrossValues, CrossRows, CrossBaseCouplings, CrossBindings>);
|
||||
|
||||
// Supplying row/column identities cannot stand in for their absent owning physical prescription.
|
||||
using MissingCrossOwner = decltype(model::StellarModel(SymbolicEOS{}, Description{1}, CrossAlpha{}));
|
||||
static_assert(!model::SpecificationTopologyValid<MissingCrossOwner, CrossValues, CrossRows, CrossCouplings, CrossBindings>);
|
||||
|
||||
struct UngeneratedSpecification
|
||||
{
|
||||
using ModelDefinition = model::SpecificationDefinition<UngeneratedSpecification, "no scalar", model::SpecificationRole::boundary_condition>;
|
||||
};
|
||||
|
||||
struct InvalidGeneratedReference
|
||||
{
|
||||
using ModelDefinition = model::SpecificationDefinition<InvalidGeneratedReference, "invalid generated reference", model::SpecificationRole::invariant, model::GeneratedStateKind::multiplier,
|
||||
CustomConstraint::ScalarDescription, model::Reads<model::GeneratedCoordinateOf<UngeneratedSpecification>>, model::Changes<model::equation::HydrostaticBalance>>;
|
||||
/** @brief Exercise a syntactically valid identity whose owner does not generate a scalar. */
|
||||
dimensions::MassValue target() const;
|
||||
};
|
||||
using InvalidReferenceModel = decltype(model::StellarModel(SymbolicEOS{}, Description{1}, UngeneratedSpecification{}, InvalidGeneratedReference{}));
|
||||
using InvalidReferenceBindings = model::SpecificationBindings<dependencies::Binding<model::GeneratedCoordinateOf<InvalidGeneratedReference>, CustomCoordinate>,
|
||||
dependencies::Binding<model::ConstraintOf<InvalidGeneratedReference>, CustomResidual>, dependencies::Binding<model::GeneratedCoordinateOf<UngeneratedSpecification>, Field>>;
|
||||
static_assert(!model::SpecificationTopologyValid<InvalidReferenceModel, Values, Rows, Declared, InvalidReferenceBindings>);
|
||||
static_assert(!model::SpecificationEndpointsValid<InvalidReferenceModel, Values, Values, Rows, dependencies::KeyList<>, InvalidReferenceBindings>);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#include <concepts>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "serif/discretization/domain/schema/schemas.hpp"
|
||||
#include "serif/equations/contributions.hpp"
|
||||
#include "serif/equations/point.hpp"
|
||||
#include "serif/backend/mfem/operators/jacobian_operator.hpp"
|
||||
#include "serif/backend/mfem/operators/volume_computations.hpp"
|
||||
|
||||
namespace serif::operators::backend::mfem {
|
||||
using Schema = discretization::domain::schema::CoreEnvelopeVacuumDomainSchema;
|
||||
/** @brief A minimal density-valued volume equation, sufficient to instantiate a volume cache. */
|
||||
struct Closure final : equations::Contribution<discretization::forms::Operand<discretization::blocks::density::mass>> {
|
||||
using Operands = utils::types::TypeList<TestOperand>;
|
||||
using ResidualQuantity = dimensions::MassDensity;
|
||||
static constexpr std::string_view name = "density value";
|
||||
/** @brief Declare the point residual; only the cache types are inspected. */
|
||||
static equations::PointLinearization<Operands> evaluate(const equations::EquationPoint<Operands>&);
|
||||
/** @brief Add no quadrature order beyond the operand degree. */
|
||||
static int additional_order(TestOperand, int);
|
||||
};
|
||||
|
||||
using Density = discretization::forms::Operand<discretization::blocks::density::mass>;
|
||||
using DensityBinding = discretization::backend::mfem::TermBinding<Closure::Test, Schema>;
|
||||
using Vector = ::mfem::Vector;
|
||||
|
||||
using VolumeCoefficients = decltype(make_term_coefficients(
|
||||
bind(std::declval<const DensityBinding&>(), std::declval<const Vector&>())
|
||||
));
|
||||
using VolumeMesh = VolumeDiscretization<Closure::Test, Schema>;
|
||||
using VolumeInputs = decltype(make_volume_inputs<Closure>(
|
||||
std::declval<const VolumeMesh&>(), std::declval<const VolumeCoefficients&>(), std::declval<const Vector&>()
|
||||
));
|
||||
using VolumeCache = decltype(make_volume_equation_cache(
|
||||
std::declval<Closure>(), std::declval<VolumeMesh>(), std::declval<const VolumeInputs&>()
|
||||
));
|
||||
|
||||
template <typename KeyT>
|
||||
concept ClosureResult = requires(const VolumeCache& cache) {
|
||||
cache.template get<KeyT>();
|
||||
};
|
||||
|
||||
template <typename DisplacementT>
|
||||
concept ClosureInputsAccept = requires(
|
||||
const VolumeMesh& discretization,
|
||||
const VolumeCoefficients& coefficients,
|
||||
DisplacementT&& displacement
|
||||
) {
|
||||
make_volume_inputs<Closure>(discretization, coefficients, std::forward<DisplacementT>(displacement));
|
||||
};
|
||||
|
||||
static_assert(ClosureInputsAccept<Vector&>);
|
||||
static_assert(ClosureInputsAccept<const Vector&>);
|
||||
static_assert(!ClosureInputsAccept<Vector>);
|
||||
static_assert(!ClosureInputsAccept<const Vector>);
|
||||
|
||||
// Caches own their graphs and are movable; assignment cannot invalidate borrowed adapter addresses.
|
||||
static_assert(std::move_constructible<VolumeCache> && !std::copy_constructible<VolumeCache>);
|
||||
static_assert(!std::is_move_assignable_v<VolumeCache>);
|
||||
static_assert(std::constructible_from<JacobianOperator<VolumeCache>, VolumeCache&>);
|
||||
static_assert(!std::constructible_from<JacobianOperator<VolumeCache>, VolumeCache&&>);
|
||||
|
||||
static_assert(ClosureResult<volume_keys::Basis<0>>);
|
||||
static_assert(ClosureResult<volume_keys::Geometry<0>>);
|
||||
static_assert(ClosureResult<volume_keys::ReferenceValues<0, Density>>);
|
||||
static_assert(ClosureResult<volume_keys::QuadratureLinearization<0>>);
|
||||
static_assert(ClosureResult<volume_keys::Linearization>);
|
||||
static_assert(!ClosureResult<volume_keys::Basis<1>>);
|
||||
static_assert(!ClosureResult<volume_keys::PhysicalValues<0, Density>>);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "serif/eos/models/polytropic.hpp"
|
||||
#include "serif/equations/barotropic_density.hpp"
|
||||
#include "serif/equations/mass_normalization.hpp"
|
||||
#include "serif/equations/primal_gravity.hpp"
|
||||
#include "serif/operators/plan.hpp"
|
||||
|
||||
namespace serif::operators
|
||||
{
|
||||
// MeanField contribution coordinates and generated mass couplings.
|
||||
using Density = discretization::forms::Operand<discretization::blocks::density::mass>;
|
||||
using Potential = discretization::forms::Operand<discretization::blocks::gravity::potential>;
|
||||
using Constant = discretization::forms::Operand<discretization::blocks::barotropic_constant::mass_normalization>;
|
||||
using FirstOrder = equations::OperandValues<utils::types::TypeList<Density, Potential, Constant>>;
|
||||
using PermutedOrder = equations::OperandValues<utils::types::TypeList<Constant, Density, Potential>>;
|
||||
|
||||
static_assert(FirstOrder::index<Density>() == 0);
|
||||
static_assert(PermutedOrder::index<Density>() == 1);
|
||||
static_assert(FirstOrder::index<Potential>() == 1);
|
||||
static_assert(PermutedOrder::index<Potential>() == 2);
|
||||
static_assert(FirstOrder::index<Constant>() == 2);
|
||||
static_assert(PermutedOrder::index<Constant>() == 0);
|
||||
static_assert(std::same_as<decltype(std::declval<const FirstOrder&>().get<Density>()), const double&>);
|
||||
static_assert(std::same_as<decltype(std::declval<PermutedOrder&>().get<Density>()), double&>);
|
||||
|
||||
using MassPlan = EquationPlan<equations::MassNormalization>;
|
||||
using BalancePlan = EquationPlan<equations::BarotropicDensityBalance<eos::models::Polytrope>>;
|
||||
static_assert(std::same_as<MassPlan::Test, Constant::term_type>);
|
||||
static_assert(std::same_as<MassPlan::Columns, utils::types::TypeList<Density>>);
|
||||
static_assert(BalancePlan::column_index<Constant>() < BalancePlan::column_count);
|
||||
static_assert(std::same_as<BalancePlan::Test, Density::term_type>);
|
||||
|
||||
// A pointwise contribution receives exactly its declared fields by const reference.
|
||||
using MassPoint = equations::EquationPoint<equations::MassNormalization::Operands>;
|
||||
static_assert(decltype(MassPoint::fields)::count == 1);
|
||||
static_assert(std::same_as<decltype(std::declval<const MassPoint&>().fields.get<Density>()), const double&>);
|
||||
|
||||
// Gravity assembly names its physical contributions by their declared order.
|
||||
using PotentialContributions = equations::ContributionsOf<equations::PrimalPoissonBalance>;
|
||||
static_assert(std::same_as<std::tuple_element_t<0, PotentialContributions>, equations::PrimalPoissonBalance::PotentialFlux>);
|
||||
static_assert(std::same_as<std::tuple_element_t<1, PotentialContributions>, equations::PrimalPoissonBalance::DensitySource>);
|
||||
}
|
||||
174
src/compile_time_checks/serif/operators/plan.cpp
Normal file
174
src/compile_time_checks/serif/operators/plan.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
#include <type_traits>
|
||||
|
||||
#include "serif/eos/models/polytropic.hpp"
|
||||
#include "serif/operators/plan.hpp"
|
||||
#include "serif/equations/registry.hpp"
|
||||
#include "serif/equations/primal_gravity.hpp"
|
||||
#include "serif/equations/barotropic_density.hpp"
|
||||
#include "serif/backend/mfem/operators/operand_evaluation.hpp"
|
||||
#include "serif/backend/mfem/discretization/quadrature/order.hpp"
|
||||
#include "serif/discretization/domain/schema/schemas.hpp"
|
||||
|
||||
namespace serif::operators {
|
||||
using equations::EquationRegistry;
|
||||
using equations::OperandValues;
|
||||
using Balance = equations::BarotropicDensityBalance<eos::models::Polytrope>;
|
||||
using Density = discretization::blocks::density::mass;
|
||||
using Potential = discretization::blocks::gravity::potential;
|
||||
using Displacement = discretization::blocks::displacement::vector;
|
||||
using Schema = discretization::domain::schema::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
// Keep discontinuous-space rejection independent of the production density representation.
|
||||
struct DiscontinuousScalar : discretization::blocks::term<discretization::blocks::density> {
|
||||
using FunctionalSpace = discretization::functional_space::DiscreteFunctionalSpace<discretization::functional_space::L2, 2, 0>;
|
||||
using Support = discretization::domain::StellarDomains;
|
||||
struct residual : discretization::blocks::residual_block_base {};
|
||||
};
|
||||
|
||||
template <typename TermT, typename OperationT = discretization::forms::Operation::Value, bool PositionIndependent = true>
|
||||
struct NodalRequest : equations::Contribution<discretization::forms::Operand<TermT, OperationT>, discretization::domain::StellarSurfaceBoundary> {
|
||||
using Operands = utils::types::TypeList<discretization::forms::Operand<TermT>>;
|
||||
using Result = discretization::forms::OperandValue<discretization::forms::Operand<TermT, OperationT>>;
|
||||
static constexpr std::string_view name = "nodal capability test";
|
||||
static constexpr bool position_independent = PositionIndependent;
|
||||
/** @brief Supply a point linearization so capability checks depend only on the declared operand and support. */
|
||||
equations::PointLinearization<Operands, Result> evaluate(const equations::EquationPoint<Operands>&) const {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
using BoundaryValue = NodalRequest<Potential>;
|
||||
static_assert(equations::Equation<BoundaryValue> && equations::PointEquation<BoundaryValue>);
|
||||
static_assert(!VolumeEquation<BoundaryValue> && !ScalarVolumeEquation<BoundaryValue>);
|
||||
static_assert(std::same_as<equations::SupportOf<BoundaryValue>, discretization::domain::StellarSurfaceBoundary>);
|
||||
|
||||
static_assert(ScalarVolumeEquation<Balance>, "Barotropic density balance is a scalar volume equation.");
|
||||
static_assert(!ScalarVolumeEquation<equations::PrimalPoissonBalance>, "Primal Poisson balance samples a potential gradient.");
|
||||
static_assert(!ScalarVolumeEquation<eos::models::Polytrope>, "Polytrope is not a scalar volume equation.");
|
||||
static_assert(!ScalarValueTerm<Displacement>, "A vector field is not a scalar value term.");
|
||||
static_assert(!ScalarValueOperand<discretization::forms::Operand<Potential, discretization::forms::Operation::Gradient>>, "Gradient operand is not a scalar value operand.");
|
||||
static_assert(std::same_as<EquationPlan<Balance>::Test, Density>, "Barotropic density balance test is density.");
|
||||
static_assert(std::same_as<EquationPlan<Balance>::Support, discretization::domain::StellarDomains>, "Barotropic density balance support is stellar domains.");
|
||||
static_assert(EquationPlan<Balance>::operand_count == 4, "Barotropic density balance has four operands.");
|
||||
static_assert(OperandValues<Balance::Operands>::index<Density>() == 0, "Barotropic density balance density operand is first.");
|
||||
static_assert(OperandValues<Balance::Operands>::index<Potential>() == 1, "Barotropic density balance potential operand is second.");
|
||||
static_assert(std::same_as<
|
||||
EquationPlan<Balance>::Form<discretization::forms::Operand<Density>>,
|
||||
discretization::forms::mass<Density, Density>>, "Barotropic density balance density form is mass.");
|
||||
static_assert(std::same_as<
|
||||
EquationPlan<Balance>::Form<discretization::forms::Operand<Potential>>,
|
||||
discretization::forms::source<Potential, Density>>, "Barotropic density balance potential form is source.");
|
||||
static_assert(std::same_as<
|
||||
EquationRegistry<Balance, equations::PrimalPoissonBalance>::Equations,
|
||||
utils::types::TypeList<Balance, equations::PrimalPoissonBalance>>, "Equation registry contains density balance and Poisson balance.");
|
||||
|
||||
using DensityValue = discretization::forms::Operand<Density>;
|
||||
using PotentialValue = discretization::forms::Operand<Potential>;
|
||||
using PotentialGradient = discretization::forms::Operand<Potential, discretization::forms::Operation::Gradient>;
|
||||
static_assert(backend::mfem::EvaluableOperand<DensityValue>);
|
||||
static_assert(backend::mfem::EvaluableOperand<PotentialValue>);
|
||||
static_assert(backend::mfem::EvaluableOperand<PotentialGradient>);
|
||||
static_assert(backend::mfem::EvaluableOperand<discretization::forms::Operand<Density, discretization::forms::Operation::Gradient>>);
|
||||
static_assert(!backend::mfem::EvaluableOperand<discretization::forms::Operand<DiscontinuousScalar, discretization::forms::Operation::Gradient>>);
|
||||
static_assert(!backend::mfem::EvaluableOperand<int>);
|
||||
|
||||
using DisplacementDivergence = discretization::forms::Operand<discretization::blocks::displacement::vector, discretization::forms::Operation::Divergence>;
|
||||
static_assert(backend::mfem::EvaluableOperand<DisplacementDivergence>);
|
||||
static_assert(discretization::quadrature::backend::mfem::resolve_operand_order<DisplacementDivergence>() == 2);
|
||||
static_assert(std::same_as<discretization::forms::OperandValue<DisplacementDivergence>, double>);
|
||||
|
||||
// Describing an equation does not imply a particular backend can execute it.
|
||||
struct GradientRequest {
|
||||
using Test = Density;
|
||||
using Operands = utils::types::TypeList<PotentialGradient>;
|
||||
static constexpr std::string_view name = "scalar gradient request";
|
||||
/// Supply a syntactically valid authoring interface for the capability check.
|
||||
equations::PointLinearization<Operands> evaluate(const equations::EquationPoint<Operands>&) const { return {}; }
|
||||
/// No extra polynomial factor accompanies this gradient sample.
|
||||
int additional_order(PotentialGradient, int) const { return 0; }
|
||||
};
|
||||
static_assert(equations::Equation<GradientRequest>);
|
||||
static_assert(!ScalarVolumeEquation<GradientRequest>);
|
||||
static_assert(VolumeEquation<GradientRequest>);
|
||||
static_assert(VolumeEquation<equations::PrimalPoissonBalance>);
|
||||
static_assert(EquationPlan<equations::PrimalPoissonBalance>::column_count == 2);
|
||||
static_assert(std::same_as<discretization::forms::OperandValue<PotentialGradient>, discretization::forms::VectorValue>);
|
||||
using DisplacementValue = discretization::forms::Operand<Displacement>;
|
||||
static_assert(backend::mfem::EvaluableOperand<DisplacementValue>);
|
||||
static_assert(backend::mfem::EvaluableOperand<discretization::forms::Operand<Displacement, discretization::forms::Operation::Divergence>>);
|
||||
using DisplacementGradient = discretization::forms::Operand<Displacement, discretization::forms::Operation::Gradient>;
|
||||
using VectorTensor = discretization::forms::MatrixValue;
|
||||
using CartesianVector = discretization::forms::VectorValue;
|
||||
static_assert(backend::mfem::EvaluableOperand<DisplacementGradient>);
|
||||
static_assert(std::same_as<discretization::forms::OperandValue<DisplacementGradient>, VectorTensor>);
|
||||
static_assert(discretization::forms::component_count<VectorTensor> == 9);
|
||||
static_assert(discretization::forms::component_count<discretization::forms::PartialDerivative<VectorTensor, VectorTensor>> == 81);
|
||||
static_assert(discretization::forms::component_count<discretization::forms::PartialDerivative<VectorTensor, CartesianVector>> == 27);
|
||||
static_assert(discretization::forms::component_count<discretization::forms::PartialDerivative<CartesianVector, VectorTensor>> == 27);
|
||||
static_assert(std::same_as<discretization::forms::PartialDerivative<VectorTensor, double>, VectorTensor>);
|
||||
static_assert(std::same_as<discretization::forms::PartialDerivative<double, VectorTensor>, VectorTensor>);
|
||||
static_assert(discretization::quadrature::backend::mfem::resolve_operand_order<DisplacementGradient>() == 2);
|
||||
static_assert(std::same_as<discretization::forms::OperandValue<DisplacementValue>, discretization::forms::VectorValue>);
|
||||
static_assert(!backend::mfem::EvaluableOperand<
|
||||
discretization::forms::Operand<Displacement, discretization::forms::Operation::Curl>>);
|
||||
static_assert(!backend::mfem::EvaluableOperand<discretization::forms::Operand<Displacement,
|
||||
discretization::forms::Operation::NormalTrace>>);
|
||||
using MixedSamples = utils::types::TypeList<DisplacementValue, DisplacementDivergence, DensityValue>;
|
||||
static_assert(OperandValues<MixedSamples>::index<DisplacementValue>() == 0);
|
||||
static_assert(OperandValues<MixedSamples>::index<DisplacementDivergence>() == 1);
|
||||
static_assert(std::same_as<discretization::forms::PartialDerivative<discretization::forms::VectorValue, double>,
|
||||
discretization::forms::VectorValue>);
|
||||
static_assert(
|
||||
std::same_as<discretization::forms::PartialDerivative<discretization::forms::VectorValue, discretization::forms::VectorValue>,
|
||||
discretization::forms::MatrixValue>);
|
||||
struct BadContributionRow {
|
||||
using Test = Density;
|
||||
static constexpr std::string_view name = "malformed contribution";
|
||||
/// Bad user descriptions should fail the concept rather than instantiate invalid aliases.
|
||||
auto contributions() const {
|
||||
return std::tuple{1};
|
||||
}
|
||||
};
|
||||
static_assert(!equations::Equation<BadContributionRow>);
|
||||
|
||||
struct BadContributionCollection {
|
||||
using Test = Displacement;
|
||||
static constexpr std::string_view name = "non-tuple contributions";
|
||||
|
||||
auto contributions() const {
|
||||
return equations::PrimalPoissonBalance{1.0};
|
||||
}
|
||||
};
|
||||
|
||||
struct NestedBadCollection {
|
||||
using Test = Displacement;
|
||||
static constexpr std::string_view name = "nested non-tuple contributions";
|
||||
|
||||
auto contributions() const {
|
||||
return std::tuple{equations::PrimalPoissonBalance{1.0}, BadContributionCollection{}};
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(!equations::Equation<BadContributionCollection>);
|
||||
static_assert(!equations::Equation<NestedBadCollection>);
|
||||
} // namespace serif::operators
|
||||
|
||||
#include "serif/equations/mass_normalization.hpp"
|
||||
#include "serif/equations/angular_momentum.hpp"
|
||||
|
||||
namespace serif::operators {
|
||||
using Mass = equations::MassNormalization;
|
||||
using Constant = discretization::blocks::barotropic_constant::mass_normalization;
|
||||
using Omega = discretization::blocks::rotation::angular_velocity;
|
||||
using Momentum = equations::AngularMomentumConstraint;
|
||||
static_assert(ScalarVolumeEquation<Momentum> && VolumeEquation<Momentum>);
|
||||
static_assert(std::same_as<EquationPlan<Momentum>::Test, Omega>);
|
||||
static_assert(std::same_as<EquationPlan<Momentum>::Columns, utils::types::TypeList<DensityValue, discretization::forms::Operand<Omega>>>);
|
||||
static_assert(ScalarVolumeEquation<Mass> && VolumeEquation<Mass>);
|
||||
static_assert(EquationPlan<Mass>::column_count == 1);
|
||||
static_assert(EquationPlan<Balance>::column_count == 4);
|
||||
static_assert(backend::mfem::EvaluableOperand<discretization::forms::Operand<Constant>>);
|
||||
static_assert(backend::mfem::EvaluableOperand<discretization::forms::Operand<Omega>>);
|
||||
static_assert(!MappedOperand<discretization::forms::Operand<Omega, discretization::forms::Operation::Divergence>>);
|
||||
static_assert(!discretization::blocks::GlobalScalarTerm<discretization::blocks::surface_deformation::coefficients>);
|
||||
}
|
||||
|
||||
76
src/compile_time_checks/serif/solver/continuation.cpp
Normal file
76
src/compile_time_checks/serif/solver/continuation.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include <memory>
|
||||
|
||||
#include "serif/solver/continuation/polytropic.hpp"
|
||||
#include "serif/solver/continuation/identified_equations.hpp"
|
||||
#include "serif/solver/continuation/options.hpp"
|
||||
|
||||
namespace {
|
||||
namespace family = serif::solver::continuation;
|
||||
using Index = family::PolytropicIndex;
|
||||
using Constant = family::PolytropicConstant;
|
||||
using Momentum = family::AngularMomentum;
|
||||
using Definition = decltype([]() {});
|
||||
using Model = decltype(serif::model::make_stellar_model(std::declval<serif::eos::models::Polytrope>(), Definition{},
|
||||
std::declval<serif::model::FixedAngularMomentum>()));
|
||||
using NoRotation = decltype(serif::model::make_stellar_model(std::declval<serif::eos::models::Polytrope>(), Definition{}));
|
||||
|
||||
/** @brief Selected coordinates must be unique typed physical parameters. */
|
||||
template <typename... Parameters>
|
||||
concept Selectable = requires(Parameters... parameters) { family::in(parameters...); };
|
||||
/** @brief Dependent laws must read selected inputs, have distinct outputs, and avoid independent outputs. */
|
||||
template <typename Selection, typename Law>
|
||||
concept Attachable = requires(Selection selection, Law law) { std::move(selection).with(std::move(law)); };
|
||||
/** @brief A family must account for parameter ownership, required dependent laws, and physical identity. */
|
||||
template <typename Selection, typename Target>
|
||||
concept Bindable = requires(Selection selection, Target target) { std::move(selection).bind(std::move(target)); };
|
||||
/** @brief Laws must preserve physical units and callable signatures. */
|
||||
template <typename Function>
|
||||
concept ConstantLaw = requires(Function function) { family::law(Constant{}, family::depends_on(Index{}), std::move(function)); };
|
||||
/** @brief Starting values have the selected parameter's units. */
|
||||
template <typename Parameter, typename Value>
|
||||
concept StartingValue = requires(Parameter parameter, Value value) { family::starting_at(parameter, value); };
|
||||
/** @brief Strict stage settings must cover each bound coordinate, not only a subset. */
|
||||
template <typename Setting, typename Bound>
|
||||
concept Applicable = requires(Setting setting, serif::solver::continuation::Options<Bound>& options) { setting.apply(options); };
|
||||
|
||||
using IndexSelection = decltype(family::in(Index{}));
|
||||
using MomentumSelection = decltype(family::in(Momentum{}));
|
||||
using KLaw = decltype(family::law(Constant{}, family::depends_on(Index{}), [](double value) { return 1.0 / value; }));
|
||||
using Complete = decltype(std::declval<IndexSelection>().with(std::declval<KLaw>()));
|
||||
using Both = decltype(family::in(Index{}, Momentum{}).with(std::declval<KLaw>()));
|
||||
using Bound = decltype(std::declval<Both>().bind(std::declval<Model>()));
|
||||
using SelfLaw = decltype(family::law(Constant{}, family::depends_on(Constant{}), [](double value) { return value; }));
|
||||
struct UnidentifiedEOS {};
|
||||
using Unidentified = decltype(serif::model::make_stellar_model(UnidentifiedEOS{}, Definition{}, std::declval<serif::model::FixedAngularMomentum>()));
|
||||
struct OpaqueEquations { double pressure; };
|
||||
using Opaque = decltype(serif::model::make_stellar_model(std::declval<serif::eos::models::Polytrope>(), OpaqueEquations{}, std::declval<serif::model::FixedAngularMomentum>()));
|
||||
using Identified = decltype(family::identified_equations([](const auto&) { return 1; }, serif::dimensions::PressureValue{0.1}));
|
||||
using ExplicitIdentity = decltype(serif::model::make_stellar_model(std::declval<serif::eos::models::Polytrope>(),
|
||||
std::declval<Identified>(), std::declval<serif::model::FixedAngularMomentum>()));
|
||||
|
||||
static_assert(family::PhysicalParameter<Index> && family::PhysicalParameter<Constant> && family::PhysicalParameter<Momentum>);
|
||||
static_assert(!Selectable<> && !Selectable<int> && !Selectable<Index, Index>);
|
||||
static_assert(Attachable<IndexSelection, KLaw>);
|
||||
static_assert(!Attachable<MomentumSelection, KLaw>);
|
||||
static_assert(!Attachable<Complete, KLaw>);
|
||||
static_assert(!Attachable<IndexSelection, SelfLaw>);
|
||||
static_assert(!Attachable<decltype(family::in(Index{}, Constant{})), KLaw>);
|
||||
static_assert(!ConstantLaw<decltype([](double) { return serif::dimensions::AngularMomentumValue{1.0}; })>);
|
||||
static_assert(!ConstantLaw<decltype([](serif::dimensions::AngularMomentumValue) { return 1.0; })>);
|
||||
static_assert(!Bindable<IndexSelection, Model>);
|
||||
static_assert(Bindable<Complete, Model>);
|
||||
static_assert(!Bindable<MomentumSelection, NoRotation>);
|
||||
static_assert(!Bindable<MomentumSelection, Unidentified>);
|
||||
static_assert(!Bindable<MomentumSelection, Opaque>);
|
||||
static_assert(Bindable<MomentumSelection, ExplicitIdentity>);
|
||||
static_assert(std::same_as<std::tuple_element_t<0, decltype(std::declval<Identified>().identity)>, serif::dimensions::PressureValue>);
|
||||
static_assert(StartingValue<Index, double>);
|
||||
static_assert(StartingValue<Momentum, serif::dimensions::AngularMomentumValue>);
|
||||
static_assert(!StartingValue<Momentum, double>);
|
||||
static_assert(Bound::coordinate_count == 2);
|
||||
static_assert(Bound::coordinate_index(Index{}) == 0 && Bound::coordinate_index(Momentum{}) == 1);
|
||||
static_assert(!Bound::has_coordinate<Constant>);
|
||||
static_assert(Applicable<decltype(family::prefer(Index{})), Bound>);
|
||||
static_assert(!Applicable<decltype(family::stages(Index{})), Bound>);
|
||||
static_assert(Applicable<decltype(family::stages(Momentum{}, Index{})), Bound>);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#include <concepts>
|
||||
#include <span>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem/linalg/densemat.hpp>
|
||||
|
||||
#include "serif/solver/normalization/scales.hpp"
|
||||
#include "serif/backend/mfem/solver/normalization/operators.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace dimensions = serif::dimensions;
|
||||
namespace normalization = serif::solver::normalization;
|
||||
namespace backend = normalization::backend::mfem;
|
||||
using Map = normalization::DiagonalNormalization;
|
||||
|
||||
template <typename Quantity>
|
||||
concept HasStellarScale = requires(const normalization::StellarReferenceScales &scales)
|
||||
{
|
||||
{ normalization::characteristic_scale(scales, Quantity{}) } -> std::same_as<dimensions::QuantityValue<Quantity>>;
|
||||
};
|
||||
|
||||
static_assert(HasStellarScale<dimensions::Mass>);
|
||||
static_assert(HasStellarScale<dimensions::SpecificEnthalpy>);
|
||||
static_assert(HasStellarScale<dimensions::GravitationalPotential>);
|
||||
static_assert(HasStellarScale<dimensions::InverseTimeSquared>);
|
||||
static_assert(!HasStellarScale<dimensions::Temperature>);
|
||||
static_assert(!HasStellarScale<int>);
|
||||
static_assert(!std::convertible_to<double, dimensions::QuantityValue<dimensions::GravitationalConstant>>);
|
||||
static_assert(std::equality_comparable<normalization::StellarReferenceScales>);
|
||||
static_assert(!std::default_initializable<Map>);
|
||||
static_assert(std::same_as<decltype(std::declval<const Map &>().state_factors()), std::span<const double>>);
|
||||
static_assert(std::same_as<decltype(std::declval<const Map &>().residual_factors()), std::span<const double>>);
|
||||
|
||||
static_assert(std::constructible_from<backend::ScaledJacobianOperator, const mfem::DenseMatrix &, const Map &>);
|
||||
static_assert(std::constructible_from<backend::ScaledJacobianOperator, const mfem::DenseMatrix &, Map &&>);
|
||||
static_assert(!std::constructible_from<backend::ScaledJacobianOperator, mfem::DenseMatrix &&, const Map &>);
|
||||
static_assert(!std::constructible_from<backend::ScaledJacobianOperator, const mfem::DenseMatrix &&, const Map &>);
|
||||
static_assert(std::constructible_from<backend::ScaledInverseOperator, const mfem::DenseMatrix &, const Map &>);
|
||||
static_assert(!std::constructible_from<backend::ScaledInverseOperator, mfem::DenseMatrix &&, const Map &>);
|
||||
static_assert(!std::constructible_from<backend::ScaledInverseOperator, const mfem::DenseMatrix &&, const Map &>);
|
||||
static_assert(!std::is_copy_assignable_v<backend::ScaledJacobianOperator>);
|
||||
static_assert(!std::is_move_assignable_v<backend::ScaledJacobianOperator>);
|
||||
static_assert(std::constructible_from<backend::ScaledPreconditioner, mfem::Solver &, const backend::ScaledJacobianOperator &>);
|
||||
static_assert(!std::constructible_from<backend::ScaledPreconditioner, mfem::Solver &&, const backend::ScaledJacobianOperator &>);
|
||||
static_assert(!std::constructible_from<backend::ScaledPreconditioner, mfem::Solver &, backend::ScaledJacobianOperator &&>);
|
||||
static_assert(!std::constructible_from<backend::ScaledPreconditioner, mfem::Solver &, const backend::ScaledJacobianOperator &&>);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#include <concepts>
|
||||
|
||||
#include "serif/solver/normalization/computations.hpp"
|
||||
|
||||
namespace serif::solver::normalization::checks {
|
||||
namespace dependencies = utils::dependencies;
|
||||
using Density = discretization::blocks::density::mass;
|
||||
using Potential = discretization::blocks::gravity::potential;
|
||||
using Shape = discretization::blocks::surface_deformation::coefficients;
|
||||
using ShapeRow = discretization::blocks::surface_deformation::shape_equilibrium;
|
||||
using MassConstant = discretization::blocks::barotropic_constant::mass_normalization;
|
||||
using SurfaceMetric = geometry::deformation::keys::SurfaceMetric;
|
||||
using DensityMethod = DimensionlessRieszCoordinate<dimensions::MassDensity, ReferenceVolumeMetric<Density>>;
|
||||
|
||||
static_assert(DensityMethod::geometry == ReferenceGeometry::volume);
|
||||
static_assert(DimensionlessRieszCoordinate<dimensions::Length, SurfaceMetric>::geometry == ReferenceGeometry::surface);
|
||||
static_assert(DimensionlessRieszCoordinate<dimensions::SpecificEnthalpy, UnitMetric>::geometry == ReferenceGeometry::algebraic);
|
||||
static_assert(std::same_as<DensityMethod::Quantity, dimensions::MassDensity>);
|
||||
static_assert(std::same_as<DensityMethod::Metric, ReferenceVolumeMetric<Density>>);
|
||||
|
||||
using DensityLayout = CoordinateLayout<dependencies::KeyList<Density>, dependencies::KeyList<Density>>;
|
||||
using DensityPlan = NormalizationPlan<dependencies::Bindings<dependencies::Binding<Density, DensityMethod>>,
|
||||
dependencies::Bindings<dependencies::Binding<Density, DensityMethod>>>;
|
||||
static_assert(NormalizationPreparable<DensityPlan, DensityLayout>);
|
||||
static_assert(!NormalizationPlanType<NormalizationPlan<dependencies::Bindings<dependencies::Binding<Potential, DensityMethod>>, dependencies::Bindings<>>>);
|
||||
static_assert(!NormalizationPlanType<NormalizationPlan<dependencies::Bindings<dependencies::Binding<Density,
|
||||
DimensionlessRieszCoordinate<dimensions::Pressure, ReferenceVolumeMetric<Density>>>>, dependencies::Bindings<>>>);
|
||||
static_assert(!NormalizationPlanType<NormalizationPlan<dependencies::Bindings<dependencies::Binding<Density,
|
||||
DimensionlessRieszCoordinate<dimensions::MassDensity, ReferenceVolumeMetric<Density>, static_cast<ReferenceGeometry>(99)>>>, dependencies::Bindings<>>>);
|
||||
|
||||
struct PressureRow {
|
||||
using Test = Density;
|
||||
using ResidualQuantity = dimensions::Pressure;
|
||||
};
|
||||
using PressurePlan = NormalizationPlan<dependencies::Bindings<>, dependencies::Bindings<dependencies::Binding<PressureRow,
|
||||
DimensionlessRieszCoordinate<dimensions::Pressure, ReferenceVolumeMetric<Density>>>>>;
|
||||
static_assert(NormalizationPreparable<PressurePlan, CoordinateLayout<dependencies::KeyList<>, dependencies::KeyList<PressureRow>>>);
|
||||
static_assert(!NormalizationPlanType<NormalizationPlan<dependencies::Bindings<>, dependencies::Bindings<dependencies::Binding<PressureRow, DensityMethod>>>>);
|
||||
|
||||
struct Selection {};
|
||||
using SelectedMethod = WithPointRows<DensityMethod, dimensions::Pressure, Selection>;
|
||||
static_assert(NormalizationPreparable<NormalizationPlan<dependencies::Bindings<>, dependencies::Bindings<dependencies::Binding<Density, SelectedMethod>>>,
|
||||
CoordinateLayout<dependencies::KeyList<>, dependencies::KeyList<Density>>>);
|
||||
static_assert(!NormalizationPlanType<NormalizationPlan<dependencies::Bindings<dependencies::Binding<Density, SelectedMethod>>, dependencies::Bindings<>>>);
|
||||
|
||||
using SurfacePlan = NormalizationPlan<dependencies::Bindings<dependencies::Binding<Shape, DimensionlessRieszCoordinate<dimensions::Length, SurfaceMetric>>>,
|
||||
dependencies::Bindings<dependencies::Binding<ShapeRow, DimensionlessRieszCoordinate<dimensions::Force, SurfaceMetric>>>>;
|
||||
static_assert(NormalizationPreparable<SurfacePlan, CoordinateLayout<dependencies::KeyList<Shape>, dependencies::KeyList<ShapeRow>>>);
|
||||
using ScalarPlan = NormalizationPlan<dependencies::Bindings<dependencies::Binding<MassConstant, DimensionlessRieszCoordinate<dimensions::GravitationalPotential, UnitMetric>>>,
|
||||
dependencies::Bindings<dependencies::Binding<MassConstant, DimensionlessRieszCoordinate<dimensions::Mass, UnitMetric>>>>;
|
||||
static_assert(NormalizationPreparable<ScalarPlan, CoordinateLayout<dependencies::KeyList<MassConstant>, dependencies::KeyList<MassConstant>>>);
|
||||
static_assert(!NormalizationPlanType<NormalizationPlan<dependencies::Bindings<dependencies::Binding<MassConstant,
|
||||
DimensionlessRieszCoordinate<dimensions::GravitationalPotential, UnitMetric, ReferenceGeometry::volume>>>, dependencies::Bindings<>>>);
|
||||
|
||||
struct CustomMetric {
|
||||
using Coordinates = dependencies::KeyList<Density>;
|
||||
};
|
||||
|
||||
/** @brief Custom metrics must state their coordinate convention instead of silently assuming volume weighting. */
|
||||
template <typename Metric>
|
||||
concept HasDefaultDimensionlessGeometry = requires {
|
||||
typename DimensionlessRieszCoordinate<dimensions::MassDensity, Metric>;
|
||||
};
|
||||
static_assert(!HasDefaultDimensionlessGeometry<CustomMetric>);
|
||||
using CustomPlan = NormalizationPlan<dependencies::Bindings<dependencies::Binding<Density,
|
||||
DimensionlessRieszCoordinate<dimensions::MassDensity, CustomMetric, ReferenceGeometry::volume>>>, dependencies::Bindings<>>;
|
||||
static_assert(NormalizationPreparable<CustomPlan, CoordinateLayout<dependencies::KeyList<Density>, dependencies::KeyList<>>>);
|
||||
}
|
||||
426
src/compile_time_checks/serif/solver/normalization/plan.cpp
Normal file
426
src/compile_time_checks/serif/solver/normalization/plan.cpp
Normal file
@@ -0,0 +1,426 @@
|
||||
#include <array>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <expected>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "serif/solver/normalization/computations.hpp"
|
||||
#include "serif/solver/normalization/physical_coordinates.hpp"
|
||||
#include "serif/equations/angular_momentum.hpp"
|
||||
#include "serif/equations/mass_normalization.hpp"
|
||||
#include "serif/equations/primal_gravity.hpp"
|
||||
#include "serif/eos/models/polytropic.hpp"
|
||||
#include "serif/utils/dependencies/computation_cache.hpp"
|
||||
|
||||
namespace serif::solver::normalization::checks
|
||||
{
|
||||
namespace blocks = discretization::blocks;
|
||||
namespace dependencies = utils::dependencies;
|
||||
using dependencies::Binding;
|
||||
using dependencies::Bindings;
|
||||
using dependencies::KeyList;
|
||||
using Density = blocks::density::mass;
|
||||
using Potential = blocks::gravity::potential;
|
||||
using Displacement = blocks::displacement::vector;
|
||||
using MassConstant = blocks::barotropic_constant::mass_normalization;
|
||||
using AngularVelocity = blocks::rotation::angular_velocity;
|
||||
using CentralBorder = blocks::frame_acceleration::component<0>;
|
||||
using SurfaceCoefficients = blocks::surface_deformation::coefficients;
|
||||
using SurfaceEquilibrium = blocks::surface_deformation::shape_equilibrium;
|
||||
using SurfaceMetric = geometry::deformation::keys::SurfaceMetric;
|
||||
|
||||
struct FirstValue
|
||||
{
|
||||
};
|
||||
|
||||
struct SecondValue
|
||||
{
|
||||
};
|
||||
|
||||
struct FirstResidual
|
||||
{
|
||||
};
|
||||
|
||||
struct SecondResidual
|
||||
{
|
||||
};
|
||||
|
||||
struct ForeignCoordinate
|
||||
{
|
||||
};
|
||||
|
||||
using Values = KeyList<FirstValue, SecondValue>;
|
||||
using Residuals = KeyList<FirstResidual, SecondResidual>;
|
||||
using ValueRules = Bindings<Binding<FirstValue, IdentityCoordinate>, Binding<SecondValue, IdentityCoordinate>>;
|
||||
using ResidualRules = Bindings<Binding<FirstResidual, IdentityCoordinate>, Binding<SecondResidual, IdentityCoordinate>>;
|
||||
using CompletePlan = NormalizationPlan<ValueRules, ResidualRules>;
|
||||
using ReorderedPlan = NormalizationPlan<
|
||||
Bindings<Binding<SecondValue, IdentityCoordinate>, Binding<FirstValue, IdentityCoordinate>>,
|
||||
Bindings<Binding<SecondResidual, IdentityCoordinate>, Binding<FirstResidual, IdentityCoordinate>>>;
|
||||
|
||||
// MeanField plan.cpp: both roles prove exact ownership independently of declaration order.
|
||||
static_assert(NormalizationPlanType<CompletePlan>);
|
||||
static_assert(NormalizationPlanType<const CompletePlan&>);
|
||||
static_assert(CompleteNormalizationFor<CompletePlan, Values, Residuals>);
|
||||
static_assert(CompleteNormalizationFor<ReorderedPlan, Values, Residuals>);
|
||||
static_assert(CompleteNormalizationFor<const ReorderedPlan&, const Values&, volatile Residuals&&>);
|
||||
static_assert(CompleteNormalizationFor<CompletePlan, KeyList<SecondValue, FirstValue>, KeyList<SecondResidual, FirstResidual>>);
|
||||
static_assert(CompleteNormalizationFor<NormalizationPlan<Bindings<>, Bindings<>>, KeyList<>, KeyList<>>);
|
||||
static_assert(CompleteNormalizationFor<NormalizationPlan<Bindings<Binding<Density, IdentityCoordinate>>, Bindings<Binding<Density, IdentityCoordinate>>>, KeyList<Density>, KeyList<Density>>);
|
||||
|
||||
using MissingValue = NormalizationPlan<Bindings<Binding<FirstValue, IdentityCoordinate>>, ResidualRules>;
|
||||
using MissingResidual = NormalizationPlan<ValueRules, Bindings<Binding<FirstResidual, IdentityCoordinate>>>;
|
||||
using DuplicateValue = NormalizationPlan<Bindings<Binding<FirstValue, IdentityCoordinate>, Binding<FirstValue, IdentityCoordinate>>, ResidualRules>;
|
||||
using DuplicateResidual = NormalizationPlan<ValueRules, Bindings<Binding<FirstResidual, IdentityCoordinate>, Binding<FirstResidual, IdentityCoordinate>>>;
|
||||
using ForeignValue = NormalizationPlan<Bindings<Binding<FirstValue, IdentityCoordinate>, Binding<ForeignCoordinate, IdentityCoordinate>>, ResidualRules>;
|
||||
using ForeignResidual = NormalizationPlan<ValueRules, Bindings<Binding<FirstResidual, IdentityCoordinate>, Binding<ForeignCoordinate, IdentityCoordinate>>>;
|
||||
static_assert(!CompleteNormalizationFor<MissingValue, Values, Residuals>);
|
||||
static_assert(!CompleteNormalizationFor<MissingResidual, Values, Residuals>);
|
||||
static_assert(!NormalizationPlanType<DuplicateValue>);
|
||||
static_assert(!NormalizationPlanType<DuplicateResidual>);
|
||||
static_assert(!CompleteNormalizationFor<ForeignValue, Values, Residuals>);
|
||||
static_assert(!CompleteNormalizationFor<ForeignResidual, Values, Residuals>);
|
||||
static_assert(!CompleteNormalizationFor<CompletePlan, KeyList<FirstValue, FirstValue>, Residuals>);
|
||||
static_assert(!CompleteNormalizationFor<CompletePlan, Values, KeyList<FirstResidual, FirstResidual>>);
|
||||
static_assert(!CompleteNormalizationFor<CompletePlan, Residuals, Values>);
|
||||
static_assert(!CompleteNormalizationFor<CompletePlan, int, Residuals>);
|
||||
static_assert(!CompleteNormalizationFor<const int&, Values, Residuals>);
|
||||
static_assert(!NormalizationPlanType<NormalizationPlan<int, ResidualRules>>);
|
||||
static_assert(!NormalizationPlanType<NormalizationPlan<ValueRules, void>>);
|
||||
|
||||
using Layout = CoordinateLayout<const Values&, volatile Residuals&&>;
|
||||
static_assert(std::same_as<Layout::Values, Values>);
|
||||
static_assert(std::same_as<Layout::Residuals, Residuals>);
|
||||
static_assert(std::same_as<decltype(Layout::state_sizes), std::array<std::size_t, 2>>);
|
||||
static_assert(std::same_as<decltype(Layout::residual_sizes), std::array<std::size_t, 2>>);
|
||||
static_assert(std::is_aggregate_v<Layout>);
|
||||
|
||||
/** @brief Keep invalid layout declarations safely testable without instantiating their storage. */
|
||||
template <typename StateKeys, typename ResidualKeys>
|
||||
concept CanDeclareLayout = requires
|
||||
{
|
||||
typename CoordinateLayout<StateKeys, ResidualKeys>;
|
||||
};
|
||||
|
||||
static_assert(!CanDeclareLayout<int, Residuals>);
|
||||
static_assert(!CanDeclareLayout<Values, int>);
|
||||
static_assert(!CanDeclareLayout<KeyList<FirstValue, FirstValue>, Residuals>);
|
||||
|
||||
using DensityRiesz = PhysicalRieszCoordinate<dimensions::MassDensity, ReferenceVolumeMetric<Density>>;
|
||||
using DisplacementRiesz = PhysicalRieszCoordinate<dimensions::Length, ReferenceVolumeMetric<Displacement>>;
|
||||
using PhysicalValues = NormalizationPlan<Bindings<Binding<Density, DensityRiesz>, Binding<Displacement, DisplacementRiesz>>, Bindings<>>;
|
||||
static_assert(CompleteNormalizationFor<PhysicalValues, KeyList<Displacement, Density>, KeyList<>>);
|
||||
static_assert(!MetricFor<ReferenceVolumeMetric<Displacement>, Density>);
|
||||
static_assert(!MetricFor<ReferenceVolumeMetric<Density>, Potential>);
|
||||
static_assert(!MetricFor<UnitMetric, Density>);
|
||||
static_assert(!MetricFor<int, Density>);
|
||||
static_assert(!MetricFor<UnitMetric, int>);
|
||||
static_assert(!NormalizationPlanType<NormalizationPlan<Bindings<Binding<Density, PhysicalRieszCoordinate<dimensions::Pressure, ReferenceVolumeMetric<Density>>>>, Bindings<>>>);
|
||||
static_assert(!NormalizationPlanType<NormalizationPlan<Bindings<Binding<Density, PhysicalRieszCoordinate<dimensions::MassDensity, ReferenceVolumeMetric<Displacement>>>>, Bindings<>>>);
|
||||
|
||||
struct PressureRow
|
||||
{
|
||||
using Test = Density;
|
||||
using Quantity = dimensions::MassDensity;
|
||||
using ResidualQuantity = dimensions::Pressure;
|
||||
};
|
||||
|
||||
struct MassRow
|
||||
{
|
||||
using Test = MassConstant;
|
||||
using ResidualQuantity = dimensions::Mass;
|
||||
};
|
||||
|
||||
struct CentralRow
|
||||
{
|
||||
using Test = Density;
|
||||
using Border = CentralBorder;
|
||||
using ResidualQuantity = dimensions::MassDensity;
|
||||
};
|
||||
|
||||
// Row physics overrides the sampled field's quantity; a central point row uses its scalar border metric.
|
||||
using PressureRiesz = PhysicalRieszCoordinate<dimensions::Pressure, ReferenceVolumeMetric<Density>>;
|
||||
using PressurePlan = NormalizationPlan<Bindings<>, Bindings<Binding<PressureRow, PressureRiesz>>>;
|
||||
static_assert(CompleteNormalizationFor<PressurePlan, KeyList<>, KeyList<PressureRow>>);
|
||||
static_assert(!NormalizationPlanType<NormalizationPlan<Bindings<>, Bindings<Binding<PressureRow, DensityRiesz>>>>);
|
||||
static_assert(NormalizationPlanType<NormalizationPlan<Bindings<>, Bindings<Binding<Density, PressureRiesz>>>>);
|
||||
static_assert(MetricFor<UnitMetric, MassRow>);
|
||||
static_assert(MetricFor<UnitMetric, CentralRow>);
|
||||
static_assert(!MetricFor<ReferenceVolumeMetric<Density>, CentralRow>);
|
||||
static_assert(NormalizationPlanType<NormalizationPlan<Bindings<>, Bindings<Binding<MassRow, PhysicalRieszCoordinate<dimensions::Mass, UnitMetric>>>>>);
|
||||
static_assert(!NormalizationPlanType<NormalizationPlan<Bindings<>, Bindings<Binding<MassRow, PhysicalRieszCoordinate<dimensions::SpecificEnthalpy, UnitMetric>>>>>);
|
||||
|
||||
struct SelectedSurfaceRows
|
||||
{
|
||||
};
|
||||
|
||||
using PotentialRiesz = PhysicalRieszCoordinate<dimensions::GravitationalPotential, ReferenceVolumeMetric<Potential>>;
|
||||
using HybridResidual = WithPointRows<PotentialRiesz, dimensions::Pressure, SelectedSurfaceRows>;
|
||||
static_assert(NormalizationPlanType<NormalizationPlan<Bindings<>, Bindings<Binding<Potential, HybridResidual>>>>);
|
||||
static_assert(!NormalizationPlanType<NormalizationPlan<Bindings<Binding<Potential, HybridResidual>>, Bindings<>>>);
|
||||
static_assert(!NormalizationPlanType<NormalizationPlan<Bindings<>, Bindings<Binding<Potential, WithPointRows<IdentityCoordinate, dimensions::Pressure, SelectedSurfaceRows>>>>>);
|
||||
|
||||
// Inherited scalar block aliases coincide; physical coordinate identities must never do so.
|
||||
static_assert(std::same_as<MassConstant::value, AngularVelocity::value>);
|
||||
static_assert(std::same_as<MassConstant::residual, CentralBorder::residual>);
|
||||
static_assert(!std::same_as<MassConstant, AngularVelocity>);
|
||||
using ScalarValues = KeyList<MassConstant, AngularVelocity, CentralBorder>;
|
||||
using ScalarPlan = NormalizationPlan<Bindings<
|
||||
Binding<AngularVelocity, PhysicalRieszCoordinate<dimensions::AngularVelocity, UnitMetric>>,
|
||||
Binding<CentralBorder, PhysicalRieszCoordinate<dimensions::Acceleration, UnitMetric>>,
|
||||
Binding<MassConstant, PhysicalRieszCoordinate<dimensions::GravitationalPotential, UnitMetric>>>, Bindings<>>;
|
||||
static_assert(CompleteNormalizationFor<ScalarPlan, ScalarValues, KeyList<>>);
|
||||
static_assert(!CompleteNormalizationFor<ScalarPlan, KeyList<MassConstant::value>, KeyList<>>);
|
||||
static_assert(!MetricFor<UnitMetric, MassConstant::value>);
|
||||
|
||||
struct CustomDensityMetric
|
||||
{
|
||||
using Coordinates = KeyList<Density>;
|
||||
};
|
||||
|
||||
struct SharedFieldMetric
|
||||
{
|
||||
using Coordinates = KeyList<Density, Potential>;
|
||||
};
|
||||
|
||||
struct MalformedMetric
|
||||
{
|
||||
using Coordinates = int;
|
||||
};
|
||||
|
||||
struct MissingMetricMetadata
|
||||
{
|
||||
};
|
||||
|
||||
struct DuplicateMetricCoordinates
|
||||
{
|
||||
using Coordinates = KeyList<Density, Density>;
|
||||
};
|
||||
|
||||
struct PretendRieszMethod
|
||||
{
|
||||
using Quantity = dimensions::MassDensity;
|
||||
using Metric = CustomDensityMetric;
|
||||
};
|
||||
|
||||
static_assert(MetricFor<CustomDensityMetric, Density>);
|
||||
static_assert(MetricFor<CustomDensityMetric, PressureRow>);
|
||||
static_assert(!MetricFor<CustomDensityMetric, Potential>);
|
||||
static_assert(MetricFor<SharedFieldMetric, Density>);
|
||||
static_assert(MetricFor<SharedFieldMetric, Potential>);
|
||||
static_assert(!MetricFor<MalformedMetric, Density>);
|
||||
static_assert(!MetricFor<MissingMetricMetadata, Density>);
|
||||
static_assert(!MetricFor<DuplicateMetricCoordinates, Density>);
|
||||
static_assert(NormalizationPlanType<NormalizationPlan<Bindings<Binding<Density, PhysicalRieszCoordinate<dimensions::MassDensity, CustomDensityMetric>>>, Bindings<>>>);
|
||||
static_assert(!NormalizationPlanType<NormalizationPlan<Bindings<Binding<Density, PretendRieszMethod>>, Bindings<>>>);
|
||||
static_assert(!NormalizationPlanType<NormalizationPlan<Bindings<Binding<Density, int>>, Bindings<>>>);
|
||||
static_assert(!NormalizationPlanType<NormalizationPlan<Bindings<Binding<Density, const IdentityCoordinate&>>, Bindings<>>>);
|
||||
|
||||
// Native shape coordinates share deformation's trace metric, independently of the volume displacement basis.
|
||||
static_assert(MetricFor<SurfaceMetric, SurfaceCoefficients>);
|
||||
static_assert(MetricFor<SurfaceMetric, SurfaceEquilibrium>);
|
||||
static_assert(!MetricFor<SurfaceMetric, blocks::displacement::vector>);
|
||||
static_assert(!MetricFor<UnitMetric, SurfaceCoefficients>);
|
||||
using SurfacePlan = NormalizationPlan<
|
||||
Bindings<Binding<SurfaceCoefficients, PhysicalRieszCoordinate<dimensions::Length, SurfaceMetric>>>,
|
||||
Bindings<Binding<SurfaceEquilibrium, PhysicalRieszCoordinate<dimensions::Force, SurfaceMetric>>>>;
|
||||
static_assert(CompleteNormalizationFor<SurfacePlan, KeyList<SurfaceCoefficients>, KeyList<SurfaceEquilibrium>>);
|
||||
|
||||
struct MoveOnlyPrescription final : NormalizationPrescriptionTag
|
||||
{
|
||||
/** @brief Retain runtime policy ownership without requiring a copyable prescription. */
|
||||
MoveOnlyPrescription() = default;
|
||||
/** @brief Prevent accidental duplication of the method's owned state. */
|
||||
MoveOnlyPrescription(const MoveOnlyPrescription&) = delete;
|
||||
/** @brief Prevent replacing an owned method through a copy. */
|
||||
MoveOnlyPrescription& operator=(const MoveOnlyPrescription&) = delete;
|
||||
/** @brief Transfer the custom prescription into its dependency resource. */
|
||||
MoveOnlyPrescription(MoveOnlyPrescription&&) = default;
|
||||
/** @brief Transfer replacement policy state explicitly. */
|
||||
MoveOnlyPrescription& operator=(MoveOnlyPrescription&&) = default;
|
||||
};
|
||||
|
||||
/** @brief Require explicit policy identity rather than accepting unrelated preparation-like objects. */
|
||||
template <typename Candidate>
|
||||
concept CanDeclareRuntimeMethod = requires
|
||||
{
|
||||
typename RuntimePreparedCoordinate<Candidate>;
|
||||
};
|
||||
|
||||
using CustomMethod = RuntimePreparedCoordinate<const MoveOnlyPrescription&>;
|
||||
using CustomPlan = NormalizationPlan<Bindings<Binding<Density, CustomMethod>>, Bindings<Binding<PressureRow, CustomMethod>>>;
|
||||
static_assert(!std::copy_constructible<MoveOnlyPrescription>);
|
||||
static_assert(std::move_constructible<MoveOnlyPrescription>);
|
||||
static_assert(NormalizationPrescription<const MoveOnlyPrescription&>);
|
||||
static_assert(std::same_as<CustomMethod::Prescription, MoveOnlyPrescription>);
|
||||
static_assert(CompleteNormalizationFor<CustomPlan, KeyList<Density>, KeyList<PressureRow>>);
|
||||
static_assert(!CanDeclareRuntimeMethod<int>);
|
||||
static_assert(!NormalizationPrescription<PretendRieszMethod>);
|
||||
|
||||
struct PreparedPrescription : NormalizationPrescriptionTag
|
||||
{
|
||||
/** @brief Supply owning factors for the precise role and coordinate requested by the graph. */
|
||||
template <blocks::RepresentedValueKind Kind, typename Coordinate>
|
||||
[[nodiscard]] std::expected<std::vector<double>, std::string> prepare_factors(keys::Factors<Kind, Coordinate>, std::size_t size) const
|
||||
{
|
||||
return std::vector<double>(size, 0.5);
|
||||
}
|
||||
|
||||
/** @brief The stateless prescription is suitable for the dependency value snapshot policy. */
|
||||
[[nodiscard]] bool operator==(const PreparedPrescription&) const noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct DerivedPrescription final : PreparedPrescription
|
||||
{
|
||||
};
|
||||
|
||||
struct ConvertiblePrescription final
|
||||
{
|
||||
PreparedPrescription prescription;
|
||||
|
||||
/** @brief Implicit conversion must not substitute for the exact declared prescription source. */
|
||||
operator const PreparedPrescription&() const noexcept
|
||||
{
|
||||
return prescription;
|
||||
}
|
||||
|
||||
/** @brief Keep snapshot compatibility independent of the intentionally wrong source type. */
|
||||
[[nodiscard]] bool operator==(const ConvertiblePrescription&) const = default;
|
||||
};
|
||||
|
||||
struct WrongPreparationResult final : NormalizationPrescriptionTag
|
||||
{
|
||||
/** @brief Deliberately violate the preparation result contract. */
|
||||
template <blocks::RepresentedValueKind Kind, typename Coordinate>
|
||||
[[nodiscard]] int prepare_factors(keys::Factors<Kind, Coordinate>, std::size_t) const;
|
||||
};
|
||||
|
||||
struct BorrowedPreparationResult final : NormalizationPrescriptionTag
|
||||
{
|
||||
/** @brief A borrowed result cannot become the graph's independently owned frozen factors. */
|
||||
template <blocks::RepresentedValueKind Kind, typename Coordinate>
|
||||
[[nodiscard]] const std::expected<std::vector<double>, std::string>& prepare_factors(keys::Factors<Kind, Coordinate>, std::size_t) const;
|
||||
};
|
||||
|
||||
struct StateOnlyPrescription final : NormalizationPrescriptionTag
|
||||
{
|
||||
/** @brief A state method does not implicitly supply the inverse residual coordinate transform. */
|
||||
[[nodiscard]] std::expected<std::vector<double>, std::string> prepare_factors(keys::Factors<blocks::RepresentedValueKind::value, Density>, std::size_t) const;
|
||||
};
|
||||
|
||||
/** @brief Apply one declared policy independently to state and residual coordinates. */
|
||||
template <typename Policy>
|
||||
using PolicyPlan = NormalizationPlan<
|
||||
Bindings<Binding<Density, RuntimePreparedCoordinate<Policy>>>,
|
||||
Bindings<Binding<PressureRow, RuntimePreparedCoordinate<Policy>>>>;
|
||||
|
||||
using PolicyLayout = CoordinateLayout<KeyList<Density>, KeyList<PressureRow>>;
|
||||
static_assert(NormalizationPreparable<CompletePlan, Layout>);
|
||||
static_assert(NormalizationPreparable<const CompletePlan&, const Layout&>);
|
||||
static_assert(NormalizationPreparable<PolicyPlan<PreparedPrescription>, PolicyLayout>);
|
||||
static_assert(!NormalizationPreparable<PolicyPlan<MoveOnlyPrescription>, PolicyLayout>);
|
||||
static_assert(!NormalizationPreparable<PolicyPlan<WrongPreparationResult>, PolicyLayout>);
|
||||
static_assert(!NormalizationPreparable<PolicyPlan<BorrowedPreparationResult>, PolicyLayout>);
|
||||
static_assert(!NormalizationPreparable<PolicyPlan<StateOnlyPrescription>, PolicyLayout>);
|
||||
static_assert(!NormalizationPreparable<int, PolicyLayout>);
|
||||
static_assert(!NormalizationPreparable<CompletePlan, int>);
|
||||
static_assert(!NormalizationPreparable<MissingValue, Layout>);
|
||||
static_assert(!NormalizationPreparable<PhysicalValues, CoordinateLayout<KeyList<Density, Displacement>, KeyList<>>, int>);
|
||||
|
||||
struct AliasesOnlyLayout
|
||||
{
|
||||
using Values = checks::Values;
|
||||
using Residuals = checks::Residuals;
|
||||
};
|
||||
|
||||
static_assert(!NormalizationPreparable<CompletePlan, AliasesOnlyLayout>);
|
||||
|
||||
using QualifiedPolicyComputation = decltype(detail::coordinate_computation<blocks::RepresentedValueKind::value, Density, PolicyLayout, StellarReferenceScales>(RuntimePreparedCoordinate<const PreparedPrescription&>{}));
|
||||
static_assert(std::same_as<QualifiedPolicyComputation::Inputs, KeyList<keys::Layout, keys::Prescription<PreparedPrescription>>>);
|
||||
|
||||
// The generated rules read residual physics from equations, never from the scalar unknown they close.
|
||||
static_assert(std::same_as<PhysicalValueRule<AngularVelocity>::Target, PhysicalRieszCoordinate<dimensions::AngularVelocity, UnitMetric>>);
|
||||
static_assert(std::same_as<PhysicalResidualRule<equations::AngularMomentumConstraint>::Target, PhysicalRieszCoordinate<dimensions::AngularMomentum, UnitMetric>>);
|
||||
static_assert(std::same_as<PhysicalResidualRule<equations::MassNormalization>::Target, PhysicalRieszCoordinate<dimensions::Mass, UnitMetric>>);
|
||||
static_assert(std::same_as<PhysicalResidualRule<equations::PrimalPoissonBalance>::Target, PhysicalRieszCoordinate<dimensions::InverseTimeSquared, ReferenceVolumeMetric<blocks::gravity::potential>>>);
|
||||
|
||||
struct TemperatureCoordinate
|
||||
{
|
||||
using Quantity = dimensions::Temperature;
|
||||
};
|
||||
|
||||
struct TemperatureMetric
|
||||
{
|
||||
using Coordinates = KeyList<TemperatureCoordinate>;
|
||||
};
|
||||
|
||||
struct TemperatureScales
|
||||
{
|
||||
};
|
||||
|
||||
/** @brief External thermodynamic scales participate through their own typed ADL provider. */
|
||||
[[nodiscard]] dimensions::QuantityValue<dimensions::Temperature> characteristic_scale(const TemperatureScales&, dimensions::Temperature);
|
||||
|
||||
struct UntypedTemperatureScales
|
||||
{
|
||||
};
|
||||
|
||||
/** @brief A raw number cannot silently stand in for a scale carrying the requested quantity. */
|
||||
[[nodiscard]] double characteristic_scale(const UntypedTemperatureScales&, dimensions::Temperature);
|
||||
|
||||
using TemperaturePlan = NormalizationPlan<Bindings<Binding<TemperatureCoordinate, PhysicalRieszCoordinate<dimensions::Temperature, TemperatureMetric>>>, Bindings<>>;
|
||||
using TemperatureLayout = CoordinateLayout<KeyList<TemperatureCoordinate>, KeyList<>>;
|
||||
static_assert(NormalizationPlanType<TemperaturePlan>);
|
||||
static_assert(!NormalizationPreparable<TemperaturePlan, TemperatureLayout>);
|
||||
static_assert(NormalizationPreparable<TemperaturePlan, TemperatureLayout, TemperatureScales>);
|
||||
static_assert(NormalizationPreparable<const TemperaturePlan&, const TemperatureLayout&, const TemperatureScales&>);
|
||||
static_assert(!NormalizationPreparable<TemperaturePlan, TemperatureLayout, UntypedTemperatureScales>);
|
||||
|
||||
/** @brief Inspect the public graph's type without constructing or preparing numerical objects. */
|
||||
template <typename... Computations>
|
||||
auto graph_type(std::tuple<Computations...>) -> decltype(dependencies::make_computation_graph(std::declval<Computations>()...));
|
||||
|
||||
/** @brief Describe an explicitly owned source using the same factory as runtime clients. */
|
||||
template <typename Key, typename Value>
|
||||
using OwnedSource = decltype(dependencies::own<Key>(std::declval<Value>()));
|
||||
|
||||
using MetricLayout = CoordinateLayout<KeyList<Density, Displacement>, KeyList<>>;
|
||||
using MetricComputations = decltype(make_normalization_computations<const PhysicalValues&>(std::declval<const MetricLayout&>()));
|
||||
using MetricGraph = decltype(graph_type(std::declval<MetricComputations>()));
|
||||
using LayoutSource = OwnedSource<keys::Layout, MetricLayout>;
|
||||
using ScaleSource = OwnedSource<keys::Scales, StellarReferenceScales>;
|
||||
using DensityMetricSource = OwnedSource<ReferenceVolumeMetric<Density>, std::vector<double>>;
|
||||
using DisplacementMetricSource = OwnedSource<ReferenceVolumeMetric<Displacement>, std::vector<double>>;
|
||||
using CompleteMetricContext = dependencies::DependencyContext<LayoutSource, ScaleSource, DensityMetricSource, DisplacementMetricSource>;
|
||||
using MissingMetricContext = dependencies::DependencyContext<LayoutSource, ScaleSource, DensityMetricSource>;
|
||||
using WrongMetricContext = dependencies::DependencyContext<LayoutSource, ScaleSource, DensityMetricSource, OwnedSource<ReferenceVolumeMetric<Displacement>, std::vector<int>>>;
|
||||
static_assert(dependencies::CanPrepare<MetricGraph, CompleteMetricContext, std::string>);
|
||||
static_assert(!dependencies::CanPrepare<MetricGraph, MissingMetricContext, std::string>);
|
||||
static_assert(!dependencies::CanPrepare<MetricGraph, WrongMetricContext, std::string>);
|
||||
static_assert(!dependencies::CanPrepare<MetricGraph, CompleteMetricContext, int>);
|
||||
|
||||
using PolicyComputations = decltype(make_normalization_computations<PolicyPlan<PreparedPrescription>>(std::declval<const PolicyLayout&>()));
|
||||
using PolicyGraph = decltype(graph_type(std::declval<PolicyComputations>()));
|
||||
|
||||
/** @brief Vary the supplied resource type while keeping the plan's prescription key unchanged. */
|
||||
template <typename SuppliedPrescription>
|
||||
using PolicyContext = dependencies::DependencyContext<OwnedSource<keys::Layout, PolicyLayout>, OwnedSource<keys::Prescription<PreparedPrescription>, SuppliedPrescription>>;
|
||||
|
||||
static_assert(std::equality_comparable<DerivedPrescription>);
|
||||
static_assert(std::equality_comparable<ConvertiblePrescription>);
|
||||
static_assert(dependencies::CanPrepare<PolicyGraph, PolicyContext<PreparedPrescription>, std::string>);
|
||||
static_assert(!dependencies::CanPrepare<PolicyGraph, PolicyContext<DerivedPrescription>, std::string>);
|
||||
static_assert(!dependencies::CanPrepare<PolicyGraph, PolicyContext<ConvertiblePrescription>, std::string>);
|
||||
}
|
||||
|
||||
|
||||
namespace serif::solver::normalization::checks {
|
||||
static_assert(DimensionlessMetricGeometry<geometry::deformation::keys::SurfaceMetric>::value == ReferenceGeometry::surface);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#include <concepts>
|
||||
#include <expected>
|
||||
#include <span>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "serif/backend/mfem/solver/preconditioner/border.hpp"
|
||||
|
||||
namespace serif::solver::preconditioner::checks
|
||||
{
|
||||
namespace backend = preconditioner::backend::mfem;
|
||||
|
||||
/** @brief A borrowed raw operator cannot satisfy factories which retain their numerical prerequisites. */
|
||||
template <typename Candidate>
|
||||
concept Restrictable = requires(Candidate candidate, std::span<const int> indices)
|
||||
{
|
||||
backend::try_restrict_operator(candidate, indices, indices);
|
||||
};
|
||||
|
||||
static_assert(Restrictable<backend::OperatorHandle>);
|
||||
static_assert(!Restrictable<::mfem::Operator&>);
|
||||
static_assert(!Restrictable<const ::mfem::Operator*>);
|
||||
static_assert(std::same_as<typename backend::OperatorHandle::element_type, const ::mfem::Operator>);
|
||||
static_assert(std::same_as<decltype(backend::try_coupled_inverse(backend::Inverse{}, backend::Inverse{}, backend::TwoBlockCouplings{}, strategies::coupling::CouplingOrder::independent)), std::expected<backend::Inverse, Failure>>);
|
||||
static_assert(std::same_as<decltype(backend::try_bordered_inverse(backend::Inverse{}, backend::TwoBlockCouplings{}, backend::OperatorHandle{})), std::expected<backend::Inverse, Failure>>);
|
||||
} // namespace serif::solver::preconditioner::checks
|
||||
@@ -0,0 +1,13 @@
|
||||
#include <concepts>
|
||||
#include "serif/backend/mfem/solver/preconditioner/inverse/amg.hpp"
|
||||
#include "serif/backend/mfem/solver/preconditioner/inverse/dense.hpp"
|
||||
#include "serif/solver/preconditioner/strategies/inverse/diagonal.hpp"
|
||||
|
||||
namespace serif::solver::preconditioner
|
||||
{
|
||||
namespace inverse = strategies::inverse;
|
||||
|
||||
static_assert(inverse::Diagonal::application_contract == ApplicationContract::fixed_linear);
|
||||
static_assert(inverse::DenseDirect::application_contract == ApplicationContract::fixed_linear);
|
||||
static_assert(inverse::FixedAMG::application_contract == ApplicationContract::fixed_linear);
|
||||
} // namespace serif::solver::preconditioner
|
||||
150
src/compile_time_checks/serif/solver/preconditioner/layout.cpp
Normal file
150
src/compile_time_checks/serif/solver/preconditioner/layout.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
#include <concepts>
|
||||
#include <array>
|
||||
#include <span>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "serif/discretization/blocks/fields.hpp"
|
||||
#include "serif/discretization/blocks/vector_view.hpp"
|
||||
#include "serif/operators/linearized_system_view.hpp"
|
||||
#include "serif/solver/normalization/layout.hpp"
|
||||
#include "serif/solver/normalization/keys.hpp"
|
||||
#include "serif/backend/mfem/solver/preconditioner/computations/ordering.hpp"
|
||||
#include "serif/solver/preconditioner/default_preconditioners.hpp"
|
||||
#include "serif/utils/dependencies/dependencies.hpp"
|
||||
|
||||
namespace serif::solver::preconditioner::checks
|
||||
{
|
||||
namespace blocks = discretization::blocks;
|
||||
namespace dependencies = utils::dependencies;
|
||||
using Kind = blocks::RepresentedValueKind;
|
||||
using Mass = blocks::barotropic_constant::mass_normalization;
|
||||
using Reaction = blocks::frame_acceleration::component<0>;
|
||||
using Density = blocks::density::mass;
|
||||
using Keys = dependencies::KeyList<Mass, Reaction>;
|
||||
using Layout = blocks::SystemLayout<Keys, Keys>;
|
||||
using State = blocks::StateView<Layout>;
|
||||
using ConstantState = blocks::StateView<Layout, const double>;
|
||||
using Physical = operators::PhysicalBlock<Mass, Reaction>;
|
||||
using Sources = decltype(dependencies::make_dependency_context(dependencies::own<Physical>(1)));
|
||||
using System = operators::LinearizedSystemView<Layout, Sources>;
|
||||
|
||||
/** @brief Detection of an absent coordinate must fail in constraints instead of instantiating a function body. */
|
||||
template <typename View, typename Key>
|
||||
concept ReadsCoordinate = requires(const View& view)
|
||||
{
|
||||
view.template get<Key>();
|
||||
};
|
||||
|
||||
/** @brief Only declared equation/unknown pairs are exposed by a borrowed linearization. */
|
||||
template <typename View, typename Row, typename Value>
|
||||
concept ReadsBlock = requires(const View& view)
|
||||
{
|
||||
view.template block<Row, Value>();
|
||||
};
|
||||
|
||||
/** @brief Malformed coordinate requests remain detection-safe on the owning metadata as well. */
|
||||
template <typename Candidate, typename Key>
|
||||
concept HasOffset = requires(const Candidate& layout)
|
||||
{
|
||||
layout.template offset<Kind::value, Key>();
|
||||
};
|
||||
|
||||
static_assert(std::same_as<Mass::value, Reaction::value>);
|
||||
static_assert(Layout::index<Kind::value, Mass>() == 0);
|
||||
static_assert(Layout::index<Kind::value, Reaction>() == 1);
|
||||
static_assert(std::same_as<normalization::CoordinateLayout<Keys, Keys>, Layout>);
|
||||
static_assert(std::same_as<normalization::keys::Layout, keys::Layout>);
|
||||
static_assert(std::same_as<keys::Layout, blocks::keys::Layout>);
|
||||
|
||||
using ReorderedLayout = blocks::SystemLayout<Keys, dependencies::KeyList<Reaction, Mass>>;
|
||||
constexpr ReorderedLayout resolved_layout = blocks::make_system_layout<ReorderedLayout>([]<typename Coordinate>() {
|
||||
if constexpr (std::same_as<Coordinate, Mass>) {
|
||||
return 3;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
static_assert(resolved_layout.state_sizes == std::array<std::size_t, 2>{3, 1});
|
||||
static_assert(resolved_layout.residual_sizes == std::array<std::size_t, 2>{1, 3});
|
||||
|
||||
static_assert(blocks::SystemLayoutType<const Layout&>);
|
||||
static_assert(std::same_as<typename blocks::SystemLayout<const Keys&, Keys>::Values, Keys>);
|
||||
static_assert(ReadsCoordinate<State, Mass>);
|
||||
static_assert(!ReadsCoordinate<State, Density>);
|
||||
static_assert(!ReadsCoordinate<State, Mass::value>);
|
||||
static_assert(HasOffset<Layout, Mass>);
|
||||
static_assert(!HasOffset<Layout, Density>);
|
||||
static_assert(std::same_as<decltype(std::declval<const State&>().get<Mass>()), std::span<double>>);
|
||||
static_assert(std::same_as<decltype(std::declval<const ConstantState&>().get<Mass>()), std::span<const double>>);
|
||||
static_assert(std::constructible_from<State, const Layout&, std::span<double>>);
|
||||
static_assert(!std::constructible_from<State, Layout&&, std::span<double>>);
|
||||
static_assert(!std::constructible_from<State, const Layout&&, std::span<double>>);
|
||||
static_assert(!std::constructible_from<State, const Layout&, std::span<const double>>);
|
||||
static_assert(!std::constructible_from<ConstantState, const Layout&, std::vector<double>&&>);
|
||||
static_assert(!std::constructible_from<ConstantState, const Layout&, const std::vector<double>&&>);
|
||||
static_assert(!std::constructible_from<ConstantState, const Layout&, std::array<double, 2>&&>);
|
||||
static_assert(std::constructible_from<ConstantState, const Layout&, const std::vector<double>&>);
|
||||
static_assert(std::constructible_from<ConstantState, const Layout&, std::span<const double, 2>>);
|
||||
static_assert(std::constructible_from<blocks::StateView<const Layout&>, const Layout&, std::span<double>>);
|
||||
static_assert(!std::constructible_from<blocks::StateView<Layout&>, Layout&&, std::span<double>>);
|
||||
static_assert(ReadsBlock<System, Mass, Reaction>);
|
||||
static_assert(!ReadsBlock<System, Reaction, Mass>);
|
||||
static_assert(std::constructible_from<System, const Layout&, const Sources&>);
|
||||
static_assert(!std::constructible_from<System, Layout&&, const Sources&>);
|
||||
static_assert(!std::constructible_from<System, const Layout&, Sources&&>);
|
||||
static_assert(!std::constructible_from<System, const Layout&&, const Sources&&>);
|
||||
static_assert(std::constructible_from<operators::LinearizedSystemView<const Layout&, const Sources&>, const Layout&, const Sources&>);
|
||||
} // namespace serif::solver::preconditioner::checks
|
||||
|
||||
|
||||
namespace serif::solver::preconditioner::checks {
|
||||
using Potential = blocks::gravity::potential;
|
||||
using Rotation = blocks::rotation::angular_velocity;
|
||||
using Shape = blocks::surface_deformation::coefficients;
|
||||
using ShapeRow = blocks::surface_deformation::shape_equilibrium;
|
||||
|
||||
// Extra generated scalars need no new entry in the recipe registry, even when equation and value identities differ.
|
||||
struct ExtraField : blocks::field {};
|
||||
struct ExtraValue : blocks::term<ExtraField>, blocks::global_scalar {
|
||||
using Support = discretization::domain::StellarDomains;
|
||||
};
|
||||
struct ExtraRow : blocks::term<ExtraField>, blocks::global_scalar {
|
||||
using Support = discretization::domain::StellarDomains;
|
||||
};
|
||||
using PrimalFields = dependencies::KeyList<Density, Potential>;
|
||||
using PrimalFixedLayout = blocks::SystemLayout<PrimalFields, PrimalFields>;
|
||||
using PrimalMovingLayout = blocks::SystemLayout<dependencies::KeyList<Potential, Shape, Mass, Density, Rotation>,
|
||||
dependencies::KeyList<Rotation, Mass, ShapeRow, Potential, Density>>;
|
||||
using PrimalRecipe = decltype(make_primal_preconditioner(std::declval<const PrimalMovingLayout&>()));
|
||||
using PrimalExtraLayout = blocks::SystemLayout<dependencies::CombinedKeyList<PrimalFields, dependencies::KeyList<ExtraValue>>,
|
||||
dependencies::CombinedKeyList<PrimalFields, dependencies::KeyList<ExtraRow>>>;
|
||||
using PrimalExtraRecipe = decltype(make_primal_preconditioner(std::declval<const PrimalExtraLayout&>()));
|
||||
|
||||
/** @brief Keep unsupported coordinate sets out of the primal recipe before its body is instantiated. */
|
||||
template <typename Candidate>
|
||||
concept HasPrimalPreconditioner = requires(const Candidate& layout) {
|
||||
make_primal_preconditioner(layout);
|
||||
};
|
||||
|
||||
static_assert(HasPrimalPreconditioner<PrimalFixedLayout>);
|
||||
static_assert(HasPrimalPreconditioner<const PrimalMovingLayout&>);
|
||||
static_assert(HasPrimalPreconditioner<PrimalExtraLayout>);
|
||||
static_assert(!HasPrimalPreconditioner<int>);
|
||||
static_assert(!HasPrimalPreconditioner<blocks::SystemLayout<dependencies::KeyList<Density>, dependencies::KeyList<Density>>>);
|
||||
static_assert(!HasPrimalPreconditioner<blocks::SystemLayout<
|
||||
dependencies::CombinedKeyList<PrimalFields, dependencies::KeyList<Shape>>, PrimalFields>>);
|
||||
static_assert(!HasPrimalPreconditioner<blocks::SystemLayout<
|
||||
dependencies::CombinedKeyList<PrimalFields, dependencies::KeyList<Mass>>, PrimalFields>>);
|
||||
static_assert(!HasPrimalPreconditioner<blocks::SystemLayout<
|
||||
dependencies::CombinedKeyList<PrimalFields, dependencies::KeyList<blocks::displacement::vector>>,
|
||||
dependencies::CombinedKeyList<PrimalFields, dependencies::KeyList<blocks::displacement::vector>>>>);
|
||||
static_assert(std::same_as<typename PrimalRecipe::Values, dependencies::KeyList<Density, Potential, Shape, Mass, Rotation>>);
|
||||
static_assert(std::same_as<typename PrimalRecipe::Rows, dependencies::KeyList<Density, Potential, Rotation, Mass, ShapeRow>>);
|
||||
static_assert(std::same_as<typename PrimalExtraRecipe::Values, dependencies::KeyList<Density, Potential, ExtraValue>>);
|
||||
static_assert(std::same_as<typename PrimalExtraRecipe::Rows, dependencies::KeyList<Density, Potential, ExtraRow>>);
|
||||
static_assert(std::same_as<decltype(std::declval<PrimalRecipe>().fields.coupling), strategies::coupling::SecondThenFirst>);
|
||||
static_assert(std::same_as<decltype(std::declval<PrimalRecipe>().fields.first.inverse), strategies::inverse::Diagonal>);
|
||||
static_assert(std::same_as<decltype(std::declval<PrimalRecipe>().fields.second.inverse), strategies::inverse::FixedAMG>);
|
||||
}
|
||||
47
src/compile_time_checks/serif/solver/public_contracts.cpp
Normal file
47
src/compile_time_checks/serif/solver/public_contracts.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include <concepts>
|
||||
#include <utility>
|
||||
#include "serif/types.hpp"
|
||||
|
||||
#include "serif/solver/nonlinear/result.hpp"
|
||||
#include "serif/solver/nonlinear/newton.hpp"
|
||||
#include "serif/solver/seed/polytrope.hpp"
|
||||
|
||||
namespace {
|
||||
namespace solver = serif::solver;
|
||||
|
||||
static_assert(std::same_as<serif::Vector, mfem::Vector>);
|
||||
static_assert(std::same_as<serif::DenseMatrix, mfem::DenseMatrix>);
|
||||
static_assert(std::same_as<serif::SparseMatrix, mfem::SparseMatrix>);
|
||||
static_assert(std::same_as<serif::LinearOperator, mfem::Operator>);
|
||||
|
||||
struct EquationDefinition {};
|
||||
using MassModel = decltype(serif::model::make_stellar_model(std::declval<serif::eos::models::Polytrope>(), EquationDefinition{},
|
||||
std::declval<serif::model::FixedTotalMass>()));
|
||||
using UnspecifiedMass = decltype(serif::model::make_stellar_model(std::declval<serif::eos::models::Polytrope>(), EquationDefinition{}));
|
||||
template <typename Model>
|
||||
concept DefaultRadialSeed = requires(const Model& model) { solver::seed::make_polytropic_seed(model); };
|
||||
static_assert(DefaultRadialSeed<MassModel>);
|
||||
static_assert(!DefaultRadialSeed<UnspecifiedMass>);
|
||||
static_assert(!DefaultRadialSeed<int>);
|
||||
|
||||
struct State {};
|
||||
struct System {};
|
||||
|
||||
struct Problem {
|
||||
using Vector = serif::Vector;
|
||||
std::expected<std::shared_ptr<const System>, std::string> prepare(const State&);
|
||||
};
|
||||
struct InvalidSolve : solver::StandardNewton {
|
||||
solver::linear::Report solve(int);
|
||||
};
|
||||
struct DirectionOnly : solver::StandardNewton {
|
||||
solver::DirectionAssessment direction(auto&, const auto&, const auto&, const auto&, auto&, const auto&) { return {}; }
|
||||
};
|
||||
static_assert(solver::detail::NewtonStrategyFor<Problem, State, solver::SolverOptions<>>);
|
||||
static_assert(solver::detail::NewtonStrategyFor<Problem, State, solver::SolverOptions<solver::NormalizedResidualMetric, DirectionOnly>>);
|
||||
static_assert(!solver::detail::NewtonStrategyFor<Problem, State, solver::SolverOptions<solver::NormalizedResidualMetric, InvalidSolve>>);
|
||||
using Result = solver::SolveResult<State, System>;
|
||||
static_assert(std::same_as<decltype(std::declval<Result&>().state()), const State&>);
|
||||
static_assert(std::same_as<decltype(std::declval<Result&>().system()), const std::shared_ptr<const System>&>);
|
||||
static_assert(std::same_as<decltype(std::declval<Result&>().diagnostics()), const solver::SolveDiagnostics&>);
|
||||
}
|
||||
120
src/compile_time_checks/serif/utils/dependencies/binding.cpp
Normal file
120
src/compile_time_checks/serif/utils/dependencies/binding.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
#include "serif/utils/dependencies/binding.hpp"
|
||||
|
||||
namespace {
|
||||
namespace dependencies = serif::utils::dependencies;
|
||||
|
||||
struct FirstKey {};
|
||||
struct SecondKey {};
|
||||
struct MissingKey {};
|
||||
|
||||
struct UnrelatedBinding {
|
||||
using Key = FirstKey;
|
||||
using Target = int;
|
||||
};
|
||||
|
||||
using Empty = dependencies::Bindings<>;
|
||||
using Collection = dependencies::Bindings<dependencies::Binding<FirstKey, int>, dependencies::Binding<SecondKey, double>>;
|
||||
using RepeatedTargets = dependencies::Bindings<dependencies::Binding<FirstKey, int>, dependencies::Binding<SecondKey, int>>;
|
||||
using DuplicateKeys = dependencies::Bindings<dependencies::Binding<FirstKey, int>, dependencies::Binding<FirstKey, double>>;
|
||||
using QualifiedKeys = dependencies::Bindings<
|
||||
dependencies::Binding<const FirstKey, int>,
|
||||
dependencies::Binding<FirstKey&, double>
|
||||
>;
|
||||
using QualifiedTarget = dependencies::Bindings<dependencies::Binding<FirstKey, const int&>>;
|
||||
|
||||
template <typename BindingsT, typename KeyT>
|
||||
concept HasBoundType = requires {
|
||||
typename dependencies::bound_type_t<BindingsT, KeyT>;
|
||||
};
|
||||
|
||||
template <typename BindingsT, typename KeyT>
|
||||
concept HasBoundTypeMember = requires {
|
||||
typename dependencies::BoundType<BindingsT, KeyT>::target_type;
|
||||
};
|
||||
|
||||
static_assert(dependencies::IsBinding<dependencies::Binding<FirstKey, int>>);
|
||||
static_assert(!dependencies::IsBinding<UnrelatedBinding>);
|
||||
static_assert(!dependencies::IsBinding<int>);
|
||||
static_assert(dependencies::IsBindings<Empty>);
|
||||
static_assert(dependencies::IsBindings<Collection>);
|
||||
static_assert(dependencies::IsBindings<RepeatedTargets>);
|
||||
static_assert(!dependencies::IsBindings<DuplicateKeys>);
|
||||
static_assert(!dependencies::IsBindings<int>);
|
||||
|
||||
static_assert(dependencies::HasBinding<Collection, FirstKey>::value);
|
||||
static_assert(dependencies::has_binding_v<Collection, SecondKey>);
|
||||
static_assert(!dependencies::has_binding_v<Collection, MissingKey>);
|
||||
static_assert(!dependencies::has_binding_v<Empty, FirstKey>);
|
||||
static_assert(std::is_same_v<dependencies::bound_type_t<Collection, FirstKey>, int>);
|
||||
static_assert(std::is_same_v<dependencies::bound_type_t<Collection, SecondKey>, double>);
|
||||
static_assert(std::is_same_v<dependencies::bound_type_t<RepeatedTargets, SecondKey>, int>);
|
||||
static_assert(std::is_same_v<dependencies::bound_type_t<QualifiedTarget, FirstKey>, const int&>);
|
||||
|
||||
// Every public lookup accepts the qualified collection types recognized by IsBindings.
|
||||
static_assert(dependencies::IsBindings<const volatile Collection&>);
|
||||
static_assert(dependencies::HasBinding<const Collection&, FirstKey>::value);
|
||||
static_assert(dependencies::has_binding_v<const volatile Collection&, FirstKey>);
|
||||
static_assert(dependencies::BindingsWithBinding<Collection&&, FirstKey>);
|
||||
static_assert(std::is_same_v<dependencies::BoundType<const Collection&, FirstKey>::target_type, int>);
|
||||
static_assert(std::is_same_v<dependencies::bound_type_t<const volatile Collection&, SecondKey>, double>);
|
||||
|
||||
// Qualified keys are distinct associations; queries must agree with their exact stored types.
|
||||
static_assert(dependencies::IsBindings<QualifiedKeys>);
|
||||
static_assert(dependencies::has_binding_v<QualifiedKeys, const FirstKey>);
|
||||
static_assert(dependencies::BindingsWithBinding<QualifiedKeys, const FirstKey>);
|
||||
static_assert(dependencies::BindingsWithBinding<QualifiedKeys, FirstKey&>);
|
||||
static_assert(!dependencies::has_binding_v<QualifiedKeys, FirstKey>);
|
||||
static_assert(!dependencies::BindingsWithBinding<QualifiedKeys, FirstKey>);
|
||||
static_assert(std::is_same_v<dependencies::bound_type_t<QualifiedKeys, const FirstKey>, int>);
|
||||
static_assert(std::is_same_v<dependencies::bound_type_t<QualifiedKeys, FirstKey&>, double>);
|
||||
|
||||
// Missing keys and invalid collections must fail these requirements without a hard compiler error.
|
||||
static_assert(HasBoundType<Collection, FirstKey>);
|
||||
static_assert(HasBoundTypeMember<Collection, SecondKey>);
|
||||
static_assert(!HasBoundType<Collection, MissingKey>);
|
||||
static_assert(!HasBoundTypeMember<Collection, MissingKey>);
|
||||
static_assert(!HasBoundType<Empty, FirstKey>);
|
||||
static_assert(!HasBoundTypeMember<Empty, FirstKey>);
|
||||
static_assert(!HasBoundType<QualifiedKeys, FirstKey>);
|
||||
static_assert(!HasBoundType<DuplicateKeys, FirstKey>);
|
||||
static_assert(!HasBoundTypeMember<DuplicateKeys, FirstKey>);
|
||||
static_assert(!dependencies::BindingsWithBinding<DuplicateKeys, FirstKey>);
|
||||
static_assert(!dependencies::BindingsWithBinding<int, FirstKey>);
|
||||
static_assert(!HasBoundType<int, FirstKey>);
|
||||
static_assert(!HasBoundTypeMember<int, FirstKey>);
|
||||
|
||||
template <std::size_t Index>
|
||||
struct IndexedKey {};
|
||||
|
||||
template <std::size_t Index>
|
||||
struct IndexedTarget {
|
||||
IndexedTarget() = delete;
|
||||
};
|
||||
|
||||
/** @brief Resolve a stellar-graph-sized collection without constructing its targets. */
|
||||
template <std::size_t... Indices>
|
||||
consteval bool every_binding_resolves(std::index_sequence<Indices...>) {
|
||||
using LargeCollection = dependencies::Bindings<dependencies::Binding<IndexedKey<Indices>, IndexedTarget<Indices>>...>;
|
||||
constexpr std::array<bool, sizeof...(Indices)> resolved{
|
||||
std::is_same_v<dependencies::bound_type_t<LargeCollection, IndexedKey<Indices>>, IndexedTarget<Indices>>...};
|
||||
for (const bool correct : resolved) {
|
||||
if (!correct) { return false; }
|
||||
}
|
||||
return !dependencies::has_binding_v<LargeCollection, MissingKey>;
|
||||
}
|
||||
|
||||
static_assert(every_binding_resolves(std::make_index_sequence<300>{}));
|
||||
/** @brief Invalid members remain a failed collection requirement, even when their aliases are missing. */
|
||||
template <typename... Types>
|
||||
concept CanMakeBindings = requires { typename dependencies::Bindings<Types...>; };
|
||||
static_assert(CanMakeBindings<>);
|
||||
static_assert(CanMakeBindings<dependencies::Binding<FirstKey, int>>);
|
||||
static_assert(!CanMakeBindings<int, dependencies::Binding<FirstKey, int>>);
|
||||
static_assert(!CanMakeBindings<dependencies::Binding<FirstKey, int>, int>);
|
||||
static_assert(!CanMakeBindings<UnrelatedBinding>);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
#include "serif/utils/dependencies/computation_cache.hpp"
|
||||
#include "serif/utils/dependencies/timing.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <concepts>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
namespace dependencies = serif::utils::dependencies;
|
||||
|
||||
struct Source {};
|
||||
struct First {};
|
||||
struct Second {};
|
||||
struct Missing {};
|
||||
struct Unused {};
|
||||
struct Untrackable {};
|
||||
|
||||
using DependencyContext = decltype(dependencies::make_dependency_context(dependencies::own<Source>(1), dependencies::own<Unused>(Untrackable{})));
|
||||
using EmptyContext = dependencies::DependencyContext<>;
|
||||
using FirstCalculation = decltype(dependencies::make_computation<First, dependencies::InputList<Source>>([](int value) { return value + 1; }));
|
||||
using SecondCalculation = decltype(dependencies::make_computation<Second, dependencies::InputList<First>>([](int value) { return 2 * value; }));
|
||||
using ComputationGraph = dependencies::ComputationGraph<SecondCalculation, FirstCalculation>;
|
||||
|
||||
static_assert(dependencies::CompatibleComputations<FirstCalculation, SecondCalculation>);
|
||||
static_assert(!dependencies::CompatibleComputations<FirstCalculation, FirstCalculation>);
|
||||
static_assert(!dependencies::CompatibleComputations<int>);
|
||||
static_assert(dependencies::ComputationGraphFor<ComputationGraph, DependencyContext>);
|
||||
static_assert(dependencies::ComputationGraphFor<const ComputationGraph&, const DependencyContext&>);
|
||||
static_assert(!dependencies::ComputationGraphFor<ComputationGraph, EmptyContext>);
|
||||
static_assert(!dependencies::ComputationGraphFor<int, DependencyContext> && !dependencies::ComputationGraphFor<ComputationGraph, int>);
|
||||
static_assert(dependencies::ComputationGraphFor<dependencies::ComputationGraph<>, EmptyContext>);
|
||||
static_assert(ComputationGraph::Dependencies::order == std::array<std::size_t, 2>{1, 0});
|
||||
static_assert(ComputationGraph::Dependencies::required(dependencies::KeyList<Second>{}) == std::array<bool, 2>{true, true});
|
||||
static_assert(ComputationGraph::Dependencies::required(dependencies::KeyList<First>{}) == std::array<bool, 2>{false, true});
|
||||
static_assert(ComputationGraph::Dependencies::required(dependencies::KeyList<>{}) == std::array<bool, 2>{false, false});
|
||||
|
||||
using CyclicFirst = decltype(dependencies::make_computation<First, dependencies::InputList<Second>>([](int value) { return value; }));
|
||||
using SelfCycle = decltype(dependencies::make_computation<First, dependencies::InputList<First>>([](int value) { return value; }));
|
||||
static_assert(!dependencies::CompatibleComputations<CyclicFirst, SecondCalculation>);
|
||||
static_assert(!dependencies::CompatibleComputations<SelfCycle>);
|
||||
|
||||
using DuplicateProducer = decltype(dependencies::make_dependency_context(dependencies::own<Source>(1), dependencies::own<First>(2)));
|
||||
static_assert(!dependencies::ComputationGraphFor<ComputationGraph, DuplicateProducer>);
|
||||
|
||||
template <typename FunctionT, typename InputsT = dependencies::InputList<Source>>
|
||||
using SingleGraph = dependencies::ComputationGraph<dependencies::Computation<First, InputsT, FunctionT>>;
|
||||
|
||||
using MutableInput = decltype([](int& value) { return ++value; });
|
||||
using MutableCallable = decltype([value = 0](int) mutable { return ++value; });
|
||||
using WrongArity = decltype([](int, int) { return 0; });
|
||||
using WrongType = decltype([](const std::string&) { return 0; });
|
||||
using VoidResult = decltype([](int) {});
|
||||
using ReferenceResult = decltype([](const int& value) -> const int& { return value; });
|
||||
using MoveOnlyResult = decltype([](int value) { return std::make_unique<int>(value); });
|
||||
using VoidExpected = decltype([](int) -> std::expected<void, std::string> { return {}; });
|
||||
using Unary = decltype([](int value) { return value; });
|
||||
|
||||
static_assert(!dependencies::ComputationGraphFor<SingleGraph<MutableInput>, DependencyContext>);
|
||||
static_assert(!dependencies::ComputationGraphFor<SingleGraph<MutableCallable>, DependencyContext>);
|
||||
static_assert(!dependencies::ComputationGraphFor<SingleGraph<WrongArity>, DependencyContext>);
|
||||
static_assert(!dependencies::ComputationGraphFor<SingleGraph<WrongType>, DependencyContext>);
|
||||
static_assert(!dependencies::ComputationGraphFor<SingleGraph<VoidResult>, DependencyContext>);
|
||||
static_assert(!dependencies::ComputationGraphFor<SingleGraph<ReferenceResult>, DependencyContext>);
|
||||
static_assert(!dependencies::ComputationGraphFor<SingleGraph<VoidExpected>, DependencyContext>);
|
||||
static_assert(!dependencies::ComputationGraphFor<SingleGraph<Unary, dependencies::InputList<Missing>>, DependencyContext>);
|
||||
static_assert(dependencies::ComputationGraphFor<SingleGraph<MoveOnlyResult>, DependencyContext>);
|
||||
|
||||
using Fallible = decltype([](int value) -> std::expected<int, int> { return value; });
|
||||
using ConstFallible = decltype([](int value) -> const std::expected<int, int> { return value; });
|
||||
static_assert(dependencies::CanPrepare<ComputationGraph, DependencyContext, std::string>); // Unused resources need no tracking policy.
|
||||
static_assert(dependencies::CanPrepare<SingleGraph<Fallible>, DependencyContext, int>);
|
||||
static_assert(!dependencies::CanPrepare<SingleGraph<Fallible>, DependencyContext, std::string>);
|
||||
static_assert(dependencies::CanPrepare<SingleGraph<ConstFallible>, DependencyContext, int>);
|
||||
static_assert(!dependencies::CanPrepare<SingleGraph<ConstFallible>, DependencyContext, std::string>);
|
||||
static_assert(dependencies::CanPrepare<SingleGraph<MoveOnlyResult>, DependencyContext, std::string>);
|
||||
static_assert(!dependencies::CanPrepare<ComputationGraph, DependencyContext, void>);
|
||||
static_assert(!dependencies::CanPrepare<ComputationGraph, DependencyContext, int&>);
|
||||
static_assert(!dependencies::CanPrepare<int, DependencyContext, int>);
|
||||
|
||||
using ReadUntrackable = decltype([](const Untrackable&) { return 1; });
|
||||
using UntrackableGraph = SingleGraph<ReadUntrackable, dependencies::InputList<Unused>>;
|
||||
static_assert(dependencies::ComputationGraphFor<UntrackableGraph, DependencyContext>);
|
||||
static_assert(!dependencies::CanPrepare<UntrackableGraph, DependencyContext, std::string>);
|
||||
|
||||
using ComputationCache = dependencies::ComputationCache<ComputationGraph, DependencyContext>;
|
||||
template <typename RequestedT>
|
||||
concept CanRequest = requires(ComputationCache& cache, const DependencyContext& context, RequestedT request) {
|
||||
cache.try_prepare(context, request);
|
||||
};
|
||||
static_assert(CanRequest<dependencies::RequestedResults<>>);
|
||||
static_assert(CanRequest<dependencies::RequestedResults<First>>);
|
||||
static_assert(CanRequest<dependencies::RequestedResults<Second, First>>);
|
||||
static_assert(!CanRequest<dependencies::InputList<Source>>);
|
||||
static_assert(!CanRequest<dependencies::InputList<Missing>>);
|
||||
static_assert(!CanRequest<dependencies::RequestedResults<First, First>>);
|
||||
static_assert(!CanRequest<int>);
|
||||
static_assert(!std::copy_constructible<ComputationCache>);
|
||||
static_assert(std::move_constructible<ComputationCache>);
|
||||
static_assert(std::same_as<decltype(std::declval<const ComputationCache&>().get<First>()), const int&>);
|
||||
|
||||
using Inputs = dependencies::InputList<Source>;
|
||||
|
||||
struct WrongIdentity : dependencies::ValueSnapshot {
|
||||
std::string identity(const int&) const;
|
||||
};
|
||||
static_assert(!dependencies::SnapshotPolicyFor<WrongIdentity, int>);
|
||||
|
||||
template <typename FunctionT, typename InputsT = dependencies::InputList<Source>>
|
||||
concept CanMakeComputation = requires(FunctionT&& function) {
|
||||
dependencies::make_computation<First, InputsT>(std::forward<FunctionT>(function));
|
||||
};
|
||||
|
||||
template <typename ContextT, typename... Computations>
|
||||
concept CanMakeCache = requires(const ContextT& context, Computations&&... computations) {
|
||||
dependencies::make_computation_cache(context, std::forward<Computations>(computations)...);
|
||||
};
|
||||
|
||||
template <typename ErrorT, typename... Computations>
|
||||
concept CanMakeFallibleCache = requires(const DependencyContext& context, Computations&&... computations) {
|
||||
dependencies::make_computation_cache<ErrorT>(context, std::forward<Computations>(computations)...);
|
||||
};
|
||||
|
||||
static_assert(CanMakeComputation<Unary>);
|
||||
static_assert(!CanMakeComputation<Unary, int>);
|
||||
static_assert(!CanMakeComputation<Unary, dependencies::InputList<Source, Source>>);
|
||||
static_assert(CanMakeCache<DependencyContext, const SecondCalculation&, const FirstCalculation&>);
|
||||
static_assert(CanMakeCache<EmptyContext>);
|
||||
static_assert(!CanMakeCache<int, FirstCalculation>);
|
||||
static_assert(!CanMakeCache<DependencyContext, int>);
|
||||
static_assert(!CanMakeCache<EmptyContext, FirstCalculation>);
|
||||
static_assert(!CanMakeCache<DependencyContext, FirstCalculation, FirstCalculation>);
|
||||
static_assert(!CanMakeCache<DependencyContext, CyclicFirst, SecondCalculation>);
|
||||
static_assert(!CanMakeCache<DependencyContext, SelfCycle>);
|
||||
static_assert(!CanMakeCache<DuplicateProducer, FirstCalculation>);
|
||||
static_assert(!CanMakeCache<DependencyContext, dependencies::Computation<First, Inputs, WrongType>>);
|
||||
static_assert(CanMakeFallibleCache<int, dependencies::Computation<First, Inputs, Fallible>>);
|
||||
static_assert(!CanMakeFallibleCache<std::string, dependencies::Computation<First, Inputs, Fallible>>);
|
||||
|
||||
using MoveOnlyCallable = decltype([value = std::make_unique<int>(2)](int input) { return input + *value; });
|
||||
using MoveOnlyComputation = dependencies::Computation<First, Inputs, MoveOnlyCallable>;
|
||||
static_assert(CanMakeComputation<MoveOnlyCallable>);
|
||||
static_assert(!CanMakeComputation<MoveOnlyCallable&>);
|
||||
static_assert(CanMakeCache<DependencyContext, MoveOnlyComputation>);
|
||||
static_assert(!CanMakeCache<DependencyContext, MoveOnlyComputation&>);
|
||||
|
||||
using OwningCalculation = decltype(dependencies::make_owning_computation<Second, dependencies::InputList<First>>([](std::shared_ptr<const int> value)
|
||||
{
|
||||
return value;
|
||||
}));
|
||||
using OwningGraph = dependencies::ComputationGraph<OwningCalculation, FirstCalculation>;
|
||||
static_assert(dependencies::CanPrepare<OwningGraph, DependencyContext, std::string>);
|
||||
static_assert(CanMakeCache<DependencyContext, OwningCalculation, FirstCalculation>);
|
||||
static_assert(!dependencies::CompatibleComputations<OwningCalculation>);
|
||||
static_assert(!CanMakeCache<DependencyContext, OwningCalculation>);
|
||||
static_assert(!CanMakeCache<DuplicateProducer, OwningCalculation>);
|
||||
|
||||
using OwnsSource = decltype(dependencies::make_owning_computation<Second, dependencies::InputList<Source>>([](std::shared_ptr<const int> value)
|
||||
{
|
||||
return value;
|
||||
}));
|
||||
static_assert(!CanMakeCache<DependencyContext, OwnsSource, FirstCalculation>);
|
||||
|
||||
using MutableOwnedInput = decltype(dependencies::make_owning_computation<Second, dependencies::InputList<First>>([](std::shared_ptr<int> value)
|
||||
{
|
||||
return *value;
|
||||
}));
|
||||
using MutableOwnedGraph = dependencies::ComputationGraph<FirstCalculation, MutableOwnedInput>;
|
||||
static_assert(!dependencies::CanPrepare<MutableOwnedGraph, DependencyContext, std::string>);
|
||||
|
||||
using EmptyOwningCalculation = decltype(dependencies::make_owning_computation<First, dependencies::InputList<>>([]()
|
||||
{
|
||||
return 3;
|
||||
}));
|
||||
static_assert(CanMakeCache<EmptyContext, EmptyOwningCalculation>);
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
template <std::size_t Index>
|
||||
struct ChainKey {};
|
||||
|
||||
template <std::size_t Index>
|
||||
struct ReverseChainNode {
|
||||
using Key = ChainKey<Index>;
|
||||
using Inputs = dependencies::KeyList<ChainKey<Index + 1>>;
|
||||
};
|
||||
|
||||
/** @brief Exercise the graph ordering directly without unrelated callable and storage template instantiations. */
|
||||
template <std::size_t... Indices>
|
||||
[[nodiscard]] consteval auto reverse_chain(std::index_sequence<Indices...>) {
|
||||
return dependencies::detail::Graph<ReverseChainNode<Indices>...>{};
|
||||
}
|
||||
|
||||
using LongChain = decltype(reverse_chain(std::make_index_sequence<144>{}));
|
||||
|
||||
/** @brief A reverse-declared chain must compile within the toolchain's ordinary constexpr evaluation budget. */
|
||||
[[nodiscard]] consteval bool long_chain_is_ordered() {
|
||||
for (std::size_t position = 0; position < LongChain::size; ++position) {
|
||||
if (LongChain::order[position] != LongChain::size - position - 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <std::size_t Index, std::size_t... Prerequisites>
|
||||
struct OrderingNode {
|
||||
using Key = ChainKey<Index>;
|
||||
using Inputs = dependencies::KeyList<ChainKey<Prerequisites>...>;
|
||||
};
|
||||
|
||||
// Nodes 1 and 2 become ready together. After 1, newly ready node 0 must precede already-ready node 2.
|
||||
using StableDiamond = dependencies::detail::Graph<OrderingNode<0, 1>, OrderingNode<1, 3>, OrderingNode<2, 3>,
|
||||
OrderingNode<3>, OrderingNode<4, 0, 2>>;
|
||||
using PartialCycle = dependencies::detail::Graph<OrderingNode<0, 1>, OrderingNode<1, 0>, OrderingNode<2>>;
|
||||
static_assert(LongChain::acyclic());
|
||||
static_assert(long_chain_is_ordered());
|
||||
static_assert(StableDiamond::order == std::array<std::size_t, 5>{3, 1, 0, 2, 4});
|
||||
static_assert(StableDiamond::acyclic());
|
||||
static_assert(PartialCycle::order == std::array<std::size_t, 3>{2, 3, 3});
|
||||
static_assert(!PartialCycle::acyclic());
|
||||
}
|
||||
|
||||
namespace {
|
||||
using NodeOutcome = dependencies::NodeOutcome;
|
||||
using ObserverBefore = decltype([] { return std::make_unique<int>(0); });
|
||||
using ObserverAfter = decltype([](std::unique_ptr<int>&, NodeOutcome) noexcept {});
|
||||
|
||||
/** @brief Observer lifetime callbacks must agree on an owned state and cannot fail while closing it. */
|
||||
template <typename BeforeT, typename AfterT>
|
||||
concept CanObserveNode = requires(BeforeT&& before, AfterT&& after) {
|
||||
dependencies::observe_node<First>(std::forward<BeforeT>(before), std::forward<AfterT>(after));
|
||||
};
|
||||
|
||||
using VoidBefore = decltype([] {});
|
||||
using ReferenceBefore = decltype([]() -> int& { static int state = 0; return state; });
|
||||
using WrongStateAfter = decltype([](int&, NodeOutcome) noexcept {});
|
||||
using ThrowingAfter = decltype([](std::unique_ptr<int>&, NodeOutcome) {});
|
||||
using NonvoidAfter = decltype([](std::unique_ptr<int>&, NodeOutcome) noexcept { return 1; });
|
||||
static_assert(CanObserveNode<ObserverBefore, ObserverAfter>);
|
||||
static_assert(!CanObserveNode<VoidBefore, ObserverAfter>);
|
||||
static_assert(!CanObserveNode<ReferenceBefore, WrongStateAfter>);
|
||||
static_assert(!CanObserveNode<ObserverBefore, WrongStateAfter>);
|
||||
static_assert(!CanObserveNode<ObserverBefore, ThrowingAfter>);
|
||||
static_assert(!CanObserveNode<ObserverBefore, NonvoidAfter>);
|
||||
|
||||
using NodeObserver = decltype(dependencies::observe_node<First>(ObserverBefore{}, ObserverAfter{}));
|
||||
using SourceObserver = decltype(dependencies::observe_node<Source>(ObserverBefore{}, ObserverAfter{}));
|
||||
using MissingObserver = decltype(dependencies::observe_node<Missing>(ObserverBefore{}, ObserverAfter{}));
|
||||
using InputObserver = decltype([](const int&) {});
|
||||
using SourceEdge = decltype(dependencies::observe_edge<Source, First>(InputObserver{}));
|
||||
using InternalEdge = decltype(dependencies::observe_edge<First, Second>(InputObserver{}));
|
||||
using MissingEdge = decltype(dependencies::observe_edge<Missing, First>(InputObserver{}));
|
||||
using ReversedEdge = decltype(dependencies::observe_edge<Second, First>(InputObserver{}));
|
||||
using IndirectEdge = decltype(dependencies::observe_edge<Source, Second>(InputObserver{}));
|
||||
using MissingConsumer = decltype(dependencies::observe_edge<Source, Missing>(InputObserver{}));
|
||||
using MutableEdge = decltype(dependencies::observe_edge<Source, First>([](int&) {}));
|
||||
using WrongTypeEdge = decltype(dependencies::observe_edge<Source, First>([](const std::string&) {}));
|
||||
using NonvoidEdge = decltype(dependencies::observe_edge<Source, First>([](const int&) { return 1; }));
|
||||
|
||||
template <typename... Observers>
|
||||
using ObserverSet = decltype(dependencies::make_observers(std::declval<Observers>()...));
|
||||
|
||||
static_assert(dependencies::ObserversFor<ObserverSet<>, ComputationGraph, DependencyContext>);
|
||||
static_assert(dependencies::ObserversFor<ObserverSet<NodeObserver, NodeObserver, SourceEdge, InternalEdge>, ComputationGraph, DependencyContext>);
|
||||
static_assert(dependencies::ObserversFor<ObserverSet<InternalEdge>, OwningGraph, DependencyContext>);
|
||||
static_assert(!dependencies::ObserversFor<ObserverSet<SourceObserver>, ComputationGraph, DependencyContext>);
|
||||
static_assert(!dependencies::ObserversFor<ObserverSet<MissingObserver>, ComputationGraph, DependencyContext>);
|
||||
static_assert(!dependencies::ObserversFor<ObserverSet<MissingEdge>, ComputationGraph, DependencyContext>);
|
||||
static_assert(!dependencies::ObserversFor<ObserverSet<ReversedEdge>, ComputationGraph, DependencyContext>);
|
||||
static_assert(!dependencies::ObserversFor<ObserverSet<IndirectEdge>, ComputationGraph, DependencyContext>);
|
||||
static_assert(!dependencies::ObserversFor<ObserverSet<MissingConsumer>, ComputationGraph, DependencyContext>);
|
||||
static_assert(!dependencies::ObserversFor<ObserverSet<MutableEdge>, ComputationGraph, DependencyContext>);
|
||||
static_assert(!dependencies::ObserversFor<ObserverSet<WrongTypeEdge>, ComputationGraph, DependencyContext>);
|
||||
static_assert(!dependencies::ObserversFor<ObserverSet<NonvoidEdge>, ComputationGraph, DependencyContext>);
|
||||
static_assert(!dependencies::ObserversFor<int, ComputationGraph, DependencyContext>);
|
||||
static_assert(!dependencies::ObserversFor<ObserverSet<>, int, DependencyContext>);
|
||||
static_assert(!dependencies::ObserversFor<ObserverSet<>, ComputationGraph, int>);
|
||||
|
||||
/** @brief Observer validation must also constrain the public cache factory. */
|
||||
template <typename ObserversT>
|
||||
concept CanMakeObservedCache = requires(const ComputationGraph& graph, const DependencyContext& context, ObserversT&& observers) {
|
||||
dependencies::make_computation_cache(graph, context, std::forward<ObserversT>(observers));
|
||||
};
|
||||
static_assert(CanMakeObservedCache<ObserverSet<NodeObserver, SourceEdge>>);
|
||||
static_assert(!CanMakeObservedCache<ObserverSet<MissingObserver>>);
|
||||
static_assert(!CanMakeObservedCache<ObserverSet<MutableEdge>>);
|
||||
|
||||
using MoveOnlyBefore = decltype([state = std::make_unique<int>(0)] { return ++*state; });
|
||||
using CountAfter = decltype([](int&, NodeOutcome) noexcept {});
|
||||
using MoveOnlyObserver = decltype(dependencies::observe_node<First>(std::declval<MoveOnlyBefore>(), CountAfter{}));
|
||||
static_assert(CanObserveNode<MoveOnlyBefore, CountAfter>);
|
||||
static_assert(!CanObserveNode<MoveOnlyBefore&, CountAfter>);
|
||||
static_assert(CanMakeObservedCache<ObserverSet<MoveOnlyObserver>>);
|
||||
static_assert(!CanMakeObservedCache<ObserverSet<MoveOnlyObserver>&>);
|
||||
}
|
||||
|
||||
namespace {
|
||||
using AllBefore = decltype([]<typename Key>(std::type_identity<Key>) { return 0; });
|
||||
using AllAfter = decltype([](int&, NodeOutcome) noexcept {});
|
||||
using AllObserver = decltype(dependencies::observe_nodes(AllBefore{}, AllAfter{}));
|
||||
using SomeBefore = decltype([](std::type_identity<First>) { return 0; });
|
||||
using SomeObserver = decltype(dependencies::observe_nodes(SomeBefore{}, AllAfter{}));
|
||||
using ThrowingAllAfter = decltype([](int&, NodeOutcome) {});
|
||||
using UnsafeAllObserver = decltype(dependencies::observe_nodes(AllBefore{}, ThrowingAllAfter{}));
|
||||
using ReferenceAllBefore = decltype([]<typename Key>(std::type_identity<Key>) -> int& { static int state = 0; return state; });
|
||||
using ReferenceAllObserver = decltype(dependencies::observe_nodes(ReferenceAllBefore{}, AllAfter{}));
|
||||
static_assert(dependencies::ObserversFor<ObserverSet<AllObserver>, ComputationGraph, DependencyContext>);
|
||||
static_assert(dependencies::ObserversFor<ObserverSet<AllObserver, NodeObserver, SourceEdge>, ComputationGraph, DependencyContext>);
|
||||
static_assert(!dependencies::ObserversFor<ObserverSet<SomeObserver>, ComputationGraph, DependencyContext>);
|
||||
static_assert(!dependencies::ObserversFor<ObserverSet<UnsafeAllObserver>, ComputationGraph, DependencyContext>);
|
||||
static_assert(!dependencies::ObserversFor<ObserverSet<ReferenceAllObserver>, ComputationGraph, DependencyContext>);
|
||||
static_assert(CanMakeObservedCache<ObserverSet<AllObserver>>);
|
||||
static_assert(!CanMakeObservedCache<ObserverSet<UnsafeAllObserver>>);
|
||||
static_assert(dependencies::NodeTimingClock<dependencies::SteadyNodeClock>);
|
||||
static_assert(!dependencies::NodeTimingClock<decltype([] { return std::chrono::steady_clock::now(); })>);
|
||||
static_assert(!dependencies::NodeTimingClock<decltype([]() noexcept { return 1.0; })>);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
#include "serif/utils/dependencies/dependency_context.hpp"
|
||||
#include "serif/utils/dependencies/key_list_operations.hpp"
|
||||
#include "serif/utils/dependencies/keys.hpp"
|
||||
|
||||
#include <concepts>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
namespace dependencies = serif::utils::dependencies;
|
||||
|
||||
struct First {};
|
||||
struct Second {};
|
||||
struct Missing {};
|
||||
|
||||
using FirstResource = decltype(dependencies::own<First>(1));
|
||||
using SecondResource = decltype(dependencies::own<Second>(2));
|
||||
using DependencyContext = dependencies::DependencyContext<FirstResource, SecondResource>;
|
||||
using Selection = decltype(std::declval<const DependencyContext&>().select(dependencies::KeyList<Second>{}));
|
||||
|
||||
template <typename... Resources>
|
||||
concept CanMakeContext = requires(Resources&&... resources) {
|
||||
dependencies::make_dependency_context(std::forward<Resources>(resources)...);
|
||||
};
|
||||
|
||||
template <typename ContextT, typename KeyT>
|
||||
concept CanRead = requires(const ContextT& context) { context.template get<KeyT>(); };
|
||||
|
||||
template <typename ContextT>
|
||||
concept CanSelect = requires(ContextT&& context) {
|
||||
std::forward<ContextT>(context).select(dependencies::KeyList<First>{});
|
||||
};
|
||||
|
||||
template <typename ValueT>
|
||||
concept CanBorrow = requires(ValueT&& value) { dependencies::borrow<First>(std::forward<ValueT>(value)); };
|
||||
|
||||
static_assert(dependencies::IsDependencyContext<DependencyContext>);
|
||||
static_assert(dependencies::IsDependencyContext<const DependencyContext&>);
|
||||
static_assert(dependencies::IsDependencyContext<Selection>);
|
||||
static_assert(!dependencies::IsDependencyContext<int>);
|
||||
static_assert(CanMakeContext<>);
|
||||
static_assert(CanMakeContext<FirstResource, SecondResource>);
|
||||
static_assert(!CanMakeContext<FirstResource, FirstResource>);
|
||||
static_assert(!CanMakeContext<int>);
|
||||
static_assert(CanRead<DependencyContext, First> && CanRead<DependencyContext, Second>);
|
||||
static_assert(!CanRead<DependencyContext, Missing>);
|
||||
static_assert(CanRead<Selection, Second> && !CanRead<Selection, First>);
|
||||
static_assert(std::same_as<decltype(std::declval<const DependencyContext&>().get<First>()), const int&>);
|
||||
static_assert(std::same_as<dependencies::bound_type_t<Selection::Bindings, Second>, int>);
|
||||
static_assert(CanSelect<DependencyContext&> && CanSelect<const DependencyContext&>);
|
||||
static_assert(!CanSelect<DependencyContext> && !CanSelect<const DependencyContext>);
|
||||
static_assert(!std::constructible_from<Selection, DependencyContext&&>);
|
||||
static_assert(CanBorrow<int&> && CanBorrow<const int&>);
|
||||
static_assert(!CanBorrow<int> && !CanBorrow<const int>);
|
||||
|
||||
using QualifiedContext = decltype(dependencies::make_dependency_context(
|
||||
dependencies::own<First>(1), dependencies::own<const First>(2), dependencies::own<First&>(3)
|
||||
));
|
||||
static_assert(CanRead<QualifiedContext, First> && CanRead<QualifiedContext, const First> && CanRead<QualifiedContext, First&>);
|
||||
|
||||
using Combined = dependencies::CombinedKeyList<
|
||||
dependencies::KeyList<First, Second>, dependencies::KeyList<Second, Missing>, const dependencies::KeyList<First>&
|
||||
>;
|
||||
static_assert(std::same_as<Combined, dependencies::KeyList<First, Second, Missing>>);
|
||||
static_assert(std::same_as<dependencies::CombinedKeyList<>, dependencies::KeyList<>>);
|
||||
|
||||
using GeneralKeys = dependencies::KeyList<dependencies::keys::Input, dependencies::keys::Output,
|
||||
dependencies::keys::Scale, dependencies::keys::Parameters, dependencies::keys::Coordinates>;
|
||||
static_assert(dependencies::IsKeyList<GeneralKeys>);
|
||||
static_assert(std::same_as<dependencies::InputList<First, Second>, dependencies::KeyList<First, Second>>);
|
||||
static_assert(std::same_as<dependencies::RequestedResults<First>, dependencies::KeyList<First>>);
|
||||
|
||||
// Keys describe roles; even a nonconstructible descriptor with data can name one.
|
||||
struct CustomTerm {
|
||||
int member;
|
||||
CustomTerm() = delete;
|
||||
};
|
||||
using CustomContext = decltype(dependencies::make_dependency_context(dependencies::own<CustomTerm>(1)));
|
||||
static_assert(CanRead<CustomContext, CustomTerm>);
|
||||
|
||||
struct NonComparable {};
|
||||
struct ReferenceSnapshot {
|
||||
const int& capture(const int&) const;
|
||||
bool matches(const int&, const int&) const;
|
||||
};
|
||||
struct RvalueOnlySnapshot {
|
||||
int capture(const int&) const;
|
||||
bool matches(int&&, const int&) const;
|
||||
};
|
||||
struct MoveOnlySnapshot {
|
||||
std::unique_ptr<int> capture(const int&) const;
|
||||
bool matches(const std::unique_ptr<int>&, const int&) const;
|
||||
};
|
||||
|
||||
static_assert(dependencies::SnapshotPolicyFor<dependencies::ValueSnapshot, int>);
|
||||
static_assert(!dependencies::SnapshotPolicyFor<dependencies::ValueSnapshot, NonComparable>);
|
||||
static_assert(!dependencies::SnapshotPolicyFor<dependencies::ValueSnapshot, std::unique_ptr<int>>);
|
||||
static_assert(!dependencies::SnapshotPolicyFor<ReferenceSnapshot, int>);
|
||||
static_assert(!dependencies::SnapshotPolicyFor<RvalueOnlySnapshot, int>);
|
||||
static_assert(dependencies::SnapshotPolicyFor<MoveOnlySnapshot, int>);
|
||||
using Projection = decltype(dependencies::track_by([](const int& value) { return value; }));
|
||||
using InvalidProjection = decltype(dependencies::track_by([](const int&) { return NonComparable{}; }));
|
||||
static_assert(dependencies::SnapshotPolicyFor<Projection, int>);
|
||||
static_assert(!dependencies::SnapshotPolicyFor<InvalidProjection, int>);
|
||||
|
||||
template <typename... Contexts>
|
||||
concept CanCombineContexts = requires(const Contexts&... contexts)
|
||||
{
|
||||
dependencies::combine_dependency_contexts(contexts...);
|
||||
};
|
||||
using FirstContext = dependencies::DependencyContext<FirstResource>;
|
||||
using SecondContext = dependencies::DependencyContext<SecondResource>;
|
||||
struct DefaultInputs {
|
||||
dependencies::DependencyContext<> inputs;
|
||||
};
|
||||
static_assert(requires { DefaultInputs{}; });
|
||||
static_assert(std::constructible_from<FirstContext, FirstResource>);
|
||||
static_assert(!std::convertible_to<FirstResource, FirstContext>);
|
||||
static_assert(!std::default_initializable<FirstContext>);
|
||||
using MoveOnlyContext = decltype(dependencies::make_dependency_context(dependencies::own<First>(std::make_unique<int>(3))));
|
||||
static_assert(CanCombineContexts<>);
|
||||
static_assert(CanCombineContexts<FirstContext, SecondContext>);
|
||||
static_assert(CanCombineContexts<FirstContext, Selection>);
|
||||
static_assert(!CanCombineContexts<FirstContext, FirstContext>);
|
||||
static_assert(!CanCombineContexts<DependencyContext, Selection>);
|
||||
static_assert(!CanCombineContexts<MoveOnlyContext>);
|
||||
static_assert(!CanCombineContexts<int>);
|
||||
static_assert(std::same_as<DependencyContext::Keys, dependencies::KeyList<First, Second>>);
|
||||
static_assert(std::same_as<Selection::Keys, dependencies::KeyList<Second>>);
|
||||
}
|
||||
157
src/compile_time_checks/serif/utils/dependencies/key_list.cpp
Normal file
157
src/compile_time_checks/serif/utils/dependencies/key_list.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
#include "serif/utils/dependencies/key_list.hpp"
|
||||
#include "serif/utils/dependencies/key_list_operations.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
namespace dependencies = serif::utils::dependencies;
|
||||
namespace types = serif::utils::types;
|
||||
|
||||
struct Enthalpy {};
|
||||
struct Entropy {};
|
||||
struct ElectronFraction {};
|
||||
struct Density {};
|
||||
struct MissingKey {};
|
||||
|
||||
struct EnthalpyTerm {};
|
||||
struct MaterialTerm {};
|
||||
struct DensityTerm {};
|
||||
|
||||
using Empty = dependencies::KeyList<>;
|
||||
using SurfaceInputs = dependencies::KeyList<Enthalpy, Entropy, ElectronFraction>;
|
||||
using DuplicateInputs = dependencies::KeyList<Enthalpy, Enthalpy>;
|
||||
using MissingInput = dependencies::KeyList<Enthalpy, MissingKey>;
|
||||
|
||||
/** @brief Invalid indexing remains a failed requirement rather than an out-of-range constant. */
|
||||
template <typename KeyT, typename KeysT>
|
||||
concept CanIndex = requires
|
||||
{
|
||||
dependencies::key_index<KeyT, KeysT>();
|
||||
};
|
||||
|
||||
static_assert(dependencies::ContainsKey<Entropy, SurfaceInputs>);
|
||||
static_assert(dependencies::ContainsKey<Entropy, const SurfaceInputs&>);
|
||||
static_assert(!dependencies::ContainsKey<MissingKey, SurfaceInputs>);
|
||||
static_assert(!dependencies::ContainsKey<Enthalpy, Empty>);
|
||||
static_assert(!dependencies::ContainsKey<Enthalpy, DuplicateInputs>);
|
||||
static_assert(!dependencies::ContainsKey<Enthalpy, int>);
|
||||
static_assert(dependencies::key_count_v<Empty> == 0);
|
||||
static_assert(dependencies::key_count_v<const SurfaceInputs&> == 3);
|
||||
static_assert(dependencies::key_index<Enthalpy, SurfaceInputs>() == 0);
|
||||
static_assert(dependencies::key_index<ElectronFraction, const SurfaceInputs&>() == 2);
|
||||
static_assert(!CanIndex<MissingKey, SurfaceInputs>);
|
||||
static_assert(!CanIndex<Enthalpy, Empty>);
|
||||
static_assert(!CanIndex<Enthalpy, DuplicateInputs>);
|
||||
static_assert(!CanIndex<Enthalpy, int>);
|
||||
|
||||
// Binding order differs from declaration order, and density is available but not required.
|
||||
using Available = dependencies::Bindings<
|
||||
dependencies::Binding<ElectronFraction, MaterialTerm>,
|
||||
dependencies::Binding<Density, DensityTerm>,
|
||||
dependencies::Binding<Enthalpy, EnthalpyTerm>,
|
||||
dependencies::Binding<Entropy, MaterialTerm>
|
||||
>;
|
||||
using Incomplete = dependencies::Bindings<dependencies::Binding<Enthalpy, EnthalpyTerm>>;
|
||||
using DuplicateBindings = dependencies::Bindings<
|
||||
dependencies::Binding<Enthalpy, EnthalpyTerm>,
|
||||
dependencies::Binding<Enthalpy, MaterialTerm>
|
||||
>;
|
||||
|
||||
template <typename BindingsT, typename KeysT>
|
||||
concept CanResolve = requires {
|
||||
typename dependencies::ResolvedTargets<BindingsT, KeysT>;
|
||||
};
|
||||
|
||||
static_assert(dependencies::IsKeyList<Empty>);
|
||||
static_assert(dependencies::IsKeyList<SurfaceInputs>);
|
||||
static_assert(dependencies::IsKeyList<const volatile SurfaceInputs&>);
|
||||
static_assert(!dependencies::IsKeyList<DuplicateInputs>);
|
||||
static_assert(!dependencies::IsKeyList<int>);
|
||||
static_assert(std::is_same_v<SurfaceInputs::Keys, types::TypeList<Enthalpy, Entropy, ElectronFraction>>);
|
||||
|
||||
static_assert(dependencies::SatisfiesKeyList<Available, SurfaceInputs>);
|
||||
static_assert(dependencies::SatisfiesKeyList<const volatile Available&, const SurfaceInputs&>);
|
||||
static_assert(dependencies::SatisfiesKeyList<Available&&, SurfaceInputs&&>);
|
||||
static_assert(!dependencies::SatisfiesKeyList<Incomplete, SurfaceInputs>);
|
||||
static_assert(!dependencies::SatisfiesKeyList<Available, MissingInput>);
|
||||
static_assert(!dependencies::SatisfiesKeyList<Available, DuplicateInputs>);
|
||||
static_assert(!dependencies::SatisfiesKeyList<DuplicateBindings, SurfaceInputs>);
|
||||
static_assert(!dependencies::SatisfiesKeyList<int, SurfaceInputs>);
|
||||
static_assert(!dependencies::SatisfiesKeyList<Available, int>);
|
||||
|
||||
// Distinct physical inputs remain distinct entries even when they share a target type.
|
||||
using SurfaceTargets = types::TypeList<EnthalpyTerm, MaterialTerm, MaterialTerm>;
|
||||
static_assert(std::is_same_v<dependencies::ResolvedTargets<Available, SurfaceInputs>, SurfaceTargets>);
|
||||
static_assert(std::is_same_v<dependencies::ResolvedTargets<const volatile Available&, const SurfaceInputs&>, SurfaceTargets>);
|
||||
static_assert(std::is_same_v<
|
||||
dependencies::ResolvedTargets<Available, dependencies::KeyList<Entropy, Enthalpy>>,
|
||||
types::TypeList<MaterialTerm, EnthalpyTerm>
|
||||
>);
|
||||
|
||||
static_assert(dependencies::SatisfiesKeyList<dependencies::Bindings<>, Empty>);
|
||||
static_assert(dependencies::SatisfiesKeyList<Available, Empty>);
|
||||
static_assert(std::is_same_v<dependencies::ResolvedTargets<dependencies::Bindings<>, Empty>, types::TypeList<>>);
|
||||
static_assert(std::is_same_v<dependencies::ResolvedTargets<Available, Empty>, types::TypeList<>>);
|
||||
static_assert(!dependencies::SatisfiesKeyList<DuplicateBindings, Empty>);
|
||||
static_assert(!dependencies::SatisfiesKeyList<int, Empty>);
|
||||
|
||||
// Normalize the containers, while preserving the exact key and target types they contain.
|
||||
using QualifiedInputs = dependencies::KeyList<const Enthalpy, Enthalpy&>;
|
||||
using QualifiedBindings = dependencies::Bindings<
|
||||
dependencies::Binding<const Enthalpy, const double&>,
|
||||
dependencies::Binding<Enthalpy&, void>
|
||||
>;
|
||||
static_assert(dependencies::IsKeyList<QualifiedInputs>);
|
||||
static_assert(dependencies::key_index<const Enthalpy, QualifiedInputs>() == 0);
|
||||
static_assert(dependencies::key_index<Enthalpy&, QualifiedInputs>() == 1);
|
||||
static_assert(!CanIndex<Enthalpy, QualifiedInputs>);
|
||||
static_assert(dependencies::IsKeyList<dependencies::KeyList<Enthalpy, const Enthalpy>>);
|
||||
static_assert(dependencies::SatisfiesKeyList<QualifiedBindings, QualifiedInputs>);
|
||||
static_assert(!dependencies::SatisfiesKeyList<Available, QualifiedInputs>);
|
||||
static_assert(!dependencies::SatisfiesKeyList<QualifiedBindings, dependencies::KeyList<Enthalpy>>);
|
||||
static_assert(std::is_same_v<dependencies::ResolvedTargets<QualifiedBindings, QualifiedInputs>, types::TypeList<const double&, void>>);
|
||||
|
||||
// Unsupported resolution must fail a requirement without producing a hard compiler error.
|
||||
static_assert(CanResolve<Available, SurfaceInputs>);
|
||||
static_assert(CanResolve<dependencies::Bindings<>, Empty>);
|
||||
static_assert(!CanResolve<Incomplete, SurfaceInputs>);
|
||||
static_assert(!CanResolve<Available, MissingInput>);
|
||||
static_assert(!CanResolve<Available, DuplicateInputs>);
|
||||
static_assert(!CanResolve<DuplicateBindings, SurfaceInputs>);
|
||||
static_assert(!CanResolve<int, SurfaceInputs>);
|
||||
static_assert(!CanResolve<Available, int>);
|
||||
static_assert(!CanResolve<int, Empty>);
|
||||
static_assert(!CanResolve<DuplicateBindings, Empty>);
|
||||
static_assert(!CanResolve<Available, QualifiedInputs>);
|
||||
// Full model graphs may contain more keys than the compiler's default fold-expression depth.
|
||||
template <std::size_t Index>
|
||||
struct LargeKey {};
|
||||
|
||||
/** @brief Name a large key pack without constructing any resources or computations. */
|
||||
template <std::size_t... Indices>
|
||||
auto large_keys(std::index_sequence<Indices...>) -> dependencies::KeyList<LargeKey<Indices>...>;
|
||||
|
||||
/** @brief Keep a duplicate at the end of a large public key list to test validation rather than union deduplication. */
|
||||
template <std::size_t... Indices>
|
||||
auto duplicate_large_keys(std::index_sequence<Indices...>) -> dependencies::KeyList<LargeKey<Indices>..., LargeKey<299>>;
|
||||
|
||||
using Large = decltype(large_keys(std::make_index_sequence<300>{}));
|
||||
using DuplicateLarge = decltype(duplicate_large_keys(std::make_index_sequence<300>{}));
|
||||
static_assert(!dependencies::IsKeyList<DuplicateLarge>);
|
||||
static_assert(dependencies::IsKeyList<dependencies::KeyList<LargeKey<0>, const LargeKey<0>, LargeKey<0>&>>);
|
||||
static_assert(!dependencies::IsKeyList<dependencies::KeyList<const LargeKey<0>, LargeKey<0>&, const LargeKey<0>>>);
|
||||
using Extended = dependencies::CombinedKeyList<Large, dependencies::KeyList<LargeKey<0>, MissingKey>>;
|
||||
static_assert(dependencies::ContainsKey<LargeKey<299>, Large>);
|
||||
static_assert(!dependencies::ContainsKey<MissingKey, Large>);
|
||||
static_assert(dependencies::key_index<LargeKey<299>, Large>() == 299);
|
||||
static_assert(dependencies::detail::key_list_subset_v<Large, Large>);
|
||||
static_assert(dependencies::detail::key_list_subset_v<Empty, Large>);
|
||||
static_assert(!dependencies::detail::key_list_subset_v<Large, Empty>);
|
||||
static_assert(!dependencies::detail::key_list_subset_v<dependencies::KeyList<MissingKey>, Large>);
|
||||
static_assert(dependencies::key_count_v<Extended> == 301);
|
||||
static_assert(dependencies::key_index<LargeKey<0>, Extended>() == 0);
|
||||
static_assert(dependencies::key_index<LargeKey<299>, Extended>() == 299);
|
||||
static_assert(dependencies::key_index<MissingKey, Extended>() == 300);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#include <concepts>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include "serif/utils/dependencies/computation_cache.hpp"
|
||||
|
||||
namespace {
|
||||
namespace dependencies = serif::utils::dependencies;
|
||||
|
||||
struct Source {};
|
||||
struct First {};
|
||||
struct Second {};
|
||||
struct Output {};
|
||||
struct InputFailure {};
|
||||
struct PhysicsFailure {};
|
||||
struct OwnedFailure {
|
||||
std::unique_ptr<int> value;
|
||||
};
|
||||
|
||||
template <typename Failure>
|
||||
struct Fallible {
|
||||
/// Exercise the expected-result type without a runtime library dependency.
|
||||
[[nodiscard]] constexpr std::expected<int, Failure> operator()(int value) const {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
struct Infallible {
|
||||
/// Value-returning stages must remain compatible with every valid chosen error type.
|
||||
[[nodiscard]] constexpr int operator()(int value) const {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
using Context = decltype(dependencies::make_dependency_context(dependencies::own<Source>(1)));
|
||||
|
||||
template <typename Failure>
|
||||
using FirstCalculation = dependencies::Computation<First, dependencies::InputList<Source>, Fallible<Failure>>;
|
||||
|
||||
template <typename Failure>
|
||||
using SecondCalculation = dependencies::Computation<Second, dependencies::InputList<First>, Fallible<Failure>>;
|
||||
|
||||
using OutputCalculation = dependencies::Computation<Output, dependencies::InputList<Second>, Infallible>;
|
||||
using MixedGraph = dependencies::ComputationGraph<FirstCalculation<InputFailure>, SecondCalculation<PhysicsFailure>, OutputCalculation>;
|
||||
using FailureUnion = std::variant<InputFailure, PhysicsFailure>;
|
||||
|
||||
static_assert(dependencies::CanPrepare<MixedGraph, Context, FailureUnion>);
|
||||
static_assert(dependencies::CanPrepare<const MixedGraph&, const Context&, FailureUnion>);
|
||||
static_assert(dependencies::CanPrepare<MixedGraph, Context, std::variant<PhysicsFailure, InputFailure>>);
|
||||
static_assert(dependencies::CanPrepare<MixedGraph, Context, std::variant<std::monostate, InputFailure, PhysicsFailure>>);
|
||||
static_assert(!dependencies::CanPrepare<MixedGraph, Context, InputFailure>);
|
||||
static_assert(!dependencies::CanPrepare<MixedGraph, Context, std::variant<InputFailure>>);
|
||||
static_assert(!dependencies::CanPrepare<MixedGraph, Context, std::variant<PhysicsFailure>>);
|
||||
static_assert(!dependencies::CanPrepare<MixedGraph, Context, std::variant<InputFailure, int>>);
|
||||
static_assert(!dependencies::CanPrepare<MixedGraph, Context, std::variant<InputFailure, InputFailure, PhysicsFailure>>);
|
||||
static_assert(!dependencies::CanPrepare<MixedGraph, Context, std::variant<InputFailure, PhysicsFailure, PhysicsFailure>>);
|
||||
static_assert(!dependencies::CanPrepare<MixedGraph, Context, void>);
|
||||
|
||||
using SameErrorGraph = dependencies::ComputationGraph<FirstCalculation<InputFailure>, SecondCalculation<InputFailure>, OutputCalculation>;
|
||||
static_assert(dependencies::CanPrepare<SameErrorGraph, Context, InputFailure>);
|
||||
static_assert(dependencies::CanPrepare<SameErrorGraph, Context, FailureUnion>);
|
||||
static_assert(!dependencies::CanPrepare<SameErrorGraph, Context, PhysicsFailure>);
|
||||
|
||||
using ExistingUnionGraph = dependencies::ComputationGraph<FirstCalculation<FailureUnion>>;
|
||||
static_assert(dependencies::CanPrepare<ExistingUnionGraph, Context, FailureUnion>);
|
||||
|
||||
using TextErrorGraph = dependencies::ComputationGraph<FirstCalculation<const char*>>;
|
||||
static_assert(std::constructible_from<std::string, const char*>);
|
||||
static_assert(std::constructible_from<std::variant<std::string, int>, const char*>);
|
||||
static_assert(!dependencies::CanPrepare<TextErrorGraph, Context, std::string>);
|
||||
static_assert(!dependencies::CanPrepare<TextErrorGraph, Context, std::variant<std::string, int>>);
|
||||
static_assert(dependencies::CanPrepare<TextErrorGraph, Context, std::variant<const char*, std::string>>);
|
||||
|
||||
using OwnedErrorGraph = dependencies::ComputationGraph<FirstCalculation<InputFailure>, SecondCalculation<OwnedFailure>, OutputCalculation>;
|
||||
using OwnedErrorUnion = std::variant<InputFailure, OwnedFailure>;
|
||||
static_assert(std::move_constructible<OwnedErrorUnion>);
|
||||
static_assert(!std::copy_constructible<OwnedErrorUnion>);
|
||||
static_assert(dependencies::CanPrepare<OwnedErrorGraph, Context, OwnedErrorUnion>);
|
||||
}
|
||||
32
src/compile_time_checks/serif/utils/laneemden.cpp
Normal file
32
src/compile_time_checks/serif/utils/laneemden.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include <concepts>
|
||||
#include <expected>
|
||||
#include <format>
|
||||
#include <string>
|
||||
|
||||
#include "serif/utils/laneemden/laneemden.hpp"
|
||||
|
||||
namespace {
|
||||
namespace laneemden = serif::utils::laneemden;
|
||||
namespace dimensions = serif::dimensions;
|
||||
|
||||
/** @brief Physical reference inputs must carry their distinct dimensions. */
|
||||
template <typename Mass, typename Density, typename Gravity>
|
||||
concept CanScaleMass = requires(const laneemden::Solution &solution, Mass mass, Density density, Gravity gravity) {
|
||||
{ laneemden::scale_from_mass(solution, mass, density, gravity) } -> std::same_as<std::expected<laneemden::PhysicalScales, std::string>>;
|
||||
};
|
||||
|
||||
static_assert(CanScaleMass<dimensions::QuantityValue<dimensions::Mass>, dimensions::QuantityValue<dimensions::MassDensity>, dimensions::QuantityValue<dimensions::GravitationalConstant>>);
|
||||
static_assert(!CanScaleMass<double, double, double>);
|
||||
static_assert(!CanScaleMass<dimensions::QuantityValue<dimensions::Length>, dimensions::QuantityValue<dimensions::MassDensity>, dimensions::QuantityValue<dimensions::GravitationalConstant>>);
|
||||
static_assert(!CanScaleMass<dimensions::QuantityValue<dimensions::Mass>, dimensions::QuantityValue<dimensions::Length>, dimensions::QuantityValue<dimensions::GravitationalConstant>>);
|
||||
static_assert(std::same_as<decltype(laneemden::solve(3.0)), std::expected<laneemden::Solution, std::string>>);
|
||||
static_assert(std::equality_comparable<laneemden::IntegrationOptions>);
|
||||
static_assert(std::formattable<laneemden::Sample, char>);
|
||||
static_assert(std::formattable<laneemden::Solution, char>);
|
||||
|
||||
/** @brief Literal formatting must parse selectors at compile time for both levels of the profile. */
|
||||
constexpr std::format_string<laneemden::Sample, laneemden::Sample, laneemden::Sample, laneemden::Sample, laneemden::Sample>
|
||||
sample_formats{"{} {:all} {:xi} {:theta} {:derivative}"};
|
||||
constexpr std::format_string<laneemden::Solution, laneemden::Solution, laneemden::Solution, laneemden::Solution, laneemden::Solution>
|
||||
solution_formats{"{} {:all} {:xi} {:theta} {:derivative}"};
|
||||
}
|
||||
41
src/compile_time_checks/serif/utils/projection.cpp
Normal file
41
src/compile_time_checks/serif/utils/projection.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <expected>
|
||||
#include <string>
|
||||
|
||||
#include "serif/backend/mfem/projection/radial.hpp"
|
||||
|
||||
namespace {
|
||||
namespace projection = serif::utils::projection;
|
||||
namespace backend = projection::backend::mfem;
|
||||
|
||||
struct ScalarProfile {
|
||||
/** @brief Declare a valid infallible radial evaluator. */
|
||||
double operator()(double) const;
|
||||
};
|
||||
|
||||
struct CheckedProfile {
|
||||
/** @brief Declare a valid fallible radial evaluator. */
|
||||
projection::RadialValue operator()(double) const;
|
||||
};
|
||||
|
||||
struct VectorProfile {
|
||||
/** @brief Vectors are not scalar radial amplitudes. */
|
||||
::mfem::Vector operator()(double) const;
|
||||
};
|
||||
|
||||
struct WrongFailure {
|
||||
/** @brief Reject unadapted errors rather than silently erasing their meaning. */
|
||||
std::expected<double, int> operator()(double) const;
|
||||
};
|
||||
|
||||
static_assert(projection::RadialFunction<ScalarProfile>);
|
||||
static_assert(projection::RadialFunction<CheckedProfile>);
|
||||
static_assert(!projection::RadialFunction<VectorProfile>);
|
||||
static_assert(!projection::RadialFunction<WrongFailure>);
|
||||
static_assert(!projection::RadialFunction<int>);
|
||||
static_assert(std::same_as<decltype(backend::project_scalar(
|
||||
std::declval<::mfem::FiniteElementSpace&>(), ScalarProfile{}
|
||||
)), backend::ProjectionResult>);
|
||||
static_assert(std::same_as<decltype(backend::project_vector(
|
||||
std::declval<::mfem::FiniteElementSpace&>(), CheckedProfile{}
|
||||
)), backend::ProjectionResult>);
|
||||
}
|
||||
Reference in New Issue
Block a user