vpp_sim/sim/
controller.rs1#[derive(Debug, Default, Clone, Copy)]
5pub struct NaiveRtController;
6
7impl NaiveRtController {
8 #[cfg(test)]
13 pub fn battery_setpoint_kw(&self, net_without_battery: f32, target_kw: f32) -> f32 {
14 net_without_battery - target_kw
15 }
16
17 pub fn capped_flexible_load_kw(
20 &self,
21 net_fixed_kw: f32,
22 requested_flexible_kw: f32,
23 max_import_kw: f32,
24 battery_max_discharge_kw: f32,
25 ) -> f32 {
26 let requested = requested_flexible_kw.max(0.0);
27 let overload_kw =
28 (net_fixed_kw + requested - battery_max_discharge_kw - max_import_kw).max(0.0);
29 (requested - overload_kw).max(0.0)
30 }
31
32 pub fn constrained_battery_setpoint_kw(
34 &self,
35 net_without_battery_kw: f32,
36 target_kw: f32,
37 max_import_kw: f32,
38 max_export_kw: f32,
39 battery_max_charge_kw: f32,
40 battery_max_discharge_kw: f32,
41 ) -> f32 {
42 let min_feeder_kw = -max_export_kw;
43 let max_feeder_kw = max_import_kw;
44 let constrained_target_kw = target_kw.clamp(min_feeder_kw, max_feeder_kw);
45
46 let low_kw = (-battery_max_charge_kw).max(net_without_battery_kw - max_feeder_kw);
49 let high_kw = battery_max_discharge_kw.min(net_without_battery_kw - min_feeder_kw);
50
51 let desired_kw = net_without_battery_kw - constrained_target_kw;
52 if low_kw <= high_kw {
53 desired_kw.clamp(low_kw, high_kw)
54 } else {
55 desired_kw.clamp(-battery_max_charge_kw, battery_max_discharge_kw)
58 }
59 }
60
61 pub fn apply_demand_response_kw(
65 &self,
66 baseload_kw: f32,
67 flexible_load_kw: f32,
68 requested_reduction_kw: f32,
69 ) -> (f32, f32, f32) {
70 let mut remaining_reduction = requested_reduction_kw.max(0.0);
71
72 let flexible = flexible_load_kw.max(0.0);
73 let flex_shed = flexible.min(remaining_reduction);
74 let flexible_after = flexible - flex_shed;
75 remaining_reduction -= flex_shed;
76
77 let base = baseload_kw.max(0.0);
78 let base_shed = base.min(remaining_reduction);
79 let baseload_after = base - base_shed;
80
81 let achieved = flex_shed + base_shed;
82 (baseload_after, flexible_after, achieved)
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::NaiveRtController;
89
90 #[test]
91 fn discharges_when_load_is_above_target() {
92 let controller = NaiveRtController;
93 let setpoint = controller.battery_setpoint_kw(3.0, 1.0);
94 assert_eq!(setpoint, 2.0);
95 }
96
97 #[test]
98 fn charges_when_load_is_below_target() {
99 let controller = NaiveRtController;
100 let setpoint = controller.battery_setpoint_kw(1.0, 2.5);
101 assert_eq!(setpoint, -1.5);
102 }
103
104 #[test]
105 fn caps_flexible_load_when_import_cannot_be_met() {
106 let controller = NaiveRtController;
107 let capped = controller.capped_flexible_load_kw(6.0, 4.0, 5.0, 3.0);
108 assert_eq!(capped, 2.0);
109 }
110
111 #[test]
112 fn keeps_flexible_load_when_import_is_feasible() {
113 let controller = NaiveRtController;
114 let capped = controller.capped_flexible_load_kw(2.0, 2.5, 5.0, 3.0);
115 assert_eq!(capped, 2.5);
116 }
117
118 #[test]
119 fn constrained_battery_setpoint_respects_import_limit() {
120 let controller = NaiveRtController;
121 let battery_kw = controller.constrained_battery_setpoint_kw(6.0, 1.0, 5.0, 4.0, 4.0, 3.0);
122 let feeder_kw = 6.0 - battery_kw;
123 assert!(feeder_kw <= 5.0 + 1e-6);
124 }
125
126 #[test]
127 fn constrained_battery_setpoint_is_battery_limited_when_infeasible() {
128 let controller = NaiveRtController;
129 let battery_kw = controller.constrained_battery_setpoint_kw(10.0, 1.0, 5.0, 4.0, 4.0, 3.0);
130 let feeder_kw = 10.0 - battery_kw;
131 assert_eq!(battery_kw, 3.0);
132 assert_eq!(feeder_kw, 7.0);
133 }
134
135 #[test]
136 fn constrained_battery_setpoint_respects_export_limit() {
137 let controller = NaiveRtController;
138 let battery_kw = controller.constrained_battery_setpoint_kw(-6.0, -5.0, 5.0, 2.0, 4.0, 3.0);
139 let feeder_kw = -6.0 - battery_kw;
140 assert!(feeder_kw >= -2.0 - 1e-6);
141 }
142
143 #[test]
144 fn demand_response_sheds_flexible_then_baseload() {
145 let controller = NaiveRtController;
146 let (base_after, flex_after, achieved) = controller.apply_demand_response_kw(3.0, 2.0, 4.0);
147 assert_eq!(flex_after, 0.0);
148 assert_eq!(base_after, 1.0);
149 assert_eq!(achieved, 4.0);
150 }
151
152 #[test]
153 fn demand_response_limited_by_available_load() {
154 let controller = NaiveRtController;
155 let (base_after, flex_after, achieved) = controller.apply_demand_response_kw(1.0, 0.5, 3.0);
156 assert_eq!(flex_after, 0.0);
157 assert_eq!(base_after, 0.0);
158 assert_eq!(achieved, 1.5);
159 }
160}