Skip to main content

torsh_distributed/
green_computing.rs

1//! Green Computing for Distributed Training
2//!
3//! This module provides comprehensive energy efficiency and sustainability features for
4//! distributed deep learning, including:
5//! - Energy consumption monitoring and optimization
6//! - Carbon footprint tracking and reduction strategies
7//! - Adaptive scheduling based on renewable energy availability
8//! - Dynamic power management and GPU throttling
9#![allow(clippy::await_holding_lock)]
10//! - Green training algorithms and efficiency metrics
11//! - Sustainable distributed training policies
12
13// Framework infrastructure - components designed for future use
14#![allow(dead_code)]
15use crate::{TorshDistributedError, TorshResult};
16use serde::{Deserialize, Serialize};
17use std::collections::{HashMap, VecDeque};
18use std::sync::{Arc, Mutex, RwLock};
19use std::time::{Duration, SystemTime};
20use tokio::time::interval;
21
22/// Green computing configuration for sustainable distributed training
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct GreenComputingConfig {
25    /// Enable energy monitoring and optimization
26    pub energy_monitoring: bool,
27    /// Enable carbon footprint tracking
28    pub carbon_tracking: bool,
29    /// Target energy efficiency (operations per joule)
30    pub target_efficiency: f64,
31    /// Maximum carbon footprint per training run (kg CO2)
32    pub max_carbon_footprint: f64,
33    /// Enable renewable energy optimization
34    pub renewable_energy_optimization: bool,
35    /// Enable dynamic power management
36    pub dynamic_power_management: bool,
37    /// Power cap per device (watts)
38    pub device_power_cap: f64,
39    /// Energy budget per training epoch (watt-hours)
40    pub energy_budget_per_epoch: f64,
41    /// Enable green training algorithms
42    pub green_algorithms: bool,
43    /// Sustainability reporting configuration
44    pub sustainability_reporting: SustainabilityReportingConfig,
45}
46
47impl Default for GreenComputingConfig {
48    fn default() -> Self {
49        Self {
50            energy_monitoring: true,
51            carbon_tracking: true,
52            target_efficiency: 100.0,   // operations per joule
53            max_carbon_footprint: 10.0, // kg CO2
54            renewable_energy_optimization: true,
55            dynamic_power_management: true,
56            device_power_cap: 250.0,         // watts
57            energy_budget_per_epoch: 1000.0, // watt-hours
58            green_algorithms: true,
59            sustainability_reporting: SustainabilityReportingConfig::default(),
60        }
61    }
62}
63
64/// Sustainability reporting configuration
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct SustainabilityReportingConfig {
67    /// Enable periodic sustainability reports
68    pub enable_reports: bool,
69    /// Reporting interval in seconds
70    pub report_interval: u64,
71    /// Include energy efficiency metrics
72    pub include_efficiency_metrics: bool,
73    /// Include carbon footprint analysis
74    pub include_carbon_analysis: bool,
75    /// Include renewable energy utilization
76    pub include_renewable_utilization: bool,
77    /// Export reports to file
78    pub export_to_file: bool,
79    /// Report file path
80    pub report_file_path: String,
81}
82
83impl Default for SustainabilityReportingConfig {
84    fn default() -> Self {
85        Self {
86            enable_reports: true,
87            report_interval: 300, // 5 minutes
88            include_efficiency_metrics: true,
89            include_carbon_analysis: true,
90            include_renewable_utilization: true,
91            export_to_file: true,
92            report_file_path: "sustainability_report.json".to_string(),
93        }
94    }
95}
96
97/// Energy consumption data for a device
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct DeviceEnergyData {
100    /// Device identifier
101    pub device_id: String,
102    /// Current power consumption (watts)
103    pub current_power: f64,
104    /// Peak power consumption (watts)
105    pub peak_power: f64,
106    /// Average power consumption (watts)
107    pub average_power: f64,
108    /// Total energy consumed (watt-hours)
109    pub total_energy: f64,
110    /// Energy efficiency (operations per joule)
111    pub efficiency: f64,
112    /// Power utilization percentage
113    pub power_utilization: f64,
114    /// Temperature (celsius)
115    pub temperature: f64,
116    /// Timestamp of last update
117    pub last_updated: SystemTime,
118}
119
120impl DeviceEnergyData {
121    pub fn new(device_id: String) -> Self {
122        Self {
123            device_id,
124            current_power: 0.0,
125            peak_power: 0.0,
126            average_power: 0.0,
127            total_energy: 0.0,
128            efficiency: 0.0,
129            power_utilization: 0.0,
130            temperature: 25.0,
131            last_updated: SystemTime::now(),
132        }
133    }
134
135    /// Update energy consumption data
136    pub fn update_power(&mut self, power: f64, operations: f64) {
137        self.current_power = power;
138        self.peak_power = self.peak_power.max(power);
139
140        // Update average power using exponential moving average
141        self.average_power = 0.9 * self.average_power + 0.1 * power;
142
143        // Calculate energy consumption (assuming 1-second intervals)
144        self.total_energy += power / 3600.0; // Convert to watt-hours
145
146        // Calculate efficiency (operations per joule)
147        if power > 0.0 {
148            self.efficiency = operations / power;
149        }
150
151        self.last_updated = SystemTime::now();
152    }
153}
154
155/// Carbon footprint data
156#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct CarbonFootprintData {
158    /// Total CO2 emissions (kg)
159    pub total_co2_kg: f64,
160    /// CO2 emissions per epoch (kg)
161    pub co2_per_epoch: f64,
162    /// CO2 emissions per operation (g)
163    pub co2_per_operation: f64,
164    /// Carbon intensity of current grid (g CO2/kWh)
165    pub grid_carbon_intensity: f64,
166    /// Renewable energy percentage
167    pub renewable_energy_percentage: f64,
168    /// Offset credits applied (kg CO2)
169    pub offset_credits: f64,
170    /// Net carbon footprint (kg CO2)
171    pub net_carbon_footprint: f64,
172}
173
174impl Default for CarbonFootprintData {
175    fn default() -> Self {
176        Self {
177            total_co2_kg: 0.0,
178            co2_per_epoch: 0.0,
179            co2_per_operation: 0.0,
180            grid_carbon_intensity: 400.0, // Global average g CO2/kWh
181            renewable_energy_percentage: 0.0,
182            offset_credits: 0.0,
183            net_carbon_footprint: 0.0,
184        }
185    }
186}
187
188/// Renewable energy data source
189#[derive(Debug, Clone, Serialize, Deserialize)]
190pub struct RenewableEnergyData {
191    /// Current renewable energy availability (percentage)
192    pub availability_percentage: f64,
193    /// Renewable energy forecast (next 24 hours)
194    pub forecast: Vec<f64>,
195    /// Current grid carbon intensity (g CO2/kWh)
196    pub current_carbon_intensity: f64,
197    /// Predicted carbon intensity (next 24 hours)
198    pub carbon_intensity_forecast: Vec<f64>,
199    /// Last updated timestamp
200    pub last_updated: SystemTime,
201}
202
203impl Default for RenewableEnergyData {
204    fn default() -> Self {
205        Self {
206            availability_percentage: 30.0, // Default 30% renewable
207            forecast: vec![25.0, 30.0, 35.0, 40.0, 45.0, 50.0], // Sample forecast
208            current_carbon_intensity: 400.0,
209            carbon_intensity_forecast: vec![380.0, 360.0, 340.0, 320.0, 300.0, 280.0],
210            last_updated: SystemTime::now(),
211        }
212    }
213}
214
215/// Green training optimization strategies
216#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
217pub enum GreenOptimizationStrategy {
218    /// Minimize total energy consumption
219    MinimizeEnergy,
220    /// Minimize carbon footprint
221    MinimizeCarbon,
222    /// Maximize use of renewable energy
223    MaximizeRenewable,
224    /// Balance performance and sustainability
225    Balanced,
226    /// Custom optimization with user-defined weights
227    Custom,
228}
229
230/// Power management strategy for devices
231#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
232pub enum PowerManagementStrategy {
233    /// Dynamic voltage and frequency scaling
234    DVFS,
235    /// GPU power limiting
236    PowerCapping,
237    /// Idle state management
238    IdleManagement,
239    /// Load balancing for power efficiency
240    LoadBalancing,
241    /// Thermal throttling
242    ThermalThrottling,
243}
244
245/// Green computing manager for sustainable distributed training
246pub struct GreenComputingManager {
247    config: GreenComputingConfig,
248    device_energy_data: Arc<RwLock<HashMap<String, DeviceEnergyData>>>,
249    carbon_footprint: Arc<Mutex<CarbonFootprintData>>,
250    renewable_energy: Arc<RwLock<RenewableEnergyData>>,
251    optimization_strategy: GreenOptimizationStrategy,
252    power_management_enabled: Arc<std::sync::atomic::AtomicBool>,
253    sustainability_metrics: Arc<Mutex<SustainabilityMetrics>>,
254    training_scheduler: Option<GreenTrainingScheduler>,
255}
256
257impl GreenComputingManager {
258    /// Create a new green computing manager
259    pub fn new(config: GreenComputingConfig) -> TorshResult<Self> {
260        Ok(Self {
261            config: config.clone(),
262            device_energy_data: Arc::new(RwLock::new(HashMap::new())),
263            carbon_footprint: Arc::new(Mutex::new(CarbonFootprintData::default())),
264            renewable_energy: Arc::new(RwLock::new(RenewableEnergyData::default())),
265            optimization_strategy: GreenOptimizationStrategy::Balanced,
266            power_management_enabled: Arc::new(std::sync::atomic::AtomicBool::new(
267                config.dynamic_power_management,
268            )),
269            sustainability_metrics: Arc::new(Mutex::new(SustainabilityMetrics::new())),
270            training_scheduler: Some(GreenTrainingScheduler::new(config)?),
271        })
272    }
273
274    /// Initialize green computing for a device
275    pub fn initialize_device(&self, device_id: String) -> TorshResult<()> {
276        let mut devices = self.device_energy_data.write().map_err(|_| {
277            TorshDistributedError::InternalError("Failed to acquire device data lock".to_string())
278        })?;
279
280        devices.insert(device_id.clone(), DeviceEnergyData::new(device_id.clone()));
281
282        tracing::info!("Initialized green computing for device: {}", device_id);
283        Ok(())
284    }
285
286    /// Update energy consumption for a device
287    pub fn update_device_energy(
288        &self,
289        device_id: &str,
290        power_watts: f64,
291        operations: f64,
292    ) -> TorshResult<()> {
293        let mut devices = self.device_energy_data.write().map_err(|_| {
294            TorshDistributedError::InternalError("Failed to acquire device data lock".to_string())
295        })?;
296
297        if let Some(device_data) = devices.get_mut(device_id) {
298            device_data.update_power(power_watts, operations);
299
300            // Check power cap compliance
301            if power_watts > self.config.device_power_cap {
302                tracing::warn!(
303                    "Device {} exceeds power cap: {:.2}W > {:.2}W",
304                    device_id,
305                    power_watts,
306                    self.config.device_power_cap
307                );
308
309                // Trigger power management if enabled
310                if self
311                    .power_management_enabled
312                    .load(std::sync::atomic::Ordering::Relaxed)
313                {
314                    self.apply_power_management(device_id, power_watts)?;
315                }
316            }
317
318            // Update carbon footprint
319            self.update_carbon_footprint(power_watts / 3600.0)?; // Convert to kWh
320        }
321
322        Ok(())
323    }
324
325    /// Update carbon footprint calculation
326    fn update_carbon_footprint(&self, energy_kwh: f64) -> TorshResult<()> {
327        let mut carbon = self.carbon_footprint.lock().map_err(|_| {
328            TorshDistributedError::InternalError("Failed to acquire carbon data lock".to_string())
329        })?;
330
331        let renewable_data = self.renewable_energy.read().map_err(|_| {
332            TorshDistributedError::InternalError(
333                "Failed to acquire renewable data lock".to_string(),
334            )
335        })?;
336
337        // Calculate CO2 emissions based on grid carbon intensity and renewable energy
338        let effective_carbon_intensity = renewable_data.current_carbon_intensity
339            * (1.0 - renewable_data.availability_percentage / 100.0);
340
341        let co2_emissions_kg = energy_kwh * effective_carbon_intensity / 1000.0; // Convert g to kg
342
343        carbon.total_co2_kg += co2_emissions_kg;
344        carbon.grid_carbon_intensity = renewable_data.current_carbon_intensity;
345        carbon.renewable_energy_percentage = renewable_data.availability_percentage;
346        carbon.net_carbon_footprint = carbon.total_co2_kg - carbon.offset_credits;
347
348        // Check carbon footprint limit
349        if carbon.net_carbon_footprint > self.config.max_carbon_footprint {
350            tracing::warn!(
351                "Carbon footprint limit exceeded: {:.3} kg > {:.3} kg",
352                carbon.net_carbon_footprint,
353                self.config.max_carbon_footprint
354            );
355        }
356
357        Ok(())
358    }
359
360    /// Apply power management strategies
361    fn apply_power_management(&self, device_id: &str, current_power: f64) -> TorshResult<()> {
362        let target_power = self.config.device_power_cap * 0.9; // Target 90% of cap
363        let power_reduction_ratio = target_power / current_power;
364
365        tracing::info!(
366            "Applying power management for device {}: reducing power by {:.1}%",
367            device_id,
368            (1.0 - power_reduction_ratio) * 100.0
369        );
370
371        // In a real implementation, this would:
372        // 1. Reduce GPU frequency/voltage
373        // 2. Limit CUDA core usage
374        // 3. Adjust memory bandwidth
375        // 4. Enable thermal throttling
376
377        Ok(())
378    }
379
380    /// Get current sustainability metrics
381    pub fn get_sustainability_metrics(&self) -> TorshResult<SustainabilityMetrics> {
382        let metrics = self.sustainability_metrics.lock().map_err(|_| {
383            TorshDistributedError::InternalError("Failed to acquire metrics lock".to_string())
384        })?;
385
386        Ok(metrics.clone())
387    }
388
389    /// Optimize training schedule based on renewable energy availability
390    pub fn optimize_training_schedule(&self) -> TorshResult<TrainingScheduleRecommendation> {
391        let renewable_data = self.renewable_energy.read().map_err(|_| {
392            TorshDistributedError::InternalError(
393                "Failed to acquire renewable data lock".to_string(),
394            )
395        })?;
396
397        // Find optimal training windows based on renewable energy forecast
398        let mut optimal_windows = Vec::new();
399        for (hour, &renewable_percentage) in renewable_data.forecast.iter().enumerate() {
400            if renewable_percentage > 40.0 {
401                // Above 40% renewable
402                optimal_windows.push(TrainingWindow {
403                    start_hour: hour,
404                    duration_hours: 1,
405                    renewable_percentage,
406                    carbon_intensity: renewable_data
407                        .carbon_intensity_forecast
408                        .get(hour)
409                        .copied()
410                        .unwrap_or(400.0),
411                    priority: if renewable_percentage > 60.0 {
412                        Priority::High
413                    } else {
414                        Priority::Medium
415                    },
416                });
417            }
418        }
419
420        Ok(TrainingScheduleRecommendation {
421            current_renewable_percentage: renewable_data.availability_percentage,
422            recommended_action: if renewable_data.availability_percentage > 50.0 {
423                ScheduleAction::TrainNow
424            } else if renewable_data.availability_percentage < 20.0 {
425                ScheduleAction::Defer
426            } else {
427                ScheduleAction::ReduceIntensity
428            },
429            optimal_windows,
430            estimated_carbon_savings: self.calculate_carbon_savings(&renewable_data)?,
431        })
432    }
433
434    /// Calculate potential carbon savings from green optimization
435    fn calculate_carbon_savings(&self, renewable_data: &RenewableEnergyData) -> TorshResult<f64> {
436        let baseline_intensity = 500.0; // Baseline carbon intensity (g CO2/kWh)
437        let current_intensity = renewable_data.current_carbon_intensity
438            * (1.0 - renewable_data.availability_percentage / 100.0);
439
440        let savings_percentage = (baseline_intensity - current_intensity) / baseline_intensity;
441        Ok(savings_percentage.max(0.0))
442    }
443
444    /// Generate sustainability report
445    pub async fn generate_sustainability_report(&self) -> TorshResult<SustainabilityReport> {
446        let devices = self.device_energy_data.read().map_err(|_| {
447            TorshDistributedError::InternalError("Failed to acquire device data lock".to_string())
448        })?;
449
450        let carbon = self.carbon_footprint.lock().map_err(|_| {
451            TorshDistributedError::InternalError("Failed to acquire carbon data lock".to_string())
452        })?;
453
454        let renewable = self.renewable_energy.read().map_err(|_| {
455            TorshDistributedError::InternalError(
456                "Failed to acquire renewable data lock".to_string(),
457            )
458        })?;
459
460        let total_energy: f64 = devices.values().map(|d| d.total_energy).sum();
461        let average_efficiency: f64 =
462            devices.values().map(|d| d.efficiency).sum::<f64>() / devices.len() as f64;
463        let peak_power: f64 = devices.values().map(|d| d.peak_power).sum();
464
465        let report = SustainabilityReport {
466            timestamp: SystemTime::now(),
467            total_energy_kwh: total_energy,
468            total_carbon_kg: carbon.total_co2_kg,
469            renewable_energy_percentage: renewable.availability_percentage,
470            average_efficiency,
471            peak_power_kw: peak_power / 1000.0,
472            device_count: devices.len(),
473            carbon_intensity: renewable.current_carbon_intensity,
474            net_carbon_footprint: carbon.net_carbon_footprint,
475            sustainability_score: self
476                .calculate_sustainability_score(&devices, &carbon, &renewable)?,
477        };
478
479        // Export to file if configured
480        if self.config.sustainability_reporting.export_to_file {
481            self.export_report_to_file(&report).await?;
482        }
483
484        Ok(report)
485    }
486
487    /// Calculate overall sustainability score (0-100)
488    fn calculate_sustainability_score(
489        &self,
490        devices: &HashMap<String, DeviceEnergyData>,
491        carbon: &CarbonFootprintData,
492        renewable: &RenewableEnergyData,
493    ) -> TorshResult<f64> {
494        let energy_efficiency_score = devices
495            .values()
496            .map(|d| d.efficiency.min(1000.0) / 1000.0 * 100.0)
497            .sum::<f64>()
498            / devices.len() as f64;
499
500        let renewable_score = renewable.availability_percentage;
501
502        let carbon_score = ((self.config.max_carbon_footprint - carbon.net_carbon_footprint)
503            / self.config.max_carbon_footprint
504            * 100.0)
505            .max(0.0);
506
507        // Weighted average: 40% efficiency, 30% renewable, 30% carbon
508        let overall_score =
509            0.4 * energy_efficiency_score + 0.3 * renewable_score + 0.3 * carbon_score;
510
511        Ok(overall_score.clamp(0.0, 100.0))
512    }
513
514    /// Export sustainability report to file
515    async fn export_report_to_file(&self, report: &SustainabilityReport) -> TorshResult<()> {
516        let json_data = serde_json::to_string_pretty(report)
517            .map_err(|e| TorshDistributedError::SerializationError(e.to_string()))?;
518
519        tokio::fs::write(
520            &self.config.sustainability_reporting.report_file_path,
521            json_data,
522        )
523        .await
524        .map_err(|e| TorshDistributedError::IoError(e.to_string()))?;
525
526        tracing::info!(
527            "Sustainability report exported to: {}",
528            self.config.sustainability_reporting.report_file_path
529        );
530
531        Ok(())
532    }
533
534    /// Start automatic sustainability monitoring
535    pub async fn start_monitoring(&self) -> TorshResult<()> {
536        if !self.config.sustainability_reporting.enable_reports {
537            return Ok(());
538        }
539
540        let report_interval =
541            Duration::from_secs(self.config.sustainability_reporting.report_interval);
542        let mut interval_timer = interval(report_interval);
543
544        loop {
545            interval_timer.tick().await;
546
547            match self.generate_sustainability_report().await {
548                Ok(report) => {
549                    tracing::info!(
550                        "Sustainability report generated - Score: {:.1}/100, Carbon: {:.3} kg CO2",
551                        report.sustainability_score,
552                        report.net_carbon_footprint
553                    );
554                }
555                Err(e) => {
556                    tracing::error!("Failed to generate sustainability report: {}", e);
557                }
558            }
559        }
560    }
561}
562
563/// Sustainability metrics tracker
564#[derive(Debug, Clone, Serialize, Deserialize)]
565pub struct SustainabilityMetrics {
566    /// Energy efficiency trend (operations per joule over time)
567    pub efficiency_trend: VecDeque<(SystemTime, f64)>,
568    /// Carbon intensity trend (g CO2/kWh over time)
569    pub carbon_intensity_trend: VecDeque<(SystemTime, f64)>,
570    /// Renewable energy utilization trend (percentage over time)
571    pub renewable_utilization_trend: VecDeque<(SystemTime, f64)>,
572    /// Power consumption trend (watts over time)
573    pub power_consumption_trend: VecDeque<(SystemTime, f64)>,
574}
575
576impl Default for SustainabilityMetrics {
577    fn default() -> Self {
578        Self::new()
579    }
580}
581
582impl SustainabilityMetrics {
583    pub fn new() -> Self {
584        Self {
585            efficiency_trend: VecDeque::with_capacity(1000),
586            carbon_intensity_trend: VecDeque::with_capacity(1000),
587            renewable_utilization_trend: VecDeque::with_capacity(1000),
588            power_consumption_trend: VecDeque::with_capacity(1000),
589        }
590    }
591
592    /// Add data point to trends
593    pub fn add_data_point(
594        &mut self,
595        efficiency: f64,
596        carbon_intensity: f64,
597        renewable_percentage: f64,
598        power_consumption: f64,
599    ) {
600        let timestamp = SystemTime::now();
601
602        self.efficiency_trend.push_back((timestamp, efficiency));
603        self.carbon_intensity_trend
604            .push_back((timestamp, carbon_intensity));
605        self.renewable_utilization_trend
606            .push_back((timestamp, renewable_percentage));
607        self.power_consumption_trend
608            .push_back((timestamp, power_consumption));
609
610        // Keep only last 1000 data points
611        if self.efficiency_trend.len() > 1000 {
612            self.efficiency_trend.pop_front();
613            self.carbon_intensity_trend.pop_front();
614            self.renewable_utilization_trend.pop_front();
615            self.power_consumption_trend.pop_front();
616        }
617    }
618}
619
620/// Green training scheduler for optimizing training based on sustainability
621pub struct GreenTrainingScheduler {
622    config: GreenComputingConfig,
623    schedule_recommendations: Arc<RwLock<Vec<TrainingScheduleRecommendation>>>,
624}
625
626impl GreenTrainingScheduler {
627    pub fn new(config: GreenComputingConfig) -> TorshResult<Self> {
628        Ok(Self {
629            config,
630            schedule_recommendations: Arc::new(RwLock::new(Vec::new())),
631        })
632    }
633
634    /// Get current training recommendation
635    pub fn get_current_recommendation(
636        &self,
637    ) -> TorshResult<Option<TrainingScheduleRecommendation>> {
638        let recommendations = self.schedule_recommendations.read().map_err(|_| {
639            TorshDistributedError::InternalError(
640                "Failed to acquire recommendations lock".to_string(),
641            )
642        })?;
643
644        Ok(recommendations.last().cloned())
645    }
646}
647
648/// Training schedule recommendation based on green computing optimization
649#[derive(Debug, Clone, Serialize, Deserialize)]
650pub struct TrainingScheduleRecommendation {
651    /// Current renewable energy percentage
652    pub current_renewable_percentage: f64,
653    /// Recommended action
654    pub recommended_action: ScheduleAction,
655    /// Optimal training windows
656    pub optimal_windows: Vec<TrainingWindow>,
657    /// Estimated carbon savings percentage
658    pub estimated_carbon_savings: f64,
659}
660
661/// Recommended scheduling action
662#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
663pub enum ScheduleAction {
664    /// Start training immediately
665    TrainNow,
666    /// Defer training to optimal window
667    Defer,
668    /// Reduce training intensity
669    ReduceIntensity,
670    /// Pause training temporarily
671    Pause,
672}
673
674/// Optimal training window
675#[derive(Debug, Clone, Serialize, Deserialize)]
676pub struct TrainingWindow {
677    /// Start hour (0-23)
678    pub start_hour: usize,
679    /// Duration in hours
680    pub duration_hours: usize,
681    /// Renewable energy percentage during window
682    pub renewable_percentage: f64,
683    /// Carbon intensity (g CO2/kWh)
684    pub carbon_intensity: f64,
685    /// Window priority
686    pub priority: Priority,
687}
688
689/// Priority level for training windows
690#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
691pub enum Priority {
692    High,
693    Medium,
694    Low,
695}
696
697/// Comprehensive sustainability report
698#[derive(Debug, Clone, Serialize, Deserialize)]
699pub struct SustainabilityReport {
700    /// Report timestamp
701    pub timestamp: SystemTime,
702    /// Total energy consumption (kWh)
703    pub total_energy_kwh: f64,
704    /// Total carbon emissions (kg CO2)
705    pub total_carbon_kg: f64,
706    /// Renewable energy percentage
707    pub renewable_energy_percentage: f64,
708    /// Average energy efficiency (operations per joule)
709    pub average_efficiency: f64,
710    /// Peak power consumption (kW)
711    pub peak_power_kw: f64,
712    /// Number of devices monitored
713    pub device_count: usize,
714    /// Current grid carbon intensity (g CO2/kWh)
715    pub carbon_intensity: f64,
716    /// Net carbon footprint after offsets (kg CO2)
717    pub net_carbon_footprint: f64,
718    /// Overall sustainability score (0-100)
719    pub sustainability_score: f64,
720}
721
722#[cfg(test)]
723mod tests {
724    use super::*;
725
726    #[test]
727    fn test_green_computing_config_default() {
728        let config = GreenComputingConfig::default();
729        assert!(config.energy_monitoring);
730        assert!(config.carbon_tracking);
731        assert_eq!(config.target_efficiency, 100.0);
732        assert_eq!(config.max_carbon_footprint, 10.0);
733    }
734
735    #[test]
736    fn test_device_energy_data_update() {
737        let mut device = DeviceEnergyData::new("gpu-0".to_string());
738        device.update_power(200.0, 1000.0);
739
740        assert_eq!(device.current_power, 200.0);
741        assert_eq!(device.peak_power, 200.0);
742        assert_eq!(device.efficiency, 5.0); // 1000 ops / 200 watts
743    }
744
745    #[tokio::test]
746    async fn test_green_computing_manager_creation() {
747        let config = GreenComputingConfig::default();
748        let manager = GreenComputingManager::new(config).unwrap();
749
750        // Test device initialization
751        manager.initialize_device("gpu-0".to_string()).unwrap();
752
753        // Test energy update
754        manager.update_device_energy("gpu-0", 150.0, 500.0).unwrap();
755    }
756
757    #[tokio::test]
758    async fn test_sustainability_report_generation() {
759        let config = GreenComputingConfig::default();
760        let manager = GreenComputingManager::new(config).unwrap();
761
762        manager.initialize_device("gpu-0".to_string()).unwrap();
763        manager.update_device_energy("gpu-0", 150.0, 500.0).unwrap();
764
765        let report = manager.generate_sustainability_report().await.unwrap();
766        assert!(report.sustainability_score >= 0.0 && report.sustainability_score <= 100.0);
767    }
768
769    #[test]
770    fn test_training_schedule_optimization() {
771        let config = GreenComputingConfig::default();
772        let manager = GreenComputingManager::new(config).unwrap();
773
774        let recommendation = manager.optimize_training_schedule().unwrap();
775        assert!(recommendation.current_renewable_percentage >= 0.0);
776        assert!(recommendation.estimated_carbon_savings >= 0.0);
777    }
778
779    #[test]
780    fn test_carbon_footprint_calculation() {
781        let mut carbon = CarbonFootprintData {
782            grid_carbon_intensity: 400.0, // g CO2/kWh
783            ..Default::default()
784        };
785
786        // Simulate 1 kWh consumption
787        let co2_emissions = 1.0 * carbon.grid_carbon_intensity / 1000.0; // Convert to kg
788        carbon.total_co2_kg += co2_emissions;
789
790        assert_eq!(carbon.total_co2_kg, 0.4); // 400g = 0.4kg CO2
791    }
792
793    #[test]
794    fn test_sustainability_metrics() {
795        let mut metrics = SustainabilityMetrics::new();
796        metrics.add_data_point(100.0, 350.0, 45.0, 200.0);
797
798        assert_eq!(metrics.efficiency_trend.len(), 1);
799        assert_eq!(metrics.carbon_intensity_trend.len(), 1);
800        assert_eq!(metrics.renewable_utilization_trend.len(), 1);
801        assert_eq!(metrics.power_consumption_trend.len(), 1);
802    }
803}