#pragma once #include "serif/model/equilibrium_equations.hpp" #include "serif/model/plan.hpp" #include "serif/model/detail/surface_plan.hpp" #include "serif/operators/concepts.hpp" #include "serif/equations/surface_potential.hpp" #include "serif/equations/primal_gravity.hpp" namespace serif::model { namespace detail { template struct ShapePlan; template <> struct ShapePlan { using Coefficients = utils::dependencies::KeyList<>; using Values = utils::dependencies::KeyList<>; using Rows = utils::dependencies::KeyList<>; }; /** @brief The isobar level couples only existing scalar potential, rotation and mass coordinates. */ template <> struct ShapePlan { using Coefficients = typename OperandTerms::Type; using Values = utils::dependencies::KeyList; using Rows = utils::dependencies::KeyList; }; } /** * @brief Collect the explicitly selected physical coefficients and supported constraint coordinates. * @note This plan verifies representation, not mathematical closure. Equal units never merge distinct * physical identities, and prescribed coefficients remain available as graph inputs without becoming unknowns. */ template , utils::dependencies::IsKeyList AdditionalFields = utils::dependencies::KeyList<>> struct EquilibriumPlan final { static constexpr bool has_shape = !std::same_as; static constexpr bool has_surface = !std::same_as; static constexpr bool has_primal_gravity = [](utils::types::TypeList) { return (std::same_as || ...); }(typename Description::InteriorEquations::Equations{}); using Interior = detail::InteriorPlan; using Shape = detail::ShapePlan; using ResidualEquations = typename Interior::EquationsList; using AvailableFields = utils::dependencies::CombinedKeyList; using SolvedFields = typename detail::RemoveKeys::Type; using Residuals = utils::dependencies::CombinedKeyList; using Surface = detail::SurfacePlan; using Coefficients = utils::dependencies::CombinedKeyList; static_assert(!has_shape || !utils::dependencies::ContainsKey, "Prescribe geometry by selecting NoShapeForce and supplying a displacement field. An active shape-force equation uses independent solved surface coordinates."); using Values = utils::dependencies::CombinedKeyList::Type, typename Shape::Values>; // Geometry is supplied separately even when no equation solves for the shape. using PhysicalInputs = utils::dependencies::CombinedKeyList>; using InactiveRows = std::conditional_t, utils::dependencies::KeyList>; using Layout = discretization::blocks::SystemLayout; }; }