1use super::{
8 ApplicationError, ApplicationResult, IndustryConstraint, IndustryObjective, IndustrySolution,
9 OptimizationProblem,
10};
11use crate::ising::IsingModel;
12use crate::qubo::{QuboBuilder, QuboFormulation};
13use crate::simulator::{AnnealingParams, ClassicalAnnealingSimulator};
14use std::collections::HashMap;
15
16use std::fmt::Write;
17#[derive(Debug, Clone)]
19pub struct SmartGridOptimization {
20 pub num_generators: usize,
22 pub num_demand_nodes: usize,
24 pub generator_capacities: Vec<f64>,
26 pub generator_costs: Vec<f64>,
28 pub generator_ramp_rates: Vec<f64>,
30 pub demand_forecasts: Vec<Vec<f64>>,
32 pub transmission_capacities: HashMap<(usize, usize), f64>,
34 pub transmission_costs: HashMap<(usize, usize), f64>,
36 pub num_time_periods: usize,
38 pub renewable_availability: Vec<Vec<f64>>,
40 pub storage_systems: Vec<EnergyStorageSystem>,
42 pub grid_constraints: Vec<IndustryConstraint>,
44}
45
46#[derive(Debug, Clone)]
48pub struct EnergyStorageSystem {
49 pub capacity: f64,
51 pub max_power: f64,
53 pub efficiency: f64,
55 pub location: usize,
57 pub operating_cost: f64,
59}
60
61impl SmartGridOptimization {
62 pub fn new(
64 num_generators: usize,
65 num_demand_nodes: usize,
66 generator_capacities: Vec<f64>,
67 generator_costs: Vec<f64>,
68 demand_forecasts: Vec<Vec<f64>>,
69 num_time_periods: usize,
70 ) -> ApplicationResult<Self> {
71 if generator_capacities.len() != num_generators {
72 return Err(ApplicationError::InvalidConfiguration(
73 "Generator capacities must match number of generators".to_string(),
74 ));
75 }
76
77 if generator_costs.len() != num_generators {
78 return Err(ApplicationError::InvalidConfiguration(
79 "Generator costs must match number of generators".to_string(),
80 ));
81 }
82
83 if demand_forecasts.len() != num_demand_nodes {
84 return Err(ApplicationError::InvalidConfiguration(
85 "Demand forecasts must match number of demand nodes".to_string(),
86 ));
87 }
88
89 Ok(Self {
90 num_generators,
91 num_demand_nodes,
92 generator_capacities,
93 generator_costs,
94 generator_ramp_rates: vec![50.0; num_generators], demand_forecasts,
96 transmission_capacities: HashMap::new(),
97 transmission_costs: HashMap::new(),
98 num_time_periods,
99 renewable_availability: vec![vec![0.0; num_time_periods]; num_generators],
100 storage_systems: Vec::new(),
101 grid_constraints: Vec::new(),
102 })
103 }
104
105 pub fn add_transmission_line(
107 &mut self,
108 from_node: usize,
109 to_node: usize,
110 capacity: f64,
111 cost: f64,
112 ) -> ApplicationResult<()> {
113 if from_node >= self.num_demand_nodes || to_node >= self.num_demand_nodes {
114 return Err(ApplicationError::InvalidConfiguration(
115 "Node indices out of bounds".to_string(),
116 ));
117 }
118
119 self.transmission_capacities
120 .insert((from_node, to_node), capacity);
121 self.transmission_costs.insert((from_node, to_node), cost);
122 Ok(())
123 }
124
125 pub fn add_storage_system(&mut self, storage: EnergyStorageSystem) -> ApplicationResult<()> {
127 if storage.location >= self.num_demand_nodes {
128 return Err(ApplicationError::InvalidConfiguration(
129 "Storage location out of bounds".to_string(),
130 ));
131 }
132
133 self.storage_systems.push(storage);
134 Ok(())
135 }
136
137 pub fn set_renewable_availability(
139 &mut self,
140 availability: Vec<Vec<f64>>,
141 ) -> ApplicationResult<()> {
142 if availability.len() != self.num_generators {
143 return Err(ApplicationError::InvalidConfiguration(
144 "Renewable availability must match number of generators".to_string(),
145 ));
146 }
147
148 self.renewable_availability = availability;
149 Ok(())
150 }
151
152 #[must_use]
154 pub fn calculate_total_demand(&self, time_period: usize) -> f64 {
155 if time_period >= self.num_time_periods {
156 return 0.0;
157 }
158
159 self.demand_forecasts
160 .iter()
161 .map(|node_demands| node_demands.get(time_period).unwrap_or(&0.0))
162 .sum()
163 }
164
165 #[must_use]
167 pub fn calculate_generation_cost(&self, solution: &GridSolution) -> f64 {
168 let mut total_cost = 0.0;
169
170 for t in 0..self.num_time_periods {
171 for g in 0..self.num_generators {
172 let generation = solution.generation_schedule[g][t];
173 total_cost += generation * self.generator_costs[g];
174 }
175 }
176
177 total_cost
178 }
179
180 #[must_use]
182 pub fn calculate_transmission_cost(&self, solution: &GridSolution) -> f64 {
183 let mut total_cost = 0.0;
184
185 for t in 0..self.num_time_periods {
186 for ((from, to), &cost) in &self.transmission_costs {
187 if let Some(flow) = solution.transmission_flows.get(&(*from, *to)) {
188 if let Some(time_flows) = flow.get(t) {
189 total_cost += time_flows * cost;
190 }
191 }
192 }
193 }
194
195 total_cost
196 }
197}
198
199impl OptimizationProblem for SmartGridOptimization {
200 type Solution = GridSolution;
201 type ObjectiveValue = f64;
202
203 fn description(&self) -> String {
204 format!(
205 "Smart Grid Optimization with {} generators, {} nodes, {} time periods",
206 self.num_generators, self.num_demand_nodes, self.num_time_periods
207 )
208 }
209
210 fn size_metrics(&self) -> HashMap<String, usize> {
211 let mut metrics = HashMap::new();
212 metrics.insert("num_generators".to_string(), self.num_generators);
213 metrics.insert("num_demand_nodes".to_string(), self.num_demand_nodes);
214 metrics.insert("num_time_periods".to_string(), self.num_time_periods);
215 metrics.insert(
216 "num_transmission_lines".to_string(),
217 self.transmission_capacities.len(),
218 );
219 metrics.insert(
220 "num_storage_systems".to_string(),
221 self.storage_systems.len(),
222 );
223 metrics
224 }
225
226 fn validate(&self) -> ApplicationResult<()> {
227 if self.num_generators == 0 {
228 return Err(ApplicationError::DataValidationError(
229 "At least one generator required".to_string(),
230 ));
231 }
232
233 if self.num_demand_nodes == 0 {
234 return Err(ApplicationError::DataValidationError(
235 "At least one demand node required".to_string(),
236 ));
237 }
238
239 if self.num_time_periods == 0 {
240 return Err(ApplicationError::DataValidationError(
241 "At least one time period required".to_string(),
242 ));
243 }
244
245 let total_capacity: f64 = self.generator_capacities.iter().sum();
247 let peak_demand = (0..self.num_time_periods)
248 .map(|t| self.calculate_total_demand(t))
249 .fold(0.0, f64::max);
250
251 if total_capacity < peak_demand {
252 return Err(ApplicationError::DataValidationError(
253 "Insufficient generation capacity to meet peak demand".to_string(),
254 ));
255 }
256
257 Ok(())
258 }
259
260 fn to_qubo(&self) -> ApplicationResult<(crate::ising::QuboModel, HashMap<String, usize>)> {
261 let mut builder = QuboBuilder::new();
262 let precision = 10; let mut var_counter = 0;
266 let mut gen_vars = HashMap::new();
267 let mut string_var_map = HashMap::new();
268
269 for g in 0..self.num_generators {
270 for t in 0..self.num_time_periods {
271 for level in 0..precision {
272 let var_name = format!("g_{g}_{t}_{level}");
273 gen_vars.insert((g, t, level), var_counter);
274 string_var_map.insert(var_name, var_counter);
275 var_counter += 1;
276 }
277 }
278 }
279
280 for g in 0..self.num_generators {
282 for t in 0..self.num_time_periods {
283 for level in 0..precision {
284 let generation =
285 f64::from(level) * self.generator_capacities[g] / f64::from(precision);
286 let cost = generation * self.generator_costs[g];
287 let var_idx = gen_vars[&(g, t, level)];
288 builder.add_bias(var_idx, cost);
289 }
290 }
291 }
292
293 let constraint_penalty = 10_000.0;
295 for g in 0..self.num_generators {
296 for t in 0..self.num_time_periods {
297 let mut level_vars = Vec::new();
298 for level in 0..precision {
299 level_vars.push(gen_vars[&(g, t, level)]);
300 }
301
302 for &var1 in &level_vars {
304 builder.add_bias(var1, -constraint_penalty);
305 for &var2 in &level_vars {
306 if var1 != var2 {
307 builder.add_coupling(var1, var2, constraint_penalty);
308 }
309 }
310 }
311 }
312 }
313
314 for t in 0..self.num_time_periods {
316 let total_demand = self.calculate_total_demand(t);
317 let mut supply_vars = Vec::new();
318 let mut supply_coeffs = Vec::new();
319
320 for g in 0..self.num_generators {
321 for level in 0..precision {
322 let generation =
323 f64::from(level) * self.generator_capacities[g] / f64::from(precision);
324 supply_vars.push(gen_vars[&(g, t, level)]);
325 supply_coeffs.push(generation);
326 }
327 }
328
329 let mismatch_penalty = 50_000.0;
331 for (i, &var1) in supply_vars.iter().enumerate() {
332 let coeff1 = supply_coeffs[i];
333 builder.add_bias(var1, -2.0 * mismatch_penalty * total_demand * coeff1);
334
335 for (j, &var2) in supply_vars.iter().enumerate() {
336 let coeff2 = supply_coeffs[j];
337 builder.add_coupling(var1, var2, mismatch_penalty * coeff1 * coeff2);
338 }
339 }
340 }
341
342 Ok((builder.build(), string_var_map))
343 }
344
345 fn evaluate_solution(
346 &self,
347 solution: &Self::Solution,
348 ) -> ApplicationResult<Self::ObjectiveValue> {
349 let generation_cost = self.calculate_generation_cost(solution);
350 let transmission_cost = self.calculate_transmission_cost(solution);
351
352 Ok(generation_cost + transmission_cost)
353 }
354
355 fn is_feasible(&self, solution: &Self::Solution) -> bool {
356 for t in 0..self.num_time_periods {
358 let total_generation: f64 = solution
359 .generation_schedule
360 .iter()
361 .map(|gen_schedule| gen_schedule.get(t).unwrap_or(&0.0))
362 .sum();
363 let total_demand = self.calculate_total_demand(t);
364
365 if (total_generation - total_demand).abs() > 1e-6 {
366 return false;
367 }
368 }
369
370 for (g, gen_schedule) in solution.generation_schedule.iter().enumerate() {
372 for &generation in gen_schedule {
373 if generation > self.generator_capacities[g] {
374 return false;
375 }
376 }
377 }
378
379 for ((from, to), flows) in &solution.transmission_flows {
381 if let Some(&capacity) = self.transmission_capacities.get(&(*from, *to)) {
382 for &flow in flows {
383 if flow > capacity {
384 return false;
385 }
386 }
387 }
388 }
389
390 true
391 }
392}
393
394#[derive(Debug, Clone)]
396pub struct GridSolution {
397 pub generation_schedule: Vec<Vec<f64>>,
399 pub transmission_flows: HashMap<(usize, usize), Vec<f64>>,
401 pub storage_operations: Vec<Vec<f64>>,
403 pub total_cost: f64,
405 pub grid_metrics: GridMetrics,
407}
408
409#[derive(Debug, Clone)]
411pub struct GridMetrics {
412 pub load_factor: f64,
414 pub peak_demand: f64,
416 pub total_energy: f64,
418 pub renewable_penetration: f64,
420 pub efficiency: f64,
422 pub reliability_score: f64,
424}
425
426impl IndustrySolution for GridSolution {
427 type Problem = SmartGridOptimization;
428
429 fn from_binary(problem: &Self::Problem, binary_solution: &[i8]) -> ApplicationResult<Self> {
430 let precision = 10;
431 let mut generation_schedule =
432 vec![vec![0.0; problem.num_time_periods]; problem.num_generators];
433 let mut var_idx = 0;
434
435 for g in 0..problem.num_generators {
437 for t in 0..problem.num_time_periods {
438 for level in 0..precision {
439 if var_idx < binary_solution.len() && binary_solution[var_idx] == 1 {
440 generation_schedule[g][t] = f64::from(level)
441 * problem.generator_capacities[g]
442 / f64::from(precision);
443 break;
444 }
445 var_idx += 1;
446 }
447 }
448 }
449
450 let mut transmission_flows = HashMap::new();
452 for ((from, to), _) in &problem.transmission_capacities {
453 transmission_flows.insert((*from, *to), vec![0.0; problem.num_time_periods]);
454 }
455
456 let storage_operations =
458 vec![vec![0.0; problem.num_time_periods]; problem.storage_systems.len()];
459
460 let total_energy: f64 = generation_schedule.iter().flat_map(|gen| gen.iter()).sum();
462
463 let peak_demand = (0..problem.num_time_periods)
464 .map(|t| problem.calculate_total_demand(t))
465 .fold(0.0, f64::max);
466
467 let average_demand = total_energy / problem.num_time_periods as f64;
468 let load_factor = if peak_demand > 0.0 {
469 average_demand / peak_demand
470 } else {
471 0.0
472 };
473
474 let grid_metrics = GridMetrics {
475 load_factor,
476 peak_demand,
477 total_energy,
478 renewable_penetration: 0.0, efficiency: 0.95, reliability_score: 0.99, };
482
483 let total_cost = problem.calculate_generation_cost(&Self {
484 generation_schedule: generation_schedule.clone(),
485 transmission_flows: transmission_flows.clone(),
486 storage_operations: storage_operations.clone(),
487 total_cost: 0.0,
488 grid_metrics: grid_metrics.clone(),
489 });
490
491 Ok(Self {
492 generation_schedule,
493 transmission_flows,
494 storage_operations,
495 total_cost,
496 grid_metrics,
497 })
498 }
499
500 fn summary(&self) -> HashMap<String, String> {
501 let mut summary = HashMap::new();
502 summary.insert("type".to_string(), "Smart Grid Optimization".to_string());
503 summary.insert("total_cost".to_string(), format!("${:.2}", self.total_cost));
504 summary.insert(
505 "peak_demand".to_string(),
506 format!("{:.1} MW", self.grid_metrics.peak_demand),
507 );
508 summary.insert(
509 "total_energy".to_string(),
510 format!("{:.1} MWh", self.grid_metrics.total_energy),
511 );
512 summary.insert(
513 "load_factor".to_string(),
514 format!("{:.1}%", self.grid_metrics.load_factor * 100.0),
515 );
516 summary.insert(
517 "efficiency".to_string(),
518 format!("{:.1}%", self.grid_metrics.efficiency * 100.0),
519 );
520 summary.insert(
521 "reliability".to_string(),
522 format!("{:.2}%", self.grid_metrics.reliability_score * 100.0),
523 );
524 summary
525 }
526
527 fn metrics(&self) -> HashMap<String, f64> {
528 let mut metrics = HashMap::new();
529 metrics.insert("total_cost".to_string(), self.total_cost);
530 metrics.insert("load_factor".to_string(), self.grid_metrics.load_factor);
531 metrics.insert("peak_demand".to_string(), self.grid_metrics.peak_demand);
532 metrics.insert("total_energy".to_string(), self.grid_metrics.total_energy);
533 metrics.insert(
534 "renewable_penetration".to_string(),
535 self.grid_metrics.renewable_penetration,
536 );
537 metrics.insert("efficiency".to_string(), self.grid_metrics.efficiency);
538 metrics.insert(
539 "reliability_score".to_string(),
540 self.grid_metrics.reliability_score,
541 );
542
543 let num_active_generators = self
545 .generation_schedule
546 .iter()
547 .filter(|schedule| schedule.iter().any(|&gen| gen > 1e-6))
548 .count();
549 metrics.insert(
550 "active_generators".to_string(),
551 num_active_generators as f64,
552 );
553
554 let max_generation: f64 = self
555 .generation_schedule
556 .iter()
557 .flat_map(|schedule| schedule.iter())
558 .fold(0.0, |a, &b| a.max(b));
559 metrics.insert("max_generation".to_string(), max_generation);
560
561 metrics
562 }
563
564 fn export_format(&self) -> ApplicationResult<String> {
565 let mut output = String::new();
566 output.push_str("# Smart Grid Optimization Solution\n\n");
567
568 output.push_str("## Solution Summary\n");
569 let _ = writeln!(output, "Total Cost: ${:.2}", self.total_cost);
570 let _ = write!(
571 output,
572 "Peak Demand: {:.1} MW\n",
573 self.grid_metrics.peak_demand
574 );
575 let _ = write!(
576 output,
577 "Total Energy: {:.1} MWh\n",
578 self.grid_metrics.total_energy
579 );
580 let _ = write!(
581 output,
582 "Load Factor: {:.1}%\n",
583 self.grid_metrics.load_factor * 100.0
584 );
585 let _ = write!(
586 output,
587 "Grid Efficiency: {:.1}%\n",
588 self.grid_metrics.efficiency * 100.0
589 );
590
591 output.push_str("\n## Generation Schedule\n");
592 for (g, schedule) in self.generation_schedule.iter().enumerate() {
593 let _ = write!(output, "Generator {}: ", g + 1);
594 for (t, &generation) in schedule.iter().enumerate() {
595 if generation > 1e-6 {
596 let _ = write!(output, "T{}: {:.1}MW ", t + 1, generation);
597 }
598 }
599 output.push('\n');
600 }
601
602 output.push_str("\n## Grid Performance Metrics\n");
603 for (key, value) in self.metrics() {
604 let _ = writeln!(output, "{key}: {value:.3}");
605 }
606
607 Ok(output)
608 }
609}
610
611#[derive(Debug, Clone)]
613pub struct RenewableEnergyIntegration {
614 pub solar_farms: Vec<SolarFarm>,
616 pub wind_farms: Vec<WindFarm>,
618 pub battery_systems: Vec<BatterySystem>,
620 pub demand_profile: Vec<f64>,
622 pub connection_costs: Vec<f64>,
624 pub curtailment_penalty: f64,
626}
627
628#[derive(Debug, Clone)]
630pub struct SolarFarm {
631 pub capacity: f64,
633 pub irradiance_profile: Vec<f64>,
635 pub efficiency: f64,
637 pub installation_cost: f64,
639}
640
641#[derive(Debug, Clone)]
643pub struct WindFarm {
644 pub capacity: f64,
646 pub wind_profile: Vec<f64>,
648 pub power_curve: Vec<f64>,
650 pub installation_cost: f64,
652}
653
654#[derive(Debug, Clone)]
656pub struct BatterySystem {
657 pub energy_capacity: f64,
659 pub power_capacity: f64,
661 pub efficiency: f64,
663 pub installation_cost: f64,
665 pub cycle_life: usize,
667}
668
669#[derive(Debug, Clone)]
671pub struct BinarySmartGridOptimization {
672 inner: SmartGridOptimization,
673}
674
675impl BinarySmartGridOptimization {
676 #[must_use]
677 pub const fn new(inner: SmartGridOptimization) -> Self {
678 Self { inner }
679 }
680}
681
682impl OptimizationProblem for BinarySmartGridOptimization {
683 type Solution = Vec<i8>;
684 type ObjectiveValue = f64;
685
686 fn description(&self) -> String {
687 self.inner.description()
688 }
689
690 fn size_metrics(&self) -> HashMap<String, usize> {
691 self.inner.size_metrics()
692 }
693
694 fn validate(&self) -> ApplicationResult<()> {
695 self.inner.validate()
696 }
697
698 fn to_qubo(&self) -> ApplicationResult<(crate::ising::QuboModel, HashMap<String, usize>)> {
699 self.inner.to_qubo()
700 }
701
702 fn evaluate_solution(
703 &self,
704 solution: &Self::Solution,
705 ) -> ApplicationResult<Self::ObjectiveValue> {
706 let grid_solution = GridSolution::from_binary(&self.inner, solution)?;
708 self.inner.evaluate_solution(&grid_solution)
709 }
710
711 fn is_feasible(&self, solution: &Self::Solution) -> bool {
712 if let Ok(grid_solution) = GridSolution::from_binary(&self.inner, solution) {
714 self.inner.is_feasible(&grid_solution)
715 } else {
716 false
717 }
718 }
719}
720
721pub fn create_benchmark_problems(
723 size: usize,
724) -> ApplicationResult<Vec<Box<dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>>>>
725{
726 let mut problems = Vec::new();
727
728 let generator_capacities = vec![100.0, 150.0, 200.0];
730 let generator_costs = vec![30.0, 25.0, 40.0]; let demand_forecasts = vec![
732 vec![50.0, 80.0, 120.0, 100.0], vec![30.0, 60.0, 90.0, 70.0], ];
735
736 let grid_problem = SmartGridOptimization::new(
737 3, 2, generator_capacities,
740 generator_costs,
741 demand_forecasts,
742 4, )?;
744
745 problems.push(Box::new(BinarySmartGridOptimization::new(grid_problem))
746 as Box<
747 dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>,
748 >);
749
750 if size >= 5 {
752 let large_capacities = (0..size).map(|i| (i as f64).mul_add(20.0, 50.0)).collect();
753 let large_costs = (0..size).map(|i| (i as f64).mul_add(5.0, 20.0)).collect();
754 let large_demands = (0..3)
755 .map(|_| (0..6).map(|t| f64::from(t).mul_add(15.0, 30.0)).collect())
756 .collect();
757
758 let mut large_grid =
759 SmartGridOptimization::new(size, 3, large_capacities, large_costs, large_demands, 6)?;
760
761 for i in 0..2 {
763 large_grid.add_storage_system(EnergyStorageSystem {
764 capacity: 100.0,
765 max_power: 50.0,
766 efficiency: 0.90,
767 location: i,
768 operating_cost: 5.0,
769 })?;
770 }
771
772 problems.push(Box::new(BinarySmartGridOptimization::new(large_grid))
773 as Box<
774 dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>,
775 >);
776 }
777
778 Ok(problems)
779}
780
781pub fn solve_grid_optimization(
783 problem: &SmartGridOptimization,
784 params: Option<AnnealingParams>,
785) -> ApplicationResult<GridSolution> {
786 let (qubo, _var_map) = problem.to_qubo()?;
788
789 let ising = IsingModel::from_qubo(&qubo);
791
792 let annealing_params = params.unwrap_or_else(|| {
794 let mut p = AnnealingParams::default();
795 p.num_sweeps = 20_000;
796 p.num_repetitions = 30;
797 p.initial_temperature = 4.0;
798 p.final_temperature = 0.001;
799 p
800 });
801
802 let simulator = ClassicalAnnealingSimulator::new(annealing_params)
804 .map_err(|e| ApplicationError::OptimizationError(e.to_string()))?;
805
806 let result = simulator
807 .solve(&ising)
808 .map_err(|e| ApplicationError::OptimizationError(e.to_string()))?;
809
810 GridSolution::from_binary(problem, &result.best_spins)
812}
813
814#[cfg(test)]
815mod tests {
816 use super::*;
817
818 #[test]
819 fn test_grid_optimization_creation() {
820 let generator_capacities = vec![100.0, 150.0];
821 let generator_costs = vec![30.0, 25.0];
822 let demand_forecasts = vec![vec![50.0, 80.0], vec![30.0, 60.0]];
823
824 let grid = SmartGridOptimization::new(
825 2,
826 2,
827 generator_capacities,
828 generator_costs,
829 demand_forecasts,
830 2,
831 )
832 .expect("failed to create smart grid in test");
833
834 assert_eq!(grid.num_generators, 2);
835 assert_eq!(grid.num_demand_nodes, 2);
836 assert_eq!(grid.num_time_periods, 2);
837 }
838
839 #[test]
840 fn test_transmission_line_addition() {
841 let mut grid = SmartGridOptimization::new(
842 2,
843 3,
844 vec![100.0, 150.0],
845 vec![30.0, 25.0],
846 vec![vec![50.0], vec![30.0], vec![20.0]],
847 1,
848 )
849 .expect("failed to create smart grid in test");
850
851 assert!(grid.add_transmission_line(0, 1, 100.0, 2.0).is_ok());
852 assert!(grid.add_transmission_line(1, 2, 150.0, 3.0).is_ok());
853 assert_eq!(grid.transmission_capacities.len(), 2);
854 }
855
856 #[test]
857 fn test_storage_system_addition() {
858 let mut grid = SmartGridOptimization::new(
859 2,
860 2,
861 vec![100.0, 150.0],
862 vec![30.0, 25.0],
863 vec![vec![50.0], vec![30.0]],
864 1,
865 )
866 .expect("failed to create smart grid in test");
867
868 let storage = EnergyStorageSystem {
869 capacity: 100.0,
870 max_power: 50.0,
871 efficiency: 0.90,
872 location: 0,
873 operating_cost: 5.0,
874 };
875
876 assert!(grid.add_storage_system(storage).is_ok());
877 assert_eq!(grid.storage_systems.len(), 1);
878 }
879
880 #[test]
881 fn test_demand_calculation() {
882 let grid = SmartGridOptimization::new(
883 2,
884 2,
885 vec![100.0, 150.0],
886 vec![30.0, 25.0],
887 vec![vec![50.0, 80.0], vec![30.0, 60.0]],
888 2,
889 )
890 .expect("failed to create smart grid in test");
891
892 assert_eq!(grid.calculate_total_demand(0), 80.0); assert_eq!(grid.calculate_total_demand(1), 140.0); }
895
896 #[test]
897 fn test_grid_validation() {
898 let grid = SmartGridOptimization::new(
899 2,
900 2,
901 vec![100.0, 150.0],
902 vec![30.0, 25.0],
903 vec![vec![50.0, 80.0], vec![30.0, 60.0]],
904 2,
905 )
906 .expect("failed to create smart grid in test");
907
908 assert!(grid.validate().is_ok());
909
910 let insufficient_grid = SmartGridOptimization::new(
912 1,
913 2,
914 vec![50.0], vec![30.0],
916 vec![vec![50.0, 80.0], vec![30.0, 60.0]],
917 2,
918 )
919 .expect("failed to create insufficient grid in test");
920
921 assert!(insufficient_grid.validate().is_err());
922 }
923
924 #[test]
925 fn test_benchmark_problems() {
926 let problems =
927 create_benchmark_problems(5).expect("failed to create benchmark problems in test");
928 assert_eq!(problems.len(), 2);
929
930 for problem in &problems {
931 assert!(problem.validate().is_ok());
932 let metrics = problem.size_metrics();
933 assert!(metrics.contains_key("num_generators"));
934 assert!(metrics.contains_key("num_demand_nodes"));
935 }
936 }
937
938 #[test]
939 fn test_solar_farm() {
940 let solar = SolarFarm {
941 capacity: 100.0,
942 irradiance_profile: vec![0.0, 0.3, 0.8, 1.0, 0.9, 0.5, 0.0],
943 efficiency: 0.20,
944 installation_cost: 1000.0,
945 };
946
947 assert_eq!(solar.capacity, 100.0);
948 assert_eq!(solar.irradiance_profile.len(), 7);
949 }
950
951 #[test]
952 fn test_battery_system() {
953 let battery = BatterySystem {
954 energy_capacity: 50.0,
955 power_capacity: 25.0,
956 efficiency: 0.95,
957 installation_cost: 500.0,
958 cycle_life: 5000,
959 };
960
961 assert_eq!(battery.energy_capacity, 50.0);
962 assert_eq!(battery.efficiency, 0.95);
963 }
964}