#pragma once #include "serif/model/description.hpp" #include "serif/model/detail/specification_endpoints.hpp" #include "serif/model/plan.hpp" #include "serif/operators/linearized_system_view.hpp" namespace serif::model { /** @brief Name a physical derivative using the shared block-source key convention. */ template using Derivative = operators::PhysicalBlock, utils::dependencies::KeyList>; /** @brief Map physical names and built-in generated identities once; custom identities require explicit bindings. */ template using SpecificationBindings = utils::dependencies::Bindings< utils::dependencies::Binding, utils::dependencies::Binding, utils::dependencies::Binding, utils::dependencies::Binding, utils::dependencies::Binding, utils::dependencies::Binding, utils::dependencies::Binding, discretization::blocks::barotropic_constant::mass_normalization>, utils::dependencies::Binding, discretization::blocks::barotropic_constant::mass_normalization>, utils::dependencies::Binding, discretization::blocks::rotation::angular_velocity>, utils::dependencies::Binding, discretization::blocks::rotation::angular_velocity>, Additional...>; using DefaultSpecificationBindings = SpecificationBindings<>; namespace detail { template struct EquationCouplings; template struct EquationCouplings> { using Type = utils::dependencies::KeyList...>; }; template struct InteriorCouplings; template struct InteriorCouplings> { using Type = utils::dependencies::CombinedKeyList::Columns>::Type...>; }; /** @brief Check physical inputs separately from unknowns and explicitly inactive equations. */ template [[nodiscard]] consteval bool valid_specification_endpoints() { using namespace utils::dependencies; using Endpoints = ResolvedSpecification; if constexpr (!ContainsKey || !ContainsKey || !Endpoints::Reads::template owners_present || !Endpoints::Changes::template owners_present) { return false; } return [](KeyList, KeyList) { return sizeof...(Read) != 0 && (ContainsKey && ...) && (ContainsKey || ...) && ((ContainsKey || ContainsKey) && ...); }(typename Endpoints::Reads::Keys{}, typename Endpoints::Changes::Keys{}); } /** @brief Fold independent physical declarations; no equation counting or rank inference is involved. */ template [[nodiscard]] consteval bool valid_specifications(utils::types::TypeList) { using namespace utils::dependencies; constexpr bool declarations = (((!GeneratesState::value && std::same_as> && std::same_as>) || DimensionalScalarSpecification) && ...); if constexpr (!declarations) { return false; } else { return [](utils::types::TypeList) { using Coordinates = CombinedKeyList::Coordinate>...>; using Residuals = CombinedKeyList::Constraint>...>; return key_count_v == sizeof...(Generated) && key_count_v == sizeof...(Generated) && (valid_specification_endpoints() && ...); }(GeneratedSpecifications{}); } } /** @brief Validate declared derivative contracts without inferring zeros from missing providers. */ template [[nodiscard]] consteval bool specification_derivatives_available() { using namespace utils::dependencies; using Endpoints = ResolvedSpecification; return [](KeyList, KeyList) { constexpr auto changed_row = []() { return ContainsKey, Couplings> && (ContainsKey, Couplings> && ...); }; return (ContainsKey, Couplings> && ...) && (changed_row.template operator()() && ...); }(typename Endpoints::Reads::Keys{}, typename Endpoints::Changes::Keys{}); } /** @brief Check each prescription against independently supplied derivative registrations. */ template [[nodiscard]] consteval bool specification_derivatives_available(utils::types::TypeList) { return (specification_derivatives_available() && ...); } } // namespace detail /** @brief Derive declared interior couplings from actual operands rather than a second physical sparsity table. */ template using DeclaredEquationCouplings = typename detail::InteriorCouplings>::Type; /** @brief Validate physical ownership, available inputs and uniquely owned generated equations. */ template , typename Bindings = DefaultSpecificationBindings> concept SpecificationEndpointsValid = StellarModelType && utils::dependencies::IsKeyList && utils::dependencies::IsKeyList && utils::dependencies::IsKeyList && utils::dependencies::IsKeyList && utils::dependencies::IsBindings && detail::valid_specifications, Values, Inputs, Rows, InactiveRows, Bindings>(typename std::remove_cvref_t::SpecificationTypes{}); /** @brief Validate physical endpoints and every derivative in an explicitly declared contract. */ template concept SpecificationTopologyValid = SpecificationEndpointsValid, Bindings> && utils::dependencies::IsKeyList && detail::specification_derivatives_available(detail::GeneratedSpecifications>{}); /** * @brief Require a registered implementation for every declared generated derivative, including explicit zeros. * @note ImplementedCouplings must come from admitted providers. Missing methods never imply zero; this checks * specification contributions only, leaving the equation backend's own execution contract independent. */ template concept SpecificationBackendAvailable = SpecificationTopologyValid && SpecificationTopologyValid; } // namespace serif::model