qualia_core_db/inference/ambient_orchestration/
power.rs1use super::*;
4use std::time::{Duration, Instant};
5
6pub struct PowerManager {
8 power_policy: PowerPolicy,
9 pub(super) battery_monitor: BatteryMonitor,
10 thermal_monitor: ThermalMonitor,
11 power_optimizer: PowerOptimizer,
12 orchestration_state: AmbientOrchestrationState,
15 active_model_count: usize,
17}
18
19pub struct BatteryMonitor {
21 pub(super) current_level: f64,
22 voltage: f64,
23 temperature: f64,
24 health: f64,
25 charging: bool,
26 estimated_time_remaining: Duration,
27}
28
29pub struct ThermalMonitor {
31 cpu_temperature: f64,
32 gpu_temperature: f64,
33 battery_temperature: f64,
34 ambient_temperature: f64,
35 thermal_state: ThermalState,
36}
37
38pub struct PowerOptimizer {
40 optimization_algorithm: OptimizationAlgorithm,
41 optimization_history: Vec<OptimizationRecord>,
42 target_efficiency: f64,
43}
44
45impl PowerManager {
46 pub fn new() -> Self {
48 Self {
49 power_policy: PowerPolicy::Balanced,
50 battery_monitor: BatteryMonitor::new(),
51 thermal_monitor: ThermalMonitor::new(),
52 power_optimizer: PowerOptimizer::new(),
53 orchestration_state: AmbientOrchestrationState::Idle,
54 active_model_count: 0,
55 }
56 }
57
58 pub fn set_orchestration_state(&mut self, state: AmbientOrchestrationState) {
61 self.orchestration_state = state;
62 }
63
64 pub fn set_active_model_count(&mut self, count: usize) {
66 self.active_model_count = count;
67 }
68
69 pub fn can_execute(&self, _device: &AmbientDevice) -> bool {
71 let battery_level = self.battery_monitor.current_level;
72 let thermal_state = &self.thermal_monitor.thermal_state;
73
74 battery_level > 20.0 && *thermal_state != ThermalState::Critical
75 }
76
77 pub fn update_power_consumption(
82 &mut self,
83 device: &mut AmbientDevice,
84 execution_time: Duration,
85 ) {
86 let power_consumed = device.power_profile.active_power * execution_time.as_secs_f64();
87
88 self.battery_monitor.drain(power_consumed, execution_time);
90
91 self.thermal_monitor
93 .apply_heat(power_consumed, execution_time);
94
95 let input_state = PowerState {
97 power_consumption: power_consumed,
98 performance: device.performance_profile.sustainable_performance,
99 efficiency: if power_consumed > 0.0 {
100 device.performance_profile.sustainable_performance / power_consumed
101 } else {
102 0.0
103 },
104 thermal_state: self.thermal_monitor.state(),
105 battery_level: self.battery_monitor.level(),
106 };
107 self.power_optimizer.optimize(&input_state);
108 }
109
110 pub fn power_policy(&self) -> &PowerPolicy {
112 &self.power_policy
113 }
114
115 pub fn set_power_policy(&mut self, policy: PowerPolicy) {
117 self.power_policy = policy;
118 }
119
120 pub fn power_optimizer_mut(&mut self) -> &mut PowerOptimizer {
122 &mut self.power_optimizer
123 }
124
125 pub fn battery_monitor_mut(&mut self) -> &mut BatteryMonitor {
127 &mut self.battery_monitor
128 }
129
130 pub fn thermal_monitor_mut(&mut self) -> &mut ThermalMonitor {
132 &mut self.thermal_monitor
133 }
134
135 pub fn get_battery_level(&self, _device_id: &str) -> f64 {
137 self.battery_monitor.current_level
138 }
139
140 pub fn get_thermal_state(&self, device_id: &str) -> ThermalState {
147 let power = self.get_power_consumption(device_id);
148 if power > 7.0 {
149 ThermalState::Critical
150 } else if power >= 3.0 {
151 ThermalState::Warm
152 } else {
153 ThermalState::Normal
154 }
155 }
156
157 pub fn get_power_consumption(&self, device_id: &str) -> f64 {
172 let _ = device_id; match self.orchestration_state {
177 AmbientOrchestrationState::Idle => 0.5,
178 AmbientOrchestrationState::ActiveInference => {
179 5.0 + (self.active_model_count as f64) * 2.0
180 }
181 AmbientOrchestrationState::Scrubbing => 3.0,
182 AmbientOrchestrationState::Streaming => 4.0,
183 }
184 }
185
186 pub fn estimate_battery_life_remaining(
193 &self,
194 current_battery_pct: f64,
195 battery_capacity_wh: f64,
196 ) -> f64 {
197 if current_battery_pct <= 0.0 || battery_capacity_wh <= 0.0 {
198 return 0.0;
199 }
200 let power = self.get_power_consumption("");
201 if power <= 0.0 {
202 return 0.0;
203 }
204 (battery_capacity_wh * current_battery_pct / 100.0) / power
205 }
206
207 pub fn should_throttle_inference(&self) -> bool {
214 let thermal = self.get_thermal_state("");
215 if thermal == ThermalState::Critical {
216 return true;
217 }
218 let battery_pct = self.battery_monitor.current_level;
221 if battery_pct <= 0.0 {
222 return true; }
224 let estimated_hours = self.estimate_battery_life_remaining(battery_pct, 15.0);
225 estimated_hours < 1.0
226 }
227
228 pub fn get_power_metrics(&self) -> PowerMetrics {
235 let current_power_w = self.get_power_consumption("");
236 let thermal_state = self.get_thermal_state("");
237
238 let battery_pct = self.battery_monitor.current_level;
241 let estimated_battery_hours = if battery_pct > 0.0 {
242 let hours = self.estimate_battery_life_remaining(battery_pct, 15.0);
243 if hours > 0.0 {
244 Some(hours)
245 } else {
246 None
247 }
248 } else {
249 None
250 };
251
252 PowerMetrics {
253 current_power_w,
254 thermal_state,
255 estimated_battery_hours,
256 active_model_count: self.active_model_count,
257 }
258 }
259}
260
261impl BatteryMonitor {
262 pub fn new() -> Self {
263 Self {
264 current_level: 100.0,
265 voltage: 3.7,
266 temperature: 25.0,
267 health: 100.0,
268 charging: false,
269 estimated_time_remaining: Duration::from_secs(3600 * 10), }
271 }
272
273 pub fn level(&self) -> f64 {
275 self.current_level
276 }
277
278 pub fn voltage(&self) -> f64 {
280 self.voltage
281 }
282
283 pub fn temperature(&self) -> f64 {
285 self.temperature
286 }
287
288 pub fn health(&self) -> f64 {
290 self.health
291 }
292
293 pub fn is_charging(&self) -> bool {
295 self.charging
296 }
297
298 pub fn time_remaining(&self) -> Duration {
300 self.estimated_time_remaining
301 }
302
303 pub fn update(&mut self, level: f64, voltage: f64, temperature: f64, charging: bool) {
305 self.current_level = level.clamp(0.0, 100.0);
306 self.voltage = voltage;
307 self.temperature = temperature;
308 self.charging = charging;
309 if !charging && level > 0.0 {
313 let hours = level / 10.0;
314 self.estimated_time_remaining = Duration::from_secs((hours * 3600.0) as u64);
315 } else if charging {
316 let hours_to_full = (100.0 - level) / 20.0;
318 self.estimated_time_remaining = Duration::from_secs((hours_to_full * 3600.0) as u64);
319 }
320 }
321
322 pub fn drain(&mut self, power_w: f64, duration: Duration) {
325 if self.charging {
326 return; }
328 let wh_consumed = power_w * duration.as_secs_f64() / 3600.0;
329 let battery_capacity_wh = 15.0;
330 let pct_drained = (wh_consumed / battery_capacity_wh) * 100.0;
331 self.current_level = (self.current_level - pct_drained).max(0.0);
332 self.temperature += power_w * 0.5 * duration.as_secs_f64();
334 }
335}
336
337impl ThermalMonitor {
338 pub fn new() -> Self {
339 Self {
340 cpu_temperature: 45.0,
341 gpu_temperature: 40.0,
342 battery_temperature: 30.0,
343 ambient_temperature: 25.0,
344 thermal_state: ThermalState::Normal,
345 }
346 }
347
348 pub fn cpu_temp(&self) -> f64 {
350 self.cpu_temperature
351 }
352
353 pub fn gpu_temp(&self) -> f64 {
355 self.gpu_temperature
356 }
357
358 pub fn battery_temp(&self) -> f64 {
360 self.battery_temperature
361 }
362
363 pub fn ambient_temp(&self) -> f64 {
365 self.ambient_temperature
366 }
367
368 pub fn state(&self) -> ThermalState {
370 self.thermal_state
371 }
372
373 pub fn update(&mut self, cpu: f64, gpu: f64, battery: f64, ambient: f64) {
382 self.cpu_temperature = cpu;
383 self.gpu_temperature = gpu;
384 self.battery_temperature = battery;
385 self.ambient_temperature = ambient;
386 self.thermal_state = if cpu > 85.0 {
387 ThermalState::Critical
388 } else if cpu > 70.0 {
389 ThermalState::Hot
390 } else if cpu > 50.0 {
391 ThermalState::Warm
392 } else {
393 ThermalState::Normal
394 };
395 }
396
397 pub fn apply_heat(&mut self, power_w: f64, duration: Duration) {
400 let secs = duration.as_secs_f64();
401 self.cpu_temperature += power_w * 0.1 * secs;
403 self.gpu_temperature += power_w * 0.08 * secs;
404 self.thermal_state = if self.cpu_temperature > 85.0 {
406 ThermalState::Critical
407 } else if self.cpu_temperature > 70.0 {
408 ThermalState::Hot
409 } else if self.cpu_temperature > 50.0 {
410 ThermalState::Warm
411 } else {
412 ThermalState::Normal
413 };
414 }
415
416 pub fn cool(&mut self, duration: Duration) {
418 let secs = duration.as_secs_f64();
419 let cooling = 0.5 * secs;
421 self.cpu_temperature =
422 self.cpu_temperature.max(self.ambient_temperature + cooling) - cooling;
423 self.gpu_temperature =
424 self.gpu_temperature.max(self.ambient_temperature + cooling) - cooling;
425 self.thermal_state = if self.cpu_temperature > 85.0 {
427 ThermalState::Critical
428 } else if self.cpu_temperature > 70.0 {
429 ThermalState::Hot
430 } else if self.cpu_temperature > 50.0 {
431 ThermalState::Warm
432 } else {
433 ThermalState::Normal
434 };
435 }
436}
437
438impl PowerOptimizer {
439 pub fn new() -> Self {
440 Self {
441 optimization_algorithm: OptimizationAlgorithm::Greedy,
442 optimization_history: Vec::new(),
443 target_efficiency: 0.85,
444 }
445 }
446
447 pub fn optimize(&mut self, input: &PowerState) -> PowerState {
455 let mut output = input.clone();
456
457 if output.efficiency < self.target_efficiency {
459 output.power_consumption *= 0.8;
461 if output.power_consumption > 0.0 {
463 output.efficiency = output.performance / output.power_consumption;
464 }
465 output.thermal_state = if output.power_consumption > 7.0 {
467 ThermalState::Critical
468 } else if output.power_consumption >= 3.0 {
469 ThermalState::Warm
470 } else {
471 ThermalState::Normal
472 };
473 }
474
475 let gain = if input.power_consumption > 0.0 {
477 (input.power_consumption - output.power_consumption) / input.power_consumption
478 } else {
479 0.0
480 };
481 self.optimization_history.push(OptimizationRecord {
482 timestamp: Instant::now(),
483 algorithm: self.optimization_algorithm.clone(),
484 input_state: input.clone(),
485 output_state: output.clone(),
486 efficiency_gain: gain,
487 });
488
489 if self.optimization_history.len() > 200 {
491 let drop = self.optimization_history.len() - 200;
492 self.optimization_history.drain(0..drop);
493 }
494
495 output
496 }
497
498 pub fn average_efficiency_gain(&self) -> f64 {
500 if self.optimization_history.is_empty() {
501 return 0.0;
502 }
503 self.optimization_history
504 .iter()
505 .map(|r| r.efficiency_gain)
506 .sum::<f64>()
507 / self.optimization_history.len() as f64
508 }
509
510 pub fn target_efficiency(&self) -> f64 {
512 self.target_efficiency
513 }
514}