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/bespoke/bespoke.py
CHANGED
@@ -222,6 +222,7 @@ class BespokeSinglePlant:
|
|
222
222
|
capital_cost_function,
|
223
223
|
fixed_operating_cost_function,
|
224
224
|
variable_operating_cost_function,
|
225
|
+
balance_of_system_cost_function,
|
225
226
|
min_spacing="5x",
|
226
227
|
wake_loss_multiplier=1,
|
227
228
|
ga_kwargs=None,
|
@@ -262,19 +263,33 @@ class BespokeSinglePlant:
|
|
262
263
|
return the objective to be minimized during layout optimization.
|
263
264
|
Variables available are:
|
264
265
|
|
265
|
-
- n_turbines
|
266
|
-
- system_capacity
|
267
|
-
- aep
|
268
|
-
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
-
|
266
|
+
- ``n_turbines``: the number of turbines
|
267
|
+
- ``system_capacity``: wind plant capacity
|
268
|
+
- ``aep``: annual energy production
|
269
|
+
- ``avg_sl_dist_to_center_m``: Average straight-line
|
270
|
+
distance to the supply curve point center from all
|
271
|
+
turbine locations (in m). Useful for computing plant
|
272
|
+
BOS costs.
|
273
|
+
- ``avg_sl_dist_to_medoid_m``: Average straight-line
|
274
|
+
distance to the medoid of all turbine locations
|
275
|
+
(in m). Useful for computing plant BOS costs.
|
276
|
+
- ``nn_conn_dist_m``: Total BOS connection distance
|
277
|
+
using nearest-neighbor connections. This variable is
|
278
|
+
only available for the
|
279
|
+
``balance_of_system_cost_function`` equation.
|
280
|
+
- ``fixed_charge_rate``: user input fixed_charge_rate if
|
281
|
+
included as part of the sam system config.
|
282
|
+
- ``capital_cost``: plant capital cost as evaluated
|
273
283
|
by `capital_cost_function`
|
274
|
-
- fixed_operating_cost
|
275
|
-
evaluated by `fixed_operating_cost_function`
|
276
|
-
- variable_operating_cost
|
277
|
-
as evaluated by
|
284
|
+
- ``fixed_operating_cost``: plant fixed annual operating
|
285
|
+
cost as evaluated by `fixed_operating_cost_function`
|
286
|
+
- ``variable_operating_cost``: plant variable annual
|
287
|
+
operating cost as evaluated by
|
288
|
+
`variable_operating_cost_function`
|
289
|
+
- ``balance_of_system_cost``: plant balance of system
|
290
|
+
cost as evaluated by `balance_of_system_cost_function`
|
291
|
+
- ``self.wind_plant``: the SAM wind plant object,
|
292
|
+
through which all SAM variables can be accessed
|
278
293
|
|
279
294
|
capital_cost_function : str
|
280
295
|
The plant capital cost function as a string, must return the total
|
@@ -287,6 +302,16 @@ class BespokeSinglePlant:
|
|
287
302
|
variable_operating_cost_function : str
|
288
303
|
The plant annual variable operating cost function as a string, must
|
289
304
|
return the variable operating cost in $/kWh. Has access to the same
|
305
|
+
variables as the objective_function. You can set this to "0"
|
306
|
+
to effectively ignore variable operating costs.
|
307
|
+
balance_of_system_cost_function : str
|
308
|
+
The plant balance-of-system cost function as a string, must
|
309
|
+
return the variable operating cost in $. Has access to the
|
310
|
+
same variables as the objective_function. You can set this
|
311
|
+
to "0" to effectively ignore balance-of-system costs.
|
312
|
+
balance_of_system_cost_function : str
|
313
|
+
The plant balance-of-system cost function as a string, must
|
314
|
+
return the variable operating cost in $. Has access to the same
|
290
315
|
variables as the objective_function.
|
291
316
|
min_spacing : float | int | str
|
292
317
|
Minimum spacing between turbines in meters. Can also be a string
|
@@ -431,6 +456,7 @@ class BespokeSinglePlant:
|
|
431
456
|
self.variable_operating_cost_function = (
|
432
457
|
variable_operating_cost_function
|
433
458
|
)
|
459
|
+
self.balance_of_system_cost_function = balance_of_system_cost_function
|
434
460
|
self.min_spacing = min_spacing
|
435
461
|
self.wake_loss_multiplier = wake_loss_multiplier
|
436
462
|
self.ga_kwargs = ga_kwargs or {}
|
@@ -540,7 +566,7 @@ class BespokeSinglePlant:
|
|
540
566
|
|
541
567
|
# {meta_column: sam_sys_input_key}
|
542
568
|
required = {
|
543
|
-
SupplyCurveField.
|
569
|
+
SupplyCurveField.CAPACITY_AC_MW: "system_capacity",
|
544
570
|
SupplyCurveField.TURBINE_X_COORDS: "wind_farm_xCoordinates",
|
545
571
|
SupplyCurveField.TURBINE_Y_COORDS: "wind_farm_yCoordinates",
|
546
572
|
}
|
@@ -812,28 +838,23 @@ class BespokeSinglePlant:
|
|
812
838
|
[float(np.round(n, 1)) for n in self.sc_point.gid_counts]
|
813
839
|
)
|
814
840
|
|
815
|
-
with SupplyCurveExtent(
|
816
|
-
self.sc_point._excl_fpath, resolution=self.sc_point.resolution
|
817
|
-
) as sc:
|
818
|
-
row_ind, col_ind = sc.get_sc_row_col_ind(self.sc_point.gid)
|
819
|
-
|
820
841
|
self._meta = pd.DataFrame(
|
821
842
|
{
|
822
|
-
|
823
|
-
SupplyCurveField.SC_ROW_IND: row_ind,
|
824
|
-
SupplyCurveField.SC_COL_IND: col_ind,
|
825
|
-
SupplyCurveField.GID: self.sc_point.gid,
|
843
|
+
"gid": self.sc_point.gid, # needed for collection
|
826
844
|
SupplyCurveField.LATITUDE: self.sc_point.latitude,
|
827
845
|
SupplyCurveField.LONGITUDE: self.sc_point.longitude,
|
828
|
-
SupplyCurveField.TIMEZONE: self.sc_point.timezone,
|
829
846
|
SupplyCurveField.COUNTRY: self.sc_point.country,
|
830
847
|
SupplyCurveField.STATE: self.sc_point.state,
|
831
848
|
SupplyCurveField.COUNTY: self.sc_point.county,
|
832
849
|
SupplyCurveField.ELEVATION: self.sc_point.elevation,
|
833
|
-
SupplyCurveField.
|
850
|
+
SupplyCurveField.TIMEZONE: self.sc_point.timezone,
|
851
|
+
SupplyCurveField.SC_POINT_GID: self.sc_point.sc_point_gid,
|
852
|
+
SupplyCurveField.SC_ROW_IND: self.sc_point.sc_row_ind,
|
853
|
+
SupplyCurveField.SC_COL_IND: self.sc_point.sc_col_ind,
|
834
854
|
SupplyCurveField.RES_GIDS: res_gids,
|
835
855
|
SupplyCurveField.GID_COUNTS: gid_counts,
|
836
856
|
SupplyCurveField.N_GIDS: self.sc_point.n_gids,
|
857
|
+
SupplyCurveField.OFFSHORE: self.sc_point.offshore,
|
837
858
|
SupplyCurveField.AREA_SQ_KM: self.sc_point.area,
|
838
859
|
},
|
839
860
|
index=[self.sc_point.gid],
|
@@ -1026,6 +1047,7 @@ class BespokeSinglePlant:
|
|
1026
1047
|
self.capital_cost_function,
|
1027
1048
|
self.fixed_operating_cost_function,
|
1028
1049
|
self.variable_operating_cost_function,
|
1050
|
+
self.balance_of_system_cost_function,
|
1029
1051
|
self.include_mask,
|
1030
1052
|
self.pixel_side_length,
|
1031
1053
|
self.min_spacing,
|
@@ -1046,13 +1068,13 @@ class BespokeSinglePlant:
|
|
1046
1068
|
"multi-year mean AEP."
|
1047
1069
|
)
|
1048
1070
|
|
1049
|
-
fcr = lcoe_kwargs[
|
1050
|
-
|
1051
|
-
foc = lcoe_kwargs[
|
1052
|
-
voc = lcoe_kwargs[
|
1053
|
-
aep = self.outputs[
|
1071
|
+
fcr = lcoe_kwargs['fixed_charge_rate']
|
1072
|
+
cc = lcoe_kwargs['capital_cost']
|
1073
|
+
foc = lcoe_kwargs['fixed_operating_cost']
|
1074
|
+
voc = lcoe_kwargs['variable_operating_cost']
|
1075
|
+
aep = self.outputs['annual_energy-means']
|
1054
1076
|
|
1055
|
-
my_mean_lcoe = lcoe_fcr(fcr,
|
1077
|
+
my_mean_lcoe = lcoe_fcr(fcr, cc, foc, aep, voc)
|
1056
1078
|
|
1057
1079
|
self._outputs["lcoe_fcr-means"] = my_mean_lcoe
|
1058
1080
|
self._meta[SupplyCurveField.MEAN_LCOE] = my_mean_lcoe
|
@@ -1068,21 +1090,28 @@ class BespokeSinglePlant:
|
|
1068
1090
|
sam_sys_inputs, normalized to the original system_capacity, and
|
1069
1091
|
updated based on the bespoke optimized system_capacity, includes
|
1070
1092
|
fixed_charge_rate, system_capacity (kW), capital_cost ($),
|
1071
|
-
fixed_operating_cos ($), variable_operating_cost ($/kWh)
|
1072
|
-
Data source priority: outputs,
|
1073
|
-
original_sam_sys_inputs, meta
|
1093
|
+
fixed_operating_cos ($), variable_operating_cost ($/kWh),
|
1094
|
+
balance_of_system_cost ($). Data source priority: outputs,
|
1095
|
+
plant_optimizer, original_sam_sys_inputs, meta
|
1074
1096
|
"""
|
1075
1097
|
|
1076
|
-
|
1077
|
-
"fixed_charge_rate",
|
1078
|
-
"system_capacity",
|
1079
|
-
"capital_cost",
|
1080
|
-
"fixed_operating_cost"
|
1081
|
-
|
1082
|
-
|
1098
|
+
kwargs_map = {
|
1099
|
+
"fixed_charge_rate": SupplyCurveField.FIXED_CHARGE_RATE,
|
1100
|
+
"system_capacity": SupplyCurveField.CAPACITY_AC_MW,
|
1101
|
+
"capital_cost": SupplyCurveField.BESPOKE_CAPITAL_COST,
|
1102
|
+
"fixed_operating_cost": (
|
1103
|
+
SupplyCurveField.BESPOKE_FIXED_OPERATING_COST
|
1104
|
+
),
|
1105
|
+
"variable_operating_cost": (
|
1106
|
+
SupplyCurveField.BESPOKE_VARIABLE_OPERATING_COST
|
1107
|
+
),
|
1108
|
+
"balance_of_system_cost": (
|
1109
|
+
SupplyCurveField.BESPOKE_BALANCE_OF_SYSTEM_COST
|
1110
|
+
),
|
1111
|
+
}
|
1083
1112
|
lcoe_kwargs = {}
|
1084
1113
|
|
1085
|
-
for kwarg in
|
1114
|
+
for kwarg, meta_field in kwargs_map.items():
|
1086
1115
|
if kwarg in self.outputs:
|
1087
1116
|
lcoe_kwargs[kwarg] = self.outputs[kwarg]
|
1088
1117
|
elif getattr(self.plant_optimizer, kwarg, None) is not None:
|
@@ -1092,11 +1121,13 @@ class BespokeSinglePlant:
|
|
1092
1121
|
elif kwarg in self.meta:
|
1093
1122
|
value = float(self.meta[kwarg].values[0])
|
1094
1123
|
lcoe_kwargs[kwarg] = value
|
1124
|
+
elif meta_field in self.meta:
|
1125
|
+
value = float(self.meta[meta_field].values[0])
|
1126
|
+
if meta_field == SupplyCurveField.CAPACITY_AC_MW:
|
1127
|
+
value *= 1000 # MW to kW
|
1128
|
+
lcoe_kwargs[kwarg] = value
|
1095
1129
|
|
1096
|
-
for k
|
1097
|
-
self._meta[k] = v
|
1098
|
-
|
1099
|
-
missing = [k for k in kwargs_list if k not in lcoe_kwargs]
|
1130
|
+
missing = [k for k in kwargs_map if k not in lcoe_kwargs]
|
1100
1131
|
if any(missing):
|
1101
1132
|
msg = (
|
1102
1133
|
"Could not find these LCOE kwargs in outputs, "
|
@@ -1107,6 +1138,8 @@ class BespokeSinglePlant:
|
|
1107
1138
|
logger.error(msg)
|
1108
1139
|
raise KeyError(msg)
|
1109
1140
|
|
1141
|
+
bos = lcoe_kwargs.pop("balance_of_system_cost")
|
1142
|
+
lcoe_kwargs["capital_cost"] = lcoe_kwargs["capital_cost"] + bos
|
1110
1143
|
return lcoe_kwargs
|
1111
1144
|
|
1112
1145
|
@staticmethod
|
@@ -1153,19 +1186,18 @@ class BespokeSinglePlant:
|
|
1153
1186
|
raise ModuleNotFoundError(msg)
|
1154
1187
|
|
1155
1188
|
@staticmethod
|
1156
|
-
def _check_sys_inputs(
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
):
|
1189
|
+
def _check_sys_inputs(plant1, plant2,
|
1190
|
+
ignore=('wind_resource_model_choice',
|
1191
|
+
'wind_resource_data',
|
1192
|
+
'wind_turbine_powercurve_powerout',
|
1193
|
+
'hourly',
|
1194
|
+
'capital_cost',
|
1195
|
+
'fixed_operating_cost',
|
1196
|
+
'variable_operating_cost',
|
1197
|
+
'balance_of_system_cost',
|
1198
|
+
'base_capital_cost',
|
1199
|
+
'base_fixed_operating_cost',
|
1200
|
+
'base_variable_operating_cost')):
|
1169
1201
|
"""Check two reV-SAM models for matching system inputs.
|
1170
1202
|
|
1171
1203
|
Parameters
|
@@ -1230,9 +1262,14 @@ class BespokeSinglePlant:
|
|
1230
1262
|
|
1231
1263
|
self._outputs.update(means)
|
1232
1264
|
|
1265
|
+
self._meta[SupplyCurveField.MEAN_RES] = self.res_df["windspeed"].mean()
|
1266
|
+
self._meta[SupplyCurveField.MEAN_CF_DC] = np.nan
|
1267
|
+
self._meta[SupplyCurveField.MEAN_CF_AC] = np.nan
|
1268
|
+
self._meta[SupplyCurveField.MEAN_LCOE] = np.nan
|
1269
|
+
self._meta[SupplyCurveField.SC_POINT_ANNUAL_ENERGY_MW] = np.nan
|
1233
1270
|
# copy dataset outputs to meta data for supply curve table summary
|
1234
1271
|
if "cf_mean-means" in self.outputs:
|
1235
|
-
self._meta.loc[:, SupplyCurveField.
|
1272
|
+
self._meta.loc[:, SupplyCurveField.MEAN_CF_AC] = self.outputs[
|
1236
1273
|
"cf_mean-means"
|
1237
1274
|
]
|
1238
1275
|
if "lcoe_fcr-means" in self.outputs:
|
@@ -1240,6 +1277,10 @@ class BespokeSinglePlant:
|
|
1240
1277
|
"lcoe_fcr-means"
|
1241
1278
|
]
|
1242
1279
|
self.recalc_lcoe()
|
1280
|
+
if "annual_energy-means" in self.outputs:
|
1281
|
+
self._meta[SupplyCurveField.SC_POINT_ANNUAL_ENERGY_MW] = (
|
1282
|
+
self.outputs["annual_energy-means"] / 1000
|
1283
|
+
)
|
1243
1284
|
|
1244
1285
|
logger.debug("Timeseries analysis complete!")
|
1245
1286
|
|
@@ -1268,9 +1309,12 @@ class BespokeSinglePlant:
|
|
1268
1309
|
logger.exception(msg)
|
1269
1310
|
raise RuntimeError(msg) from e
|
1270
1311
|
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1312
|
+
self._outputs["full_polygons"] = self.plant_optimizer.full_polygons
|
1313
|
+
self._outputs["packing_polygons"] = (
|
1314
|
+
self.plant_optimizer.packing_polygons
|
1315
|
+
)
|
1316
|
+
system_capacity_kw = self.plant_optimizer.capacity
|
1317
|
+
self._outputs["system_capacity"] = system_capacity_kw
|
1274
1318
|
|
1275
1319
|
txc = [int(np.round(c)) for c in self.plant_optimizer.turbine_x]
|
1276
1320
|
tyc = [int(np.round(c)) for c in self.plant_optimizer.turbine_y]
|
@@ -1284,58 +1328,92 @@ class BespokeSinglePlant:
|
|
1284
1328
|
|
1285
1329
|
self._meta[SupplyCurveField.TURBINE_X_COORDS] = txc
|
1286
1330
|
self._meta[SupplyCurveField.TURBINE_Y_COORDS] = tyc
|
1287
|
-
self._meta[
|
1288
|
-
self._meta[
|
1331
|
+
self._meta[SupplyCurveField.POSSIBLE_X_COORDS] = pxc
|
1332
|
+
self._meta[SupplyCurveField.POSSIBLE_Y_COORDS] = pyc
|
1289
1333
|
|
1290
|
-
self.
|
1291
|
-
self.
|
1292
|
-
self.plant_optimizer.
|
1334
|
+
self._meta[SupplyCurveField.N_TURBINES] = self.plant_optimizer.nturbs
|
1335
|
+
self._meta["avg_sl_dist_to_center_m"] = (
|
1336
|
+
self.plant_optimizer.avg_sl_dist_to_center_m
|
1293
1337
|
)
|
1294
|
-
self.
|
1295
|
-
|
1296
|
-
|
1297
|
-
self._meta["
|
1298
|
-
self._meta[
|
1299
|
-
self._meta[
|
1300
|
-
|
1338
|
+
self._meta["avg_sl_dist_to_medoid_m"] = (
|
1339
|
+
self.plant_optimizer.avg_sl_dist_to_medoid_m
|
1340
|
+
)
|
1341
|
+
self._meta["nn_conn_dist_m"] = self.plant_optimizer.nn_conn_dist_m
|
1342
|
+
self._meta[SupplyCurveField.BESPOKE_AEP] = self.plant_optimizer.aep
|
1343
|
+
self._meta[SupplyCurveField.BESPOKE_OBJECTIVE] = (
|
1344
|
+
self.plant_optimizer.objective
|
1345
|
+
)
|
1346
|
+
self._meta[SupplyCurveField.BESPOKE_CAPITAL_COST] = (
|
1347
|
+
self.plant_optimizer.capital_cost
|
1348
|
+
)
|
1349
|
+
self._meta[SupplyCurveField.BESPOKE_FIXED_OPERATING_COST] = (
|
1301
1350
|
self.plant_optimizer.fixed_operating_cost
|
1302
1351
|
)
|
1303
|
-
self._meta[
|
1352
|
+
self._meta[SupplyCurveField.BESPOKE_VARIABLE_OPERATING_COST] = (
|
1304
1353
|
self.plant_optimizer.variable_operating_cost
|
1305
1354
|
)
|
1306
|
-
self._meta[
|
1307
|
-
|
1355
|
+
self._meta[SupplyCurveField.BESPOKE_BALANCE_OF_SYSTEM_COST] = (
|
1356
|
+
self.plant_optimizer.balance_of_system_cost
|
1357
|
+
)
|
1358
|
+
self._meta[SupplyCurveField.INCLUDED_AREA] = self.plant_optimizer.area
|
1359
|
+
self._meta[SupplyCurveField.INCLUDED_AREA_CAPACITY_DENSITY] = (
|
1308
1360
|
self.plant_optimizer.capacity_density
|
1309
1361
|
)
|
1310
|
-
self._meta[
|
1311
|
-
|
1362
|
+
self._meta[SupplyCurveField.CONVEX_HULL_AREA] = (
|
1363
|
+
self.plant_optimizer.convex_hull_area
|
1364
|
+
)
|
1365
|
+
self._meta[SupplyCurveField.CONVEX_HULL_CAPACITY_DENSITY] = (
|
1312
1366
|
self.plant_optimizer.convex_hull_capacity_density
|
1313
1367
|
)
|
1314
|
-
self._meta[
|
1368
|
+
self._meta[SupplyCurveField.FULL_CELL_CAPACITY_DENSITY] = (
|
1315
1369
|
self.plant_optimizer.full_cell_capacity_density
|
1316
1370
|
)
|
1317
1371
|
|
1318
|
-
logger.debug("Plant layout optimization complete!")
|
1319
|
-
|
1320
1372
|
# copy dataset outputs to meta data for supply curve table summary
|
1321
1373
|
# convert SAM system capacity in kW to reV supply curve cap in MW
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1374
|
+
capacity_ac_mw = system_capacity_kw / 1e3
|
1375
|
+
self._meta[SupplyCurveField.CAPACITY_AC_MW] = capacity_ac_mw
|
1376
|
+
self._meta[SupplyCurveField.CAPACITY_DC_MW] = np.nan
|
1325
1377
|
|
1326
1378
|
# add required ReEDS multipliers to meta
|
1327
1379
|
baseline_cost = self.plant_optimizer.capital_cost_per_kw(
|
1328
1380
|
capacity_mw=self._baseline_cap_mw
|
1329
1381
|
)
|
1330
|
-
|
1382
|
+
eos_mult = (self.plant_optimizer.capital_cost
|
1383
|
+
/ self.plant_optimizer.capacity
|
1384
|
+
/ baseline_cost)
|
1385
|
+
reg_mult = self.sam_sys_inputs.get("capital_cost_multiplier", 1)
|
1386
|
+
|
1387
|
+
self._meta[SupplyCurveField.EOS_MULT] = eos_mult
|
1388
|
+
self._meta[SupplyCurveField.REG_MULT] = reg_mult
|
1389
|
+
|
1390
|
+
cap_cost = (
|
1331
1391
|
self.plant_optimizer.capital_cost
|
1332
|
-
|
1333
|
-
|
1392
|
+
+ self.plant_optimizer.balance_of_system_cost
|
1393
|
+
)
|
1394
|
+
self._meta[SupplyCurveField.COST_SITE_OCC_USD_PER_AC_MW] = (
|
1395
|
+
cap_cost / capacity_ac_mw
|
1396
|
+
)
|
1397
|
+
self._meta[SupplyCurveField.COST_BASE_OCC_USD_PER_AC_MW] = (
|
1398
|
+
cap_cost / eos_mult / reg_mult / capacity_ac_mw
|
1399
|
+
)
|
1400
|
+
self._meta[SupplyCurveField.COST_SITE_FOC_USD_PER_AC_MW] = (
|
1401
|
+
self.plant_optimizer.fixed_operating_cost / capacity_ac_mw
|
1402
|
+
)
|
1403
|
+
self._meta[SupplyCurveField.COST_BASE_FOC_USD_PER_AC_MW] = (
|
1404
|
+
self.plant_optimizer.fixed_operating_cost / capacity_ac_mw
|
1405
|
+
)
|
1406
|
+
self._meta[SupplyCurveField.COST_SITE_VOC_USD_PER_AC_MW] = (
|
1407
|
+
self.plant_optimizer.variable_operating_cost / capacity_ac_mw
|
1334
1408
|
)
|
1335
|
-
self._meta[SupplyCurveField.
|
1336
|
-
|
1409
|
+
self._meta[SupplyCurveField.COST_BASE_VOC_USD_PER_AC_MW] = (
|
1410
|
+
self.plant_optimizer.variable_operating_cost / capacity_ac_mw
|
1411
|
+
)
|
1412
|
+
self._meta[SupplyCurveField.FIXED_CHARGE_RATE] = (
|
1413
|
+
self.plant_optimizer.fixed_charge_rate
|
1337
1414
|
)
|
1338
1415
|
|
1416
|
+
logger.debug("Plant layout optimization complete!")
|
1339
1417
|
return self.outputs
|
1340
1418
|
|
1341
1419
|
def agg_data_layers(self):
|
@@ -1402,36 +1480,18 @@ class BespokeSinglePlant:
|
|
1402
1480
|
class BespokeWindPlants(BaseAggregation):
|
1403
1481
|
"""BespokeWindPlants"""
|
1404
1482
|
|
1405
|
-
def __init__(
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
wake_loss_multiplier=1,
|
1418
|
-
ga_kwargs=None,
|
1419
|
-
output_request=("system_capacity", "cf_mean"),
|
1420
|
-
ws_bins=(0.0, 20.0, 5.0),
|
1421
|
-
wd_bins=(0.0, 360.0, 45.0),
|
1422
|
-
excl_dict=None,
|
1423
|
-
area_filter_kernel="queen",
|
1424
|
-
min_area=None,
|
1425
|
-
resolution=64,
|
1426
|
-
excl_area=None,
|
1427
|
-
data_layers=None,
|
1428
|
-
pre_extract_inclusions=False,
|
1429
|
-
prior_run=None,
|
1430
|
-
gid_map=None,
|
1431
|
-
bias_correct=None,
|
1432
|
-
pre_load_data=False,
|
1433
|
-
):
|
1434
|
-
r"""ReV bespoke analysis class.
|
1483
|
+
def __init__(self, excl_fpath, res_fpath, tm_dset, objective_function,
|
1484
|
+
capital_cost_function, fixed_operating_cost_function,
|
1485
|
+
variable_operating_cost_function,
|
1486
|
+
balance_of_system_cost_function, project_points,
|
1487
|
+
sam_files, min_spacing='5x', wake_loss_multiplier=1,
|
1488
|
+
ga_kwargs=None, output_request=('system_capacity', 'cf_mean'),
|
1489
|
+
ws_bins=(0.0, 20.0, 5.0), wd_bins=(0.0, 360.0, 45.0),
|
1490
|
+
excl_dict=None, area_filter_kernel='queen', min_area=None,
|
1491
|
+
resolution=64, excl_area=None, data_layers=None,
|
1492
|
+
pre_extract_inclusions=False, prior_run=None, gid_map=None,
|
1493
|
+
bias_correct=None, pre_load_data=False):
|
1494
|
+
"""reV bespoke analysis class.
|
1435
1495
|
|
1436
1496
|
Much like generation, ``reV`` bespoke analysis runs SAM
|
1437
1497
|
simulations by piping in renewable energy resource data (usually
|
@@ -1503,17 +1563,30 @@ class BespokeWindPlants(BaseAggregation):
|
|
1503
1563
|
- ``n_turbines``: the number of turbines
|
1504
1564
|
- ``system_capacity``: wind plant capacity
|
1505
1565
|
- ``aep``: annual energy production
|
1566
|
+
- ``avg_sl_dist_to_center_m``: Average straight-line
|
1567
|
+
distance to the supply curve point center from all
|
1568
|
+
turbine locations (in m). Useful for computing plant
|
1569
|
+
BOS costs.
|
1570
|
+
- ``avg_sl_dist_to_medoid_m``: Average straight-line
|
1571
|
+
distance to the medoid of all turbine locations
|
1572
|
+
(in m). Useful for computing plant BOS costs.
|
1573
|
+
- ``nn_conn_dist_m``: Total BOS connection distance
|
1574
|
+
using nearest-neighbor connections. This variable is
|
1575
|
+
only available for the
|
1576
|
+
``balance_of_system_cost_function`` equation.
|
1506
1577
|
- ``fixed_charge_rate``: user input fixed_charge_rate if
|
1507
1578
|
included as part of the sam system config.
|
1508
|
-
- ``self.wind_plant``: the SAM wind plant object,
|
1509
|
-
through which all SAM variables can be accessed
|
1510
1579
|
- ``capital_cost``: plant capital cost as evaluated
|
1511
1580
|
by `capital_cost_function`
|
1512
1581
|
- ``fixed_operating_cost``: plant fixed annual operating
|
1513
1582
|
cost as evaluated by `fixed_operating_cost_function`
|
1514
1583
|
- ``variable_operating_cost``: plant variable annual
|
1515
|
-
operating cost
|
1584
|
+
operating cost as evaluated by
|
1516
1585
|
`variable_operating_cost_function`
|
1586
|
+
- ``balance_of_system_cost``: plant balance of system
|
1587
|
+
cost as evaluated by `balance_of_system_cost_function`
|
1588
|
+
- ``self.wind_plant``: the SAM wind plant object,
|
1589
|
+
through which all SAM variables can be accessed
|
1517
1590
|
|
1518
1591
|
capital_cost_function : str
|
1519
1592
|
The plant capital cost function written out as a string.
|
@@ -1530,6 +1603,13 @@ class BespokeWindPlants(BaseAggregation):
|
|
1530
1603
|
out as a string. This expression must return the variable
|
1531
1604
|
operating cost in $/kWh. This expression has access to the
|
1532
1605
|
same variables as the `objective_function` argument above.
|
1606
|
+
You can set this to "0" to effectively ignore variable
|
1607
|
+
operating costs.
|
1608
|
+
balance_of_system_cost_function : str
|
1609
|
+
The plant balance-of-system cost function as a string, must
|
1610
|
+
return the variable operating cost in $. Has access to the
|
1611
|
+
same variables as the objective_function. You can set this
|
1612
|
+
to "0" to effectively ignore balance-of-system costs.
|
1533
1613
|
project_points : int | list | tuple | str | dict | pd.DataFrame | slice
|
1534
1614
|
Input specifying which sites to process. A single integer
|
1535
1615
|
representing the supply curve GID of a site may be specified
|
@@ -1538,7 +1618,7 @@ class BespokeWindPlants(BaseAggregation):
|
|
1538
1618
|
multiple sites can be specified to evaluate ``reV`` at
|
1539
1619
|
multiple specific locations. A string pointing to a project
|
1540
1620
|
points CSV file may also be specified. Typically, the CSV
|
1541
|
-
contains
|
1621
|
+
contains the following columns:
|
1542
1622
|
|
1543
1623
|
- ``gid``: Integer specifying the supply curve GID of
|
1544
1624
|
each site.
|
@@ -1555,16 +1635,18 @@ class BespokeWindPlants(BaseAggregation):
|
|
1555
1635
|
site-specific capital cost value for each location). Columns
|
1556
1636
|
that do not correspond to a config key may also be included,
|
1557
1637
|
but they will be ignored. The CSV file input can also have
|
1558
|
-
these extra columns:
|
1638
|
+
these extra, optional columns:
|
1559
1639
|
|
1560
1640
|
- ``capital_cost_multiplier``
|
1561
1641
|
- ``fixed_operating_cost_multiplier``
|
1562
1642
|
- ``variable_operating_cost_multiplier``
|
1643
|
+
- ``balance_of_system_cost_multiplier``
|
1563
1644
|
|
1564
1645
|
These particular inputs are treated as multipliers to be
|
1565
1646
|
applied to the respective cost curves
|
1566
1647
|
(`capital_cost_function`, `fixed_operating_cost_function`,
|
1567
|
-
|
1648
|
+
`variable_operating_cost_function`, and
|
1649
|
+
`balance_of_system_cost_function`) both during and
|
1568
1650
|
after the optimization. A DataFrame following the same
|
1569
1651
|
guidelines as the CSV input (or a dictionary that can be
|
1570
1652
|
used to initialize such a DataFrame) may be used for this
|
@@ -1839,30 +1921,23 @@ class BespokeWindPlants(BaseAggregation):
|
|
1839
1921
|
"""
|
1840
1922
|
|
1841
1923
|
log_versions(logger)
|
1842
|
-
logger.info(
|
1843
|
-
logger.info(
|
1844
|
-
logger.info(
|
1845
|
-
logger.debug(
|
1846
|
-
logger.info(
|
1847
|
-
|
1848
|
-
|
1849
|
-
|
1850
|
-
|
1851
|
-
|
1852
|
-
logger.info(
|
1853
|
-
|
1854
|
-
|
1855
|
-
|
1856
|
-
|
1857
|
-
|
1858
|
-
|
1859
|
-
variable_operating_cost_function
|
1860
|
-
)
|
1861
|
-
)
|
1862
|
-
logger.info(
|
1863
|
-
"Bespoke wake loss multiplier: {}".format(wake_loss_multiplier)
|
1864
|
-
)
|
1865
|
-
logger.info("Bespoke GA initialization kwargs: {}".format(ga_kwargs))
|
1924
|
+
logger.info('Initializing BespokeWindPlants...')
|
1925
|
+
logger.info('Resource filepath: {}'.format(res_fpath))
|
1926
|
+
logger.info('Exclusion filepath: {}'.format(excl_fpath))
|
1927
|
+
logger.debug('Exclusion dict: {}'.format(excl_dict))
|
1928
|
+
logger.info('Bespoke objective function: {}'
|
1929
|
+
.format(objective_function))
|
1930
|
+
logger.info('Bespoke capital cost function: {}'
|
1931
|
+
.format(capital_cost_function))
|
1932
|
+
logger.info('Bespoke fixed operating cost function: {}'
|
1933
|
+
.format(fixed_operating_cost_function))
|
1934
|
+
logger.info('Bespoke variable operating cost function: {}'
|
1935
|
+
.format(variable_operating_cost_function))
|
1936
|
+
logger.info('Bespoke balance of system cost function: {}'
|
1937
|
+
.format(balance_of_system_cost_function))
|
1938
|
+
logger.info('Bespoke wake loss multiplier: {}'
|
1939
|
+
.format(wake_loss_multiplier))
|
1940
|
+
logger.info('Bespoke GA initialization kwargs: {}'.format(ga_kwargs))
|
1866
1941
|
|
1867
1942
|
logger.info(
|
1868
1943
|
"Bespoke pre-extracting exclusions: {}".format(
|
@@ -1897,6 +1972,7 @@ class BespokeWindPlants(BaseAggregation):
|
|
1897
1972
|
self._cap_cost_fun = capital_cost_function
|
1898
1973
|
self._foc_fun = fixed_operating_cost_function
|
1899
1974
|
self._voc_fun = variable_operating_cost_function
|
1975
|
+
self._bos_fun = balance_of_system_cost_function
|
1900
1976
|
self._min_spacing = min_spacing
|
1901
1977
|
self._wake_loss_multiplier = wake_loss_multiplier
|
1902
1978
|
self._ga_kwargs = ga_kwargs or {}
|
@@ -1931,7 +2007,7 @@ class BespokeWindPlants(BaseAggregation):
|
|
1931
2007
|
Slice or list specifying project points, string pointing to a
|
1932
2008
|
project points csv, or a fully instantiated PointsControl object.
|
1933
2009
|
Can also be a single site integer value. Points csv should have
|
1934
|
-
`
|
2010
|
+
`SiteDataField.GID` and 'config' column, the config maps to the
|
1935
2011
|
sam_configs dict keys.
|
1936
2012
|
sam_configs : dict | str | SAMConfig
|
1937
2013
|
SAM input configuration ID(s) and file path(s). Keys are the SAM
|
@@ -1986,6 +2062,7 @@ class BespokeWindPlants(BaseAggregation):
|
|
1986
2062
|
|
1987
2063
|
with Outputs(prior_run, mode="r") as f:
|
1988
2064
|
meta = f.meta
|
2065
|
+
meta = meta.rename(columns=SupplyCurveField.map_from_legacy())
|
1989
2066
|
|
1990
2067
|
# pylint: disable=no-member
|
1991
2068
|
for col in meta.columns:
|
@@ -2011,7 +2088,7 @@ class BespokeWindPlants(BaseAggregation):
|
|
2011
2088
|
meta = None
|
2012
2089
|
|
2013
2090
|
if self._prior_meta is not None:
|
2014
|
-
mask = self._prior_meta[SupplyCurveField.
|
2091
|
+
mask = self._prior_meta[SupplyCurveField.SC_POINT_GID] == gid
|
2015
2092
|
if any(mask):
|
2016
2093
|
meta = self._prior_meta[mask]
|
2017
2094
|
|
@@ -2363,37 +2440,21 @@ class BespokeWindPlants(BaseAggregation):
|
|
2363
2440
|
|
2364
2441
|
# pylint: disable=arguments-renamed
|
2365
2442
|
@classmethod
|
2366
|
-
def run_serial(
|
2367
|
-
|
2368
|
-
|
2369
|
-
|
2370
|
-
|
2371
|
-
|
2372
|
-
|
2373
|
-
|
2374
|
-
|
2375
|
-
|
2376
|
-
|
2377
|
-
|
2378
|
-
|
2379
|
-
|
2380
|
-
|
2381
|
-
wd_bins=(0.0, 360.0, 45.0),
|
2382
|
-
excl_dict=None,
|
2383
|
-
inclusion_mask=None,
|
2384
|
-
area_filter_kernel="queen",
|
2385
|
-
min_area=None,
|
2386
|
-
resolution=64,
|
2387
|
-
excl_area=0.0081,
|
2388
|
-
data_layers=None,
|
2389
|
-
gids=None,
|
2390
|
-
exclusion_shape=None,
|
2391
|
-
slice_lookup=None,
|
2392
|
-
prior_meta=None,
|
2393
|
-
gid_map=None,
|
2394
|
-
bias_correct=None,
|
2395
|
-
pre_loaded_data=None,
|
2396
|
-
):
|
2443
|
+
def run_serial(cls, excl_fpath, res_fpath, tm_dset,
|
2444
|
+
sam_sys_inputs, objective_function,
|
2445
|
+
capital_cost_function,
|
2446
|
+
fixed_operating_cost_function,
|
2447
|
+
variable_operating_cost_function,
|
2448
|
+
balance_of_system_cost_function,
|
2449
|
+
min_spacing='5x', wake_loss_multiplier=1, ga_kwargs=None,
|
2450
|
+
output_request=('system_capacity', 'cf_mean'),
|
2451
|
+
ws_bins=(0.0, 20.0, 5.0), wd_bins=(0.0, 360.0, 45.0),
|
2452
|
+
excl_dict=None, inclusion_mask=None,
|
2453
|
+
area_filter_kernel='queen', min_area=None,
|
2454
|
+
resolution=64, excl_area=0.0081, data_layers=None,
|
2455
|
+
gids=None, exclusion_shape=None, slice_lookup=None,
|
2456
|
+
prior_meta=None, gid_map=None, bias_correct=None,
|
2457
|
+
pre_loaded_data=None):
|
2397
2458
|
"""
|
2398
2459
|
Standalone serial method to run bespoke optimization.
|
2399
2460
|
See BespokeWindPlants docstring for parameter description.
|
@@ -2446,6 +2507,7 @@ class BespokeWindPlants(BaseAggregation):
|
|
2446
2507
|
capital_cost_function,
|
2447
2508
|
fixed_operating_cost_function,
|
2448
2509
|
variable_operating_cost_function,
|
2510
|
+
balance_of_system_cost_function,
|
2449
2511
|
min_spacing=min_spacing,
|
2450
2512
|
wake_loss_multiplier=wake_loss_multiplier,
|
2451
2513
|
ga_kwargs=ga_kwargs,
|
@@ -2523,39 +2585,37 @@ class BespokeWindPlants(BaseAggregation):
|
|
2523
2585
|
rs, cs = self.slice_lookup[gid]
|
2524
2586
|
gid_incl_mask = self._inclusion_mask[rs, cs]
|
2525
2587
|
|
2526
|
-
futures.append(
|
2527
|
-
|
2528
|
-
|
2529
|
-
|
2530
|
-
|
2531
|
-
|
2532
|
-
|
2533
|
-
|
2534
|
-
|
2535
|
-
|
2536
|
-
|
2537
|
-
|
2538
|
-
|
2539
|
-
|
2540
|
-
|
2541
|
-
|
2542
|
-
|
2543
|
-
|
2544
|
-
|
2545
|
-
|
2546
|
-
|
2547
|
-
|
2548
|
-
|
2549
|
-
|
2550
|
-
|
2551
|
-
|
2552
|
-
|
2553
|
-
|
2554
|
-
|
2555
|
-
|
2556
|
-
|
2557
|
-
)
|
2558
|
-
)
|
2588
|
+
futures.append(exe.submit(
|
2589
|
+
self.run_serial,
|
2590
|
+
self._excl_fpath,
|
2591
|
+
self._res_fpath,
|
2592
|
+
self._tm_dset,
|
2593
|
+
self.sam_sys_inputs_with_site_data(gid),
|
2594
|
+
self._obj_fun,
|
2595
|
+
self._cap_cost_fun,
|
2596
|
+
self._foc_fun,
|
2597
|
+
self._voc_fun,
|
2598
|
+
self._bos_fun,
|
2599
|
+
self._min_spacing,
|
2600
|
+
wake_loss_multiplier=self._wake_loss_multiplier,
|
2601
|
+
ga_kwargs=self._ga_kwargs,
|
2602
|
+
output_request=self._output_request,
|
2603
|
+
ws_bins=self._ws_bins,
|
2604
|
+
wd_bins=self._wd_bins,
|
2605
|
+
excl_dict=self._excl_dict,
|
2606
|
+
inclusion_mask=gid_incl_mask,
|
2607
|
+
area_filter_kernel=self._area_filter_kernel,
|
2608
|
+
min_area=self._min_area,
|
2609
|
+
resolution=self._resolution,
|
2610
|
+
excl_area=self._excl_area,
|
2611
|
+
data_layers=self._data_layers,
|
2612
|
+
gids=gid,
|
2613
|
+
exclusion_shape=self.shape,
|
2614
|
+
slice_lookup=copy.deepcopy(self.slice_lookup),
|
2615
|
+
prior_meta=self._get_prior_meta(gid),
|
2616
|
+
gid_map=self._gid_map,
|
2617
|
+
bias_correct=self._get_bc_for_gid(gid),
|
2618
|
+
pre_loaded_data=self._pre_loaded_data_for_sc_gid(gid)))
|
2559
2619
|
|
2560
2620
|
# gather results
|
2561
2621
|
for future in as_completed(futures):
|
@@ -2617,35 +2677,34 @@ class BespokeWindPlants(BaseAggregation):
|
|
2617
2677
|
wlm = self._wake_loss_multiplier
|
2618
2678
|
i_bc = self._get_bc_for_gid(gid)
|
2619
2679
|
|
2620
|
-
si = self.run_serial(
|
2621
|
-
|
2622
|
-
|
2623
|
-
|
2624
|
-
|
2625
|
-
|
2626
|
-
|
2627
|
-
|
2628
|
-
|
2629
|
-
|
2630
|
-
|
2631
|
-
|
2632
|
-
|
2633
|
-
|
2634
|
-
|
2635
|
-
|
2636
|
-
|
2637
|
-
|
2638
|
-
|
2639
|
-
|
2640
|
-
|
2641
|
-
|
2642
|
-
|
2643
|
-
|
2644
|
-
|
2645
|
-
|
2646
|
-
|
2647
|
-
|
2648
|
-
)
|
2680
|
+
si = self.run_serial(self._excl_fpath,
|
2681
|
+
self._res_fpath,
|
2682
|
+
self._tm_dset,
|
2683
|
+
sam_inputs,
|
2684
|
+
self._obj_fun,
|
2685
|
+
self._cap_cost_fun,
|
2686
|
+
self._foc_fun,
|
2687
|
+
self._voc_fun,
|
2688
|
+
self._bos_fun,
|
2689
|
+
min_spacing=self._min_spacing,
|
2690
|
+
wake_loss_multiplier=wlm,
|
2691
|
+
ga_kwargs=self._ga_kwargs,
|
2692
|
+
output_request=self._output_request,
|
2693
|
+
ws_bins=self._ws_bins,
|
2694
|
+
wd_bins=self._wd_bins,
|
2695
|
+
excl_dict=self._excl_dict,
|
2696
|
+
inclusion_mask=gid_incl_mask,
|
2697
|
+
area_filter_kernel=afk,
|
2698
|
+
min_area=self._min_area,
|
2699
|
+
resolution=self._resolution,
|
2700
|
+
excl_area=self._excl_area,
|
2701
|
+
data_layers=self._data_layers,
|
2702
|
+
slice_lookup=slice_lookup,
|
2703
|
+
prior_meta=prior_meta,
|
2704
|
+
gid_map=self._gid_map,
|
2705
|
+
bias_correct=i_bc,
|
2706
|
+
gids=gid,
|
2707
|
+
pre_loaded_data=pre_loaded_data)
|
2649
2708
|
self._outputs.update(si)
|
2650
2709
|
else:
|
2651
2710
|
self._outputs = self.run_parallel(max_workers=max_workers)
|