5
5
from __future__ import annotations
6
6
7
7
import logging
8
- import random
9
8
from collections .abc import Callable
10
9
from pathlib import Path
11
10
from typing import TYPE_CHECKING
@@ -612,6 +611,9 @@ def _solve_spores(self, solver_config: config_schema.Solve) -> xr.Dataset:
612
611
# We store the results from each iteration in the `results_list` to later concatenate into a single dataset.
613
612
results_list : list [xr .Dataset ] = [baseline_results ]
614
613
spore_range = range (1 , spores_config .number + 1 )
614
+ LOGGER .info (
615
+ f"Optimisation model | Running SPORES with `{ spores_config .scoring_algorithm } ` scoring algorithm."
616
+ )
615
617
for spore in spore_range :
616
618
LOGGER .info (f"Optimisation model | Running SPORE { spore } ." )
617
619
self ._spores_update_model (baseline_results , results_list , spores_config )
@@ -639,8 +641,21 @@ def _spores_update_model(
639
641
all_previous_results : list [xr .Dataset ],
640
642
spores_config : config_schema .SolveSpores ,
641
643
):
644
+ """Assign SPORES scores for the next iteration of the model run.
645
+
646
+ Algorithms applied are based on those introduced in <https://doi.org/10.1016/j.apenergy.2023.121002>.
647
+
648
+ Args:
649
+ baseline_results (xr.Dataset): The initial results (before applying SPORES scoring)
650
+ all_previous_results (list[xr.Dataset]):
651
+ A list of all previous iterations.
652
+ This includes the baseline results, which will be the first item in the list.
653
+ spores_config (config_schema.SolveSpores):
654
+ The SPORES configuration.
655
+ """
656
+
642
657
def _score_integer () -> xr .DataArray :
643
- # Look at capacity deployment in the previous iteration
658
+ """Integer scoring algorithm."""
644
659
previous_cap = latest_results ["flow_cap" ].where (spores_techs )
645
660
646
661
# Make sure that penalties are applied only to non-negligible deployments of capacity
@@ -658,36 +673,33 @@ def _score_integer() -> xr.DataArray:
658
673
return new_score
659
674
660
675
def _score_relative_deployment () -> xr .DataArray :
661
- # Look at capacity deployment in the previous iteration
676
+ """Relative deployment scoring algorithm."""
662
677
previous_cap = latest_results ["flow_cap" ].where (spores_techs )
663
-
664
- # Look at capacity deployment in the previous iteration
665
678
relative_cap = previous_cap / self .inputs ["flow_cap_max" ].where (
666
679
spores_techs
667
680
)
668
681
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
682
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 )
683
+ # Make sure that penalties are applied only to non-negligible relative capacities
684
+ relative_cap .where (relative_cap > spores_config . score_threshold_factor )
675
685
.fillna (0 )
676
686
.where (spores_techs )
677
687
)
678
- return new_score . to_pandas ()
688
+ return new_score
679
689
680
690
def _score_random () -> xr .DataArray :
691
+ """Random scoring algorithm."""
681
692
previous_cap = latest_results ["flow_cap" ].where (spores_techs )
682
693
new_score = (
683
694
previous_cap .fillna (0 )
684
- .where (previous_cap .isnull (), other = lambda x : random .random ( ))
695
+ .where (previous_cap .isnull (), other = np .random . rand ( * previous_cap . shape ))
685
696
.where (spores_techs )
686
697
)
687
698
688
699
return new_score
689
700
690
701
def _score_evolving_average () -> xr .DataArray :
702
+ """Evolving average scoring algorithm."""
691
703
previous_cap = latest_results ["flow_cap" ]
692
704
evolving_average = sum (
693
705
results ["flow_cap" ] for results in all_previous_results
@@ -700,17 +712,21 @@ def _score_evolving_average() -> xr.DataArray:
700
712
new_score = _score_integer ()
701
713
else :
702
714
# 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 )
715
+ # which will give it a _large_ score since we take the reciprocal of the change.
716
+ cleaned_relative_change = (
717
+ relative_change .clip (min = 0.001 ).fillna (0 ).where (spores_techs )
718
+ )
719
+ # Any zero values that make their way through to the scoring are kept as zero after taking the reciprocal.
720
+ new_score = (cleaned_relative_change ** - 1 ).where (
721
+ cleaned_relative_change > 0 , other = 0
707
722
)
708
- new_score = relative_change ** - 1
709
723
710
724
return new_score
711
725
712
726
latest_results = all_previous_results [- 1 ]
713
- allowed_methods : dict [str , Callable [[], xr .DataArray ]] = {
727
+ allowed_methods : dict [
728
+ config_schema .SPORES_SCORING_OPTIONS , Callable [[], xr .DataArray ]
729
+ ] = {
714
730
"integer" : _score_integer ,
715
731
"relative_deployment" : _score_relative_deployment ,
716
732
"random" : _score_random ,
0 commit comments