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/generation/generation.py
CHANGED
@@ -113,7 +113,7 @@ class Gen(BaseGen):
|
|
113
113
|
allowed and/or required SAM config file inputs. If economic
|
114
114
|
parameters are supplied in the SAM config, then you can bundle a
|
115
115
|
"follow-on" econ calculation by just adding the desired econ
|
116
|
-
output keys to the `output_request`. You can request ``reV`` to
|
116
|
+
output keys to the `output_request`. You can request ``reV`` to
|
117
117
|
run the analysis for one or more "sites", which correspond to
|
118
118
|
the meta indices in the resource data (also commonly called the
|
119
119
|
``gid's``).
|
@@ -128,7 +128,7 @@ class Gen(BaseGen):
|
|
128
128
|
>>> import os
|
129
129
|
>>> from reV import Gen, TESTDATADIR
|
130
130
|
>>>
|
131
|
-
>>> sam_tech = '
|
131
|
+
>>> sam_tech = 'pvwattsv8'
|
132
132
|
>>> sites = 0
|
133
133
|
>>> fp_sam = os.path.join(TESTDATADIR, 'SAM/naris_pv_1axis_inv13.json')
|
134
134
|
>>> fp_res = os.path.join(TESTDATADIR, 'nsrdb/ri_100_nsrdb_2013.h5')
|
@@ -145,15 +145,16 @@ class Gen(BaseGen):
|
|
145
145
|
>>> gen.run()
|
146
146
|
>>>
|
147
147
|
>>> gen.out
|
148
|
-
{'
|
149
|
-
'
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
148
|
+
{'fixed_charge_rate': array([0.096, 0.096, 0.096, 0.096],
|
149
|
+
'base_capital_cost': array([39767200, 39767200, 39767200, 39767200],
|
150
|
+
'base_variable_operating_cost': array([0, 0, 0, 0],
|
151
|
+
'base_fixed_operating_cost': array([260000, 260000, 260000, 260000],
|
152
|
+
'capital_cost': array([39767200, 39767200, 39767200, 39767200],
|
153
|
+
'fixed_operating_cost': array([260000, 260000, 260000, 260000],
|
154
|
+
'variable_operating_cost': array([0, 0, 0, 0],
|
155
|
+
'capital_cost_multiplier': array([1, 1, 1, 1],
|
156
|
+
'cf_mean': array([0.17859147, 0.17869979, 0.1834818 , 0.18646291],
|
157
|
+
'lcoe_fcr': array([130.32126, 130.24226, 126.84782, 124.81981]}
|
157
158
|
|
158
159
|
Parameters
|
159
160
|
----------
|
@@ -170,7 +171,7 @@ class Gen(BaseGen):
|
|
170
171
|
multiple sites can be specified to evaluate reV at multiple
|
171
172
|
specific locations. A string pointing to a project points
|
172
173
|
CSV file may also be specified. Typically, the CSV contains
|
173
|
-
|
174
|
+
the following columns:
|
174
175
|
|
175
176
|
- ``gid``: Integer specifying the generation GID of each
|
176
177
|
site.
|
@@ -180,16 +181,25 @@ class Gen(BaseGen):
|
|
180
181
|
``None`` (or left out completely) if you specify only
|
181
182
|
a single SAM configuration file as the `sam_files`
|
182
183
|
input.
|
183
|
-
|
184
|
-
|
184
|
+
- ``capital_cost_multiplier``: This is an *optional*
|
185
|
+
multiplier input that, if included, will be used to
|
186
|
+
regionally scale the ``capital_cost`` input in the SAM
|
187
|
+
config. If you include this column in your CSV, you
|
188
|
+
*do not* need to specify ``capital_cost``, unless you
|
189
|
+
would like that value to vary regionally and
|
190
|
+
independently of the multiplier (i.e. the multiplier
|
191
|
+
will still be applied on top of the ``capital_cost``
|
192
|
+
input).
|
193
|
+
|
194
|
+
The CSV file may also contain other site-specific inputs by
|
185
195
|
including a column named after a config keyword (e.g. a
|
186
|
-
column called ``
|
187
|
-
site-specific
|
188
|
-
that do not correspond to a config
|
189
|
-
but they will be ignored. A
|
190
|
-
guidelines as the CSV input
|
191
|
-
|
192
|
-
input as well.
|
196
|
+
column called ``wind_turbine_rotor_diameter`` may be
|
197
|
+
included to specify a site-specific turbine diameter for
|
198
|
+
each location). Columns that do not correspond to a config
|
199
|
+
key may also be included, but they will be ignored. A
|
200
|
+
DataFrame following the same guidelines as the CSV input
|
201
|
+
(or a dictionary that can be used to initialize such a
|
202
|
+
DataFrame) may be used for this input as well.
|
193
203
|
|
194
204
|
.. Note:: By default, the generation GID of each site is
|
195
205
|
assumed to match the resource GID to be evaluated for that
|
@@ -454,7 +464,7 @@ class Gen(BaseGen):
|
|
454
464
|
Meta data df for sites in project points. Column names are meta
|
455
465
|
data variables, rows are different sites. The row index
|
456
466
|
does not indicate the site number if the project points are
|
457
|
-
non-sequential or do not start from 0, so a `
|
467
|
+
non-sequential or do not start from 0, so a `SiteDataField.GID`
|
458
468
|
column is added.
|
459
469
|
"""
|
460
470
|
if self._meta is None:
|
@@ -712,6 +722,8 @@ class Gen(BaseGen):
|
|
712
722
|
for k in site_output.keys():
|
713
723
|
# iterate through variable names in each site's output dict
|
714
724
|
if k in cls.OUT_ATTRS:
|
725
|
+
if out[site][k] is None:
|
726
|
+
continue
|
715
727
|
# get dtype and scale for output variable name
|
716
728
|
dtype = cls.OUT_ATTRS[k].get("dtype", "float32")
|
717
729
|
scale_factor = cls.OUT_ATTRS[k].get("scale_factor", 1)
|
@@ -947,12 +959,20 @@ class Gen(BaseGen):
|
|
947
959
|
Output variables requested from SAM.
|
948
960
|
"""
|
949
961
|
|
950
|
-
output_request =
|
962
|
+
output_request = super()._parse_output_request(req)
|
951
963
|
|
952
964
|
# ensure that cf_mean is requested from output
|
953
965
|
if "cf_mean" not in output_request:
|
954
966
|
output_request.append("cf_mean")
|
955
967
|
|
968
|
+
if _is_solar_run_with_ac_outputs(self.tech):
|
969
|
+
if "dc_ac_ratio" not in output_request:
|
970
|
+
output_request.append("dc_ac_ratio")
|
971
|
+
for dset in ["cf_mean", "cf_profile"]:
|
972
|
+
ac_dset = f"{dset}_ac"
|
973
|
+
if dset in output_request and ac_dset not in output_request:
|
974
|
+
output_request.append(ac_dset)
|
975
|
+
|
956
976
|
for request in output_request:
|
957
977
|
if request not in self.OUT_ATTRS:
|
958
978
|
msg = (
|
@@ -1097,3 +1117,10 @@ class Gen(BaseGen):
|
|
1097
1117
|
raise e
|
1098
1118
|
|
1099
1119
|
return self._out_fpath
|
1120
|
+
|
1121
|
+
|
1122
|
+
def _is_solar_run_with_ac_outputs(tech):
|
1123
|
+
"""True if tech is pvwattsv8+"""
|
1124
|
+
if "pvwatts" not in tech.casefold():
|
1125
|
+
return False
|
1126
|
+
return tech.casefold() not in {f"pvwattsv{i}" for i in range(8)}
|
@@ -4,7 +4,7 @@
|
|
4
4
|
"dtype": "float32",
|
5
5
|
"scale_factor": 1,
|
6
6
|
"type": "scalar",
|
7
|
-
"units": "
|
7
|
+
"units": "usd"
|
8
8
|
},
|
9
9
|
"fixed_charge_rate": {
|
10
10
|
"chunks": null,
|
@@ -18,13 +18,48 @@
|
|
18
18
|
"dtype": "float32",
|
19
19
|
"scale_factor": 1,
|
20
20
|
"type": "scalar",
|
21
|
-
"units": "
|
21
|
+
"units": "usd"
|
22
22
|
},
|
23
23
|
"variable_operating_cost": {
|
24
24
|
"chunks": null,
|
25
25
|
"dtype": "float32",
|
26
26
|
"scale_factor": 1,
|
27
27
|
"type": "scalar",
|
28
|
-
"units": "
|
28
|
+
"units": "usd/kWh"
|
29
|
+
},
|
30
|
+
"base_capital_cost": {
|
31
|
+
"chunks": null,
|
32
|
+
"dtype": "float32",
|
33
|
+
"scale_factor": 1,
|
34
|
+
"type": "scalar",
|
35
|
+
"units": "usd"
|
36
|
+
},
|
37
|
+
"base_fixed_operating_cost": {
|
38
|
+
"chunks": null,
|
39
|
+
"dtype": "float32",
|
40
|
+
"scale_factor": 1,
|
41
|
+
"type": "scalar",
|
42
|
+
"units": "usd"
|
43
|
+
},
|
44
|
+
"base_variable_operating_cost": {
|
45
|
+
"chunks": null,
|
46
|
+
"dtype": "float32",
|
47
|
+
"scale_factor": 1,
|
48
|
+
"type": "scalar",
|
49
|
+
"units": "usd/kWh"
|
50
|
+
},
|
51
|
+
"capital_cost_multiplier": {
|
52
|
+
"chunks": null,
|
53
|
+
"dtype": "float32",
|
54
|
+
"scale_factor": 1,
|
55
|
+
"type": "scalar",
|
56
|
+
"units": "unitless"
|
57
|
+
},
|
58
|
+
"system_capacity": {
|
59
|
+
"chunks": null,
|
60
|
+
"dtype": "float32",
|
61
|
+
"scale_factor": 1,
|
62
|
+
"type": "scalar",
|
63
|
+
"units": "kW"
|
29
64
|
}
|
30
65
|
}
|
reV/handlers/__init__.py
CHANGED
reV/handlers/multi_year.py
CHANGED
@@ -18,6 +18,7 @@ from rex.utilities.utilities import (
|
|
18
18
|
parse_year,
|
19
19
|
)
|
20
20
|
|
21
|
+
from reV.generation.base import LCOE_REQUIRED_OUTPUTS
|
21
22
|
from reV.config.output_request import SAMOutputRequest
|
22
23
|
from reV.handlers.outputs import Outputs
|
23
24
|
from reV.utilities import ModuleName, log_versions
|
@@ -60,8 +61,12 @@ class MultiYearGroup:
|
|
60
61
|
source files. This takes priority over `source_dir` and
|
61
62
|
`source_prefix` but is not used if `source_files` are
|
62
63
|
specified explicitly. By default, ``None``.
|
63
|
-
dsets : list | tuple, optional
|
64
|
-
List of datasets to collect.
|
64
|
+
dsets : str | list | tuple, optional
|
65
|
+
List of datasets to collect. This can be set to
|
66
|
+
``"PIPELINE"`` if running from the command line as part of a
|
67
|
+
reV pipeline. In this case, all the datasets from the
|
68
|
+
previous pipeline step will be collected.
|
69
|
+
By default, ``('cf_mean',)``.
|
65
70
|
pass_through_dsets : list | tuple, optional
|
66
71
|
Optional list of datasets that are identical in the
|
67
72
|
multi-year files (e.g. input datasets that don't vary from
|
@@ -76,10 +81,35 @@ class MultiYearGroup:
|
|
76
81
|
self._source_prefix = source_prefix
|
77
82
|
self._source_pattern = source_pattern
|
78
83
|
self._pass_through_dsets = None
|
79
|
-
|
80
|
-
self._pass_through_dsets = SAMOutputRequest(pass_through_dsets)
|
84
|
+
self._dsets = None
|
81
85
|
|
82
|
-
self.
|
86
|
+
self._parse_pass_through_dsets(dsets, pass_through_dsets or [])
|
87
|
+
self._parse_dsets(dsets)
|
88
|
+
|
89
|
+
def _parse_pass_through_dsets(self, dsets, pass_through_dsets):
|
90
|
+
"""Parse a multi-year pass-through dataset collection request.
|
91
|
+
|
92
|
+
Parameters
|
93
|
+
----------
|
94
|
+
dsets : str | list
|
95
|
+
One or more datasets to collect, or "PIPELINE"
|
96
|
+
pass_through_dsets : list
|
97
|
+
List of pass through datasets.
|
98
|
+
"""
|
99
|
+
if isinstance(dsets, str) and dsets == 'PIPELINE':
|
100
|
+
files = parse_previous_status(self._dirout, ModuleName.MULTI_YEAR)
|
101
|
+
with Resource(files[0]) as res:
|
102
|
+
dsets = res.datasets
|
103
|
+
|
104
|
+
if "lcoe_fcr" in dsets:
|
105
|
+
for dset in LCOE_REQUIRED_OUTPUTS:
|
106
|
+
if dset not in pass_through_dsets:
|
107
|
+
pass_through_dsets.append(dset)
|
108
|
+
if "dc_ac_ratio" in dsets:
|
109
|
+
if "dc_ac_ratio" not in pass_through_dsets:
|
110
|
+
pass_through_dsets.append("dc_ac_ratio")
|
111
|
+
|
112
|
+
self._pass_through_dsets = SAMOutputRequest(pass_through_dsets)
|
83
113
|
|
84
114
|
def _parse_dsets(self, dsets):
|
85
115
|
"""Parse a multi-year dataset collection request. Can handle PIPELINE
|
@@ -90,11 +120,6 @@ class MultiYearGroup:
|
|
90
120
|
----------
|
91
121
|
dsets : str | list
|
92
122
|
One or more datasets to collect, or "PIPELINE"
|
93
|
-
|
94
|
-
Returns
|
95
|
-
-------
|
96
|
-
dsets : SAMOutputRequest
|
97
|
-
Dataset list object.
|
98
123
|
"""
|
99
124
|
if isinstance(dsets, str) and dsets == 'PIPELINE':
|
100
125
|
files = parse_previous_status(self._dirout, ModuleName.MULTI_YEAR)
|
@@ -104,9 +129,7 @@ class MultiYearGroup:
|
|
104
129
|
and d != 'meta'
|
105
130
|
and d not in self.pass_through_dsets]
|
106
131
|
|
107
|
-
|
108
|
-
|
109
|
-
return dsets
|
132
|
+
self._dsets = SAMOutputRequest(dsets)
|
110
133
|
|
111
134
|
@property
|
112
135
|
def name(self):
|
@@ -815,10 +838,15 @@ def my_collect_groups(out_fpath, groups, clobber=True):
|
|
815
838
|
MultiYear.collect_means(out_fpath, group['source_files'],
|
816
839
|
dset, group=group['group'])
|
817
840
|
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
841
|
+
pass_through_dsets = group.get('pass_through_dsets') or []
|
842
|
+
if "lcoe_fcr" in group['dsets']:
|
843
|
+
for dset in LCOE_REQUIRED_OUTPUTS:
|
844
|
+
if dset not in pass_through_dsets:
|
845
|
+
pass_through_dsets.append(dset)
|
846
|
+
|
847
|
+
for dset in pass_through_dsets:
|
848
|
+
MultiYear.pass_through(out_fpath, group['source_files'],
|
849
|
+
dset, group=group['group'])
|
822
850
|
|
823
851
|
runtime = (time.time() - t0) / 60
|
824
852
|
logger.info('- {} collection completed in: {:.2f} min.'
|
reV/handlers/transmission.py
CHANGED
@@ -9,6 +9,7 @@ import os
|
|
9
9
|
import pandas as pd
|
10
10
|
from warnings import warn
|
11
11
|
|
12
|
+
from reV.utilities import SupplyCurveField
|
12
13
|
from reV.utilities.exceptions import (HandlerWarning, HandlerKeyError,
|
13
14
|
HandlerRuntimeError)
|
14
15
|
|
@@ -153,12 +154,17 @@ class TransmissionFeatures:
|
|
153
154
|
raise
|
154
155
|
|
155
156
|
trans_table = \
|
156
|
-
trans_table.rename(
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
157
|
+
trans_table.rename(
|
158
|
+
columns={'trans_line_gid': SupplyCurveField.TRANS_GID,
|
159
|
+
'trans_gids': 'trans_line_gids'})
|
160
|
+
|
161
|
+
contains_dist_in_miles = "dist_mi" in trans_table
|
162
|
+
missing_km_dist = SupplyCurveField.DIST_SPUR_KM not in trans_table
|
163
|
+
if contains_dist_in_miles and missing_km_dist:
|
164
|
+
trans_table = trans_table.rename(
|
165
|
+
columns={"dist_mi": SupplyCurveField.DIST_SPUR_KM}
|
166
|
+
)
|
167
|
+
trans_table[SupplyCurveField.DIST_SPUR_KM] *= 1.60934
|
162
168
|
|
163
169
|
return trans_table
|
164
170
|
|
@@ -184,23 +190,28 @@ class TransmissionFeatures:
|
|
184
190
|
features = {}
|
185
191
|
|
186
192
|
cap_frac = self._avail_cap_frac
|
187
|
-
trans_features = trans_table.groupby(
|
193
|
+
trans_features = trans_table.groupby(SupplyCurveField.TRANS_GID)
|
194
|
+
trans_features = trans_features.first()
|
188
195
|
|
189
196
|
for gid, feature in trans_features.iterrows():
|
190
|
-
name = feature[
|
197
|
+
name = feature[SupplyCurveField.TRANS_TYPE].lower()
|
191
198
|
feature_dict = {'type': name}
|
192
199
|
|
193
200
|
if name == 'transline':
|
194
|
-
feature_dict[
|
201
|
+
feature_dict[SupplyCurveField.TRANS_CAPACITY] = (
|
202
|
+
feature['ac_cap'] * cap_frac
|
203
|
+
)
|
195
204
|
|
196
205
|
elif name == 'substation':
|
197
206
|
feature_dict['lines'] = json.loads(feature['trans_line_gids'])
|
198
207
|
|
199
208
|
elif name == 'loadcen':
|
200
|
-
feature_dict[
|
209
|
+
feature_dict[SupplyCurveField.TRANS_CAPACITY] = (
|
210
|
+
feature['ac_cap'] * cap_frac
|
211
|
+
)
|
201
212
|
|
202
213
|
elif name == 'pcaloadcen':
|
203
|
-
feature_dict[
|
214
|
+
feature_dict[SupplyCurveField.TRANS_CAPACITY] = None
|
204
215
|
|
205
216
|
else:
|
206
217
|
msg = ('Cannot not recognize feature type "{}" '
|
@@ -297,7 +308,8 @@ class TransmissionFeatures:
|
|
297
308
|
Substation available capacity
|
298
309
|
"""
|
299
310
|
try:
|
300
|
-
line_caps = [self[l_gid][
|
311
|
+
line_caps = [self[l_gid][SupplyCurveField.TRANS_CAPACITY]
|
312
|
+
for l_gid in line_gids]
|
301
313
|
except HandlerKeyError as e:
|
302
314
|
msg = ('Could not find capacities for substation gid {} and '
|
303
315
|
'connected lines: {}'.format(gid, line_gids))
|
@@ -331,8 +343,8 @@ class TransmissionFeatures:
|
|
331
343
|
|
332
344
|
feature = self[gid]
|
333
345
|
|
334
|
-
if
|
335
|
-
avail_cap = feature[
|
346
|
+
if SupplyCurveField.TRANS_CAPACITY in feature:
|
347
|
+
avail_cap = feature[SupplyCurveField.TRANS_CAPACITY]
|
336
348
|
|
337
349
|
elif 'lines' in feature:
|
338
350
|
avail_cap = self._substation_capacity(gid, feature['lines'])
|
@@ -387,7 +399,7 @@ class TransmissionFeatures:
|
|
387
399
|
capacity : float
|
388
400
|
Capacity needed in MW
|
389
401
|
"""
|
390
|
-
avail_cap = self[gid][
|
402
|
+
avail_cap = self[gid][SupplyCurveField.TRANS_CAPACITY]
|
391
403
|
|
392
404
|
if avail_cap < capacity:
|
393
405
|
msg = ("Cannot connect to {}: "
|
@@ -397,7 +409,7 @@ class TransmissionFeatures:
|
|
397
409
|
logger.error(msg)
|
398
410
|
raise RuntimeError(msg)
|
399
411
|
|
400
|
-
self[gid][
|
412
|
+
self[gid][SupplyCurveField.TRANS_CAPACITY] -= capacity
|
401
413
|
|
402
414
|
def _fill_lines(self, line_gids, line_caps, capacity):
|
403
415
|
"""
|
@@ -471,7 +483,7 @@ class TransmissionFeatures:
|
|
471
483
|
Substation connection is limited by maximum capacity of the
|
472
484
|
attached lines
|
473
485
|
"""
|
474
|
-
line_caps = np.array([self[gid][
|
486
|
+
line_caps = np.array([self[gid][SupplyCurveField.TRANS_CAPACITY]
|
475
487
|
for gid in line_gids])
|
476
488
|
if self._line_limited:
|
477
489
|
gid = line_gids[np.argmax(line_caps)]
|
@@ -603,8 +615,8 @@ class TransmissionFeatures:
|
|
603
615
|
raise
|
604
616
|
|
605
617
|
feature_cap = pd.Series(feature_cap)
|
606
|
-
feature_cap.name =
|
607
|
-
feature_cap.index.name =
|
618
|
+
feature_cap.name = SupplyCurveField.TRANS_CAPACITY
|
619
|
+
feature_cap.index.name = SupplyCurveField.TRANS_GID
|
608
620
|
feature_cap = feature_cap.to_frame().reset_index()
|
609
621
|
|
610
622
|
return feature_cap
|
@@ -635,16 +647,20 @@ class TransmissionCosts(TransmissionFeatures):
|
|
635
647
|
|
636
648
|
features = {}
|
637
649
|
|
638
|
-
if
|
650
|
+
if SupplyCurveField.TRANS_CAPACITY not in trans_table:
|
639
651
|
kwargs = {'avail_cap_frac': self._avail_cap_frac}
|
640
652
|
fc = TransmissionFeatures.feature_capacity(trans_table,
|
641
653
|
**kwargs)
|
642
|
-
trans_table = trans_table.merge(fc, on=
|
654
|
+
trans_table = trans_table.merge(fc, on=SupplyCurveField.TRANS_GID)
|
643
655
|
|
644
|
-
trans_features = trans_table.groupby(
|
656
|
+
trans_features = trans_table.groupby(SupplyCurveField.TRANS_GID)
|
657
|
+
trans_features = trans_features.first()
|
645
658
|
for gid, feature in trans_features.iterrows():
|
646
|
-
name = feature[
|
647
|
-
feature_dict = {'type': name,
|
659
|
+
name = feature[SupplyCurveField.TRANS_TYPE].lower()
|
660
|
+
feature_dict = {'type': name,
|
661
|
+
SupplyCurveField.TRANS_CAPACITY: (
|
662
|
+
feature[SupplyCurveField.TRANS_CAPACITY]
|
663
|
+
)}
|
648
664
|
features[gid] = feature_dict
|
649
665
|
|
650
666
|
return features
|
@@ -665,7 +681,7 @@ class TransmissionCosts(TransmissionFeatures):
|
|
665
681
|
default = 100%
|
666
682
|
"""
|
667
683
|
|
668
|
-
return self[gid][
|
684
|
+
return self[gid][SupplyCurveField.TRANS_CAPACITY]
|
669
685
|
|
670
686
|
@classmethod
|
671
687
|
def feature_costs(cls, trans_table, capacity=None, line_tie_in_cost=14000,
|
@@ -722,8 +738,9 @@ class TransmissionCosts(TransmissionFeatures):
|
|
722
738
|
costs = []
|
723
739
|
for _, row in trans_table.iterrows():
|
724
740
|
tm = row.get('transmission_multiplier', 1)
|
725
|
-
costs.append(feature.cost(row[
|
726
|
-
row[
|
741
|
+
costs.append(feature.cost(row[SupplyCurveField.TRANS_GID],
|
742
|
+
row[SupplyCurveField.DIST_SPUR_KM],
|
743
|
+
capacity=capacity,
|
727
744
|
transmission_multiplier=tm))
|
728
745
|
except Exception:
|
729
746
|
logger.exception("Error computing costs for all connections in {}"
|
reV/hybrids/hybrid_methods.py
CHANGED
@@ -31,9 +31,9 @@ def aggregate_solar_capacity(h):
|
|
31
31
|
capacity ratio and the solar capacity is copied into this new
|
32
32
|
column.
|
33
33
|
"""
|
34
|
-
if f'hybrid_solar_{SupplyCurveField.
|
34
|
+
if f'hybrid_solar_{SupplyCurveField.CAPACITY_AC_MW}' in h.hybrid_meta:
|
35
35
|
return None
|
36
|
-
return h.hybrid_meta[f'solar_{SupplyCurveField.
|
36
|
+
return h.hybrid_meta[f'solar_{SupplyCurveField.CAPACITY_AC_MW}']
|
37
37
|
|
38
38
|
|
39
39
|
def aggregate_wind_capacity(h):
|
@@ -60,9 +60,9 @@ def aggregate_wind_capacity(h):
|
|
60
60
|
exist, it is assumed that there is no limit on the solar to wind
|
61
61
|
capacity ratio and the wind capacity is copied into this new column.
|
62
62
|
"""
|
63
|
-
if f'hybrid_wind_{SupplyCurveField.
|
63
|
+
if f'hybrid_wind_{SupplyCurveField.CAPACITY_AC_MW}' in h.hybrid_meta:
|
64
64
|
return None
|
65
|
-
return h.hybrid_meta[f'wind_{SupplyCurveField.
|
65
|
+
return h.hybrid_meta[f'wind_{SupplyCurveField.CAPACITY_AC_MW}']
|
66
66
|
|
67
67
|
|
68
68
|
def aggregate_capacity(h):
|
@@ -81,8 +81,8 @@ def aggregate_capacity(h):
|
|
81
81
|
A series of data containing the aggregated capacity, or `None`
|
82
82
|
if the capacity columns are missing.
|
83
83
|
"""
|
84
|
-
sc = f'hybrid_solar_{SupplyCurveField.
|
85
|
-
wc = f'hybrid_wind_{SupplyCurveField.
|
84
|
+
sc = f'hybrid_solar_{SupplyCurveField.CAPACITY_AC_MW}'
|
85
|
+
wc = f'hybrid_wind_{SupplyCurveField.CAPACITY_AC_MW}'
|
86
86
|
missing_solar_cap = sc not in h.hybrid_meta.columns
|
87
87
|
missing_wind_cap = wc not in h.hybrid_meta.columns
|
88
88
|
if missing_solar_cap or missing_wind_cap:
|
@@ -109,10 +109,10 @@ def aggregate_capacity_factor(h):
|
|
109
109
|
if the capacity and/or mean_cf columns are missing.
|
110
110
|
"""
|
111
111
|
|
112
|
-
sc = f'hybrid_solar_{SupplyCurveField.
|
113
|
-
wc = f'hybrid_wind_{SupplyCurveField.
|
114
|
-
scf = f'solar_{SupplyCurveField.
|
115
|
-
wcf = f'wind_{SupplyCurveField.
|
112
|
+
sc = f'hybrid_solar_{SupplyCurveField.CAPACITY_AC_MW}'
|
113
|
+
wc = f'hybrid_wind_{SupplyCurveField.CAPACITY_AC_MW}'
|
114
|
+
scf = f'solar_{SupplyCurveField.MEAN_CF_AC}'
|
115
|
+
wcf = f'wind_{SupplyCurveField.MEAN_CF_AC}'
|
116
116
|
missing_solar_cap = sc not in h.hybrid_meta.columns
|
117
117
|
missing_wind_cap = wc not in h.hybrid_meta.columns
|
118
118
|
missing_solar_mean_cf = scf not in h.hybrid_meta.columns
|
@@ -130,8 +130,10 @@ def aggregate_capacity_factor(h):
|
|
130
130
|
|
131
131
|
|
132
132
|
HYBRID_METHODS = {
|
133
|
-
f'hybrid_solar_{SupplyCurveField.
|
134
|
-
|
135
|
-
|
136
|
-
f'
|
133
|
+
f'hybrid_solar_{SupplyCurveField.CAPACITY_AC_MW}': (
|
134
|
+
aggregate_solar_capacity
|
135
|
+
),
|
136
|
+
f'hybrid_wind_{SupplyCurveField.CAPACITY_AC_MW}': aggregate_wind_capacity,
|
137
|
+
f'hybrid_{SupplyCurveField.CAPACITY_AC_MW}': aggregate_capacity,
|
138
|
+
f'hybrid_{SupplyCurveField.MEAN_CF_AC}': aggregate_capacity_factor
|
137
139
|
}
|
reV/hybrids/hybrids.py
CHANGED
@@ -38,11 +38,11 @@ NON_DUPLICATE_COLS = {
|
|
38
38
|
SupplyCurveField.SC_POINT_GID, SupplyCurveField.SC_ROW_IND,
|
39
39
|
SupplyCurveField.SC_COL_IND
|
40
40
|
}
|
41
|
-
|
42
|
-
DEFAULT_FILL_VALUES = {f'solar_{SupplyCurveField.
|
43
|
-
f'wind_{SupplyCurveField.
|
44
|
-
f'solar_{SupplyCurveField.
|
45
|
-
f'wind_{SupplyCurveField.
|
41
|
+
HYBRIDS_GID_COL = "gid"
|
42
|
+
DEFAULT_FILL_VALUES = {f'solar_{SupplyCurveField.CAPACITY_AC_MW}': 0,
|
43
|
+
f'wind_{SupplyCurveField.CAPACITY_AC_MW}': 0,
|
44
|
+
f'solar_{SupplyCurveField.MEAN_CF_AC}': 0,
|
45
|
+
f'wind_{SupplyCurveField.MEAN_CF_AC}': 0}
|
46
46
|
OUTPUT_PROFILE_NAMES = ['hybrid_profile',
|
47
47
|
'hybrid_solar_profile',
|
48
48
|
'hybrid_wind_profile']
|
@@ -702,7 +702,7 @@ class MetaHybridizer:
|
|
702
702
|
self._propagate_duplicate_cols(duplicate_cols)
|
703
703
|
self._drop_cols(duplicate_cols)
|
704
704
|
self._hybrid_meta.rename(self.__col_name_map, inplace=True, axis=1)
|
705
|
-
self._hybrid_meta.index.name =
|
705
|
+
self._hybrid_meta.index.name = HYBRIDS_GID_COL
|
706
706
|
|
707
707
|
def _propagate_duplicate_cols(self, duplicate_cols):
|
708
708
|
"""Fill missing column values from outer merge."""
|
@@ -713,9 +713,9 @@ class MetaHybridizer:
|
|
713
713
|
self._hybrid_meta.loc[null_idx, no_suffix] = non_null_vals
|
714
714
|
|
715
715
|
def _drop_cols(self, duplicate_cols):
|
716
|
-
"""Drop any
|
716
|
+
"""Drop any remaining duplicate and 'HYBRIDS_GID_COL' columns."""
|
717
717
|
self._hybrid_meta.drop(
|
718
|
-
duplicate_cols +
|
718
|
+
duplicate_cols + [HYBRIDS_GID_COL],
|
719
719
|
axis=1,
|
720
720
|
inplace=True,
|
721
721
|
errors="ignore",
|
@@ -1196,8 +1196,8 @@ class Hybridization:
|
|
1196
1196
|
def __rep_profile_hybridization_params(self):
|
1197
1197
|
"""Zip the rep profile hybridization parameters."""
|
1198
1198
|
|
1199
|
-
cap_col_names = [f"hybrid_solar_{SupplyCurveField.
|
1200
|
-
f"hybrid_wind_{SupplyCurveField.
|
1199
|
+
cap_col_names = [f"hybrid_solar_{SupplyCurveField.CAPACITY_AC_MW}",
|
1200
|
+
f"hybrid_wind_{SupplyCurveField.CAPACITY_AC_MW}"]
|
1201
1201
|
idx_maps = [
|
1202
1202
|
self.meta_hybridizer.solar_profile_indices_map,
|
1203
1203
|
self.meta_hybridizer.wind_profile_indices_map,
|
reV/nrwal/nrwal.py
CHANGED
@@ -37,7 +37,7 @@ class RevNrwal:
|
|
37
37
|
|
38
38
|
def __init__(self, gen_fpath, site_data, sam_files, nrwal_configs,
|
39
39
|
output_request, save_raw=True,
|
40
|
-
meta_gid_col=ResourceMetaField.GID,
|
40
|
+
meta_gid_col=str(ResourceMetaField.GID), # str() to fix docs
|
41
41
|
site_meta_cols=None):
|
42
42
|
"""Framework to handle reV-NRWAL analysis.
|
43
43
|
|
reV/qa_qc/qa_qc.py
CHANGED
@@ -105,7 +105,7 @@ class QaQc:
|
|
105
105
|
if file.endswith(".csv"):
|
106
106
|
summary_csv = os.path.join(self.out_dir, file)
|
107
107
|
summary = pd.read_csv(summary_csv)
|
108
|
-
has_right_cols = (
|
108
|
+
has_right_cols = ("gid" in summary
|
109
109
|
and SupplyCurveField.LATITUDE in summary
|
110
110
|
and SupplyCurveField.LONGITUDE in summary)
|
111
111
|
if has_right_cols:
|
reV/qa_qc/summary.py
CHANGED
@@ -597,11 +597,11 @@ class SummaryPlots(PlotBase):
|
|
597
597
|
sc_df : pandas.DataFrame
|
598
598
|
Supply curve data
|
599
599
|
"""
|
600
|
-
values = [SupplyCurveField.
|
600
|
+
values = [SupplyCurveField.CAPACITY_AC_MW, lcoe]
|
601
601
|
self._check_value(self.summary, values, scatter=False)
|
602
602
|
sc_df = self.summary[values].sort_values(lcoe)
|
603
603
|
sc_df['cumulative_capacity'] = (
|
604
|
-
sc_df[SupplyCurveField.
|
604
|
+
sc_df[SupplyCurveField.CAPACITY_AC_MW].cumsum()
|
605
605
|
)
|
606
606
|
|
607
607
|
return sc_df
|
@@ -800,11 +800,11 @@ class SupplyCurvePlot(PlotBase):
|
|
800
800
|
sc_df : pandas.DataFrame
|
801
801
|
Supply curve data
|
802
802
|
"""
|
803
|
-
values = [SupplyCurveField.
|
803
|
+
values = [SupplyCurveField.CAPACITY_AC_MW, lcoe]
|
804
804
|
self._check_value(self.sc_table, values, scatter=False)
|
805
805
|
sc_df = self.sc_table[values].sort_values(lcoe)
|
806
806
|
sc_df['cumulative_capacity'] = (
|
807
|
-
sc_df[SupplyCurveField.
|
807
|
+
sc_df[SupplyCurveField.CAPACITY_AC_MW].cumsum()
|
808
808
|
)
|
809
809
|
|
810
810
|
return sc_df
|
reV/rep_profiles/rep_profiles.py
CHANGED
@@ -953,7 +953,7 @@ class RepProfiles(RepProfilesBase):
|
|
953
953
|
def __init__(self, gen_fpath, rev_summary, reg_cols,
|
954
954
|
cf_dset='cf_profile',
|
955
955
|
rep_method='meanoid', err_method='rmse',
|
956
|
-
weight=SupplyCurveField.GID_COUNTS,
|
956
|
+
weight=str(SupplyCurveField.GID_COUNTS), # str() to fix docs
|
957
957
|
n_profiles=1, aggregate_profiles=False):
|
958
958
|
"""ReV rep profiles class.
|
959
959
|
|
reV/supply_curve/exclusions.py
CHANGED
@@ -111,7 +111,7 @@ class LayerMask:
|
|
111
111
|
specifications to create a boolean mask that defines the
|
112
112
|
extent to which the original mask should be applied.
|
113
113
|
For example, suppose you specify the input the following
|
114
|
-
way
|
114
|
+
way::
|
115
115
|
|
116
116
|
input_dict = {
|
117
117
|
"viewsheds": {
|