NREL-reV 0.8.9__py3-none-any.whl → 0.9.0__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.0.dist-info}/METADATA +2 -1
- {NREL_reV-0.8.9.dist-info → NREL_reV-0.9.0.dist-info}/RECORD +34 -34
- reV/SAM/SAM.py +35 -0
- reV/SAM/generation.py +3 -3
- reV/bespoke/bespoke.py +302 -243
- 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 +2 -2
- reV/econ/economies_of_scale.py +58 -35
- reV/generation/base.py +22 -2
- reV/generation/generation.py +31 -13
- 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 +250 -132
- reV/supply_curve/sc_aggregation.py +13 -45
- reV/supply_curve/supply_curve.py +200 -140
- reV/utilities/__init__.py +114 -39
- reV/version.py +1 -1
- {NREL_reV-0.8.9.dist-info → NREL_reV-0.9.0.dist-info}/LICENSE +0 -0
- {NREL_reV-0.8.9.dist-info → NREL_reV-0.9.0.dist-info}/WHEEL +0 -0
- {NREL_reV-0.8.9.dist-info → NREL_reV-0.9.0.dist-info}/entry_points.txt +0 -0
- {NREL_reV-0.8.9.dist-info → NREL_reV-0.9.0.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,6 +1116,7 @@ class SupplyCurve:
|
|
1063
1116
|
|
1064
1117
|
return table
|
1065
1118
|
|
1119
|
+
# pylint: disable=C901
|
1066
1120
|
def _full_sort(
|
1067
1121
|
self,
|
1068
1122
|
trans_table,
|
@@ -1070,15 +1124,15 @@ class SupplyCurve:
|
|
1070
1124
|
avail_cap_frac=1,
|
1071
1125
|
comp_wind_dirs=None,
|
1072
1126
|
total_lcoe_fric=None,
|
1073
|
-
sort_on=
|
1127
|
+
sort_on=SupplyCurveField.TOTAL_LCOE,
|
1074
1128
|
columns=(
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1129
|
+
SupplyCurveField.TRANS_GID,
|
1130
|
+
SupplyCurveField.TRANS_CAPACITY,
|
1131
|
+
SupplyCurveField.TRANS_TYPE,
|
1132
|
+
SupplyCurveField.TOTAL_TRANS_CAP_COST_PER_MW,
|
1133
|
+
SupplyCurveField.DIST_SPUR_KM,
|
1134
|
+
SupplyCurveField.LCOT,
|
1135
|
+
SupplyCurveField.TOTAL_LCOE,
|
1082
1136
|
),
|
1083
1137
|
downwind=False,
|
1084
1138
|
):
|
@@ -1134,22 +1188,20 @@ class SupplyCurve:
|
|
1134
1188
|
trans_sc_gids = trans_table[SupplyCurveField.SC_GID].values.astype(int)
|
1135
1189
|
|
1136
1190
|
# 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)
|
1191
|
+
all_cols = list(columns)
|
1192
|
+
essentials = [SupplyCurveField.TRANS_GID,
|
1193
|
+
SupplyCurveField.TRANS_CAPACITY,
|
1194
|
+
SupplyCurveField.TRANS_TYPE,
|
1195
|
+
SupplyCurveField.DIST_SPUR_KM,
|
1196
|
+
SupplyCurveField.TOTAL_TRANS_CAP_COST_PER_MW,
|
1197
|
+
SupplyCurveField.LCOT,
|
1198
|
+
SupplyCurveField.TOTAL_LCOE]
|
1148
1199
|
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1200
|
+
for col in essentials:
|
1201
|
+
if col not in all_cols:
|
1202
|
+
all_cols.append(col)
|
1203
|
+
|
1204
|
+
arrays = {col: trans_table[col].values for col in all_cols}
|
1153
1205
|
|
1154
1206
|
sc_capacities = trans_table[self._sc_capacity_col].values
|
1155
1207
|
|
@@ -1159,7 +1211,7 @@ class SupplyCurve:
|
|
1159
1211
|
sc_gid = trans_sc_gids[i]
|
1160
1212
|
if self._mask[sc_gid]:
|
1161
1213
|
connect = trans_features.connect(
|
1162
|
-
arrays[
|
1214
|
+
arrays[SupplyCurveField.TRANS_GID][i], sc_capacities[i]
|
1163
1215
|
)
|
1164
1216
|
if connect:
|
1165
1217
|
connected += 1
|
@@ -1222,36 +1274,50 @@ class SupplyCurve:
|
|
1222
1274
|
Add the transmission connection feature capacity to the trans table if
|
1223
1275
|
needed
|
1224
1276
|
"""
|
1225
|
-
if
|
1277
|
+
if SupplyCurveField.TRANS_CAPACITY not in self._trans_table:
|
1226
1278
|
kwargs = {"avail_cap_frac": avail_cap_frac}
|
1227
1279
|
fc = TF.feature_capacity(self._trans_table, **kwargs)
|
1228
|
-
self._trans_table = self._trans_table.merge(
|
1280
|
+
self._trans_table = self._trans_table.merge(
|
1281
|
+
fc, on=SupplyCurveField.TRANS_GID)
|
1229
1282
|
|
1230
1283
|
def _adjust_output_columns(self, columns, consider_friction):
|
1231
1284
|
"""Add extra output columns, if needed."""
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1285
|
+
|
1286
|
+
for col in _REQUIRED_COMPUTE_AND_OUTPUT_COLS:
|
1287
|
+
if col not in columns:
|
1288
|
+
columns.append(col)
|
1289
|
+
|
1290
|
+
for col in _REQUIRED_OUTPUT_COLS:
|
1291
|
+
if col not in self._trans_table:
|
1292
|
+
self._trans_table[col] = None
|
1293
|
+
if col not in columns:
|
1294
|
+
columns.append(col)
|
1295
|
+
|
1296
|
+
missing_cols = [col for col in columns if col not in self._trans_table]
|
1297
|
+
if missing_cols:
|
1298
|
+
msg = (f"The following requested columns are not found in "
|
1299
|
+
f"transmission table: {missing_cols}.\nSkipping...")
|
1300
|
+
logger.warning(msg)
|
1301
|
+
warn(msg)
|
1302
|
+
|
1303
|
+
columns = [col for col in columns if col in self._trans_table]
|
1304
|
+
|
1305
|
+
fric_col = SupplyCurveField.TOTAL_LCOE_FRICTION
|
1306
|
+
if consider_friction and fric_col in self._trans_table:
|
1307
|
+
columns.append(fric_col)
|
1308
|
+
|
1309
|
+
return sorted(columns, key=_column_sort_key)
|
1249
1310
|
|
1250
1311
|
def _determine_sort_on(self, sort_on):
|
1251
1312
|
"""Determine the `sort_on` column from user input and trans table"""
|
1252
|
-
|
1313
|
+
r_cost_col = SupplyCurveField.REINFORCEMENT_COST_PER_MW
|
1314
|
+
found_reinforcement_costs = (
|
1315
|
+
r_cost_col in self._trans_table
|
1316
|
+
and not self._trans_table[r_cost_col].isna().all()
|
1317
|
+
)
|
1318
|
+
if found_reinforcement_costs:
|
1253
1319
|
sort_on = sort_on or "lcoe_no_reinforcement"
|
1254
|
-
return sort_on or
|
1320
|
+
return sort_on or SupplyCurveField.TOTAL_LCOE
|
1255
1321
|
|
1256
1322
|
def full_sort(
|
1257
1323
|
self,
|
@@ -1264,13 +1330,13 @@ class SupplyCurve:
|
|
1264
1330
|
consider_friction=True,
|
1265
1331
|
sort_on=None,
|
1266
1332
|
columns=(
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1333
|
+
SupplyCurveField.TRANS_GID,
|
1334
|
+
SupplyCurveField.TRANS_CAPACITY,
|
1335
|
+
SupplyCurveField.TRANS_TYPE,
|
1336
|
+
SupplyCurveField.TOTAL_TRANS_CAP_COST_PER_MW,
|
1337
|
+
SupplyCurveField.DIST_SPUR_KM,
|
1338
|
+
SupplyCurveField.LCOT,
|
1339
|
+
SupplyCurveField.TOTAL_LCOE,
|
1274
1340
|
),
|
1275
1341
|
wind_dirs=None,
|
1276
1342
|
n_dirs=2,
|
@@ -1351,8 +1417,10 @@ class SupplyCurve:
|
|
1351
1417
|
sort_on = self._determine_sort_on(sort_on)
|
1352
1418
|
|
1353
1419
|
trans_table = self._trans_table.copy()
|
1354
|
-
pos = trans_table[
|
1355
|
-
trans_table = trans_table.loc[~pos].sort_values(
|
1420
|
+
pos = trans_table[SupplyCurveField.LCOT].isnull()
|
1421
|
+
trans_table = trans_table.loc[~pos].sort_values(
|
1422
|
+
[sort_on, SupplyCurveField.TRANS_GID]
|
1423
|
+
)
|
1356
1424
|
|
1357
1425
|
total_lcoe_fric = None
|
1358
1426
|
col_in_table = SupplyCurveField.MEAN_LCOE_FRICTION in trans_table
|
@@ -1400,14 +1468,7 @@ class SupplyCurve:
|
|
1400
1468
|
max_workers=None,
|
1401
1469
|
consider_friction=True,
|
1402
1470
|
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
|
-
),
|
1471
|
+
columns=DEFAULT_COLUMNS,
|
1411
1472
|
wind_dirs=None,
|
1412
1473
|
n_dirs=2,
|
1413
1474
|
downwind=False,
|
@@ -1446,9 +1507,8 @@ class SupplyCurve:
|
|
1446
1507
|
will be built first, by default `None`, which will use
|
1447
1508
|
total LCOE without any reinforcement costs as the sort value.
|
1448
1509
|
columns : list | tuple, optional
|
1449
|
-
Columns to preserve in output connections dataframe
|
1450
|
-
|
1451
|
-
'trans_cap_cost_per_mw', 'dist_km', 'lcot', 'total_lcoe')
|
1510
|
+
Columns to preserve in output connections dataframe.
|
1511
|
+
By default, :obj:`DEFAULT_COLUMNS`.
|
1452
1512
|
wind_dirs : pandas.DataFrame | str, optional
|
1453
1513
|
path to .csv or reVX.wind_dirs.wind_dirs.WindDirs output with
|
1454
1514
|
the neighboring supply curve point gids and power-rose value at
|
@@ -1476,19 +1536,16 @@ class SupplyCurve:
|
|
1476
1536
|
max_workers=max_workers,
|
1477
1537
|
consider_friction=consider_friction,
|
1478
1538
|
)
|
1479
|
-
|
1539
|
+
sort_on = self._determine_sort_on(sort_on)
|
1480
1540
|
|
1481
1541
|
if isinstance(columns, tuple):
|
1482
1542
|
columns = list(columns)
|
1483
|
-
|
1484
1543
|
columns = self._adjust_output_columns(columns, consider_friction)
|
1485
|
-
sort_on = self._determine_sort_on(sort_on)
|
1486
1544
|
|
1487
|
-
|
1545
|
+
trans_table = self._trans_table.copy()
|
1546
|
+
connections = trans_table.sort_values(
|
1547
|
+
[sort_on, SupplyCurveField.TRANS_GID])
|
1488
1548
|
connections = connections.groupby(SupplyCurveField.SC_GID).first()
|
1489
|
-
rename = {'trans_gid': 'trans_gid',
|
1490
|
-
'category': 'trans_type'}
|
1491
|
-
connections = connections.rename(columns=rename)
|
1492
1549
|
connections = connections[columns].reset_index()
|
1493
1550
|
|
1494
1551
|
supply_curve = self._sc_points.merge(connections,
|
@@ -1517,14 +1574,7 @@ class SupplyCurve:
|
|
1517
1574
|
transmission_costs=None,
|
1518
1575
|
consider_friction=True,
|
1519
1576
|
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
|
-
),
|
1577
|
+
columns=DEFAULT_COLUMNS,
|
1528
1578
|
max_workers=None,
|
1529
1579
|
competition=None,
|
1530
1580
|
):
|
@@ -1592,8 +1642,7 @@ class SupplyCurve:
|
|
1592
1642
|
By default ``None``.
|
1593
1643
|
columns : list | tuple, optional
|
1594
1644
|
Columns to preserve in output supply curve dataframe.
|
1595
|
-
By default,
|
1596
|
-
'trans_cap_cost_per_mw', 'dist_km', 'lcot', 'total_lcoe')``.
|
1645
|
+
By default, :obj:`DEFAULT_COLUMNS`.
|
1597
1646
|
max_workers : int, optional
|
1598
1647
|
Number of workers to use to compute LCOT. If > 1,
|
1599
1648
|
computation is run in parallel. If ``None``, computation
|
@@ -1659,3 +1708,14 @@ def _format_sc_out_fpath(out_fpath):
|
|
1659
1708
|
project_dir, out_fn = os.path.split(out_fpath)
|
1660
1709
|
out_fn = out_fn.replace("supply_curve", "supply-curve")
|
1661
1710
|
return os.path.join(project_dir, out_fn)
|
1711
|
+
|
1712
|
+
|
1713
|
+
def _column_sort_key(col):
|
1714
|
+
"""Determine the sort order of the input column. """
|
1715
|
+
col_value = _REQUIRED_COMPUTE_AND_OUTPUT_COLS.get(col)
|
1716
|
+
if col_value is None:
|
1717
|
+
col_value = _REQUIRED_OUTPUT_COLS.get(col)
|
1718
|
+
if col_value is None:
|
1719
|
+
col_value = 1e6
|
1720
|
+
|
1721
|
+
return col_value, str(col)
|