OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
Controls.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2//
3// Copyright 2026 Caleb Buahin
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
36
37#ifndef OPENSWMM_CONTROLS_HPP
38#define OPENSWMM_CONTROLS_HPP
39
40#include <cstdint>
41#include <vector>
42#include <string>
43#include <unordered_map>
45#include "../math/MathExpr.hpp"
46
47namespace openswmm {
48
50
51namespace controls {
52
53// ============================================================================
54// Comparison and logic operators
55// ============================================================================
56
57enum class CompareOp : int { EQ = 0, NE = 1, LT = 2, LE = 3, GT = 4, GE = 5 };
58enum class LogicOp : int { AND = 0, OR = 1 };
59
60// ============================================================================
61// Condition variable types (matching ALL legacy RuleAttrib values)
62// ============================================================================
63
64enum class ConditionVar : int {
65 // Node attributes
71
72 // Link attributes
84
85 // Gage attributes
86 GAGE_RAIN = 30,
88
89 // System / time
93 SIM_DAY = 43,
94 SIM_MONTH = 44,
96};
97
98// ============================================================================
99// Action attribute (what is being set on the link)
100// ============================================================================
101
102enum class ActionType : int {
104 CURVE = 1,
106 PID = 3
107};
108
109// ============================================================================
110// Rule premise (condition)
111// ============================================================================
112
129
130// ============================================================================
131// Rule action
132// ============================================================================
133
134struct Action {
135 int link_idx = -1;
136 int rule_idx = -1;
138 double value = 0.0;
139 int curve_idx = -1;
140 int tseries_idx = -1;
141 int pid_idx = -1;
142};
143
144// ============================================================================
145// PID controller state (per action)
146// ============================================================================
147
148struct PIDState {
149 double kp = 0.0;
150 double ki = 0.0;
151 double kd = 0.0;
152 double setpoint = 0.0;
153 double e1 = 0.0;
154 double e2 = 0.0;
155 int action_idx = -1;
156};
157
158// ============================================================================
159// Named variable (for rule expressions)
160// ============================================================================
161
163 std::string name;
165 int idx = -1;
166};
167
168// ============================================================================
169// Control rule
170// ============================================================================
171
172struct Rule {
173 std::string name;
174 std::vector<Premise> premises;
175 std::vector<Action> then_actions;
176 std::vector<Action> else_actions;
177 double priority = 0.0;
178};
179
180// ============================================================================
181// Control engine
182// ============================================================================
183
193 int line = -1;
194 std::string message;
195};
196
198public:
199 void init(const std::vector<Rule>& rules);
200
216 int evaluate(SimulationContext& ctx, double current_time, double dt);
217
218 // Rule parsing (from [CONTROLS] text)
219 int parseRuleText(const std::string& text, SimulationContext& ctx);
220
223 const ParseError& lastParseError() const noexcept { return last_parse_error_; }
224
230 void clearRules();
231
232 // Named variables
233 void addNamedVariable(const std::string& name, ConditionVar var, int idx);
234
235 // Math expressions
236 int addExpression(const std::string& name, const std::string& formula);
237
238 std::vector<Rule>& rules() { return rules_; }
239
241 int lastActionCount() const { return last_action_count_; }
242
246 void resetRuleStep() { next_rule_eval_time_ = -1.0; }
247
248 // ========================================================================
249 // SoA batch evaluation index (AD-14)
250 // ========================================================================
251
267 struct PremiseSoA {
269 int count = 0;
270
271 // Per-premise SoA (contiguous, aligned for SIMD)
272 std::vector<int> rule_idx;
273 std::vector<int> premise_idx;
274 std::vector<int> obj_idx;
275 std::vector<int> flat_idx;
276 std::vector<int> op;
277 std::vector<double> rhs_value;
278
279 // Per-premise flags
280 std::vector<bool> rhs_is_variable;
281 std::vector<bool> is_expression;
282
283 // Working buffer (reused each evaluate call)
284 std::vector<double> lhs_values;
285 std::vector<bool> results;
286 };
287
289 void buildPremiseSoA();
290
292 void batchEvaluateGroup(PremiseSoA& group,
293 const SimulationContext& ctx,
294 double current_time, double half_step);
295
296private:
297 int last_action_count_ = 0;
298 std::vector<Rule> rules_;
299 std::vector<PIDState> pid_states_;
300 std::vector<NamedVariable> named_vars_;
301 std::vector<mathexpr::Expression> expressions_;
303 std::unordered_map<std::string, int, CiHash, CiEqual> expr_index_;
304 ParseError last_parse_error_;
305
306 // SoA premise groups (one per variable type that has premises)
307 std::vector<PremiseSoA> premise_groups_;
308
309 // ------------------------------------------------------------------
310 // Two-phase evaluator state (ยง7 of CONTROL_RULES_LEGACY_PARITY_AUDIT)
311 // ------------------------------------------------------------------
312 // Phase 1 batch-evaluates premises (group-major) into the flat cache.
313 // Phase 2 walks each rule's premises in declaration order and applies
314 // legacy AND/OR semantics by reading the flat cache.
315 //
316 // Layout: premise_results_[ rule_premise_offset_[r] + p ] holds the
317 // result of rule r's premise p. uint8_t (not bool) so Phase 1's
318 // compare loops remain SIMD-writable.
319
320 std::vector<uint8_t> premise_results_;
321 std::vector<int> rule_premise_offset_;
322 int total_premises_ = 0;
323
324 // Per-rule premise result tracking (for combining AND/OR)
325 std::vector<bool> rule_results_;
326
327 // TIMEOPEN/TIMECLOSED tracking: when each link's setting last changed.
328 // Stored as absolute date (decimal days) matching legacy Link[j].timeLastSet.
329 // Step 6 of the parity remediation will move this to ctx.links.time_last_set.
330 std::vector<double> link_time_last_set_;
331
335 double next_rule_eval_time_ = -1.0;
336
337 // Tracked across a single evaluate() call
338 double control_value_ = 0.0;
339 double set_point_ = 0.0;
340
341 // Pending action list (for priority deduplication)
342 struct PendingAction {
343 int link_idx;
344 double value;
345 double priority;
346 int rule_idx;
348 };
349 std::vector<PendingAction> pending_actions_;
350
351 double getVariableValue(const SimulationContext& ctx,
352 ConditionVar var, int idx,
353 double current_time, int param = 0) const;
354
355 bool evaluatePremise(const SimulationContext& ctx,
356 const Premise& p, double current_time, double half_step);
357
358 bool compareValues(double lhs, CompareOp op, double rhs) const;
359 bool compareTimes(double lhs, CompareOp op, double rhs, double half_step) const;
360
361 double computePIDSetting(PIDState& pid, double control_value,
362 double current_setting, bool is_pump, double dt);
363
365 double current_time, double dt);
366
368 int applyPendingActions(SimulationContext& ctx, double current_time);
369
371 double getNamedVariableValue(const std::string& name,
372 const SimulationContext& ctx,
373 double current_time) const;
374};
375
376} // namespace controls
377} // namespace openswmm
378
379#endif // OPENSWMM_CONTROLS_HPP
Mathematical expression parser and evaluator.
Case-insensitive string helpers matching legacy SWMM name semantics.
Definition Controls.hpp:197
void resetRuleStep()
Definition Controls.hpp:246
void clearRules()
Definition Controls.cpp:974
int parseRuleText(const std::string &text, SimulationContext &ctx)
Definition Controls.cpp:989
std::vector< Rule > & rules()
Definition Controls.hpp:238
void buildPremiseSoA()
Build SoA index from rules. Called once at init().
Definition Controls.cpp:67
int addExpression(const std::string &name, const std::string &formula)
Definition Controls.cpp:660
const ParseError & lastParseError() const noexcept
Definition Controls.hpp:223
void init(const std::vector< Rule > &rules)
Definition Controls.cpp:49
int lastActionCount() const
Number of actions taken in the last evaluate() call.
Definition Controls.hpp:241
int evaluate(SimulationContext &ctx, double current_time, double dt)
Evaluate all control rules and set link target settings.
Definition Controls.cpp:196
void batchEvaluateGroup(PremiseSoA &group, const SimulationContext &ctx, double current_time, double half_step)
Batch evaluate all premises of one variable type.
Definition Controls.cpp:148
void addNamedVariable(const std::string &name, ConditionVar var, int idx)
Definition Controls.cpp:656
int compareValues(double lhsValue, int relation, double rhsValue)
Compares two values.
Definition controls.c:2007
int compareTimes(double lhsValue, int relation, double rhsValue, double halfStep)
Compares two times.
Definition controls.c:1982
int evaluatePremise(struct TPremise *p, double tStep)
Evaluates premise clause of a control rule.
Definition controls.c:1788
void updateActionValue(struct TAction *a, DateTime currentTime, double dt)
Updates the value of a control action.
Definition controls.c:1624
double getNamedVariableValue(int varIndex)
Gets named variable value.
Definition controls.c:1055
double getVariableValue(struct TVariable v)
Gets the value of a variable.
Definition controls.c:1831
Definition Controls.cpp:45
ConditionVar
Definition Controls.hpp:64
@ LINK_TIMECLOSED
Definition Controls.hpp:83
@ SIM_TIME
Definition Controls.hpp:90
@ LINK_FULLFLOW
Definition Controls.hpp:77
@ CLOCK_TIME
Definition Controls.hpp:92
@ GAGE_RAIN_PAST
Past n-hours rainfall (n stored in idx field)
Definition Controls.hpp:87
@ NODE_INFLOW
Definition Controls.hpp:70
@ LINK_FULLDEPTH
Definition Controls.hpp:78
@ LINK_STATUS
Definition Controls.hpp:76
@ LINK_VELOCITY
Definition Controls.hpp:79
@ NODE_VOLUME
Definition Controls.hpp:69
@ NODE_DEPTH
Definition Controls.hpp:66
@ LINK_DEPTH
Definition Controls.hpp:74
@ NODE_MAXDEPTH
Definition Controls.hpp:67
@ SIM_MONTH
Month (1-12)
Definition Controls.hpp:94
@ LINK_SETTING
Definition Controls.hpp:75
@ SIM_DATE
Definition Controls.hpp:91
@ LINK_TIMEOPEN
Definition Controls.hpp:82
@ NODE_HEAD
Definition Controls.hpp:68
@ GAGE_RAIN
Current rainfall intensity.
Definition Controls.hpp:86
@ LINK_LENGTH
Definition Controls.hpp:80
@ LINK_SLOPE
Definition Controls.hpp:81
@ SIM_DAYOFYEAR
Day of year (1-365)
Definition Controls.hpp:95
@ LINK_FLOW
Definition Controls.hpp:73
@ SIM_DAY
Day of week (1-7, Sun=1)
Definition Controls.hpp:93
LogicOp
Definition Controls.hpp:58
@ OR
Definition Controls.hpp:58
@ AND
Definition Controls.hpp:58
CompareOp
Definition Controls.hpp:57
@ EQ
Definition Controls.hpp:57
@ LT
Definition Controls.hpp:57
@ GT
Definition Controls.hpp:57
@ LE
Definition Controls.hpp:57
@ NE
Definition Controls.hpp:57
@ GE
Definition Controls.hpp:57
ActionType
Definition Controls.hpp:102
@ CURVE
Setting from curve lookup(ControlValue)
Definition Controls.hpp:104
@ PID
PID controller output.
Definition Controls.hpp:106
@ TIMESERIES
Setting from timeseries lookup(currentTime)
Definition Controls.hpp:105
@ NUMERIC
Direct numeric setting value.
Definition Controls.hpp:103
Definition NodeCoupling.cpp:16
Central, reentrant simulation context.
Definition SimulationContext.hpp:353
Definition Controls.hpp:134
int rule_idx
Parent rule index (for priority lookup)
Definition Controls.hpp:136
int link_idx
Link being controlled.
Definition Controls.hpp:135
ActionType type
Definition Controls.hpp:137
int curve_idx
Curve index (for CURVE type)
Definition Controls.hpp:139
int pid_idx
PID state index (for PID type)
Definition Controls.hpp:141
double value
Direct value or computed result.
Definition Controls.hpp:138
int tseries_idx
Timeseries index (for TIMESERIES type)
Definition Controls.hpp:140
Pre-sorted premise index for batch evaluation.
Definition Controls.hpp:267
ConditionVar var_type
All premises in this group test this variable.
Definition Controls.hpp:268
std::vector< bool > rhs_is_variable
True if RHS is a variable (not batch-able)
Definition Controls.hpp:280
std::vector< int > premise_idx
Position within the rule's premise list.
Definition Controls.hpp:273
std::vector< int > flat_idx
Position in flat premise_results_ cache.
Definition Controls.hpp:275
std::vector< int > op
CompareOp as int.
Definition Controls.hpp:276
std::vector< bool > results
Comparison results.
Definition Controls.hpp:285
std::vector< bool > is_expression
True if LHS is an expression.
Definition Controls.hpp:281
std::vector< double > rhs_value
RHS threshold value.
Definition Controls.hpp:277
std::vector< double > lhs_values
Gathered LHS values.
Definition Controls.hpp:284
std::vector< int > obj_idx
Object index (node/link/gage)
Definition Controls.hpp:274
std::vector< int > rule_idx
Which rule this premise belongs to.
Definition Controls.hpp:272
int count
Definition Controls.hpp:269
Definition Controls.hpp:162
int idx
Definition Controls.hpp:165
ConditionVar var
Definition Controls.hpp:164
std::string name
Definition Controls.hpp:163
Definition Controls.hpp:148
double kd
Definition Controls.hpp:151
double kp
Definition Controls.hpp:149
double e2
Error two steps back.
Definition Controls.hpp:154
double ki
Integral time (minutes, 0=disable)
Definition Controls.hpp:150
int action_idx
Associated action index.
Definition Controls.hpp:155
double setpoint
Definition Controls.hpp:152
double e1
Previous error.
Definition Controls.hpp:153
Diagnostic for the most recent parseRuleText() rejection.
Definition Controls.hpp:192
int line
1-based line in the submitted text; -1 if unknown
Definition Controls.hpp:193
std::string message
Human-readable reason for the rejection.
Definition Controls.hpp:194
Definition Controls.hpp:113
int lhs_param
Extra parameter (e.g. n-hours for GAGE_RAIN_PAST)
Definition Controls.hpp:120
double rhs_value
Definition Controls.hpp:127
ConditionVar lhs_var
Definition Controls.hpp:118
int expr_idx
Index into expressions_ (-1 if N/A)
Definition Controls.hpp:116
int lhs_idx
Object index (node/link/gage)
Definition Controls.hpp:119
bool is_expression
True if LHS is a math expression.
Definition Controls.hpp:115
ConditionVar rhs_var
Definition Controls.hpp:125
int rhs_idx
Definition Controls.hpp:126
bool rhs_is_variable
Definition Controls.hpp:124
LogicOp logic
Definition Controls.hpp:114
CompareOp op
Definition Controls.hpp:122
Definition Controls.hpp:172
std::string name
Definition Controls.hpp:173
double priority
Definition Controls.hpp:177
std::vector< Action > else_actions
Definition Controls.hpp:176
std::vector< Action > then_actions
Definition Controls.hpp:175
std::vector< Premise > premises
Definition Controls.hpp:174