5
5
from __future__ import annotations
6
6
7
7
import logging
8
+ import random
9
+ from collections .abc import Callable
8
10
from pathlib import Path
9
11
from typing import TYPE_CHECKING
10
12
13
+ import numpy as np
11
14
import pandas as pd
12
15
import xarray as xr
13
16
@@ -611,7 +614,7 @@ def _solve_spores(self, solver_config: config_schema.Solve) -> xr.Dataset:
611
614
spore_range = range (1 , spores_config .number + 1 )
612
615
for spore in spore_range :
613
616
LOGGER .info (f"Optimisation model | Running SPORE { spore } ." )
614
- self ._spores_update_model (baseline_results , results_list [ - 1 ] , spores_config )
617
+ self ._spores_update_model (baseline_results , results_list , spores_config )
615
618
616
619
iteration_results = self .backend ._solve (solver_config , warmstart = False )
617
620
results_list .append (iteration_results )
@@ -633,9 +636,86 @@ def _solve_spores(self, solver_config: config_schema.Solve) -> xr.Dataset:
633
636
def _spores_update_model (
634
637
self ,
635
638
baseline_results : xr .Dataset ,
636
- previous_results : xr .Dataset ,
639
+ all_previous_results : list [ xr .Dataset ] ,
637
640
spores_config : config_schema .SolveSpores ,
638
641
):
642
+ def _score_integer () -> xr .DataArray :
643
+ # Look at capacity deployment in the previous iteration
644
+ previous_cap = latest_results ["flow_cap" ].where (spores_techs )
645
+
646
+ # Make sure that penalties are applied only to non-negligible deployments of capacity
647
+ min_relevant_size = spores_config .score_threshold_factor * previous_cap .max (
648
+ ["nodes" , "techs" ]
649
+ )
650
+
651
+ new_score = (
652
+ # Where capacity was deployed more than the minimal relevant size, assign an integer penalty (score)
653
+ previous_cap .where (previous_cap > min_relevant_size )
654
+ .clip (min = 1 , max = 1 )
655
+ .fillna (0 )
656
+ .where (spores_techs )
657
+ )
658
+ return new_score
659
+
660
+ def _score_relative_deployment () -> xr .DataArray :
661
+ # Look at capacity deployment in the previous iteration
662
+ previous_cap = latest_results ["flow_cap" ].where (spores_techs )
663
+
664
+ # Look at capacity deployment in the previous iteration
665
+ relative_cap = previous_cap / self .inputs ["flow_cap_max" ].where (
666
+ spores_techs
667
+ )
668
+
669
+ # Make sure that penalties are applied only to non-negligible deployments of capacity
670
+ min_relevant_size = spores_config .score_threshold_factor * relative_cap
671
+
672
+ new_score = (
673
+ # Where capacity was deployed more than the minimal relevant size, assign the relative deployment as a penalty (score)
674
+ relative_cap .where (previous_cap > min_relevant_size )
675
+ .fillna (0 )
676
+ .where (spores_techs )
677
+ )
678
+ return new_score .to_pandas ()
679
+
680
+ def _score_random () -> xr .DataArray :
681
+ previous_cap = latest_results ["flow_cap" ].where (spores_techs )
682
+ new_score = (
683
+ previous_cap .fillna (0 )
684
+ .where (previous_cap .isnull (), other = lambda x : random .random ())
685
+ .where (spores_techs )
686
+ )
687
+
688
+ return new_score
689
+
690
+ def _score_evolving_average () -> xr .DataArray :
691
+ previous_cap = latest_results ["flow_cap" ]
692
+ evolving_average = sum (
693
+ results ["flow_cap" ] for results in all_previous_results
694
+ ) / len (all_previous_results )
695
+
696
+ relative_change = abs (evolving_average - previous_cap ) / evolving_average
697
+ # first iteration
698
+ if relative_change .sum () == 0 :
699
+ # first iteration
700
+ new_score = _score_integer ()
701
+ else :
702
+ # If capacity is exactly the same as the average, we give the relative difference an arbitrarily small value
703
+ relative_change = (
704
+ relative_change .clip (min = 0.001 )
705
+ .where (relative_change != np .inf , other = 0 )
706
+ .where (spores_techs )
707
+ )
708
+ new_score = relative_change ** - 1
709
+
710
+ return new_score
711
+
712
+ latest_results = all_previous_results [- 1 ]
713
+ allowed_methods : dict [str , Callable [[], xr .DataArray ]] = {
714
+ "integer" : _score_integer ,
715
+ "relative_deployment" : _score_relative_deployment ,
716
+ "random" : _score_random ,
717
+ "evolving_average" : _score_evolving_average ,
718
+ }
639
719
# Update the slack-cost backend parameter based on the calculated minimum feasible system design cost
640
720
constraining_cost = baseline_results .cost .groupby ("costs" ).sum (..., min_count = 1 )
641
721
self .backend .update_parameter ("spores_baseline_cost" , constraining_cost )
@@ -647,22 +727,8 @@ def _spores_update_model(
647
727
).notnull ()
648
728
& self .inputs .definition_matrix
649
729
)
730
+ new_score = allowed_methods [spores_config .scoring_algorithm ]()
650
731
651
- # Look at capacity deployment in the previous iteration
652
- previous_cap = previous_results ["flow_cap" ].where (spores_techs )
653
-
654
- # Make sure that penalties are applied only to non-negligible deployments of capacity
655
- min_relevant_size = spores_config .score_threhsold_factor * previous_cap .max (
656
- ["nodes" , "techs" ]
657
- )
658
-
659
- new_score = (
660
- # Where capacity was deployed more than the minimal relevant size, assign an integer penalty (score)
661
- previous_cap .where (previous_cap > min_relevant_size )
662
- .clip (min = 1 , max = 1 )
663
- .fillna (0 )
664
- .where (spores_techs )
665
- )
666
732
new_score += self .backend .get_parameter (
667
733
"spores_score" , as_backend_objs = False
668
734
).fillna (0 )
0 commit comments