NREL-reV 0.8.9__py3-none-any.whl → 0.9.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {NREL_reV-0.8.9.dist-info → NREL_reV-0.9.2.dist-info}/METADATA +2 -1
- {NREL_reV-0.8.9.dist-info → NREL_reV-0.9.2.dist-info}/RECORD +34 -34
- reV/SAM/SAM.py +38 -0
- reV/SAM/generation.py +43 -10
- reV/bespoke/bespoke.py +304 -245
- reV/bespoke/cli_bespoke.py +2 -0
- reV/bespoke/place_turbines.py +181 -37
- reV/config/output_request.py +2 -1
- reV/config/project_points.py +1 -3
- reV/econ/econ.py +24 -13
- reV/econ/economies_of_scale.py +54 -35
- reV/generation/base.py +22 -2
- reV/generation/generation.py +50 -23
- reV/generation/output_attributes/lcoe_fcr_inputs.json +38 -3
- reV/handlers/__init__.py +0 -1
- reV/handlers/multi_year.py +45 -17
- reV/handlers/transmission.py +44 -27
- reV/hybrids/hybrid_methods.py +16 -14
- reV/hybrids/hybrids.py +10 -10
- reV/nrwal/nrwal.py +1 -1
- reV/qa_qc/qa_qc.py +1 -1
- reV/qa_qc/summary.py +4 -4
- reV/rep_profiles/rep_profiles.py +1 -1
- reV/supply_curve/exclusions.py +1 -1
- reV/supply_curve/extent.py +1 -1
- reV/supply_curve/points.py +254 -131
- reV/supply_curve/sc_aggregation.py +13 -45
- reV/supply_curve/supply_curve.py +200 -141
- reV/utilities/__init__.py +114 -39
- reV/version.py +1 -1
- {NREL_reV-0.8.9.dist-info → NREL_reV-0.9.2.dist-info}/LICENSE +0 -0
- {NREL_reV-0.8.9.dist-info → NREL_reV-0.9.2.dist-info}/WHEEL +0 -0
- {NREL_reV-0.8.9.dist-info → NREL_reV-0.9.2.dist-info}/entry_points.txt +0 -0
- {NREL_reV-0.8.9.dist-info → NREL_reV-0.9.2.dist-info}/top_level.txt +0 -0
reV/supply_curve/supply_curve.py
CHANGED
@@ -7,6 +7,7 @@ reV supply curve module
|
|
7
7
|
import json
|
8
8
|
import logging
|
9
9
|
import os
|
10
|
+
from itertools import chain
|
10
11
|
from copy import deepcopy
|
11
12
|
from warnings import warn
|
12
13
|
|
@@ -24,11 +25,38 @@ from reV.utilities.exceptions import SupplyCurveError, SupplyCurveInputError
|
|
24
25
|
logger = logging.getLogger(__name__)
|
25
26
|
|
26
27
|
|
28
|
+
# map is column name to relative order in which it should appear in output file
|
29
|
+
_REQUIRED_COMPUTE_AND_OUTPUT_COLS = {
|
30
|
+
SupplyCurveField.TRANS_GID: 0,
|
31
|
+
SupplyCurveField.TRANS_TYPE: 1,
|
32
|
+
SupplyCurveField.N_PARALLEL_TRANS: 2,
|
33
|
+
SupplyCurveField.DIST_SPUR_KM: 3,
|
34
|
+
SupplyCurveField.TOTAL_TRANS_CAP_COST_PER_MW: 10,
|
35
|
+
SupplyCurveField.LCOT: 11,
|
36
|
+
SupplyCurveField.TOTAL_LCOE: 12,
|
37
|
+
}
|
38
|
+
_REQUIRED_OUTPUT_COLS = {SupplyCurveField.DIST_EXPORT_KM: 4,
|
39
|
+
SupplyCurveField.REINFORCEMENT_DIST_KM: 5,
|
40
|
+
SupplyCurveField.TIE_LINE_COST_PER_MW: 6,
|
41
|
+
SupplyCurveField.CONNECTION_COST_PER_MW: 7,
|
42
|
+
SupplyCurveField.EXPORT_COST_PER_MW: 8,
|
43
|
+
SupplyCurveField.REINFORCEMENT_COST_PER_MW: 9,
|
44
|
+
SupplyCurveField.POI_LAT: 13,
|
45
|
+
SupplyCurveField.POI_LON: 14,
|
46
|
+
SupplyCurveField.REINFORCEMENT_POI_LAT: 15,
|
47
|
+
SupplyCurveField.REINFORCEMENT_POI_LON: 16}
|
48
|
+
DEFAULT_COLUMNS = tuple(str(field)
|
49
|
+
for field in chain(_REQUIRED_COMPUTE_AND_OUTPUT_COLS,
|
50
|
+
_REQUIRED_OUTPUT_COLS))
|
51
|
+
"""Default output columns from supply chain computation (not ordered)"""
|
52
|
+
|
53
|
+
|
27
54
|
class SupplyCurve:
|
28
55
|
"""SupplyCurve"""
|
29
56
|
|
30
57
|
def __init__(self, sc_points, trans_table, sc_features=None,
|
31
|
-
|
58
|
+
# str() to fix docs
|
59
|
+
sc_capacity_col=str(SupplyCurveField.CAPACITY_AC_MW)):
|
32
60
|
"""ReV LCOT calculation and SupplyCurve sorting class.
|
33
61
|
|
34
62
|
``reV`` supply curve computes the transmission costs associated
|
@@ -282,15 +310,19 @@ class SupplyCurve:
|
|
282
310
|
# also xformer_cost_p_mw -> xformer_cost_per_mw (not sure why there
|
283
311
|
# would be a *_p_mw but here we are...)
|
284
312
|
rename_map = {
|
285
|
-
"trans_line_gid":
|
313
|
+
"trans_line_gid": SupplyCurveField.TRANS_GID,
|
286
314
|
"trans_gids": "trans_line_gids",
|
287
315
|
"xformer_cost_p_mw": "xformer_cost_per_mw",
|
288
316
|
}
|
289
317
|
trans_table = trans_table.rename(columns=rename_map)
|
290
318
|
|
291
|
-
|
292
|
-
|
293
|
-
|
319
|
+
contains_dist_in_miles = "dist_mi" in trans_table
|
320
|
+
missing_km_dist = SupplyCurveField.DIST_SPUR_KM not in trans_table
|
321
|
+
if contains_dist_in_miles and missing_km_dist:
|
322
|
+
trans_table = trans_table.rename(
|
323
|
+
columns={"dist_mi": SupplyCurveField.DIST_SPUR_KM}
|
324
|
+
)
|
325
|
+
trans_table[SupplyCurveField.DIST_SPUR_KM] *= 1.60934
|
294
326
|
|
295
327
|
drop_cols = [SupplyCurveField.SC_GID, 'cap_left',
|
296
328
|
SupplyCurveField.SC_POINT_GID]
|
@@ -302,7 +334,7 @@ class SupplyCurve:
|
|
302
334
|
|
303
335
|
@staticmethod
|
304
336
|
def _map_trans_capacity(trans_sc_table,
|
305
|
-
sc_capacity_col=SupplyCurveField.
|
337
|
+
sc_capacity_col=SupplyCurveField.CAPACITY_AC_MW):
|
306
338
|
"""
|
307
339
|
Map SC gids to transmission features based on capacity. For any SC
|
308
340
|
gids with capacity > the maximum transmission feature capacity, map
|
@@ -331,7 +363,7 @@ class SupplyCurve:
|
|
331
363
|
|
332
364
|
nx = trans_sc_table[sc_capacity_col] / trans_sc_table["max_cap"]
|
333
365
|
nx = np.ceil(nx).astype(int)
|
334
|
-
trans_sc_table[
|
366
|
+
trans_sc_table[SupplyCurveField.N_PARALLEL_TRANS] = nx
|
335
367
|
|
336
368
|
if (nx > 1).any():
|
337
369
|
mask = nx > 1
|
@@ -409,11 +441,12 @@ class SupplyCurve:
|
|
409
441
|
"""
|
410
442
|
features = features.rename(
|
411
443
|
columns={
|
412
|
-
"trans_line_gid":
|
444
|
+
"trans_line_gid": SupplyCurveField.TRANS_GID,
|
413
445
|
"trans_gids": "trans_line_gids",
|
414
446
|
}
|
415
447
|
)
|
416
|
-
mask = features[
|
448
|
+
mask = (features[SupplyCurveField.TRANS_TYPE].str.casefold()
|
449
|
+
== "substation")
|
417
450
|
|
418
451
|
if not any(mask):
|
419
452
|
return []
|
@@ -424,7 +457,7 @@ class SupplyCurve:
|
|
424
457
|
|
425
458
|
line_gids = np.unique(np.concatenate(line_gids.values))
|
426
459
|
|
427
|
-
test = np.isin(line_gids, features[
|
460
|
+
test = np.isin(line_gids, features[SupplyCurveField.TRANS_GID].values)
|
428
461
|
|
429
462
|
return line_gids[~test].tolist()
|
430
463
|
|
@@ -513,10 +546,11 @@ class SupplyCurve:
|
|
513
546
|
@classmethod
|
514
547
|
def _merge_sc_trans_tables(cls, sc_points, trans_table,
|
515
548
|
sc_cols=(SupplyCurveField.SC_GID,
|
516
|
-
SupplyCurveField.
|
517
|
-
SupplyCurveField.
|
549
|
+
SupplyCurveField.CAPACITY_AC_MW,
|
550
|
+
SupplyCurveField.MEAN_CF_AC,
|
518
551
|
SupplyCurveField.MEAN_LCOE),
|
519
|
-
sc_capacity_col=SupplyCurveField.
|
552
|
+
sc_capacity_col=SupplyCurveField.CAPACITY_AC_MW
|
553
|
+
):
|
520
554
|
"""
|
521
555
|
Merge the supply curve table with the transmission features table.
|
522
556
|
|
@@ -583,11 +617,13 @@ class SupplyCurve:
|
|
583
617
|
if isinstance(sc_cols, tuple):
|
584
618
|
sc_cols = list(sc_cols)
|
585
619
|
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
620
|
+
extra_cols = [SupplyCurveField.CAPACITY_DC_MW,
|
621
|
+
SupplyCurveField.MEAN_CF_DC,
|
622
|
+
SupplyCurveField.MEAN_LCOE_FRICTION,
|
623
|
+
"transmission_multiplier"]
|
624
|
+
for col in extra_cols:
|
625
|
+
if col in sc_points:
|
626
|
+
sc_cols.append(col)
|
591
627
|
|
592
628
|
sc_cols += merge_cols
|
593
629
|
sc_points = sc_points[sc_cols].copy()
|
@@ -600,10 +636,10 @@ class SupplyCurve:
|
|
600
636
|
@classmethod
|
601
637
|
def _map_tables(cls, sc_points, trans_table,
|
602
638
|
sc_cols=(SupplyCurveField.SC_GID,
|
603
|
-
SupplyCurveField.
|
604
|
-
SupplyCurveField.
|
639
|
+
SupplyCurveField.CAPACITY_AC_MW,
|
640
|
+
SupplyCurveField.MEAN_CF_AC,
|
605
641
|
SupplyCurveField.MEAN_LCOE),
|
606
|
-
sc_capacity_col=SupplyCurveField.
|
642
|
+
sc_capacity_col=SupplyCurveField.CAPACITY_AC_MW):
|
607
643
|
"""
|
608
644
|
Map supply curve points to transmission features
|
609
645
|
|
@@ -618,8 +654,9 @@ class SupplyCurve:
|
|
618
654
|
sc_cols : tuple | list, optional
|
619
655
|
List of column from sc_points to transfer into the trans table,
|
620
656
|
If the `sc_capacity_col` is not included, it will get added.
|
621
|
-
by default (SupplyCurveField.SC_GID,
|
622
|
-
SupplyCurveField.
|
657
|
+
by default (SupplyCurveField.SC_GID,
|
658
|
+
SupplyCurveField.CAPACITY_AC_MW, SupplyCurveField.MEAN_CF_AC,
|
659
|
+
SupplyCurveField.MEAN_LCOE)
|
623
660
|
sc_capacity_col : str, optional
|
624
661
|
Name of capacity column in `trans_sc_table`. The values in
|
625
662
|
this column determine the size of transmission lines built.
|
@@ -646,9 +683,9 @@ class SupplyCurve:
|
|
646
683
|
trans_sc_table, sc_capacity_col=scc
|
647
684
|
)
|
648
685
|
|
649
|
-
|
650
|
-
|
651
|
-
|
686
|
+
sort_cols = [SupplyCurveField.SC_GID, SupplyCurveField.TRANS_GID]
|
687
|
+
trans_sc_table = trans_sc_table.sort_values(sort_cols)
|
688
|
+
trans_sc_table = trans_sc_table.reset_index(drop=True)
|
652
689
|
|
653
690
|
cls._check_sc_trans_table(sc_points, trans_sc_table)
|
654
691
|
|
@@ -718,7 +755,7 @@ class SupplyCurve:
|
|
718
755
|
|
719
756
|
@staticmethod
|
720
757
|
def _get_capacity(sc_gid, sc_table, connectable=True,
|
721
|
-
sc_capacity_col=SupplyCurveField.
|
758
|
+
sc_capacity_col=SupplyCurveField.CAPACITY_AC_MW):
|
722
759
|
"""
|
723
760
|
Get capacity of supply curve point
|
724
761
|
|
@@ -767,7 +804,8 @@ class SupplyCurve:
|
|
767
804
|
def _compute_trans_cap_cost(cls, trans_table, trans_costs=None,
|
768
805
|
avail_cap_frac=1, max_workers=None,
|
769
806
|
connectable=True, line_limited=False,
|
770
|
-
sc_capacity_col=
|
807
|
+
sc_capacity_col=(
|
808
|
+
SupplyCurveField.CAPACITY_AC_MW)):
|
771
809
|
"""
|
772
810
|
Compute levelized cost of transmission for all combinations of
|
773
811
|
supply curve points and tranmission features in trans_table
|
@@ -919,7 +957,10 @@ class SupplyCurve:
|
|
919
957
|
Flag to consider friction layer on LCOE when "mean_lcoe_friction"
|
920
958
|
is in the sc points input, by default True
|
921
959
|
"""
|
922
|
-
|
960
|
+
tcc_per_mw_col = SupplyCurveField.TOTAL_TRANS_CAP_COST_PER_MW
|
961
|
+
if tcc_per_mw_col in self._trans_table:
|
962
|
+
cost = self._trans_table[tcc_per_mw_col].values.copy()
|
963
|
+
elif "trans_cap_cost" not in self._trans_table:
|
923
964
|
scc = self._sc_capacity_col
|
924
965
|
cost = self._compute_trans_cap_cost(
|
925
966
|
self._trans_table,
|
@@ -930,39 +971,51 @@ class SupplyCurve:
|
|
930
971
|
max_workers=max_workers,
|
931
972
|
sc_capacity_col=scc,
|
932
973
|
)
|
933
|
-
self._trans_table[
|
974
|
+
self._trans_table[tcc_per_mw_col] = cost # $/MW
|
934
975
|
else:
|
935
976
|
cost = self._trans_table["trans_cap_cost"].values.copy() # $
|
936
|
-
cost /= self._trans_table[
|
937
|
-
self._trans_table[
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
977
|
+
cost /= self._trans_table[SupplyCurveField.CAPACITY_AC_MW] # $/MW
|
978
|
+
self._trans_table[tcc_per_mw_col] = cost
|
979
|
+
|
980
|
+
self._trans_table[tcc_per_mw_col] = (
|
981
|
+
self._trans_table[tcc_per_mw_col].astype("float32")
|
982
|
+
)
|
983
|
+
cost = cost.astype("float32")
|
984
|
+
cf_mean_arr = self._trans_table[SupplyCurveField.MEAN_CF_AC]
|
985
|
+
cf_mean_arr = cf_mean_arr.values.astype("float32")
|
986
|
+
resource_lcoe = self._trans_table[SupplyCurveField.MEAN_LCOE]
|
987
|
+
resource_lcoe = resource_lcoe.values.astype("float32")
|
988
|
+
|
989
|
+
if 'reinforcement_cost_floored_per_mw' in self._trans_table:
|
990
|
+
logger.info("'reinforcement_cost_floored_per_mw' column found in "
|
991
|
+
"transmission table. Adding floored reinforcement "
|
992
|
+
"cost LCOE as sorting option.")
|
993
|
+
fr_cost = (self._trans_table['reinforcement_cost_floored_per_mw']
|
994
|
+
.values.copy())
|
995
|
+
|
996
|
+
lcot_fr = ((cost + fr_cost) * fcr) / (cf_mean_arr * 8760)
|
997
|
+
lcoe_fr = lcot_fr + resource_lcoe
|
998
|
+
self._trans_table['lcot_floored_reinforcement'] = lcot_fr
|
999
|
+
self._trans_table['lcoe_floored_reinforcement'] = lcoe_fr
|
1000
|
+
|
1001
|
+
if SupplyCurveField.REINFORCEMENT_COST_PER_MW in self._trans_table:
|
1002
|
+
logger.info("%s column found in transmission table. Adding "
|
1003
|
+
"reinforcement costs to total LCOE.",
|
1004
|
+
SupplyCurveField.REINFORCEMENT_COST_PER_MW)
|
1005
|
+
lcot_nr = (cost * fcr) / (cf_mean_arr * 8760)
|
1006
|
+
lcoe_nr = lcot_nr + resource_lcoe
|
1007
|
+
self._trans_table['lcot_no_reinforcement'] = lcot_nr
|
1008
|
+
self._trans_table['lcoe_no_reinforcement'] = lcoe_nr
|
1009
|
+
|
1010
|
+
col_name = SupplyCurveField.REINFORCEMENT_COST_PER_MW
|
1011
|
+
r_cost = self._trans_table[col_name].astype("float32")
|
1012
|
+
r_cost = r_cost.values.copy()
|
1013
|
+
self._trans_table[tcc_per_mw_col] += r_cost
|
957
1014
|
cost += r_cost # $/MW
|
958
1015
|
|
959
|
-
cf_mean_arr = self._trans_table[SupplyCurveField.MEAN_CF].values
|
960
1016
|
lcot = (cost * fcr) / (cf_mean_arr * 8760)
|
961
|
-
|
962
|
-
self._trans_table[
|
963
|
-
self._trans_table['total_lcoe'] = (
|
964
|
-
self._trans_table['lcot']
|
965
|
-
+ self._trans_table[SupplyCurveField.MEAN_LCOE])
|
1017
|
+
self._trans_table[SupplyCurveField.LCOT] = lcot
|
1018
|
+
self._trans_table[SupplyCurveField.TOTAL_LCOE] = lcot + resource_lcoe
|
966
1019
|
|
967
1020
|
if consider_friction:
|
968
1021
|
self._calculate_total_lcoe_friction()
|
@@ -973,7 +1026,7 @@ class SupplyCurve:
|
|
973
1026
|
|
974
1027
|
if SupplyCurveField.MEAN_LCOE_FRICTION in self._trans_table:
|
975
1028
|
lcoe_friction = (
|
976
|
-
self._trans_table[
|
1029
|
+
self._trans_table[SupplyCurveField.LCOT]
|
977
1030
|
+ self._trans_table[SupplyCurveField.MEAN_LCOE_FRICTION])
|
978
1031
|
self._trans_table[SupplyCurveField.TOTAL_LCOE_FRICTION] = (
|
979
1032
|
lcoe_friction
|
@@ -1063,22 +1116,22 @@ class SupplyCurve:
|
|
1063
1116
|
|
1064
1117
|
return table
|
1065
1118
|
|
1066
|
-
def _full_sort(
|
1119
|
+
def _full_sort( # noqa: C901
|
1067
1120
|
self,
|
1068
1121
|
trans_table,
|
1069
1122
|
trans_costs=None,
|
1070
1123
|
avail_cap_frac=1,
|
1071
1124
|
comp_wind_dirs=None,
|
1072
1125
|
total_lcoe_fric=None,
|
1073
|
-
sort_on=
|
1126
|
+
sort_on=SupplyCurveField.TOTAL_LCOE,
|
1074
1127
|
columns=(
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1128
|
+
SupplyCurveField.TRANS_GID,
|
1129
|
+
SupplyCurveField.TRANS_CAPACITY,
|
1130
|
+
SupplyCurveField.TRANS_TYPE,
|
1131
|
+
SupplyCurveField.TOTAL_TRANS_CAP_COST_PER_MW,
|
1132
|
+
SupplyCurveField.DIST_SPUR_KM,
|
1133
|
+
SupplyCurveField.LCOT,
|
1134
|
+
SupplyCurveField.TOTAL_LCOE,
|
1082
1135
|
),
|
1083
1136
|
downwind=False,
|
1084
1137
|
):
|
@@ -1134,22 +1187,20 @@ class SupplyCurve:
|
|
1134
1187
|
trans_sc_gids = trans_table[SupplyCurveField.SC_GID].values.astype(int)
|
1135
1188
|
|
1136
1189
|
# syntax is final_key: source_key (source from trans_table)
|
1137
|
-
all_cols =
|
1138
|
-
essentials =
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
"total_lcoe": "total_lcoe",
|
1146
|
-
}
|
1147
|
-
all_cols.update(essentials)
|
1190
|
+
all_cols = list(columns)
|
1191
|
+
essentials = [SupplyCurveField.TRANS_GID,
|
1192
|
+
SupplyCurveField.TRANS_CAPACITY,
|
1193
|
+
SupplyCurveField.TRANS_TYPE,
|
1194
|
+
SupplyCurveField.DIST_SPUR_KM,
|
1195
|
+
SupplyCurveField.TOTAL_TRANS_CAP_COST_PER_MW,
|
1196
|
+
SupplyCurveField.LCOT,
|
1197
|
+
SupplyCurveField.TOTAL_LCOE]
|
1148
1198
|
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1199
|
+
for col in essentials:
|
1200
|
+
if col not in all_cols:
|
1201
|
+
all_cols.append(col)
|
1202
|
+
|
1203
|
+
arrays = {col: trans_table[col].values for col in all_cols}
|
1153
1204
|
|
1154
1205
|
sc_capacities = trans_table[self._sc_capacity_col].values
|
1155
1206
|
|
@@ -1159,7 +1210,7 @@ class SupplyCurve:
|
|
1159
1210
|
sc_gid = trans_sc_gids[i]
|
1160
1211
|
if self._mask[sc_gid]:
|
1161
1212
|
connect = trans_features.connect(
|
1162
|
-
arrays[
|
1213
|
+
arrays[SupplyCurveField.TRANS_GID][i], sc_capacities[i]
|
1163
1214
|
)
|
1164
1215
|
if connect:
|
1165
1216
|
connected += 1
|
@@ -1222,36 +1273,50 @@ class SupplyCurve:
|
|
1222
1273
|
Add the transmission connection feature capacity to the trans table if
|
1223
1274
|
needed
|
1224
1275
|
"""
|
1225
|
-
if
|
1276
|
+
if SupplyCurveField.TRANS_CAPACITY not in self._trans_table:
|
1226
1277
|
kwargs = {"avail_cap_frac": avail_cap_frac}
|
1227
1278
|
fc = TF.feature_capacity(self._trans_table, **kwargs)
|
1228
|
-
self._trans_table = self._trans_table.merge(
|
1279
|
+
self._trans_table = self._trans_table.merge(
|
1280
|
+
fc, on=SupplyCurveField.TRANS_GID)
|
1229
1281
|
|
1230
1282
|
def _adjust_output_columns(self, columns, consider_friction):
|
1231
1283
|
"""Add extra output columns, if needed."""
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1284
|
+
|
1285
|
+
for col in _REQUIRED_COMPUTE_AND_OUTPUT_COLS:
|
1286
|
+
if col not in columns:
|
1287
|
+
columns.append(col)
|
1288
|
+
|
1289
|
+
for col in _REQUIRED_OUTPUT_COLS:
|
1290
|
+
if col not in self._trans_table:
|
1291
|
+
self._trans_table[col] = np.nan
|
1292
|
+
if col not in columns:
|
1293
|
+
columns.append(col)
|
1294
|
+
|
1295
|
+
missing_cols = [col for col in columns if col not in self._trans_table]
|
1296
|
+
if missing_cols:
|
1297
|
+
msg = (f"The following requested columns are not found in "
|
1298
|
+
f"transmission table: {missing_cols}.\nSkipping...")
|
1299
|
+
logger.warning(msg)
|
1300
|
+
warn(msg)
|
1301
|
+
|
1302
|
+
columns = [col for col in columns if col in self._trans_table]
|
1303
|
+
|
1304
|
+
fric_col = SupplyCurveField.TOTAL_LCOE_FRICTION
|
1305
|
+
if consider_friction and fric_col in self._trans_table:
|
1306
|
+
columns.append(fric_col)
|
1307
|
+
|
1308
|
+
return sorted(columns, key=_column_sort_key)
|
1249
1309
|
|
1250
1310
|
def _determine_sort_on(self, sort_on):
|
1251
1311
|
"""Determine the `sort_on` column from user input and trans table"""
|
1252
|
-
|
1312
|
+
r_cost_col = SupplyCurveField.REINFORCEMENT_COST_PER_MW
|
1313
|
+
found_reinforcement_costs = (
|
1314
|
+
r_cost_col in self._trans_table
|
1315
|
+
and not self._trans_table[r_cost_col].isna().all()
|
1316
|
+
)
|
1317
|
+
if found_reinforcement_costs:
|
1253
1318
|
sort_on = sort_on or "lcoe_no_reinforcement"
|
1254
|
-
return sort_on or
|
1319
|
+
return sort_on or SupplyCurveField.TOTAL_LCOE
|
1255
1320
|
|
1256
1321
|
def full_sort(
|
1257
1322
|
self,
|
@@ -1264,13 +1329,13 @@ class SupplyCurve:
|
|
1264
1329
|
consider_friction=True,
|
1265
1330
|
sort_on=None,
|
1266
1331
|
columns=(
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1332
|
+
SupplyCurveField.TRANS_GID,
|
1333
|
+
SupplyCurveField.TRANS_CAPACITY,
|
1334
|
+
SupplyCurveField.TRANS_TYPE,
|
1335
|
+
SupplyCurveField.TOTAL_TRANS_CAP_COST_PER_MW,
|
1336
|
+
SupplyCurveField.DIST_SPUR_KM,
|
1337
|
+
SupplyCurveField.LCOT,
|
1338
|
+
SupplyCurveField.TOTAL_LCOE,
|
1274
1339
|
),
|
1275
1340
|
wind_dirs=None,
|
1276
1341
|
n_dirs=2,
|
@@ -1351,8 +1416,10 @@ class SupplyCurve:
|
|
1351
1416
|
sort_on = self._determine_sort_on(sort_on)
|
1352
1417
|
|
1353
1418
|
trans_table = self._trans_table.copy()
|
1354
|
-
pos = trans_table[
|
1355
|
-
trans_table = trans_table.loc[~pos].sort_values(
|
1419
|
+
pos = trans_table[SupplyCurveField.LCOT].isnull()
|
1420
|
+
trans_table = trans_table.loc[~pos].sort_values(
|
1421
|
+
[sort_on, SupplyCurveField.TRANS_GID]
|
1422
|
+
)
|
1356
1423
|
|
1357
1424
|
total_lcoe_fric = None
|
1358
1425
|
col_in_table = SupplyCurveField.MEAN_LCOE_FRICTION in trans_table
|
@@ -1400,14 +1467,7 @@ class SupplyCurve:
|
|
1400
1467
|
max_workers=None,
|
1401
1468
|
consider_friction=True,
|
1402
1469
|
sort_on=None,
|
1403
|
-
columns=
|
1404
|
-
"trans_gid",
|
1405
|
-
"trans_type",
|
1406
|
-
"lcot",
|
1407
|
-
"total_lcoe",
|
1408
|
-
"dist_km",
|
1409
|
-
"trans_cap_cost_per_mw",
|
1410
|
-
),
|
1470
|
+
columns=DEFAULT_COLUMNS,
|
1411
1471
|
wind_dirs=None,
|
1412
1472
|
n_dirs=2,
|
1413
1473
|
downwind=False,
|
@@ -1446,9 +1506,8 @@ class SupplyCurve:
|
|
1446
1506
|
will be built first, by default `None`, which will use
|
1447
1507
|
total LCOE without any reinforcement costs as the sort value.
|
1448
1508
|
columns : list | tuple, optional
|
1449
|
-
Columns to preserve in output connections dataframe
|
1450
|
-
|
1451
|
-
'trans_cap_cost_per_mw', 'dist_km', 'lcot', 'total_lcoe')
|
1509
|
+
Columns to preserve in output connections dataframe.
|
1510
|
+
By default, :obj:`DEFAULT_COLUMNS`.
|
1452
1511
|
wind_dirs : pandas.DataFrame | str, optional
|
1453
1512
|
path to .csv or reVX.wind_dirs.wind_dirs.WindDirs output with
|
1454
1513
|
the neighboring supply curve point gids and power-rose value at
|
@@ -1476,19 +1535,16 @@ class SupplyCurve:
|
|
1476
1535
|
max_workers=max_workers,
|
1477
1536
|
consider_friction=consider_friction,
|
1478
1537
|
)
|
1479
|
-
|
1538
|
+
sort_on = self._determine_sort_on(sort_on)
|
1480
1539
|
|
1481
1540
|
if isinstance(columns, tuple):
|
1482
1541
|
columns = list(columns)
|
1483
|
-
|
1484
1542
|
columns = self._adjust_output_columns(columns, consider_friction)
|
1485
|
-
sort_on = self._determine_sort_on(sort_on)
|
1486
1543
|
|
1487
|
-
|
1544
|
+
trans_table = self._trans_table.copy()
|
1545
|
+
connections = trans_table.sort_values(
|
1546
|
+
[sort_on, SupplyCurveField.TRANS_GID])
|
1488
1547
|
connections = connections.groupby(SupplyCurveField.SC_GID).first()
|
1489
|
-
rename = {'trans_gid': 'trans_gid',
|
1490
|
-
'category': 'trans_type'}
|
1491
|
-
connections = connections.rename(columns=rename)
|
1492
1548
|
connections = connections[columns].reset_index()
|
1493
1549
|
|
1494
1550
|
supply_curve = self._sc_points.merge(connections,
|
@@ -1517,14 +1573,7 @@ class SupplyCurve:
|
|
1517
1573
|
transmission_costs=None,
|
1518
1574
|
consider_friction=True,
|
1519
1575
|
sort_on=None,
|
1520
|
-
columns=
|
1521
|
-
"trans_gid",
|
1522
|
-
"trans_type",
|
1523
|
-
"trans_cap_cost_per_mw",
|
1524
|
-
"dist_km",
|
1525
|
-
"lcot",
|
1526
|
-
"total_lcoe",
|
1527
|
-
),
|
1576
|
+
columns=DEFAULT_COLUMNS,
|
1528
1577
|
max_workers=None,
|
1529
1578
|
competition=None,
|
1530
1579
|
):
|
@@ -1592,8 +1641,7 @@ class SupplyCurve:
|
|
1592
1641
|
By default ``None``.
|
1593
1642
|
columns : list | tuple, optional
|
1594
1643
|
Columns to preserve in output supply curve dataframe.
|
1595
|
-
By default,
|
1596
|
-
'trans_cap_cost_per_mw', 'dist_km', 'lcot', 'total_lcoe')``.
|
1644
|
+
By default, :obj:`DEFAULT_COLUMNS`.
|
1597
1645
|
max_workers : int, optional
|
1598
1646
|
Number of workers to use to compute LCOT. If > 1,
|
1599
1647
|
computation is run in parallel. If ``None``, computation
|
@@ -1659,3 +1707,14 @@ def _format_sc_out_fpath(out_fpath):
|
|
1659
1707
|
project_dir, out_fn = os.path.split(out_fpath)
|
1660
1708
|
out_fn = out_fn.replace("supply_curve", "supply-curve")
|
1661
1709
|
return os.path.join(project_dir, out_fn)
|
1710
|
+
|
1711
|
+
|
1712
|
+
def _column_sort_key(col):
|
1713
|
+
"""Determine the sort order of the input column. """
|
1714
|
+
col_value = _REQUIRED_COMPUTE_AND_OUTPUT_COLS.get(col)
|
1715
|
+
if col_value is None:
|
1716
|
+
col_value = _REQUIRED_OUTPUT_COLS.get(col)
|
1717
|
+
if col_value is None:
|
1718
|
+
col_value = 1e6
|
1719
|
+
|
1720
|
+
return col_value, str(col)
|