NREL-reV 0.8.7__py3-none-any.whl → 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {NREL_reV-0.8.7.dist-info → NREL_reV-0.9.0.dist-info}/METADATA +13 -10
- {NREL_reV-0.8.7.dist-info → NREL_reV-0.9.0.dist-info}/RECORD +43 -43
- {NREL_reV-0.8.7.dist-info → NREL_reV-0.9.0.dist-info}/WHEEL +1 -1
- reV/SAM/SAM.py +217 -133
- reV/SAM/econ.py +18 -14
- reV/SAM/generation.py +611 -422
- reV/SAM/windbos.py +93 -79
- reV/bespoke/bespoke.py +681 -377
- reV/bespoke/cli_bespoke.py +2 -0
- reV/bespoke/place_turbines.py +187 -43
- reV/config/output_request.py +2 -1
- reV/config/project_points.py +218 -140
- reV/econ/econ.py +166 -114
- reV/econ/economies_of_scale.py +91 -45
- reV/generation/base.py +331 -184
- reV/generation/generation.py +326 -200
- reV/generation/output_attributes/lcoe_fcr_inputs.json +38 -3
- reV/handlers/__init__.py +0 -1
- reV/handlers/exclusions.py +16 -15
- reV/handlers/multi_year.py +57 -26
- reV/handlers/outputs.py +6 -5
- reV/handlers/transmission.py +44 -27
- reV/hybrids/hybrid_methods.py +30 -30
- reV/hybrids/hybrids.py +305 -189
- reV/nrwal/nrwal.py +262 -168
- reV/qa_qc/cli_qa_qc.py +14 -10
- reV/qa_qc/qa_qc.py +217 -119
- reV/qa_qc/summary.py +228 -146
- reV/rep_profiles/rep_profiles.py +349 -230
- reV/supply_curve/aggregation.py +349 -188
- reV/supply_curve/competitive_wind_farms.py +90 -48
- reV/supply_curve/exclusions.py +138 -85
- reV/supply_curve/extent.py +75 -50
- reV/supply_curve/points.py +735 -390
- reV/supply_curve/sc_aggregation.py +357 -248
- reV/supply_curve/supply_curve.py +604 -347
- reV/supply_curve/tech_mapping.py +144 -82
- reV/utilities/__init__.py +274 -16
- reV/utilities/pytest_utils.py +8 -4
- reV/version.py +1 -1
- {NREL_reV-0.8.7.dist-info → NREL_reV-0.9.0.dist-info}/LICENSE +0 -0
- {NREL_reV-0.8.7.dist-info → NREL_reV-0.9.0.dist-info}/entry_points.txt +0 -0
- {NREL_reV-0.8.7.dist-info → NREL_reV-0.9.0.dist-info}/top_level.txt +0 -0
reV/SAM/windbos.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
"""
|
3
|
-
|
4
|
-
"""
|
2
|
+
"""SAM Wind Balance of System Cost Model"""
|
3
|
+
|
5
4
|
from copy import deepcopy
|
5
|
+
|
6
6
|
import numpy as np
|
7
7
|
from PySAM.PySSC import ssc_sim_from_dict
|
8
8
|
|
@@ -12,45 +12,47 @@ from reV.utilities.exceptions import SAMInputError
|
|
12
12
|
class WindBos:
|
13
13
|
"""Wind Balance of System Cost Model."""
|
14
14
|
|
15
|
-
MODULE =
|
15
|
+
MODULE = "windbos"
|
16
16
|
|
17
17
|
# keys for the windbos input data dictionary.
|
18
18
|
# Some keys may not be found explicitly in the SAM input.
|
19
|
-
KEYS = (
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
19
|
+
KEYS = (
|
20
|
+
"tech_model",
|
21
|
+
"financial_model",
|
22
|
+
"machine_rating",
|
23
|
+
"rotor_diameter",
|
24
|
+
"hub_height",
|
25
|
+
"number_of_turbines",
|
26
|
+
"interconnect_voltage",
|
27
|
+
"distance_to_interconnect",
|
28
|
+
"site_terrain",
|
29
|
+
"turbine_layout",
|
30
|
+
"soil_condition",
|
31
|
+
"construction_time",
|
32
|
+
"om_building_size",
|
33
|
+
"quantity_test_met_towers",
|
34
|
+
"quantity_permanent_met_towers",
|
35
|
+
"weather_delay_days",
|
36
|
+
"crane_breakdowns",
|
37
|
+
"access_road_entrances",
|
38
|
+
"turbine_capital_cost",
|
39
|
+
"turbine_cost_per_kw",
|
40
|
+
"tower_top_mass",
|
41
|
+
"delivery_assist_required",
|
42
|
+
"pad_mount_transformer_required",
|
43
|
+
"new_switchyard_required",
|
44
|
+
"rock_trenching_required",
|
45
|
+
"mv_thermal_backfill",
|
46
|
+
"mv_overhead_collector",
|
47
|
+
"performance_bond",
|
48
|
+
"contingency",
|
49
|
+
"warranty_management",
|
50
|
+
"sales_and_use_tax",
|
51
|
+
"overhead",
|
52
|
+
"profit_margin",
|
53
|
+
"development_fee",
|
54
|
+
"turbine_transportation",
|
55
|
+
)
|
54
56
|
|
55
57
|
def __init__(self, inputs):
|
56
58
|
"""
|
@@ -64,14 +66,15 @@ class WindBos:
|
|
64
66
|
self._datadict = {}
|
65
67
|
|
66
68
|
self._inputs = inputs
|
67
|
-
self._special = {
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
69
|
+
self._special = {
|
70
|
+
"tech_model": "windbos",
|
71
|
+
"financial_model": "none",
|
72
|
+
"machine_rating": self.machine_rating,
|
73
|
+
"hub_height": self.hub_height,
|
74
|
+
"rotor_diameter": self.rotor_diameter,
|
75
|
+
"number_of_turbines": self.number_of_turbines,
|
76
|
+
"turbine_capital_cost": self.turbine_capital_cost,
|
77
|
+
}
|
75
78
|
self._parse_inputs()
|
76
79
|
self._out = ssc_sim_from_dict(self._datadict)
|
77
80
|
|
@@ -83,52 +86,55 @@ class WindBos:
|
|
83
86
|
if k in self._special:
|
84
87
|
self._datadict[k] = self._special[k]
|
85
88
|
elif k not in self._inputs:
|
86
|
-
raise SAMInputError(
|
87
|
-
|
89
|
+
raise SAMInputError(
|
90
|
+
'Windbos requires input key: "{}"'.format(k)
|
91
|
+
)
|
88
92
|
else:
|
89
93
|
self._datadict[k] = self._inputs[k]
|
90
94
|
|
91
95
|
@property
|
92
96
|
def machine_rating(self):
|
93
97
|
"""Single turbine machine rating either from input or power curve."""
|
94
|
-
if
|
95
|
-
return self._inputs[
|
98
|
+
if "machine_rating" in self._inputs:
|
99
|
+
return self._inputs["machine_rating"]
|
96
100
|
else:
|
97
|
-
return np.max(self._inputs[
|
101
|
+
return np.max(self._inputs["wind_turbine_powercurve_powerout"])
|
98
102
|
|
99
103
|
@property
|
100
104
|
def hub_height(self):
|
101
105
|
"""Turbine hub height."""
|
102
|
-
if
|
103
|
-
return self._inputs[
|
106
|
+
if "wind_turbine_hub_ht" in self._inputs:
|
107
|
+
return self._inputs["wind_turbine_hub_ht"]
|
104
108
|
else:
|
105
|
-
return self._inputs[
|
109
|
+
return self._inputs["hub_height"]
|
106
110
|
|
107
111
|
@property
|
108
112
|
def rotor_diameter(self):
|
109
113
|
"""Turbine rotor diameter."""
|
110
|
-
if
|
111
|
-
return self._inputs[
|
114
|
+
if "wind_turbine_rotor_diameter" in self._inputs:
|
115
|
+
return self._inputs["wind_turbine_rotor_diameter"]
|
112
116
|
else:
|
113
|
-
return self._inputs[
|
117
|
+
return self._inputs["rotor_diameter"]
|
114
118
|
|
115
119
|
@property
|
116
120
|
def number_of_turbines(self):
|
117
121
|
"""Number of turbines either based on input or system (farm) capacity
|
118
122
|
and machine rating"""
|
119
123
|
|
120
|
-
if
|
121
|
-
return self._inputs[
|
124
|
+
if "number_of_turbines" in self._inputs:
|
125
|
+
return self._inputs["number_of_turbines"]
|
122
126
|
else:
|
123
|
-
return
|
127
|
+
return (
|
128
|
+
self._inputs['system_capacity'] / self.machine_rating
|
129
|
+
)
|
124
130
|
|
125
131
|
@property
|
126
132
|
def turbine_capital_cost(self):
|
127
133
|
"""Returns zero (no turbine capital cost for WindBOS input,
|
128
134
|
and assigns any input turbine_capital_cost to an attr"""
|
129
135
|
|
130
|
-
if
|
131
|
-
self._turbine_capital_cost = self._inputs[
|
136
|
+
if "turbine_capital_cost" in self._inputs:
|
137
|
+
self._turbine_capital_cost = self._inputs["turbine_capital_cost"]
|
132
138
|
else:
|
133
139
|
self._turbine_capital_cost = 0.0
|
134
140
|
return 0.0
|
@@ -136,23 +142,23 @@ class WindBos:
|
|
136
142
|
@property
|
137
143
|
def bos_cost(self):
|
138
144
|
"""Get the balance of system cost ($)."""
|
139
|
-
return self._out[
|
145
|
+
return self._out["project_total_budgeted_cost"]
|
140
146
|
|
141
147
|
@property
|
142
148
|
def turbine_cost(self):
|
143
149
|
"""Get the turbine cost ($)."""
|
144
|
-
tcost = (
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
150
|
+
tcost = (
|
151
|
+
self._inputs["turbine_cost_per_kw"]
|
152
|
+
* self.machine_rating
|
153
|
+
* self.number_of_turbines
|
154
|
+
) + (self._turbine_capital_cost * self.number_of_turbines)
|
149
155
|
return tcost
|
150
156
|
|
151
157
|
@property
|
152
158
|
def sales_tax_mult(self):
|
153
159
|
"""Get a sales tax multiplier (frac of the total installed cost)."""
|
154
|
-
basis = self._inputs.get(
|
155
|
-
tax = self._datadict.get(
|
160
|
+
basis = self._inputs.get("sales_tax_basis", 0) / 100
|
161
|
+
tax = self._datadict.get("sales_and_use_tax", 0) / 100
|
156
162
|
return basis * tax
|
157
163
|
|
158
164
|
@property
|
@@ -168,16 +174,23 @@ class WindBos:
|
|
168
174
|
@property
|
169
175
|
def output(self):
|
170
176
|
"""Get a dictionary containing the cost breakdown."""
|
171
|
-
output = {
|
172
|
-
|
173
|
-
|
174
|
-
|
177
|
+
output = {
|
178
|
+
"total_installed_cost": self.total_installed_cost,
|
179
|
+
"turbine_cost": self.turbine_cost,
|
180
|
+
"sales_tax_cost": self.sales_tax_cost,
|
181
|
+
"bos_cost": self.bos_cost,
|
182
|
+
}
|
175
183
|
return output
|
176
184
|
|
177
185
|
# pylint: disable-msg=W0613
|
178
186
|
@classmethod
|
179
|
-
def reV_run(
|
180
|
-
|
187
|
+
def reV_run(
|
188
|
+
cls,
|
189
|
+
points_control,
|
190
|
+
site_df,
|
191
|
+
output_request=("total_installed_cost",),
|
192
|
+
**kwargs,
|
193
|
+
):
|
181
194
|
"""Execute SAM SingleOwner simulations based on reV points control.
|
182
195
|
|
183
196
|
Parameters
|
@@ -216,7 +229,8 @@ class WindBos:
|
|
216
229
|
|
217
230
|
wb = cls(site_inputs)
|
218
231
|
|
219
|
-
out[site] = {
|
220
|
-
|
232
|
+
out[site] = {
|
233
|
+
k: v for k, v in wb.output.items() if k in output_request
|
234
|
+
}
|
221
235
|
|
222
236
|
return out
|