akreon-control
Optimal and robust control and state estimation in C++26.
Loading...
Searching...
No Matches
control_traits.hpp
Go to the documentation of this file.
1/*
2 * Copyright © 2025-2026 Akreon LLC
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6#pragma once
7
8#include <concepts>
9#include <cstdint>
10#include <tuple>
11
12#include "akreon/linalg/space/space_concepts.hpp"
13
14namespace control {
15
17enum struct PortRole : uint8_t {
18 EXOGENOUS,
19 CONTROL,
22};
23
25template <PortRole RoleT, std::size_t OffsetT, std::size_t SizeT>
26struct Port {
27 static constexpr PortRole Role = RoleT;
28 static constexpr std::size_t Offset = OffsetT;
29 static constexpr std::size_t Size = SizeT;
30};
31
32template <typename Channel>
33concept PortLike = requires {
34 { Channel::Role } -> std::convertible_to<PortRole>;
35 { Channel::Offset } -> std::convertible_to<std::size_t>;
36 { Channel::Size } -> std::convertible_to<std::size_t>;
37};
38
40template <PortLike... Channels>
41struct PortLayout {
42 using ChannelsTuple = std::tuple<Channels...>;
43
45 template <PortRole Role>
46 static consteval std::size_t count() {
47 return (std::size_t{0} + ... +
48 (Channels::Role == Role ? std::size_t{1} : std::size_t{0}));
49 }
50
52 template <PortRole Role>
53 static consteval std::size_t total_size() {
54 return (std::size_t{0} + ... +
55 (Channels::Role == Role ? Channels::Size : 0));
56 }
57
59 template <PortRole Role, std::size_t Index>
60 static consteval std::size_t offset() {
61 std::size_t current = 0;
62 std::size_t result = 0;
63 // Walk matching channels left-to-right. current is incremented only for
64 // matching channels, so current == Index is true for exactly one match.
65 ((Channels::Role == Role
66 ? ((current == Index ? (result = Channels::Offset, 0) : 0), ++current,
67 0)
68 : 0),
69 ...);
70 static_assert(Index < count<Role>(), "Requested port is missing");
71 return result;
72 }
73
75 template <PortRole Role, std::size_t Index>
76 static consteval std::size_t size() {
77 std::size_t current = 0;
78 std::size_t result = 0;
79 // Same matching-channel walk as offset(), but records channel size.
80 ((Channels::Role == Role
81 ? ((current == Index ? (result = Channels::Size, 0) : 0), ++current,
82 0)
83 : 0),
84 ...);
85 static_assert(Index < count<Role>(), "Requested port is missing");
86 return result;
87 }
88
90 template <std::size_t FullDim>
91 static consteval bool validates() {
92 // First fold checks every channel has positive size and in-bounds extent.
93 bool ok =
94 ((Channels::Size > 0 && Channels::Offset + Channels::Size <= FullDim) &&
95 ...);
96 bool occupied[FullDim == 0 ? 1 : FullDim]{};
97 auto mark = [&]<typename Channel>() consteval {
98 if constexpr (Channel::Offset + Channel::Size > FullDim) { return; }
99 // Mark each occupied index; seeing an index twice means overlap.
100 for (std::size_t i = Channel::Offset; i < Channel::Offset + Channel::Size;
101 ++i) {
102 if (occupied[i]) ok = false;
103 occupied[i] = true;
104 }
105 };
106 // Invoke mark once per channel type in declaration order.
107 (mark.template operator()<Channels>(), ...);
108 return ok;
109 }
110};
111
113enum struct TimeDomainType : uint8_t { CONTINUOUS = 0, DISCRETE };
114
116template <TimeDomainType Type, linalg::SquareMatrix PlantIn,
117 linalg::MatrixContainer ControlIn, linalg::MatrixContainer OutputIn>
118struct StateSpace;
119
121template <class StateSpace>
123
125template <TimeDomainType Type, linalg::SquareMatrix PlantIn,
126 linalg::MatrixContainer ControlIn, linalg::MatrixContainer OutputIn>
127struct ss_traits<StateSpace<Type, PlantIn, ControlIn, OutputIn>> {
128 using Plant = linalg::remove_cvref_t<PlantIn>;
129 using Control = linalg::remove_cvref_t<ControlIn>;
130 using Output = linalg::remove_cvref_t<OutputIn>;
131
132 using T = linalg::matrix_scalar_t<Plant>;
133 using State = linalg::Vector<T, linalg::mat_cols_v<Plant>>;
134 using Input = linalg::Vector<T, linalg::mat_cols_v<Control>>;
135 using OutVec = linalg::Vector<T, linalg::mat_rows_v<Output>>;
136 static constexpr TimeDomainType DType = Type;
137};
138
140template <typename StateSpace>
142
144template <typename StateSpace>
147
149template <typename StateSpace>
152
154template <typename StateSpace>
157
159template <typename StateSpace>
162
164template <typename StateSpace>
167
169template <typename StateSpace>
172
174template <typename StateSpace>
175inline constexpr bool is_continuous_v =
178
180template <typename StateSpace>
181concept StateSpaceLike = requires(const StateSpace& ss) {
182 { ss.A };
183 { ss.B };
184 { ss.C };
185};
186
188template <typename StateSpace>
191
193template <typename StateSpace>
196
198template <StateSpaceLike SS, typename InputPorts, typename OutputPorts>
199struct PlantModel;
200
202template <class Plant>
204
206template <StateSpaceLike SS, typename InputPortsT, typename OutputPortsT>
207struct plant_traits<PlantModel<SS, InputPortsT, OutputPortsT>> {
208 using StateSpace = linalg::remove_cvref_t<SS>;
209 using InputPorts = linalg::remove_cvref_t<InputPortsT>;
210 using OutputPorts = linalg::remove_cvref_t<OutputPortsT>;
211
217
218 static constexpr std::size_t StateDim = linalg::vector_dim_v<State>;
219 static constexpr std::size_t InputDim = linalg::vector_dim_v<Input>;
220 static constexpr std::size_t OutputDim = linalg::vector_dim_v<Output>;
221
222 static constexpr std::size_t ExogenousDim =
223 InputPorts::template total_size<PortRole::EXOGENOUS>();
224 static constexpr std::size_t ControlDim =
225 InputPorts::template total_size<PortRole::CONTROL>();
226
227 static constexpr std::size_t PerformanceDim =
228 OutputPorts::template total_size<PortRole::PERFORMANCE>();
229 static constexpr std::size_t MeasurementDim =
230 OutputPorts::template total_size<PortRole::MEASUREMENT>();
231};
232
234template <typename Plant>
236
238template <typename Plant>
240 typename plant_traits<linalg::remove_cvref_t<Plant>>::StateSpace;
241
243template <typename Plant>
245 typename plant_traits<linalg::remove_cvref_t<Plant>>::InputPorts;
246
248template <typename Plant>
250 typename plant_traits<linalg::remove_cvref_t<Plant>>::OutputPorts;
251
253template <typename Plant>
256
258template <typename Plant>
261
263template <typename Plant>
266
268template <typename Plant>
269concept PlantModelLike = requires(const Plant& plant) {
270 typename plant_traits<linalg::remove_cvref_t<Plant>>::StateSpace;
271 { plant.state_space };
272 { plant.exogenous_matrix() };
273 { plant.control_matrix() };
274 { plant.performance_matrix() };
275 { plant.measurement_matrix() };
276};
277
279template <typename Plant>
280inline constexpr bool plant_is_continuous_v =
283
285template <typename Plant>
288
290template <typename Plant>
293
294} // namespace control
ContinuousPlantModel concept.
ContinuousStateSpace concept.
DiscretePlantModel concept.
ContinuousStateSpace concept.
PlantModel concept.
StateSpace concept.
typename ss_traits< linalg::remove_cvref_t< StateSpace > >::Input input_vec_t
extract input vector
constexpr bool is_continuous_v
check if continuous or not
typename ss_traits< linalg::remove_cvref_t< StateSpace > >::Output ss_output_t
ss output type
PortRole
Plant port role.
@ PERFORMANCE
z: regulated/performance output
@ CONTROL
u: controller input to the plant
@ EXOGENOUS
w: disturbance, reference, noise, or other external input
@ MEASUREMENT
y: measured output available for feedback/estimation
typename ss_traits< linalg::remove_cvref_t< StateSpace > >::State state_vec_t
extract state vector
typename ss_traits< linalg::remove_cvref_t< StateSpace > >::T ss_type_t
extract scalar type from ss
typename plant_traits< linalg::remove_cvref_t< Plant > >::Output plant_output_vec_t
extract full output vector from plant
typename ss_traits< linalg::remove_cvref_t< StateSpace > >::OutVec output_vec_t
extract output vector
typename plant_traits< linalg::remove_cvref_t< Plant > >::State plant_state_vec_t
extract state vector from plant
typename plant_traits< linalg::remove_cvref_t< Plant > >::InputPorts plant_input_ports_t
extract input port layout type from plant
typename plant_traits< linalg::remove_cvref_t< Plant > >::T plant_type_t
extract scalar type from plant
typename ss_traits< linalg::remove_cvref_t< StateSpace > >::Control ss_control_t
ss control type
typename plant_traits< linalg::remove_cvref_t< Plant > >::StateSpace plant_state_space_t
extract state-space type from plant
typename plant_traits< linalg::remove_cvref_t< Plant > >::OutputPorts plant_output_ports_t
extract output port layout type from plant
TimeDomainType
time domain type
constexpr bool plant_is_continuous_v
check if plant model is continuous
typename ss_traits< linalg::remove_cvref_t< StateSpace > >::Plant ss_plant_t
ss plant type
typename plant_traits< linalg::remove_cvref_t< Plant > >::Input plant_input_vec_t
extract full input vector from plant
Forward declaration of PlantModel.
Port layout over either the state-space input or output axis.
static consteval std::size_t count()
count channels matching a port role
static consteval std::size_t size()
size of the Index-th channel matching a port role
static consteval std::size_t total_size()
total dimension for channels matching a port role
static consteval std::size_t offset()
offset of the Index-th channel matching a port role
static consteval bool validates()
validate port bounds and overlap against a full axis dimension
std::tuple< Channels... > ChannelsTuple
Port specification.
static constexpr PortRole Role
static constexpr std::size_t Size
static constexpr std::size_t Offset
forward decl of state space
base type traits for plant model
linalg::Vector< T, linalg::mat_rows_v< Output > > OutVec
linalg::Vector< T, linalg::mat_cols_v< Control > > Input
base type traits for state space