NREL-reV 0.14.4__py3-none-any.whl → 0.14.5__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.14.4.dist-info → nrel_rev-0.14.5.dist-info}/METADATA +1 -1
- {nrel_rev-0.14.4.dist-info → nrel_rev-0.14.5.dist-info}/RECORD +18 -18
- reV/bespoke/bespoke.py +47 -6
- reV/bespoke/place_turbines.py +38 -9
- reV/cli.py +3 -1
- reV/config/project_points.py +4 -0
- reV/supply_curve/cli_sc_aggregation.py +39 -2
- reV/supply_curve/exclusions.py +55 -26
- reV/supply_curve/points.py +8 -3
- reV/supply_curve/sc_aggregation.py +64 -15
- reV/supply_curve/tech_mapping.py +8 -7
- reV/utilities/__init__.py +401 -13
- reV/utilities/cli_functions.py +43 -1
- reV/version.py +1 -1
- {nrel_rev-0.14.4.dist-info → nrel_rev-0.14.5.dist-info}/WHEEL +0 -0
- {nrel_rev-0.14.4.dist-info → nrel_rev-0.14.5.dist-info}/entry_points.txt +0 -0
- {nrel_rev-0.14.4.dist-info → nrel_rev-0.14.5.dist-info}/licenses/LICENSE +0 -0
- {nrel_rev-0.14.4.dist-info → nrel_rev-0.14.5.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: NREL-reV
|
3
|
-
Version: 0.14.
|
3
|
+
Version: 0.14.5
|
4
4
|
Summary: National Renewable Energy Laboratory's (NREL's) Renewable Energy Potential(V) Model: reV
|
5
5
|
Author-email: Galen Maclaurin <galen.maclaurin@nrel.gov>
|
6
6
|
Maintainer-email: Grant Buster <gbuster@nrel.gov>, Paul Pinchuk <ppinchuk@nrel.gov>
|
@@ -1,7 +1,7 @@
|
|
1
|
-
nrel_rev-0.14.
|
1
|
+
nrel_rev-0.14.5.dist-info/licenses/LICENSE,sha256=hDwoTANtan2ZpufBlXm5C3W_PJ-mCqItvlcobgjxL7k,1526
|
2
2
|
reV/__init__.py,sha256=tXTpWu_qVo3uotfSw_TJ-gNbidGaIPPfUTwBlpCMJ-g,856
|
3
|
-
reV/cli.py,sha256=
|
4
|
-
reV/version.py,sha256=
|
3
|
+
reV/cli.py,sha256=tkJzwkGNz0h7PfKpjUAv6aM-Jpn_z8jUbXyjb5GCFBM,1792
|
4
|
+
reV/version.py,sha256=DNYUQmgQgGdiJDrMQAgH6IJ-51uB5WIunyqwp0YBFMM,51
|
5
5
|
reV/SAM/SAM.py,sha256=3NK9rRaJzqH6wz7CU_5XguKCRhmoilOpDdFFsFgOaxQ,33163
|
6
6
|
reV/SAM/__init__.py,sha256=LJqoncyKDY5ZP5WA4kboh561bce11F9Ge645Izah0EY,240
|
7
7
|
reV/SAM/defaults.py,sha256=JQMJomX7wsbMzxKjx_IMnA_9QFsV2yWCyl_JToDVSJo,6703
|
@@ -16,11 +16,11 @@ reV/SAM/defaults/WY Southern-Flat Lands.srw,sha256=oSlFI7nnycW7SMnTXEXBXYkoh8rhV
|
|
16
16
|
reV/SAM/defaults/geothermal.json,sha256=gnlxOBxxkbDDacIw6B9yP9iRkfmwj0WTWs8Zb71ytCo,7119
|
17
17
|
reV/SAM/defaults/i_pvwattsv5.json,sha256=sF8rSe1bcqsKLXchvRxlp25dXOVe_q7rPJTf_vWR20Y,312
|
18
18
|
reV/bespoke/__init__.py,sha256=vpXbyBUrUsTgK8UP_LafMjLiDg2CRG9WZLHPsOJoxek,109
|
19
|
-
reV/bespoke/bespoke.py,sha256=
|
19
|
+
reV/bespoke/bespoke.py,sha256=fZvNpNeKf8LYQbDDhX4I8iOgm_3MGdCf0aq-wzQcflo,116916
|
20
20
|
reV/bespoke/cli_bespoke.py,sha256=b6Xu0GKpXqPX3qVJ6-z0FrO97uCsH_1dVOa4r6IvesQ,2911
|
21
21
|
reV/bespoke/gradient_free.py,sha256=URWV1yiO2jyWk3_GOpfpLV_wlgJhXXGmTUwCB3WTV0Y,12015
|
22
22
|
reV/bespoke/pack_turbs.py,sha256=Dcd9F8obF8LPztzeycB5kxa5hXKSCiz3jC1WeAFUx28,3508
|
23
|
-
reV/bespoke/place_turbines.py,sha256=
|
23
|
+
reV/bespoke/place_turbines.py,sha256=pc5JofO9LAwwsQDGbxauGOoMeO6TXVr9uXABgVAADw0,27121
|
24
24
|
reV/config/__init__.py,sha256=oqFNU4JESU_fPxFmPyQNFAXLDAdzmTlPuabXTe3Rf2Y,92
|
25
25
|
reV/config/base_analysis_config.py,sha256=NvA3g5zQz8mIrV8ZSENLq0XBZGXa6RTGkwpZ76TVZj8,5615
|
26
26
|
reV/config/base_config.py,sha256=a748VQ3CRs9RVi5sSEPcaWOyH6R3t5tssaFqZntHyaE,10075
|
@@ -28,7 +28,7 @@ reV/config/cli_project_points.py,sha256=6edOlLNOG-ZEbcpNS2MPfu-DXjcOTEh_MEvKOvQS
|
|
28
28
|
reV/config/curtailment.py,sha256=1bH7xzxOmD4PwLKcXFNotMAa9iCfGBUm2DKTOXViCJg,5548
|
29
29
|
reV/config/execution.py,sha256=hyf8W7XYUXE6tXBXs-4En7h_aDTYu8FzbslgBsKLJkQ,5046
|
30
30
|
reV/config/output_request.py,sha256=cww5MxOUnXE_HwwLYitsFSwwUT8mWkctSElq9-zUl_8,4725
|
31
|
-
reV/config/project_points.py,sha256=
|
31
|
+
reV/config/project_points.py,sha256=BdLwWrESsDZDQJ59rki-1Z0xXF4QUXZkbB8nsPrtOEA,42147
|
32
32
|
reV/config/sam_config.py,sha256=xvvx2FTuliq0Sk-BjRE3I9zdDmIdwHVBnWtXCcsoc40,7998
|
33
33
|
reV/econ/__init__.py,sha256=UId1LNaAP9lErCEXVce6JZf0qVRUvwNFOPrajdRevGo,130
|
34
34
|
reV/econ/cli_econ.py,sha256=2KNy3JQD0EKjStaoD2r6nv3ELFw88h2E_up-UKj_sfE,4286
|
@@ -75,25 +75,25 @@ reV/rep_profiles/cli_rep_profiles.py,sha256=i3fRolT7HTzTQePXpNLDyxwgpRa7FEfHO1mO
|
|
75
75
|
reV/rep_profiles/rep_profiles.py,sha256=dJ2jcklbelYYNS3IUXIYHg8E0poXLOWrwdqeBBnqYIE,48271
|
76
76
|
reV/supply_curve/__init__.py,sha256=dbf0cO0qmt1VhV8dnDddztrcpnwua9T2HBhM6tLKaP8,274
|
77
77
|
reV/supply_curve/aggregation.py,sha256=DvIUmj0PbZ8NxgOSmpXvzn9wJnOiQO2Xm2w_WCRVK-U,40908
|
78
|
-
reV/supply_curve/cli_sc_aggregation.py,sha256=
|
78
|
+
reV/supply_curve/cli_sc_aggregation.py,sha256=fJwhOixN3VlzImHStWfcB8Bum_POY-RRgkhWi8Slgao,4633
|
79
79
|
reV/supply_curve/cli_supply_curve.py,sha256=e-XrHQIe4OqWTL6u-TUAyHrw7Alk7vkXQ2HoLbE3zTM,2163
|
80
80
|
reV/supply_curve/cli_tech_mapping.py,sha256=Dirn4JOmu_3BIP7WgcRLAepsreKqmDhChHLPEUqcDAo,1735
|
81
81
|
reV/supply_curve/competitive_wind_farms.py,sha256=eOjM72-4oWtsqxB7Wh2gnB2zVAt4LY3iPE_DqWdXbQ4,15795
|
82
|
-
reV/supply_curve/exclusions.py,sha256=
|
82
|
+
reV/supply_curve/exclusions.py,sha256=dzwgcXyDg5_MrcxKohW-j5YX7tWW6JlQimkUwLlhz2k,44056
|
83
83
|
reV/supply_curve/extent.py,sha256=168Vcr2XQNkoLLn3xCLfvE3udi39wbtQ7GAbvw1yys0,17917
|
84
|
-
reV/supply_curve/points.py,sha256=
|
85
|
-
reV/supply_curve/sc_aggregation.py,sha256=
|
84
|
+
reV/supply_curve/points.py,sha256=dl518Bv_i2jOM1N-BAVZcK3chmv23BphvHO47zcbTxU,94142
|
85
|
+
reV/supply_curve/sc_aggregation.py,sha256=VaFKS_EIPqlxlpFlxUiZiI8q_mJWdamh9fSuF6LXm28,70843
|
86
86
|
reV/supply_curve/supply_curve.py,sha256=9zhAA_9XSxE18j1Z9FuC71Wr3I0VuakfR5mt1_gHYuU,69931
|
87
|
-
reV/supply_curve/tech_mapping.py,sha256=
|
88
|
-
reV/utilities/__init__.py,sha256=
|
87
|
+
reV/supply_curve/tech_mapping.py,sha256=xP8j2KMY58kZ5MSH13nIs8IoDtFhNCzGY4hQeKxW33w,17878
|
88
|
+
reV/utilities/__init__.py,sha256=b99Su_feREbuKkP71W_NrlO7GH9vQRsH7TrP7f4-Eg0,22703
|
89
89
|
reV/utilities/_clean_readme.py,sha256=IFI9wGPX5nnLTNVLJzH8IOHq9unQlAlHRu4Namib0LA,709
|
90
|
-
reV/utilities/cli_functions.py,sha256=
|
90
|
+
reV/utilities/cli_functions.py,sha256=3q7d0MHNpb6vnzWSxQMPNXH176nGp_CHMgHCLknmv1k,5154
|
91
91
|
reV/utilities/curtailment.py,sha256=As902-2aLGnCiVEutYfAFIOwuV--_rCQhxGNOY9RB-4,5241
|
92
92
|
reV/utilities/exceptions.py,sha256=f7sRGsbFLpmL6Caq_H1cD4GfVhnLMyvYUsLPA1UVDDE,3974
|
93
93
|
reV/utilities/pytest_utils.py,sha256=spCw9yQ8KEYOkQZpCi9IEmaWIvIqHqbUPDXXNQJJ68U,3241
|
94
94
|
reV/utilities/slots.py,sha256=xsw-JuUVZ0YeoCNuwP_HxGNxFMA4xRs1tuImXHIJqaU,2618
|
95
|
-
nrel_rev-0.14.
|
96
|
-
nrel_rev-0.14.
|
97
|
-
nrel_rev-0.14.
|
98
|
-
nrel_rev-0.14.
|
99
|
-
nrel_rev-0.14.
|
95
|
+
nrel_rev-0.14.5.dist-info/METADATA,sha256=UCWoXqsoas-MeRXDg1ytuva-qI6B3XDpY1MhGb3ew_g,10718
|
96
|
+
nrel_rev-0.14.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
97
|
+
nrel_rev-0.14.5.dist-info/entry_points.txt,sha256=4IfJtZm2iMJwrbC8J0Or7VjZWnFpvCaHYVpvSWfIwDA,616
|
98
|
+
nrel_rev-0.14.5.dist-info/top_level.txt,sha256=S6YF2ZYgXUB6n28SY0K2H8YB9tMJdXQ9CyQbo6VC89M,4
|
99
|
+
nrel_rev-0.14.5.dist-info/RECORD,,
|
reV/bespoke/bespoke.py
CHANGED
@@ -310,7 +310,24 @@ class BespokeSinglePlant:
|
|
310
310
|
cost ($) as evaluated by
|
311
311
|
`balance_of_system_cost_function`
|
312
312
|
- ``self.wind_plant``: the SAM wind plant object,
|
313
|
-
through which all SAM variables can be accessed
|
313
|
+
through which all SAM variables can be accessed.
|
314
|
+
|
315
|
+
.. IMPORTANT::
|
316
|
+
When using the `self.wind_plant` variable,
|
317
|
+
DO NOT include quotes around variable names (keys).
|
318
|
+
|
319
|
+
- ❌ Wrong: ``self.wind_plant["annual_energy"]``
|
320
|
+
- ✅ Correct: ``self.wind_plant[annual_energy]``
|
321
|
+
|
322
|
+
.. IMPORTANT::
|
323
|
+
It's possible for SAM wind plant variables to be
|
324
|
+
``None``, especially if something went wrong while
|
325
|
+
optimizing the wind plant layout. In this case,
|
326
|
+
your objective function may fail to evaluate and
|
327
|
+
terminate the program entirely. To avoid this, add
|
328
|
+
a default value for the variable in your objective
|
329
|
+
function, like so:
|
330
|
+
``(self.wind_plant[annual_energy] or 0)``
|
314
331
|
|
315
332
|
capital_cost_function : str
|
316
333
|
The plant capital cost function as a string, must return the total
|
@@ -1265,6 +1282,14 @@ class BespokeSinglePlant:
|
|
1265
1282
|
for k, v in plant.outputs.items():
|
1266
1283
|
self._outputs[k + "-{}".format(year)] = v
|
1267
1284
|
|
1285
|
+
self._compute_output_means()
|
1286
|
+
self._add_extra_meta_columns()
|
1287
|
+
logger.debug("Timeseries analysis complete!")
|
1288
|
+
|
1289
|
+
return self.outputs
|
1290
|
+
|
1291
|
+
def _compute_output_means(self):
|
1292
|
+
"""Compute time series means and store them in the outputs dict"""
|
1268
1293
|
means = {}
|
1269
1294
|
for k1, v1 in self._outputs.items():
|
1270
1295
|
if isinstance(v1, Number) and parse_year(k1, option="boolean"):
|
@@ -1277,6 +1302,9 @@ class BespokeSinglePlant:
|
|
1277
1302
|
|
1278
1303
|
self._outputs.update(means)
|
1279
1304
|
|
1305
|
+
def _add_extra_meta_columns(self):
|
1306
|
+
"""Copy over some non-temporal datasets to meta"""
|
1307
|
+
|
1280
1308
|
self._meta[SupplyCurveField.MEAN_RES] = self.res_df["windspeed"].mean()
|
1281
1309
|
self._meta[SupplyCurveField.MEAN_CF_DC] = np.nan
|
1282
1310
|
self._meta[SupplyCurveField.MEAN_CF_AC] = np.nan
|
@@ -1302,10 +1330,6 @@ class BespokeSinglePlant:
|
|
1302
1330
|
self.outputs["annual_wake_loss_internal_percent-means"]
|
1303
1331
|
)
|
1304
1332
|
|
1305
|
-
logger.debug("Timeseries analysis complete!")
|
1306
|
-
|
1307
|
-
return self.outputs
|
1308
|
-
|
1309
1333
|
def run_plant_optimization(self):
|
1310
1334
|
"""Run the wind plant layout optimization and export outputs
|
1311
1335
|
to outputs property.
|
@@ -1626,7 +1650,24 @@ class BespokeWindPlants(BaseAggregation):
|
|
1626
1650
|
cost ($) as evaluated by
|
1627
1651
|
`balance_of_system_cost_function`
|
1628
1652
|
- ``self.wind_plant``: the SAM wind plant object,
|
1629
|
-
through which all SAM variables can be accessed
|
1653
|
+
through which all SAM variables can be accessed.
|
1654
|
+
|
1655
|
+
.. IMPORTANT::
|
1656
|
+
When using the `self.wind_plant` variable,
|
1657
|
+
DO NOT include quotes around variable names (keys).
|
1658
|
+
|
1659
|
+
- ❌ Wrong: ``self.wind_plant["annual_energy"]``
|
1660
|
+
- ✅ Correct: ``self.wind_plant[annual_energy]``
|
1661
|
+
|
1662
|
+
.. IMPORTANT::
|
1663
|
+
It's possible for SAM wind plant variables to be
|
1664
|
+
``None``, especially if something went wrong while
|
1665
|
+
optimizing the wind plant layout. In this case,
|
1666
|
+
your objective function may fail to evaluate and
|
1667
|
+
terminate the program entirely. To avoid this, add
|
1668
|
+
a default value for the variable in your objective
|
1669
|
+
function, like so:
|
1670
|
+
``(self.wind_plant[annual_energy] or 0)``
|
1630
1671
|
|
1631
1672
|
capital_cost_function : str
|
1632
1673
|
The plant capital cost function written out as a string.
|
reV/bespoke/place_turbines.py
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
"""
|
4
4
|
place turbines for bespoke wind plants
|
5
5
|
"""
|
6
|
+
import re
|
6
7
|
from functools import wraps
|
7
8
|
|
8
9
|
import numpy as np
|
@@ -96,7 +97,24 @@ class PlaceTurbines:
|
|
96
97
|
cost ($) as evaluated by
|
97
98
|
`balance_of_system_cost_function`
|
98
99
|
- ``self.wind_plant``: the SAM wind plant object,
|
99
|
-
through which all SAM variables can be accessed
|
100
|
+
through which all SAM variables can be accessed.
|
101
|
+
|
102
|
+
.. IMPORTANT::
|
103
|
+
When using the `self.wind_plant` variable,
|
104
|
+
DO NOT include quotes around variable names (keys).
|
105
|
+
|
106
|
+
- ❌ Wrong: ``self.wind_plant["annual_energy"]``
|
107
|
+
- ✅ Correct: ``self.wind_plant[annual_energy]``
|
108
|
+
|
109
|
+
.. IMPORTANT::
|
110
|
+
It's possible for SAM wind plant variables to be
|
111
|
+
``None``, especially if something went wrong while
|
112
|
+
optimizing the wind plant layout. In this case,
|
113
|
+
your objective function may fail to evaluate and
|
114
|
+
terminate the program entirely. To avoid this, add
|
115
|
+
a default value for the variable in your objective
|
116
|
+
function, like so:
|
117
|
+
``(self.wind_plant[annual_energy] or 0)``
|
100
118
|
|
101
119
|
capital_cost_function : str
|
102
120
|
The plant capital cost function as a string, must return the
|
@@ -133,13 +151,15 @@ class PlaceTurbines:
|
|
133
151
|
# inputs
|
134
152
|
self.wind_plant = wind_plant
|
135
153
|
|
136
|
-
self.capital_cost_function = capital_cost_function
|
137
|
-
self.fixed_operating_cost_function =
|
138
|
-
|
139
|
-
|
140
|
-
|
154
|
+
self.capital_cost_function = _fix_wp_keys(capital_cost_function)
|
155
|
+
self.fixed_operating_cost_function = _fix_wp_keys(
|
156
|
+
fixed_operating_cost_function)
|
157
|
+
self.variable_operating_cost_function = _fix_wp_keys(
|
158
|
+
variable_operating_cost_function)
|
159
|
+
self.balance_of_system_cost_function = _fix_wp_keys(
|
160
|
+
balance_of_system_cost_function)
|
141
161
|
|
142
|
-
self.objective_function = objective_function
|
162
|
+
self.objective_function = _fix_wp_keys(objective_function)
|
143
163
|
self.include_mask = include_mask
|
144
164
|
self.pixel_side_length = pixel_side_length
|
145
165
|
self.min_spacing = min_spacing
|
@@ -158,7 +178,6 @@ class PlaceTurbines:
|
|
158
178
|
self.safe_polygons = None
|
159
179
|
self._optimized_nn_conn_dist_m = None
|
160
180
|
|
161
|
-
self.ILLEGAL = ('import ', 'os.', 'sys.', '.__', '__.', 'eval', 'exec')
|
162
181
|
self._preflight(self.objective_function)
|
163
182
|
self._preflight(self.capital_cost_function)
|
164
183
|
self._preflight(self.fixed_operating_cost_function)
|
@@ -167,7 +186,10 @@ class PlaceTurbines:
|
|
167
186
|
|
168
187
|
def _preflight(self, eqn):
|
169
188
|
"""Run preflight checks on the equation string."""
|
170
|
-
|
189
|
+
_illegal_substr = ('import ', 'os.', 'sys.', '.__', '__.', 'eval',
|
190
|
+
'exec')
|
191
|
+
|
192
|
+
for substr in _illegal_substr:
|
171
193
|
if substr in str(eqn):
|
172
194
|
msg = ('Will not evaluate string which contains "{}": {}'
|
173
195
|
.format(substr, eqn))
|
@@ -656,3 +678,10 @@ def _compute_nn_conn_dist(x_coords, y_coords):
|
|
656
678
|
left_to_connect.mask[next_connection] = 1
|
657
679
|
|
658
680
|
return total_dist
|
681
|
+
|
682
|
+
|
683
|
+
def _fix_wp_keys(eqn):
|
684
|
+
"""Surround key of `self.wind_plant` in quotes"""
|
685
|
+
pattern = r'(self\.wind_plant\[\s*)([^\]]+?)(\s*\])'
|
686
|
+
replacement = r'\1"\2"\3'
|
687
|
+
return re.sub(pattern, replacement, str(eqn))
|
reV/cli.py
CHANGED
@@ -10,7 +10,8 @@ from reV.generation.cli_gen import gen_command
|
|
10
10
|
from reV.econ.cli_econ import econ_command
|
11
11
|
from reV.handlers.cli_collect import collect_command
|
12
12
|
from reV.handlers.cli_multi_year import my_command
|
13
|
-
from reV.supply_curve.cli_sc_aggregation import sc_agg_command
|
13
|
+
from reV.supply_curve.cli_sc_aggregation import (sc_agg_command,
|
14
|
+
sc_col_descriptions)
|
14
15
|
from reV.supply_curve.cli_supply_curve import sc_command
|
15
16
|
from reV.supply_curve.cli_tech_mapping import tm_command
|
16
17
|
from reV.rep_profiles.cli_rep_profiles import rep_profiles_command
|
@@ -31,6 +32,7 @@ commands = [bespoke_command, gen_command, econ_command, collect_command,
|
|
31
32
|
main = make_cli(commands, info={"name": "reV", "version": __version__})
|
32
33
|
main.add_command(qa_qc_extra)
|
33
34
|
main.add_command(project_points)
|
35
|
+
main.add_command(sc_col_descriptions)
|
34
36
|
|
35
37
|
# export GAPs commands to namespace for documentation
|
36
38
|
batch = main.commands["batch"]
|
reV/config/project_points.py
CHANGED
@@ -627,10 +627,14 @@ class ProjectPoints:
|
|
627
627
|
# pylint: disable=no-member
|
628
628
|
if SiteDataField.CONFIG not in df.columns:
|
629
629
|
df[SiteDataField.CONFIG] = None
|
630
|
+
df[SiteDataField.CONFIG] = (df[SiteDataField.CONFIG]
|
631
|
+
.replace({np.nan: None}))
|
630
632
|
|
631
633
|
# pylint: disable=no-member
|
632
634
|
if SiteDataField.CURTAILMENT not in df.columns:
|
633
635
|
df[SiteDataField.CURTAILMENT] = None
|
636
|
+
df[SiteDataField.CURTAILMENT] = (df[SiteDataField.CURTAILMENT]
|
637
|
+
.replace({np.nan: None}))
|
634
638
|
|
635
639
|
gids = df[SiteDataField.GID].values
|
636
640
|
if not np.array_equal(np.sort(gids), gids):
|
@@ -2,16 +2,21 @@
|
|
2
2
|
"""
|
3
3
|
reV Supply Curve Aggregation CLI utility functions.
|
4
4
|
"""
|
5
|
-
import os
|
6
5
|
import logging
|
6
|
+
from pathlib import Path
|
7
7
|
|
8
|
+
import click
|
9
|
+
import pandas as pd
|
10
|
+
from rex import init_logger
|
8
11
|
from rex.multi_file_resource import MultiFileResource
|
9
12
|
from rex.utilities.utilities import check_res_file
|
10
13
|
from gaps.cli import as_click_command, CLICommandFromClass
|
11
14
|
|
15
|
+
from reV import __version__
|
12
16
|
from reV.supply_curve.sc_aggregation import SupplyCurveAggregation
|
13
17
|
from reV.utilities import ModuleName
|
14
|
-
from reV.utilities.cli_functions import parse_from_pipeline
|
18
|
+
from reV.utilities.cli_functions import (parse_from_pipeline,
|
19
|
+
compile_descriptions)
|
15
20
|
from reV.utilities.exceptions import ConfigError
|
16
21
|
|
17
22
|
|
@@ -103,6 +108,38 @@ sc_agg_command = CLICommandFromClass(SupplyCurveAggregation, method="run",
|
|
103
108
|
main = as_click_command(sc_agg_command)
|
104
109
|
|
105
110
|
|
111
|
+
@click.command()
|
112
|
+
@click.version_option(version=__version__)
|
113
|
+
@click.option('--sc_fpath', '-sc', type=click.Path(), default=None,
|
114
|
+
help='Supply curve CSV file path used to subset the columns')
|
115
|
+
@click.option('--out_fp', '-of', default=None, type=click.Path(),
|
116
|
+
help='Output CSV file for the column descriptions')
|
117
|
+
@click.pass_context
|
118
|
+
def sc_col_descriptions(ctx, sc_fpath, out_fp):
|
119
|
+
"""Generate reV supply curve column descriptions"""
|
120
|
+
if ctx.obj.get('VERBOSE', False):
|
121
|
+
log_level = 'DEBUG'
|
122
|
+
else:
|
123
|
+
log_level = 'INFO'
|
124
|
+
|
125
|
+
init_logger('reV', log_level=log_level)
|
126
|
+
|
127
|
+
cols = []
|
128
|
+
if sc_fpath:
|
129
|
+
cols = pd.read_csv(sc_fpath, nrows=0).columns.tolist()
|
130
|
+
|
131
|
+
if not out_fp:
|
132
|
+
if sc_fpath:
|
133
|
+
sc_name = Path(sc_fpath).stem
|
134
|
+
out_fp = Path(sc_fpath).parent / f"{sc_name}_column_lookup.csv"
|
135
|
+
else:
|
136
|
+
out_fp = "rev_supply_curve_column_lookup.csv"
|
137
|
+
|
138
|
+
columns = compile_descriptions(cols)
|
139
|
+
columns.to_csv(out_fp, index=False)
|
140
|
+
logger.info("Column descriptions saved to %s", out_fp)
|
141
|
+
|
142
|
+
|
106
143
|
if __name__ == '__main__':
|
107
144
|
try:
|
108
145
|
main(obj={})
|
reV/supply_curve/exclusions.py
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
Generate reV inclusion mask from exclusion layers
|
4
4
|
"""
|
5
5
|
import logging
|
6
|
+
import fnmatch
|
6
7
|
from warnings import warn
|
7
8
|
|
8
9
|
import numpy as np
|
@@ -132,10 +133,10 @@ class LayerMask:
|
|
132
133
|
layer is equal to 1, 2, 3, 4, or 5**. Outside of these
|
133
134
|
regions (i.e. outside of federal park regions), the viewshed
|
134
135
|
exclusion is **NOT** applied. If the extent mask created by
|
135
|
-
these options is not boolean, an error is thrown (
|
136
|
-
not specify `weight` or `use_as_weights`
|
137
|
-
By default ``None``, which
|
138
|
-
to the full extent.
|
136
|
+
these options is not boolean, an error is thrown (in other
|
137
|
+
words, do not specify `weight` or `use_as_weights` or else
|
138
|
+
you will run into errors). By default ``None``, which
|
139
|
+
applies the original layer mask to the full extent.
|
139
140
|
**kwargs
|
140
141
|
Optional inputs to maintain legacy kwargs of ``inclusion_*``
|
141
142
|
instead of ``include_*``.
|
@@ -600,19 +601,7 @@ class ExclusionMask:
|
|
600
601
|
self._check_layers = check_layers
|
601
602
|
|
602
603
|
if layers is not None:
|
603
|
-
|
604
|
-
layers = [layers]
|
605
|
-
|
606
|
-
missing = [layer.name for layer in layers
|
607
|
-
if layer.name not in self.excl_layers]
|
608
|
-
if any(missing):
|
609
|
-
msg = ("ExclusionMask layers {} are missing from: {}"
|
610
|
-
.format(missing, self._excl_h5))
|
611
|
-
logger.error(msg)
|
612
|
-
raise KeyError(msg)
|
613
|
-
|
614
|
-
for layer in layers:
|
615
|
-
self.add_layer(layer)
|
604
|
+
self._add_many_layers(layers)
|
616
605
|
|
617
606
|
if kernel in ["queen", "rook"]:
|
618
607
|
self._min_area = min_area
|
@@ -623,6 +612,22 @@ class ExclusionMask:
|
|
623
612
|
else:
|
624
613
|
raise KeyError('kernel must be "queen" or "rook"')
|
625
614
|
|
615
|
+
def _add_many_layers(self, layers):
|
616
|
+
"""Add multiple layers (with check for missing layers)"""
|
617
|
+
if not isinstance(layers, list):
|
618
|
+
layers = [layers]
|
619
|
+
|
620
|
+
missing = [layer.name for layer in layers
|
621
|
+
if layer.name not in self.excl_layers]
|
622
|
+
if any(missing):
|
623
|
+
msg = ("ExclusionMask layers {} are missing from: {}"
|
624
|
+
.format(missing, self._excl_h5))
|
625
|
+
logger.error(msg)
|
626
|
+
raise KeyError(msg)
|
627
|
+
|
628
|
+
for layer in layers:
|
629
|
+
self.add_layer(layer)
|
630
|
+
|
626
631
|
def __enter__(self):
|
627
632
|
return self
|
628
633
|
|
@@ -1113,7 +1118,7 @@ class ExclusionMaskFromDict(ExclusionMask):
|
|
1113
1118
|
excl_h5 : str | list | tuple
|
1114
1119
|
Path to one or more exclusions .h5 files
|
1115
1120
|
layers_dict : dict | NoneType
|
1116
|
-
Dictionary of LayerMask
|
1121
|
+
Dictionary of LayerMask arguments {layer: {kwarg: value}}
|
1117
1122
|
min_area : float | NoneType
|
1118
1123
|
Minimum required contiguous area in sq-km
|
1119
1124
|
kernel : str
|
@@ -1125,16 +1130,34 @@ class ExclusionMaskFromDict(ExclusionMask):
|
|
1125
1130
|
Run a pre-flight check on each layer to ensure they contain
|
1126
1131
|
un-excluded values
|
1127
1132
|
"""
|
1128
|
-
|
1129
|
-
layers = []
|
1130
|
-
for layer, kwargs in layers_dict.items():
|
1131
|
-
layers.append(LayerMask(layer, **kwargs))
|
1132
|
-
else:
|
1133
|
-
layers = None
|
1134
|
-
|
1135
|
-
super().__init__(excl_h5, layers=layers, min_area=min_area,
|
1133
|
+
super().__init__(excl_h5, layers=layers_dict, min_area=min_area,
|
1136
1134
|
kernel=kernel, hsds=hsds, check_layers=check_layers)
|
1137
1135
|
|
1136
|
+
def _add_many_layers(self, layers):
|
1137
|
+
"""Add multiple layers (with check for missing layers)"""
|
1138
|
+
missing = set()
|
1139
|
+
final_layers = {}
|
1140
|
+
|
1141
|
+
# sort pattern-first so that users can overwrite specific layers
|
1142
|
+
sorted_layers = sorted(layers, key=_unix_patterns_first)
|
1143
|
+
for layer_pattern in sorted_layers:
|
1144
|
+
kwargs = layers[layer_pattern]
|
1145
|
+
layer_names = fnmatch.filter(self.excl_layers, layer_pattern)
|
1146
|
+
if not layer_names:
|
1147
|
+
missing.add(layer_pattern)
|
1148
|
+
|
1149
|
+
for layer in layer_names:
|
1150
|
+
final_layers[layer] = LayerMask(layer, **kwargs)
|
1151
|
+
|
1152
|
+
if any(missing):
|
1153
|
+
msg = ("ExclusionMask layers {} are missing from: {}"
|
1154
|
+
.format(missing, self._excl_h5))
|
1155
|
+
logger.error(msg)
|
1156
|
+
raise KeyError(msg)
|
1157
|
+
|
1158
|
+
for layer in final_layers.values():
|
1159
|
+
self.add_layer(layer)
|
1160
|
+
|
1138
1161
|
@classmethod
|
1139
1162
|
def extract_inclusion_mask(cls, excl_fpath, tm_dset, excl_dict=None,
|
1140
1163
|
area_filter_kernel='queen', min_area=None):
|
@@ -1295,3 +1318,9 @@ class FrictionMask(ExclusionMask):
|
|
1295
1318
|
mask = f.mask
|
1296
1319
|
|
1297
1320
|
return mask
|
1321
|
+
|
1322
|
+
|
1323
|
+
def _unix_patterns_first(layer_name):
|
1324
|
+
"""Key that will put layer names with unix patterns first"""
|
1325
|
+
special_chars = {"?", "*", "!", "[", "]"}
|
1326
|
+
return -1 * any(char in layer_name for char in special_chars), layer_name
|
reV/supply_curve/points.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
+
# pylint: disable=anomalous-backslash-in-string
|
2
3
|
"""
|
3
4
|
reV supply curve points frameworks.
|
4
5
|
"""
|
@@ -1452,8 +1453,12 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
|
|
1452
1453
|
"""Supply curve point summary framework that ties a reV SC point to its
|
1453
1454
|
respective generation and resource data."""
|
1454
1455
|
|
1455
|
-
# technology-dependent power density estimates in MW/km2
|
1456
1456
|
POWER_DENSITY = {"pv": 36, "wind": 3}
|
1457
|
+
"""Technology-dependent power density estimates (in MW/km\ :sup:`2`).
|
1458
|
+
|
1459
|
+
The PV power density is a \**DC power density*\*, while the wind power
|
1460
|
+
density is an \**AC power density*\*.
|
1461
|
+
"""
|
1457
1462
|
|
1458
1463
|
def __init__(
|
1459
1464
|
self,
|
@@ -2116,7 +2121,7 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
|
|
2116
2121
|
`None` for solar runs with "dc_ac_ratio" dataset in the
|
2117
2122
|
generation file
|
2118
2123
|
"""
|
2119
|
-
if self.
|
2124
|
+
if "dc_ac_ratio" not in self.gen.datasets:
|
2120
2125
|
return None
|
2121
2126
|
|
2122
2127
|
return self.area * self.power_density_ac
|
@@ -2138,7 +2143,7 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
|
|
2138
2143
|
`None` for solar runs with "dc_ac_ratio" dataset in the
|
2139
2144
|
generation file
|
2140
2145
|
"""
|
2141
|
-
if self.
|
2146
|
+
if "dc_ac_ratio" not in self.gen.datasets:
|
2142
2147
|
return None
|
2143
2148
|
|
2144
2149
|
return self.area * self.power_density
|
@@ -245,6 +245,7 @@ class SupplyCurveAggFileHandler(AbstractAggFileHandler):
|
|
245
245
|
class SupplyCurveAggregation(BaseAggregation):
|
246
246
|
"""SupplyCurveAggregation"""
|
247
247
|
|
248
|
+
# pylint: disable=line-too-long
|
248
249
|
def __init__(self, excl_fpath, tm_dset, econ_fpath=None,
|
249
250
|
excl_dict=None, area_filter_kernel='queen', min_area=None,
|
250
251
|
resolution=64, excl_area=None, res_fpath=None, gids=None,
|
@@ -334,6 +335,12 @@ class SupplyCurveAggregation(BaseAggregation):
|
|
334
335
|
"exclude_nodata": True,
|
335
336
|
"nodata_value": -1
|
336
337
|
},
|
338
|
+
"wildcard*exclusion": {
|
339
|
+
"exclude_values": 1,
|
340
|
+
},
|
341
|
+
"wildcard_unique_exclusion": {
|
342
|
+
"exclude_values": [1, 2, 3],
|
343
|
+
},
|
337
344
|
"partial_setback": {
|
338
345
|
"use_as_weights": True
|
339
346
|
},
|
@@ -359,9 +366,24 @@ class SupplyCurveAggregation(BaseAggregation):
|
|
359
366
|
...
|
360
367
|
}
|
361
368
|
|
362
|
-
Note that all the keys given in this dictionary
|
363
|
-
datasets of the `excl_fpath` file
|
364
|
-
|
369
|
+
Note that all the keys given in this dictionary must be
|
370
|
+
datasets of the `excl_fpath` file or you will get an error.
|
371
|
+
You *may* include Unix-style wildcards (i.e. ``*``, ``?``,
|
372
|
+
or ``[]``) in the keys, but note that the same exclusion
|
373
|
+
configuration will be applied to **all** datasets that match
|
374
|
+
the wildcard pattern unless you explicitly override it for a
|
375
|
+
specific layer. For example, in the configuration above,
|
376
|
+
**all** of the layers matching the pattern
|
377
|
+
``wildcard*exclusion`` will be used as exclusions where the
|
378
|
+
respective layer values equal ``1``, **except** for the
|
379
|
+
``wildcard_unique_exclusion`` layer, which will be used as
|
380
|
+
an exclusion wherever that particular layer values equal
|
381
|
+
``1``, ``2``, or ``3``. You can use this strategy to
|
382
|
+
"exclude" layers from the wildcard match - simply set the
|
383
|
+
``exclude_values`` key to a value that does not exist in
|
384
|
+
that layer and it will be effectively ignored. If ``None``
|
385
|
+
or empty dictionary, no exclusions are applied.
|
386
|
+
By default, ``None``.
|
365
387
|
area_filter_kernel : {"queen", "rook"}, optional
|
366
388
|
Contiguous area filter method to use on final exclusions
|
367
389
|
mask. The filters are defined as::
|
@@ -484,16 +506,36 @@ class SupplyCurveAggregation(BaseAggregation):
|
|
484
506
|
The ``"output_layer_name"`` is the column name under which
|
485
507
|
the aggregated data will appear in the output CSV file. The
|
486
508
|
``"output_layer_name"`` does not have to match the ``dset``
|
487
|
-
input value. The
|
509
|
+
input value. The ``dset`` should match the layer name in the
|
488
510
|
HDF5 from which the data to aggregate should be pulled. The
|
489
|
-
``method`` should be one of
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
511
|
+
``method`` key should be one of the following:
|
512
|
+
|
513
|
+
- ``"mode"``: Output values will be the numerical mode
|
514
|
+
of the non-excluded high resolution data layer cell
|
515
|
+
values
|
516
|
+
- ``"mean"``: Output values will be the arithmetic mean
|
517
|
+
of the non-excluded high resolution data layer cell
|
518
|
+
values
|
519
|
+
- ``"min"``: Output values will be the numerical minimum
|
520
|
+
value of the non-excluded high resolution data layer
|
521
|
+
cell values
|
522
|
+
- ``"max"``: Output values will be the numerical maximum
|
523
|
+
value of the non-excluded high resolution data layer
|
524
|
+
cell values
|
525
|
+
- ``"sum"``: Output values will be the sum of the
|
526
|
+
non-excluded high resolution data layer cell values
|
527
|
+
- ``"category"``: Output values will be a string
|
528
|
+
representation of a dictionary where the keys are the
|
529
|
+
unique values of the non-excluded high resolution data
|
530
|
+
layer cells and the values are the *total
|
531
|
+
high-resolution pixel area* corresponding to that data
|
532
|
+
layer value
|
533
|
+
|
534
|
+
``fpath`` is an optional key that can point to an HDF5 file
|
535
|
+
containing the layer data. If left out, the data is assumed
|
536
|
+
to exist in the file(s) specified by the `excl_fpath` input.
|
537
|
+
If ``None``, no data layer aggregation is performed.
|
538
|
+
By default, ``None``
|
497
539
|
power_density : float | str, optional
|
498
540
|
Power density value (in MW/km\ :sup:`2`) or filepath to
|
499
541
|
variable power density CSV file containing the following
|
@@ -503,8 +545,15 @@ class SupplyCurveAggregation(BaseAggregation):
|
|
503
545
|
- ``power_density`` : power density value (in
|
504
546
|
MW/km\ :sup:`2`)
|
505
547
|
|
506
|
-
If
|
507
|
-
|
548
|
+
If you are running reV for PV (more specifically, you have a
|
549
|
+
`dc_ac_ratio` in your generation file), then this input
|
550
|
+
should represent the \**DC power density*\*. For all other
|
551
|
+
technologies (wind, geothermal, etc), this input should
|
552
|
+
represent the \**AC power density*\*. If ``None``, a
|
553
|
+
constant power density value is pulled from
|
554
|
+
:obj:`~reV.supply_curve.points.GenerationSupplyCurvePoint.POWER_DENSITY`
|
555
|
+
by looking up the technology from the generation meta data.
|
556
|
+
By default, ``None``.
|
508
557
|
friction_fpath : str, optional
|
509
558
|
Filepath to friction surface data (cost based exclusions).
|
510
559
|
Must be paired with the `friction_dset` input below. The
|
@@ -1218,7 +1267,7 @@ class SupplyCurveAggregation(BaseAggregation):
|
|
1218
1267
|
.format(gid, zone_id))
|
1219
1268
|
else:
|
1220
1269
|
pointsum['res_class'] = ri
|
1221
|
-
pointsum[
|
1270
|
+
pointsum[SupplyCurveField.ZONE_ID] = zone_id
|
1222
1271
|
|
1223
1272
|
summary.append(pointsum)
|
1224
1273
|
logger.debug(
|
reV/supply_curve/tech_mapping.py
CHANGED
@@ -32,6 +32,7 @@ class TechMapping:
|
|
32
32
|
|
33
33
|
def __init__(self, excl_fpath, sc_resolution=1200):
|
34
34
|
"""
|
35
|
+
|
35
36
|
Parameters
|
36
37
|
----------
|
37
38
|
excl_fpath : str
|
@@ -44,9 +45,9 @@ class TechMapping:
|
|
44
45
|
map the exclusion pixels in 1200x1200 pixel chunks.
|
45
46
|
|
46
47
|
.. Note:: This parameter does not affect the exclusion to resource
|
47
|
-
|
48
|
-
|
49
|
-
|
48
|
+
(tech) mapping, which deviates from how the effect of the
|
49
|
+
``sc_resolution`` parameter works in other functionality
|
50
|
+
within ``reV``.
|
50
51
|
|
51
52
|
"""
|
52
53
|
self._excl_fpath = excl_fpath
|
@@ -433,7 +434,7 @@ class TechMapping:
|
|
433
434
|
techmap (exclusions-to-resource mapping data) will be saved.
|
434
435
|
|
435
436
|
.. Important:: If this dataset already exists in the h5 file,
|
436
|
-
|
437
|
+
it will be overwritten.
|
437
438
|
|
438
439
|
sc_resolution : int | None, optional
|
439
440
|
Defines how many exclusion pixels are mapped at a time. Units
|
@@ -442,9 +443,9 @@ class TechMapping:
|
|
442
443
|
map the exclusion pixels in 1200x1200 pixel chunks.
|
443
444
|
|
444
445
|
.. Note:: This parameter does not affect the exclusion to resource
|
445
|
-
|
446
|
-
|
447
|
-
|
446
|
+
(tech) mapping, which deviates from how the effect of the
|
447
|
+
``sc_resolution`` parameter works in other functionality
|
448
|
+
within ``reV``.
|
448
449
|
|
449
450
|
dist_margin : float, optional
|
450
451
|
Extra margin to multiply times the computed distance between
|
reV/utilities/__init__.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
"""reV utilities."""
|
3
|
-
|
3
|
+
import ast
|
4
|
+
import inspect
|
5
|
+
from enum import Enum, EnumMeta
|
4
6
|
|
5
7
|
import PySAM
|
6
8
|
from rex.utilities.loggers import log_versions as rex_log_versions
|
@@ -8,7 +10,50 @@ from rex.utilities.loggers import log_versions as rex_log_versions
|
|
8
10
|
from reV.version import __version__
|
9
11
|
|
10
12
|
|
11
|
-
class
|
13
|
+
class _DocstringEnumMeta(EnumMeta):
|
14
|
+
"""Metaclass to assign docstrings to Enum members"""
|
15
|
+
|
16
|
+
def __new__(metacls, clsname, bases, clsdict):
|
17
|
+
cls = super().__new__(metacls, clsname, bases, clsdict)
|
18
|
+
|
19
|
+
try:
|
20
|
+
source = inspect.getsource(cls)
|
21
|
+
except TypeError:
|
22
|
+
return cls # source not available (e.g., in interactive shell)
|
23
|
+
|
24
|
+
module = ast.parse(source)
|
25
|
+
|
26
|
+
for node in ast.iter_child_nodes(module):
|
27
|
+
if isinstance(node, ast.ClassDef) and node.name == cls.__name__:
|
28
|
+
prev = None
|
29
|
+
for body_item in node.body:
|
30
|
+
if isinstance(body_item, ast.Assign):
|
31
|
+
target = body_item.targets[0]
|
32
|
+
if isinstance(target, ast.Name):
|
33
|
+
name = target.id
|
34
|
+
prev = body_item
|
35
|
+
elif (isinstance(body_item, ast.Expr)
|
36
|
+
and isinstance(body_item.value, ast.Constant)):
|
37
|
+
if prev:
|
38
|
+
doc = body_item.value.s
|
39
|
+
member = cls.__members__.get(name)
|
40
|
+
if member:
|
41
|
+
member._description = (doc.strip()
|
42
|
+
.replace("\n ", " "))
|
43
|
+
prev = None
|
44
|
+
return cls
|
45
|
+
|
46
|
+
|
47
|
+
class DocEnum(Enum, metaclass=_DocstringEnumMeta):
|
48
|
+
"""Base Enum class with docstring support"""
|
49
|
+
|
50
|
+
@property
|
51
|
+
def description(self):
|
52
|
+
"""Description of enum member pulled from docstring"""
|
53
|
+
return getattr(self, '_description', None)
|
54
|
+
|
55
|
+
|
56
|
+
class FieldEnum(str, DocEnum):
|
12
57
|
"""Base Field enum with some mapping methods."""
|
13
58
|
|
14
59
|
@classmethod
|
@@ -118,90 +163,373 @@ class SupplyCurveField(FieldEnum):
|
|
118
163
|
|
119
164
|
Not all of these columns are guaranteed in every supply-curve like
|
120
165
|
output (e.g. "convex_hull_area" is a bespoke-only output).
|
166
|
+
|
167
|
+
The docstrings for each field are used as a description when
|
168
|
+
exporting metadata information about supply curve columns. See
|
169
|
+
TBA for details.
|
121
170
|
"""
|
122
171
|
|
172
|
+
# ############## #
|
173
|
+
# Shared outputs #
|
174
|
+
# ############## #
|
175
|
+
|
123
176
|
SC_GID = "sc_gid"
|
177
|
+
"""Supply curve GID (Specific to this particular supply curve output)"""
|
178
|
+
|
124
179
|
LATITUDE = "latitude"
|
180
|
+
"""Centroid latitude of the supply curve grid-cell"""
|
181
|
+
|
125
182
|
LONGITUDE = "longitude"
|
183
|
+
"""Centroid longitude of the supply curve grid-cell"""
|
184
|
+
|
126
185
|
COUNTRY = "country"
|
186
|
+
"""Country of the supply curve grid-cell"""
|
187
|
+
|
127
188
|
STATE = "state"
|
189
|
+
"""State of the supply curve grid-cell"""
|
190
|
+
|
128
191
|
COUNTY = "county"
|
192
|
+
"""County of the supply curve grid-cell"""
|
193
|
+
|
129
194
|
ELEVATION = "elevation_m"
|
195
|
+
"""Mean elevation of the supply curve grid-cell"""
|
196
|
+
|
130
197
|
TIMEZONE = "timezone"
|
198
|
+
"""
|
199
|
+
Timezone of supply curve grid-cell, expressed as an hourly offset from UTC
|
200
|
+
"""
|
201
|
+
|
131
202
|
SC_POINT_GID = "sc_point_gid"
|
203
|
+
"""
|
204
|
+
Unique ID that can be used to match supply curve grid-cells across reV
|
205
|
+
supply curves at the same resolution
|
206
|
+
"""
|
207
|
+
|
132
208
|
SC_ROW_IND = "sc_row_ind"
|
209
|
+
"""Supply curve grid-cell row ID (Invariant across supply curves)"""
|
210
|
+
|
133
211
|
SC_COL_IND = "sc_col_ind"
|
212
|
+
"""Supply curve grid-cell column ID (Invariant across supply curves)"""
|
213
|
+
|
134
214
|
SOURCE_GIDS = "source_gids"
|
215
|
+
|
135
216
|
RES_GIDS = "res_gids"
|
217
|
+
"""List of resource GID's mapped to this supply curve grid-cells"""
|
218
|
+
|
136
219
|
GEN_GIDS = "gen_gids"
|
220
|
+
"""List of generation GID's mapped to this supply curve point"""
|
221
|
+
|
137
222
|
GID_COUNTS = "gid_counts"
|
223
|
+
"""
|
224
|
+
Number of high-resolution cells corresponding to each generation GID
|
225
|
+
for this supply curve point
|
226
|
+
"""
|
227
|
+
|
138
228
|
N_GIDS = "n_gids"
|
229
|
+
"""
|
230
|
+
Total number of not fully excluded pixels associated with the available
|
231
|
+
resource/generation gids
|
232
|
+
"""
|
233
|
+
|
139
234
|
ZONE_ID = "zone_id"
|
235
|
+
"""Zone ID of the supply curve grid-cell, if applicable. Defaults to 1."""
|
236
|
+
|
140
237
|
MEAN_RES = "resource"
|
238
|
+
"""
|
239
|
+
Mean resource (e.g. wind speed, gha, temperature, etc.) across the supply
|
240
|
+
curve grid-cell
|
241
|
+
"""
|
242
|
+
|
141
243
|
MEAN_CF_AC = "capacity_factor_ac"
|
244
|
+
"""Mean capacity factor (AC) across supply curve grid-cell"""
|
245
|
+
|
142
246
|
MEAN_CF_DC = "capacity_factor_dc"
|
247
|
+
"""Mean capacity factor (DC) across supply curve grid-cell"""
|
248
|
+
|
143
249
|
WAKE_LOSSES = "losses_wakes_pct"
|
250
|
+
"""Mean wake losses across supply curve grid-cell"""
|
251
|
+
|
144
252
|
MEAN_LCOE = "lcoe_site_usd_per_mwh"
|
253
|
+
"""
|
254
|
+
Mean power plant levelized cost of energy across supply curve grid-cell
|
255
|
+
"""
|
256
|
+
|
145
257
|
CAPACITY_AC_MW = "capacity_ac_mw"
|
258
|
+
"""
|
259
|
+
Capacity of system based on area_sq_km * AC capacity density assumption
|
260
|
+
"""
|
261
|
+
|
146
262
|
CAPACITY_DC_MW = "capacity_dc_mw"
|
263
|
+
"""
|
264
|
+
Capacity of system based on area_sq_km * DC capacity density assumption
|
265
|
+
"""
|
266
|
+
|
147
267
|
OFFSHORE = "offshore"
|
268
|
+
"""
|
269
|
+
Flag value indicating if the supply curve grid-cell is offshore (1)
|
270
|
+
or not (0)
|
271
|
+
"""
|
272
|
+
|
148
273
|
AREA_SQ_KM = "area_developable_sq_km"
|
274
|
+
"""Developable area after spatial exclusions applied"""
|
275
|
+
|
149
276
|
MEAN_FRICTION = "friction_site"
|
277
|
+
|
150
278
|
MEAN_LCOE_FRICTION = "lcoe_friction_usd_per_mwh"
|
279
|
+
|
151
280
|
RAW_LCOE = "lcoe_raw_usd_per_mwh"
|
281
|
+
"""
|
282
|
+
Mean power plant levelized cost of energy across supply curve grid-cell
|
283
|
+
without any multipliers or economies of scale applied
|
284
|
+
"""
|
285
|
+
|
152
286
|
EOS_MULT = "multiplier_cc_eos"
|
287
|
+
"""
|
288
|
+
Capital cost economies of Scale (EOS) multiplier value (defaults to `1`
|
289
|
+
if no EOS curve was specified)
|
290
|
+
"""
|
291
|
+
|
153
292
|
FIXED_EOS_MULT = "multiplier_foc_eos"
|
293
|
+
"""
|
294
|
+
Fixed operating cost economies of Scale (EOS) multiplier value (defaults
|
295
|
+
to `1` if no EOS curve was specified)
|
296
|
+
"""
|
297
|
+
|
154
298
|
VAR_EOS_MULT = "multiplier_voc_eos"
|
299
|
+
"""
|
300
|
+
Variable operating cost economies of Scale (EOS) multiplier value
|
301
|
+
(defaults to `1` if no EOS curve was specified)
|
302
|
+
"""
|
303
|
+
|
155
304
|
REG_MULT = "multiplier_cc_regional"
|
305
|
+
"""
|
306
|
+
Regional capital cost multiplier to capture taxes, labor, land lease
|
307
|
+
regional differences
|
308
|
+
"""
|
309
|
+
|
156
310
|
SC_POINT_ANNUAL_ENERGY_MWH = "annual_energy_site_mwh"
|
311
|
+
"""
|
312
|
+
Total annual energy for supply curve grid-cell (computed using
|
313
|
+
"capacity_ac_mw" and "capacity_factor_ac")
|
314
|
+
"""
|
315
|
+
|
157
316
|
COST_BASE_CC_USD_PER_AC_MW = "cost_base_cc_usd_per_ac_mw"
|
317
|
+
"""
|
318
|
+
Included-area weighted capital cost for supply curve grid-cell with no
|
319
|
+
multipliers or economies of scale applied (defaults to `None` for
|
320
|
+
non-LCOE runs)
|
321
|
+
"""
|
322
|
+
|
158
323
|
COST_SITE_CC_USD_PER_AC_MW = "cost_site_cc_usd_per_ac_mw"
|
324
|
+
"""
|
325
|
+
Included-area weighted capital cost for supply curve grid-cell
|
326
|
+
(defaults to `None` for non-LCOE runs)
|
327
|
+
"""
|
328
|
+
|
159
329
|
COST_BASE_FOC_USD_PER_AC_MW = "cost_base_foc_usd_per_ac_mw"
|
330
|
+
"""
|
331
|
+
Included-area weighted fixed operating cost for supply curve grid-cell
|
332
|
+
with no multipliers or economies of scale applied (defaults to `None` for
|
333
|
+
non-LCOE runs)
|
334
|
+
"""
|
335
|
+
|
160
336
|
COST_SITE_FOC_USD_PER_AC_MW = "cost_site_foc_usd_per_ac_mw"
|
337
|
+
"""
|
338
|
+
Included-area weighted fixed operating cost for supply curve grid-cell
|
339
|
+
(defaults to `None` for non-LCOE runs)
|
340
|
+
"""
|
341
|
+
|
161
342
|
COST_BASE_VOC_USD_PER_AC_MWH = "cost_base_voc_usd_per_ac_mwh"
|
343
|
+
"""
|
344
|
+
Included-area weighted variable operating cost for supply curve grid-cell
|
345
|
+
with no multipliers or economies of scale applied (defaults to `None` for
|
346
|
+
non-LCOE runs)
|
347
|
+
"""
|
348
|
+
|
162
349
|
COST_SITE_VOC_USD_PER_AC_MWH = "cost_site_voc_usd_per_ac_mwh"
|
350
|
+
"""
|
351
|
+
Included-area weighted variable operating cost for supply curve grid-cell
|
352
|
+
(defaults to `None` for non-LCOE runs)
|
353
|
+
"""
|
354
|
+
|
163
355
|
FIXED_CHARGE_RATE = "fixed_charge_rate"
|
356
|
+
"""
|
357
|
+
Fixed charge rate used for LCOE computation
|
358
|
+
(defaults to `None` for non-LCOE runs)
|
359
|
+
"""
|
360
|
+
|
361
|
+
# ############### #
|
362
|
+
# Bespoke outputs #
|
363
|
+
# ############### #
|
164
364
|
|
165
|
-
# Bespoke outputs
|
166
365
|
POSSIBLE_X_COORDS = "possible_x_coords"
|
366
|
+
"""
|
367
|
+
List of turbine x coordinates considered during layout optimization
|
368
|
+
(in meters relative to grid-cell)
|
369
|
+
"""
|
370
|
+
|
167
371
|
POSSIBLE_Y_COORDS = "possible_y_coords"
|
372
|
+
"""
|
373
|
+
List of turbine y coordinates considered during layout optimization
|
374
|
+
(in meters relative to grid-cell)
|
375
|
+
"""
|
376
|
+
|
168
377
|
TURBINE_X_COORDS = "turbine_x_coords"
|
378
|
+
"""
|
379
|
+
List of optimized layout turbine x coordinates
|
380
|
+
(in meters relative to grid-cell)
|
381
|
+
"""
|
382
|
+
|
169
383
|
TURBINE_Y_COORDS = "turbine_y_coords"
|
384
|
+
"""
|
385
|
+
List of optimized layout turbine y coordinates
|
386
|
+
(in meters relative to grid-cell)
|
387
|
+
"""
|
388
|
+
|
170
389
|
N_TURBINES = "n_turbines"
|
390
|
+
"""
|
391
|
+
Number of turbines in the optimized layout for this supply curve
|
392
|
+
grid-cell
|
393
|
+
"""
|
394
|
+
|
171
395
|
INCLUDED_AREA = "area_included_sq_km"
|
396
|
+
"""Area available for wind turbine layout optimization"""
|
397
|
+
|
172
398
|
INCLUDED_AREA_CAPACITY_DENSITY = (
|
173
399
|
"capacity_density_included_area_mw_per_km2"
|
174
400
|
)
|
401
|
+
"""
|
402
|
+
Capacity density of the optimized wind plant layout defined using the
|
403
|
+
area available after removing the exclusions
|
404
|
+
"""
|
405
|
+
|
175
406
|
CONVEX_HULL_AREA = "area_convex_hull_sq_km"
|
407
|
+
"""Area of the convex hull of the optimized wind plant layout"""
|
408
|
+
|
176
409
|
CONVEX_HULL_CAPACITY_DENSITY = "capacity_density_convex_hull_mw_per_km2"
|
410
|
+
"""
|
411
|
+
Capacity density of the optimized wind plant layout defined using the
|
412
|
+
convex hull area of the layout
|
413
|
+
"""
|
414
|
+
|
177
415
|
FULL_CELL_CAPACITY_DENSITY = "capacity_density_full_cell_mw_per_km2"
|
416
|
+
"""
|
417
|
+
Capacity density of the optimized wind plant layout defined using the full
|
418
|
+
non-excluded area of the supply curve grid-cell
|
419
|
+
"""
|
420
|
+
|
178
421
|
BESPOKE_AEP = "optimized_plant_aep"
|
422
|
+
"""
|
423
|
+
Annual energy production of the optimized wind plant layout computed using
|
424
|
+
wind speed/direction joint probability distribution (as opposed to
|
425
|
+
historical weather data)
|
426
|
+
"""
|
427
|
+
|
179
428
|
BESPOKE_OBJECTIVE = "optimized_plant_objective"
|
429
|
+
"""
|
430
|
+
Objective function value of the optimized wind plant layout. This is
|
431
|
+
typically the LCOE computed using wind speed/direction joint probability
|
432
|
+
distribution (as opposed to historical weather data)
|
433
|
+
"""
|
434
|
+
|
180
435
|
BESPOKE_CAPITAL_COST = "optimized_plant_capital_cost"
|
436
|
+
"""Capital cost of the optimized wind plant layout"""
|
437
|
+
|
181
438
|
BESPOKE_FIXED_OPERATING_COST = "optimized_plant_fixed_operating_cost"
|
439
|
+
"""Annual fixed operating cost of the optimized wind plant layout"""
|
440
|
+
|
182
441
|
BESPOKE_VARIABLE_OPERATING_COST = "optimized_plant_variable_operating_cost"
|
442
|
+
"""Variable operating cost of the optimized wind plant layout"""
|
443
|
+
|
183
444
|
BESPOKE_BALANCE_OF_SYSTEM_COST = "optimized_plant_balance_of_system_cost"
|
445
|
+
"""Balance of system cost of the optimized wind plant layout"""
|
446
|
+
|
447
|
+
# #################### #
|
448
|
+
# Transmission outputs #
|
449
|
+
# #################### #
|
184
450
|
|
185
|
-
# Transmission outputs
|
186
451
|
TRANS_GID = "trans_gid"
|
452
|
+
"""Transmission connection feature GID"""
|
453
|
+
|
187
454
|
TRANS_TYPE = "trans_type"
|
455
|
+
"""Transmission connection feature type"""
|
456
|
+
|
188
457
|
TOTAL_LCOE_FRICTION = "lcoe_total_friction_usd_per_mwh"
|
189
458
|
TRANS_CAPACITY = "trans_capacity"
|
459
|
+
|
190
460
|
DIST_SPUR_KM = "dist_spur_km"
|
461
|
+
"""
|
462
|
+
Distance between the grid-cell centroid and cheapest available electrical
|
463
|
+
substation. Used in lcot calculations.
|
464
|
+
"""
|
465
|
+
|
191
466
|
DIST_EXPORT_KM = "dist_export_km"
|
467
|
+
"""Length of the offshore export cable"""
|
468
|
+
|
192
469
|
REINFORCEMENT_DIST_KM = "dist_reinforcement_km"
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
470
|
+
"""
|
471
|
+
Distance between the connected substation and nearest regional load
|
472
|
+
center. Used in lcot calculations.
|
473
|
+
"""
|
474
|
+
|
475
|
+
TIE_LINE_COST_PER_MW = "cost_spur_usd_per_mw_ac"
|
476
|
+
"""
|
477
|
+
Cost of the spur line used to connect the grid-cell centroid with the
|
478
|
+
cheapest available electrical substation
|
479
|
+
"""
|
480
|
+
|
481
|
+
CONNECTION_COST_PER_MW = "cost_poi_usd_per_mw_ac"
|
482
|
+
"""Substation connection/upgrade/installation cost"""
|
483
|
+
|
484
|
+
EXPORT_COST_PER_MW = "cost_export_usd_per_mw_ac"
|
485
|
+
"""Cost of the offshore export cable """
|
486
|
+
|
487
|
+
REINFORCEMENT_COST_PER_MW = "cost_reinforcement_usd_per_mw_ac"
|
488
|
+
"""Non-levelized reinforcement transmission capital costs"""
|
489
|
+
|
490
|
+
TOTAL_TRANS_CAP_COST_PER_MW = "cost_total_trans_usd_per_mw_ac"
|
491
|
+
"""
|
492
|
+
Non-levelized spur and point-of-interconnection transmission capital costs
|
493
|
+
"""
|
494
|
+
|
198
495
|
LCOT = "lcot_usd_per_mwh"
|
496
|
+
"""
|
497
|
+
Levelized cost of transmission. Includes spur-transmission,
|
498
|
+
point-of-interconnection, and reinforcement costs.
|
499
|
+
"""
|
500
|
+
|
199
501
|
TOTAL_LCOE = "lcoe_all_in_usd_per_mwh"
|
502
|
+
"""All-in LCOE. Includes site-lcoe + lcot"""
|
503
|
+
|
200
504
|
N_PARALLEL_TRANS = "count_num_parallel_trans"
|
505
|
+
"""
|
506
|
+
Number of parallel transmission lines connecting the grid-cell centroid
|
507
|
+
with the cheapest available electrical substation
|
508
|
+
"""
|
509
|
+
|
201
510
|
POI_LAT = "latitude_poi"
|
511
|
+
"""
|
512
|
+
Latitude of the cheapest available electrical substation for the supply
|
513
|
+
curve grid-cell
|
514
|
+
"""
|
515
|
+
|
202
516
|
POI_LON = "longitude_poi"
|
517
|
+
"""
|
518
|
+
Longitude of the cheapest available electrical substation for the supply
|
519
|
+
curve grid-cell
|
520
|
+
"""
|
521
|
+
|
203
522
|
REINFORCEMENT_POI_LAT = "latitude_reinforcement_poi"
|
523
|
+
"""
|
524
|
+
Latitude of the nearest regional load center for the supply curve
|
525
|
+
grid-cell
|
526
|
+
"""
|
527
|
+
|
204
528
|
REINFORCEMENT_POI_LON = "longitude_reinforcement_poi"
|
529
|
+
"""
|
530
|
+
Longitude of the nearest regional load center for the supply curve
|
531
|
+
grid-cell
|
532
|
+
"""
|
205
533
|
|
206
534
|
@classmethod
|
207
535
|
def map_from_legacy(cls):
|
@@ -222,6 +550,11 @@ class SupplyCurveField(FieldEnum):
|
|
222
550
|
|
223
551
|
return legacy_map
|
224
552
|
|
553
|
+
@property
|
554
|
+
def units(self):
|
555
|
+
"""Units of the supply curve column, or ``"N/A"`` if not applicable"""
|
556
|
+
return _SC_UNITS.get(self, "N/A")
|
557
|
+
|
225
558
|
|
226
559
|
class _LegacySCAliases(Enum):
|
227
560
|
"""Legacy supply curve column names.
|
@@ -243,10 +576,13 @@ class _LegacySCAliases(Enum):
|
|
243
576
|
TRANS_CAPACITY = "avail_cap"
|
244
577
|
DIST_SPUR_KM = "dist_km"
|
245
578
|
REINFORCEMENT_DIST_KM = "reinforcement_dist_km"
|
246
|
-
TIE_LINE_COST_PER_MW = "tie_line_cost_per_mw"
|
247
|
-
CONNECTION_COST_PER_MW = "connection_cost_per_mw"
|
248
|
-
|
249
|
-
|
579
|
+
TIE_LINE_COST_PER_MW = "tie_line_cost_per_mw", "cost_spur_usd_per_mw"
|
580
|
+
CONNECTION_COST_PER_MW = "connection_cost_per_mw", "cost_poi_usd_per_mw"
|
581
|
+
EXPORT_COST_PER_MW = "cost_export_usd_per_mw"
|
582
|
+
REINFORCEMENT_COST_PER_MW = ("reinforcement_cost_per_mw",
|
583
|
+
"cost_reinforcement_usd_per_mw")
|
584
|
+
TOTAL_TRANS_CAP_COST_PER_MW = ("trans_cap_cost_per_mw",
|
585
|
+
"cost_total_trans_usd_per_mw")
|
250
586
|
LCOT = "lcot"
|
251
587
|
TOTAL_LCOE = "total_lcoe"
|
252
588
|
TOTAL_LCOE_FRICTION = "total_lcoe_friction"
|
@@ -332,3 +668,55 @@ def log_versions(logger):
|
|
332
668
|
logger.info("Running with reV version {}".format(__version__))
|
333
669
|
rex_log_versions(logger)
|
334
670
|
logger.debug("- PySAM version {}".format(PySAM.__version__))
|
671
|
+
|
672
|
+
|
673
|
+
_SC_UNITS = {
|
674
|
+
SupplyCurveField.ELEVATION: "m",
|
675
|
+
SupplyCurveField.LATITUDE: "degrees",
|
676
|
+
SupplyCurveField.LONGITUDE: "degrees",
|
677
|
+
|
678
|
+
SupplyCurveField.AREA_SQ_KM: "km2",
|
679
|
+
SupplyCurveField.CAPACITY_AC_MW: "MWac",
|
680
|
+
SupplyCurveField.CAPACITY_DC_MW: "MWdc",
|
681
|
+
SupplyCurveField.MEAN_CF_AC: "ratio",
|
682
|
+
SupplyCurveField.MEAN_CF_DC: "ratio",
|
683
|
+
SupplyCurveField.WAKE_LOSSES: "%",
|
684
|
+
SupplyCurveField.MEAN_LCOE: "$/MWh",
|
685
|
+
SupplyCurveField.RAW_LCOE: "$/MWh",
|
686
|
+
SupplyCurveField.SC_POINT_ANNUAL_ENERGY_MWH: "MWh",
|
687
|
+
SupplyCurveField.COST_BASE_CC_USD_PER_AC_MW: "$/MWac",
|
688
|
+
SupplyCurveField.COST_SITE_CC_USD_PER_AC_MW: "$/MWac",
|
689
|
+
SupplyCurveField.COST_BASE_FOC_USD_PER_AC_MW: "$/MWac",
|
690
|
+
SupplyCurveField.COST_SITE_FOC_USD_PER_AC_MW: "$/MWac",
|
691
|
+
SupplyCurveField.COST_BASE_VOC_USD_PER_AC_MWH: "$/MWh",
|
692
|
+
SupplyCurveField.COST_SITE_VOC_USD_PER_AC_MWH: "$/MWh",
|
693
|
+
|
694
|
+
SupplyCurveField.BESPOKE_AEP: "MWh",
|
695
|
+
SupplyCurveField.BESPOKE_CAPITAL_COST: "$",
|
696
|
+
SupplyCurveField.BESPOKE_FIXED_OPERATING_COST: "$/year",
|
697
|
+
SupplyCurveField.BESPOKE_VARIABLE_OPERATING_COST: "$/kWh",
|
698
|
+
SupplyCurveField.BESPOKE_BALANCE_OF_SYSTEM_COST: "$",
|
699
|
+
SupplyCurveField.INCLUDED_AREA: "km2",
|
700
|
+
SupplyCurveField.INCLUDED_AREA_CAPACITY_DENSITY: "MW/km2",
|
701
|
+
SupplyCurveField.CONVEX_HULL_AREA: "km2",
|
702
|
+
SupplyCurveField.CONVEX_HULL_CAPACITY_DENSITY: "MW/km2",
|
703
|
+
SupplyCurveField.FULL_CELL_CAPACITY_DENSITY: "MW/km2",
|
704
|
+
|
705
|
+
SupplyCurveField.LCOT: "$/MWh",
|
706
|
+
SupplyCurveField.MEAN_RES: "varies",
|
707
|
+
SupplyCurveField.REINFORCEMENT_COST_PER_MW: "$/MWac",
|
708
|
+
SupplyCurveField.REINFORCEMENT_DIST_KM: "km",
|
709
|
+
SupplyCurveField.TOTAL_LCOE: "$/MWh",
|
710
|
+
SupplyCurveField.TOTAL_TRANS_CAP_COST_PER_MW: "$/MWac",
|
711
|
+
SupplyCurveField.DIST_SPUR_KM: "km",
|
712
|
+
SupplyCurveField.DIST_EXPORT_KM: "km",
|
713
|
+
SupplyCurveField.TIE_LINE_COST_PER_MW: "$/MWac",
|
714
|
+
SupplyCurveField.CONNECTION_COST_PER_MW: "$/MWac",
|
715
|
+
SupplyCurveField.EXPORT_COST_PER_MW: "$/MWac",
|
716
|
+
|
717
|
+
SupplyCurveField.POI_LAT: "degrees",
|
718
|
+
SupplyCurveField.POI_LON: "degrees",
|
719
|
+
SupplyCurveField.REINFORCEMENT_POI_LAT: "degrees",
|
720
|
+
SupplyCurveField.REINFORCEMENT_POI_LON: "degrees",
|
721
|
+
|
722
|
+
}
|
reV/utilities/cli_functions.py
CHANGED
@@ -5,10 +5,11 @@ General CLI utility functions.
|
|
5
5
|
import logging
|
6
6
|
from warnings import warn
|
7
7
|
|
8
|
+
import pandas as pd
|
8
9
|
from gaps.pipeline import Status
|
9
10
|
from rex.utilities.loggers import init_mult
|
10
11
|
|
11
|
-
from reV.utilities import ModuleName
|
12
|
+
from reV.utilities import ModuleName, SupplyCurveField
|
12
13
|
from reV.utilities.exceptions import ConfigWarning, PipelineError
|
13
14
|
|
14
15
|
|
@@ -115,3 +116,44 @@ def parse_from_pipeline(config, out_dir, config_key, target_modules):
|
|
115
116
|
.format(config_key, val[0]))
|
116
117
|
|
117
118
|
return config
|
119
|
+
|
120
|
+
|
121
|
+
def compile_descriptions(cols=None):
|
122
|
+
"""Compile a meta table with reV column descriptions.
|
123
|
+
|
124
|
+
Descriptions are pulled from the
|
125
|
+
:class:`~reV.utilities.SupplyCurveField` enum, which
|
126
|
+
contains the known reV supply curve field descriptions. Columns
|
127
|
+
which do not have a known description are excluded from the
|
128
|
+
output.
|
129
|
+
|
130
|
+
Parameters
|
131
|
+
----------
|
132
|
+
cols : iterable, optional
|
133
|
+
Optional iterable of column names to include in the output.
|
134
|
+
By default, ``None``, which compiles all known reV supply curve
|
135
|
+
field descriptions.
|
136
|
+
|
137
|
+
Returns
|
138
|
+
-------
|
139
|
+
pd.DataFrame
|
140
|
+
Pandas DataFrame containing column names, corresponding units,
|
141
|
+
and descriptions for each column. Only columns that have a known
|
142
|
+
description are included in the output.
|
143
|
+
"""
|
144
|
+
if not cols:
|
145
|
+
cols = [c.value for c in SupplyCurveField]
|
146
|
+
|
147
|
+
data = []
|
148
|
+
for col in cols:
|
149
|
+
try:
|
150
|
+
scf = SupplyCurveField(col)
|
151
|
+
except ValueError:
|
152
|
+
continue
|
153
|
+
|
154
|
+
if scf.description is None:
|
155
|
+
continue
|
156
|
+
|
157
|
+
data.append((str(scf), scf.units, scf.description))
|
158
|
+
|
159
|
+
return pd.DataFrame(data, columns=["reV Column", "Units", "Description"])
|
reV/version.py
CHANGED
File without changes
|
File without changes
|
File without changes
|
File without changes
|