NREL-reV 0.9.0__py3-none-any.whl → 0.9.4__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: NREL-reV
3
- Version: 0.9.0
3
+ Version: 0.9.4
4
4
  Summary: National Renewable Energy Laboratory's (NREL's) Renewable Energy Potential(V) Model: reV
5
5
  Home-page: https://nrel.github.io/reV/
6
6
  Author: Galen Maclaurin
@@ -17,22 +17,22 @@ Classifier: Programming Language :: Python :: 3.9
17
17
  Classifier: Programming Language :: Python :: 3.10
18
18
  Classifier: Programming Language :: Python :: 3.11
19
19
  Requires-Python: >=3.8
20
- Requires-Dist: NREL-gaps >=0.6.11
21
- Requires-Dist: NREL-NRWAL >=0.0.7
22
- Requires-Dist: NREL-PySAM ~=4.1.0
23
- Requires-Dist: NREL-rex >=0.2.85
24
- Requires-Dist: numpy ~=1.24.4
25
- Requires-Dist: packaging >=20.3
26
- Requires-Dist: plotly >=4.7.1
27
- Requires-Dist: plotting >=0.0.6
20
+ Requires-Dist: NREL-gaps>=0.6.11
21
+ Requires-Dist: NREL-NRWAL>=0.0.7
22
+ Requires-Dist: NREL-PySAM~=4.1.0
23
+ Requires-Dist: NREL-rex>=0.2.89
24
+ Requires-Dist: numpy~=1.24.4
25
+ Requires-Dist: packaging>=20.3
26
+ Requires-Dist: plotly>=4.7.1
27
+ Requires-Dist: plotting>=0.0.6
28
28
  Requires-Dist: shapely
29
29
  Provides-Extra: dev
30
- Requires-Dist: pytest >=5.2 ; extra == 'dev'
31
- Requires-Dist: flake8 ; extra == 'dev'
32
- Requires-Dist: pre-commit ; extra == 'dev'
33
- Requires-Dist: pylint ; extra == 'dev'
30
+ Requires-Dist: pytest>=5.2; extra == "dev"
31
+ Requires-Dist: flake8; extra == "dev"
32
+ Requires-Dist: pre-commit; extra == "dev"
33
+ Requires-Dist: pylint; extra == "dev"
34
34
  Provides-Extra: test
35
- Requires-Dist: pytest >=5.2 ; extra == 'test'
35
+ Requires-Dist: pytest>=5.2; extra == "test"
36
36
 
37
37
 
38
38
                 
@@ -1,11 +1,11 @@
1
1
  reV/__init__.py,sha256=tXTpWu_qVo3uotfSw_TJ-gNbidGaIPPfUTwBlpCMJ-g,856
2
2
  reV/cli.py,sha256=jfGGOr6QlLz8ghA7vBgx5-VgNsy4bBQo5DdHk42-q9A,1601
3
- reV/version.py,sha256=1L_LIr61Neb0VTgrPuHWPOXmcvf03SLALTnnaozT9Gs,50
4
- reV/SAM/SAM.py,sha256=KZ5EmHv2Uhoxh3xSDWYm7RmwMw-M7hfsh7P_qY-Shc8,31628
3
+ reV/version.py,sha256=yGiTX3S7R3_KR7tg1ywY2A7vCKmVDUNzRXtyt803-Ds,50
4
+ reV/SAM/SAM.py,sha256=wdrL7EAbn6246_3NP6EIksLM2yIg4IKK4taaTA7oj5o,32130
5
5
  reV/SAM/__init__.py,sha256=LJqoncyKDY5ZP5WA4kboh561bce11F9Ge645Izah0EY,240
6
6
  reV/SAM/defaults.py,sha256=2zqT_mfrVtbvU7Pe6H4anNtiVkIyQbuJcGIOARzEsbM,7193
7
7
  reV/SAM/econ.py,sha256=dFhtUXp5eozSsPyk0XXji0HGexKJnxoct8cwE1ApHVg,20704
8
- reV/SAM/generation.py,sha256=_kDNC4ZJ_i9TUw8Tt2GdxgGqZ1hUrEe25WPr_SUas0Q,87542
8
+ reV/SAM/generation.py,sha256=WYFXb39v7gJp1xxyLtsMLrP8Ze9dB15cxqxRoeNdZyk,89144
9
9
  reV/SAM/version_checker.py,sha256=q-eXsmSB08A5hSelNH7uAe_bqPpqjsxaiS3OBjXBs-0,3997
10
10
  reV/SAM/windbos.py,sha256=fnq1uxtSl-vtJth0gTlHkCSsmvDbRROCo9RGdqA7hK4,7504
11
11
  reV/SAM/defaults/USA AZ Phoenix Sky Harbor Intl Ap (TMY3).csv,sha256=8QorTX0ACjgPgNV7kLSpTHOqfY4E17gkkpKB-qseiFk,406896
@@ -14,7 +14,7 @@ reV/SAM/defaults/US_Wave.csv,sha256=wvn9vGl9ZGKVHbCyq4muhMI6x1-nAaQfDS3hC4ftwBA,
14
14
  reV/SAM/defaults/geothermal.json,sha256=ivxnpxkfBrEl1MwLKXClLJfTfvF5UrbN87wpC_BFLck,2598
15
15
  reV/SAM/defaults/i_pvwattsv5.json,sha256=pZpNlK5MiowZmNwY7i9XRWp9G8pPLirwdBWCMVJVuKE,312
16
16
  reV/bespoke/__init__.py,sha256=vpXbyBUrUsTgK8UP_LafMjLiDg2CRG9WZLHPsOJoxek,109
17
- reV/bespoke/bespoke.py,sha256=22WO3APF5Tq20oifmQzrV-08amEyOmLjGZjLHsXezOU,110323
17
+ reV/bespoke/bespoke.py,sha256=vvCyCaJc333yifTqQvGb-nV7OPen3fhpUE6vecelU78,111283
18
18
  reV/bespoke/cli_bespoke.py,sha256=nZc6rJlfxH2IXUz9bym4EO-C1k6VvulgweWoRrMg_cE,3028
19
19
  reV/bespoke/gradient_free.py,sha256=URWV1yiO2jyWk3_GOpfpLV_wlgJhXXGmTUwCB3WTV0Y,12015
20
20
  reV/bespoke/pack_turbs.py,sha256=l4btC2dPZkLvDgAAGVhAYgAZDbvwimGYRdrQDq0xT68,3740
@@ -31,13 +31,13 @@ reV/config/project_points.py,sha256=NmIj7hQm4qSMBqgefZDRNot2B8APCDk0ynTwegk-1Go,
31
31
  reV/config/sam_config.py,sha256=xvvx2FTuliq0Sk-BjRE3I9zdDmIdwHVBnWtXCcsoc40,7998
32
32
  reV/econ/__init__.py,sha256=UId1LNaAP9lErCEXVce6JZf0qVRUvwNFOPrajdRevGo,130
33
33
  reV/econ/cli_econ.py,sha256=2KNy3JQD0EKjStaoD2r6nv3ELFw88h2E_up-UKj_sfE,4286
34
- reV/econ/econ.py,sha256=_sfUhtodY0auz1yROFCFPxUu3GUV_HvwwBo1uzOrnks,22982
35
- reV/econ/economies_of_scale.py,sha256=Hac0M5_HgYm3HmfAcHIiq0flLAJjVK-wJuyu_DDH5Gs,10634
34
+ reV/econ/econ.py,sha256=edsNPZg2amhC_GZeotkY2MU3u17NgWeCpS6okZNrwAA,23637
35
+ reV/econ/economies_of_scale.py,sha256=GfM0vj4LnwsthzhSsUnmLt78PAUHrxZI5ghWaxvoF7A,10568
36
36
  reV/econ/utilities.py,sha256=ZOA49S1jfsvdenvlL7m-BAJIrAvpAHSix5-wrSW0uLc,1232
37
37
  reV/generation/__init__.py,sha256=LBecrbpL69tROol_OwVHTKBrTqgSSxJW59fs0k3o0jQ,75
38
38
  reV/generation/base.py,sha256=p6g8Bj8T98OfKu44LTocGtEozstbUvti6wSi3oroS5U,48758
39
39
  reV/generation/cli_gen.py,sha256=5RSlr8yO9zlB-1FzFZGClqsxqsrbr9rcS_8PlOjVx2s,4435
40
- reV/generation/generation.py,sha256=WOclf4DzSy4qKpRCy4RUmnh7rkcLBcutH8NmQl9T2CQ,46367
40
+ reV/generation/generation.py,sha256=o6nJlEcgs-QQ_fdqva-7jOhWl1wj-J7CIrRR5qNJyEQ,47434
41
41
  reV/generation/output_attributes/generation.json,sha256=cy8efVCJgNSYCsR3x3_VpMCtynYS3oaQG4M2MwcdzFY,4954
42
42
  reV/generation/output_attributes/lcoe_fcr.json,sha256=d_TGMkZsmsfDXQiWfhYjFkmwqi-wKF8JdHmiMCj2h38,136
43
43
  reV/generation/output_attributes/lcoe_fcr_inputs.json,sha256=Yb-WTXt9YD4wE4Uu315W2uLFH6g3fT40YY83mxS-aWE,1289
@@ -51,7 +51,7 @@ reV/handlers/__init__.py,sha256=N7KfWbXfHLizMpoUw6yo0egbyq3jb9QL4YouBLUf4mI,130
51
51
  reV/handlers/cli_collect.py,sha256=QRjhuJ1lV4BwGoguu0m4etrNRPMXGIZlijNyYsb7_5U,799
52
52
  reV/handlers/cli_multi_year.py,sha256=xMAQ70lVUMAPM10EkZwc_FreQLjnzQ9VmKl48MS1SO0,660
53
53
  reV/handlers/exclusions.py,sha256=_s-LHbjHAAHSIM6tFXCoJlLKcv0FBxbm6JAmuE_X_-I,12266
54
- reV/handlers/multi_year.py,sha256=3s4sYWBl9-1rxeTBTMkl5lDeFqCioi4uuxcZlbWmIbE,30998
54
+ reV/handlers/multi_year.py,sha256=4U-9nMVeKs03jIKdyeLq5FM9uQqAuYcgheAyMyWa5tg,30825
55
55
  reV/handlers/outputs.py,sha256=VbSPFwA0ZH-oWUCCtef7zGyPbWXKY07ilPzpZIOMmuc,6034
56
56
  reV/handlers/transmission.py,sha256=JzpmiweCez7vzGSutO21XIbG9czY_H6ysg_R7z290w0,25610
57
57
  reV/hybrids/__init__.py,sha256=oBrJSt9WeBlER7GVd-NEJgVqFlasl0WuyHJRMgLgpAY,133
@@ -74,24 +74,24 @@ reV/rep_profiles/cli_rep_profiles.py,sha256=i3fRolT7HTzTQePXpNLDyxwgpRa7FEfHO1mO
74
74
  reV/rep_profiles/rep_profiles.py,sha256=oqmtU9FbUlKurkglmZLyDBNH8VRKv8fgdSuvtYWLm04,48271
75
75
  reV/supply_curve/__init__.py,sha256=dbf0cO0qmt1VhV8dnDddztrcpnwua9T2HBhM6tLKaP8,274
76
76
  reV/supply_curve/aggregation.py,sha256=fv4FTdRUQWZ_1Ds84ubgJdrf5lR1xb3luhAnibe3Y3Q,38752
77
- reV/supply_curve/cli_sc_aggregation.py,sha256=drzYGFEHC3Ww1GBnk_iClFoQuXjT3nUEpv8I4Fgx8W4,2867
77
+ reV/supply_curve/cli_sc_aggregation.py,sha256=qGfJyZjg4tFARG2dl-wAlHv9KjEp2u4wg1Boxz18Gf0,3347
78
78
  reV/supply_curve/cli_supply_curve.py,sha256=e-XrHQIe4OqWTL6u-TUAyHrw7Alk7vkXQ2HoLbE3zTM,2163
79
79
  reV/supply_curve/competitive_wind_farms.py,sha256=eOjM72-4oWtsqxB7Wh2gnB2zVAt4LY3iPE_DqWdXbQ4,15795
80
80
  reV/supply_curve/exclusions.py,sha256=4-ZxTO5Vlu03vie0V_74uvdajQfCuC8FE96Pg8I4U_c,42950
81
81
  reV/supply_curve/extent.py,sha256=a31po753hXSxQ8lfcCvpE8hoKc4bY7MmYq0NO0jtdqA,17414
82
- reV/supply_curve/points.py,sha256=0yMaxfvDW-EDMRcEMNKK5r1wdUbboR2iVVqe8DgTT0I,85944
83
- reV/supply_curve/sc_aggregation.py,sha256=6kJg4CaLlFAC196azbfzA-T_wXkXSwNDQKOW-k2lqcE,61645
84
- reV/supply_curve/supply_curve.py,sha256=i_Wo5cI11hNt5zfGZiqqBNM4_qERD1dVNmud1tWJteI,69942
85
- reV/supply_curve/tech_mapping.py,sha256=7him2R-8kpigI47ATSN4ncnvzuF_AzeXyrtz_jmzLug,18573
82
+ reV/supply_curve/points.py,sha256=YEYHpn3KlBbGqpxywrfbrUrlhL3BndgX7cVQerdB6zg,86107
83
+ reV/supply_curve/sc_aggregation.py,sha256=R_h3xXKum4JTYMuq2us0rWhW0t2wm-G9Wj2on1d8FeY,61604
84
+ reV/supply_curve/supply_curve.py,sha256=igbloCkvYQwaH4zjbrYO17J7TtCTX5dKkk-TnlGJJeY,69931
85
+ reV/supply_curve/tech_mapping.py,sha256=nfqPIj9o1pllX3aRC1Hi2KGXYJOYy68Vbvr_1asNmFk,18573
86
86
  reV/utilities/__init__.py,sha256=iOvOuqcXv_WaSP627z2i2wF5820DbUSGRa7lSj23diQ,10534
87
87
  reV/utilities/cli_functions.py,sha256=1_T_sXz0Ct8lW-vOk3mMRcpD6NYsc9cGI7dEujIi9z4,3864
88
88
  reV/utilities/curtailment.py,sha256=fu48Lje-iyd2Awfe-6jFebvgcKSeJ3daNNTqjFI2GvM,4963
89
89
  reV/utilities/exceptions.py,sha256=f7sRGsbFLpmL6Caq_H1cD4GfVhnLMyvYUsLPA1UVDDE,3974
90
90
  reV/utilities/pytest_utils.py,sha256=T22NFEGxPOc9wRwgy0Pt2cHvw3Vam9cKwUj4AkzI7bU,3244
91
91
  reV/utilities/slots.py,sha256=xsw-JuUVZ0YeoCNuwP_HxGNxFMA4xRs1tuImXHIJqaU,2618
92
- NREL_reV-0.9.0.dist-info/LICENSE,sha256=hDwoTANtan2ZpufBlXm5C3W_PJ-mCqItvlcobgjxL7k,1526
93
- NREL_reV-0.9.0.dist-info/METADATA,sha256=hkdHRfdqhgSf-YxHU7lZss4kgeJE5fYVfWWDHpK7FRQ,10294
94
- NREL_reV-0.9.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
95
- NREL_reV-0.9.0.dist-info/entry_points.txt,sha256=KGtPEOQRZMSqKXjjv5jt_T4e4HQN0fHiaGdAWwTtuW4,617
96
- NREL_reV-0.9.0.dist-info/top_level.txt,sha256=S6YF2ZYgXUB6n28SY0K2H8YB9tMJdXQ9CyQbo6VC89M,4
97
- NREL_reV-0.9.0.dist-info/RECORD,,
92
+ NREL_reV-0.9.4.dist-info/LICENSE,sha256=hDwoTANtan2ZpufBlXm5C3W_PJ-mCqItvlcobgjxL7k,1526
93
+ NREL_reV-0.9.4.dist-info/METADATA,sha256=IN6FHUL4PezUaGzk6bmUM-0fF1Ux2HGNQoXumrFo4Lg,10279
94
+ NREL_reV-0.9.4.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
95
+ NREL_reV-0.9.4.dist-info/entry_points.txt,sha256=KGtPEOQRZMSqKXjjv5jt_T4e4HQN0fHiaGdAWwTtuW4,617
96
+ NREL_reV-0.9.4.dist-info/top_level.txt,sha256=S6YF2ZYgXUB6n28SY0K2H8YB9tMJdXQ9CyQbo6VC89M,4
97
+ NREL_reV-0.9.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: bdist_wheel (0.44.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
reV/SAM/SAM.py CHANGED
@@ -195,6 +195,11 @@ class SamResourceRetriever:
195
195
  # make precip rate available for curtailment analysis
196
196
  kwargs["precip_rate"] = True
197
197
 
198
+ sam_configs = project_points.sam_inputs.values()
199
+ needs_wd = any(_sam_config_contains_turbine_layout(sam_config)
200
+ for sam_config in sam_configs)
201
+ kwargs["require_wind_dir"] = needs_wd
202
+
198
203
  elif res_handler == GeothermalResource:
199
204
  args += (project_points.d,)
200
205
 
@@ -950,4 +955,12 @@ def _add_sys_capacity(sam_inputs):
950
955
  if cap is not None:
951
956
  cap = max(cap)
952
957
 
958
+ if cap is None:
959
+ cap = sam_inputs.get("nameplate")
960
+
953
961
  sam_inputs["system_capacity"] = cap
962
+
963
+
964
+ def _sam_config_contains_turbine_layout(sam_config):
965
+ """Detect wether SAM config contains multiple turbines in layout. """
966
+ return len(sam_config.get("wind_farm_xCoordinates", ())) > 1
reV/SAM/generation.py CHANGED
@@ -233,6 +233,15 @@ class AbstractSamGeneration(RevPySam, ScheduledLossesMixin, ABC):
233
233
  logger.error(msg)
234
234
  raise InputError(msg)
235
235
 
236
+ if len(resource) < 8760:
237
+ msg = (f"Detected resource time series of length {len(resource)}, "
238
+ "which is less than 8760. This may yield unexpected "
239
+ "results or fail altogether. If this is not intentional, "
240
+ "try setting 'time_index_step: 1' in your SAM config or "
241
+ "double check the resource input you're using.")
242
+ logger.warning(msg)
243
+ warn(msg)
244
+
236
245
  @abstractmethod
237
246
  def set_resource_data(self, resource, meta):
238
247
  """Placeholder for resource data setting (nsrdb or wtk)"""
@@ -1539,7 +1548,7 @@ class Geothermal(AbstractSamGenerationFromWeatherFile):
1539
1548
  or 1 allowed.
1540
1549
  - ``design_temp`` : EGS plant design temperature (in C). Only
1541
1550
  affects EGS runs. This value may be adjusted internally by
1542
- ``reV under the following conditions:
1551
+ ``reV`` under the following conditions:
1543
1552
 
1544
1553
  - The design temperature is larger than the resource
1545
1554
  temperature
@@ -1786,6 +1795,10 @@ class Geothermal(AbstractSamGenerationFromWeatherFile):
1786
1795
  "{}".format(self.sam_sys_inputs["nameplate"])
1787
1796
  )
1788
1797
  logger.info(msg)
1798
+ # required for downstream LCOE calcs
1799
+ self.sam_sys_inputs["system_capacity"] = (
1800
+ self.sam_sys_inputs["nameplate"]
1801
+ )
1789
1802
  return
1790
1803
 
1791
1804
  val = set(resource["potential_MW"].unique())
@@ -1801,6 +1814,8 @@ class Geothermal(AbstractSamGenerationFromWeatherFile):
1801
1814
 
1802
1815
  logger.debug("Setting the nameplate to {}".format(val))
1803
1816
  self.sam_sys_inputs["nameplate"] = val
1817
+ # required for downstream LCOE calcs
1818
+ self.sam_sys_inputs["system_capacity"] = val
1804
1819
 
1805
1820
  def _set_resource_potential_to_match_gross_output(self):
1806
1821
  """Set the resource potential input to match the gross generation.
@@ -1861,7 +1876,9 @@ class Geothermal(AbstractSamGenerationFromWeatherFile):
1861
1876
  logger.debug(
1862
1877
  "Setting the capital_cost to ${:,.2f}".format(capital_cost)
1863
1878
  )
1864
- self.sam_sys_inputs["capital_cost"] = capital_cost
1879
+ reg_mult = self.sam_sys_inputs.get("capital_cost_multiplier", 1)
1880
+ self.sam_sys_inputs["base_capital_cost"] = capital_cost
1881
+ self.sam_sys_inputs["capital_cost"] = capital_cost * reg_mult
1865
1882
 
1866
1883
  dc_per_well = self.sam_sys_inputs.pop("drill_cost_per_well", None)
1867
1884
  num_wells = self.sam_sys_inputs.pop(
@@ -1884,19 +1901,35 @@ class Geothermal(AbstractSamGenerationFromWeatherFile):
1884
1901
  drill_cost, num_wells, dc_per_well
1885
1902
  )
1886
1903
  )
1887
- self.sam_sys_inputs["capital_cost"] = capital_cost + drill_cost
1904
+ reg_mult = self.sam_sys_inputs.get(
1905
+ "capital_cost_multiplier", 1
1906
+ )
1907
+ base_cc = capital_cost / reg_mult
1908
+ new_base_cc = base_cc + drill_cost
1909
+ self.sam_sys_inputs["base_capital_cost"] = new_base_cc
1910
+ self.sam_sys_inputs["capital_cost"] = new_base_cc * reg_mult
1888
1911
 
1889
1912
  foc_per_kw = self.sam_sys_inputs.pop(
1890
1913
  "fixed_operating_cost_per_kw", None
1891
1914
  )
1892
1915
  if foc_per_kw is not None:
1893
- fixed_operating_cost = foc_per_kw * plant_size_kw
1916
+ foc = foc_per_kw * plant_size_kw
1894
1917
  logger.debug(
1895
- "Setting the fixed_operating_cost to ${:,.2f}".format(
1896
- capital_cost
1897
- )
1918
+ "Setting the fixed_operating_cost to ${:,.2f}".format(foc)
1919
+ )
1920
+ self.sam_sys_inputs["base_fixed_operating_cost"] = foc
1921
+ self.sam_sys_inputs["fixed_operating_cost"] = foc
1922
+
1923
+ voc_per_kw = self.sam_sys_inputs.pop(
1924
+ "variable_operating_cost_per_kw", None
1925
+ )
1926
+ if voc_per_kw is not None:
1927
+ voc = voc_per_kw * plant_size_kw
1928
+ logger.debug(
1929
+ "Setting the variable_operating_cost to ${:,.2f}".format(voc)
1898
1930
  )
1899
- self.sam_sys_inputs["fixed_operating_cost"] = fixed_operating_cost
1931
+ self.sam_sys_inputs["base_variable_operating_cost"] = voc
1932
+ self.sam_sys_inputs["variable_operating_cost"] = voc
1900
1933
 
1901
1934
  def _create_pysam_wfile(self, resource, meta):
1902
1935
  """Create PySAM weather input file.
reV/bespoke/bespoke.py CHANGED
@@ -20,7 +20,6 @@ import pandas as pd
20
20
  import psutil
21
21
  from rex.joint_pd.joint_pd import JointPD
22
22
  from rex.multi_year_resource import MultiYearWindResource
23
- from rex.renewable_resource import WindResource
24
23
  from rex.utilities.bc_parse_table import parse_bc_table
25
24
  from rex.utilities.execution import SpawnProcessPool
26
25
  from rex.utilities.loggers import create_dirs, log_mem
@@ -60,8 +59,13 @@ class BespokeMultiPlantData:
60
59
 
61
60
  Parameters
62
61
  ----------
63
- res_fpath : str
64
- Path to resource h5 file.
62
+ res_fpath : str | list
63
+ Unix shell style path (potentially containing wildcard (*)
64
+ patterns) to a single or multi-file resource file set(s).
65
+ Can also be an explicit list of resource file paths, which
66
+ themselves can contain wildcards. This input must be
67
+ readable by
68
+ :py:class:`rex.multi_year_resource.MultiYearWindResource`.
65
69
  sc_gid_to_hh : dict
66
70
  Dictionary mapping SC GID values to hub-heights. Data for
67
71
  each SC GID will be pulled for the corresponding hub-height
@@ -69,7 +73,7 @@ class BespokeMultiPlantData:
69
73
  sc_gid_to_res_gid : dict
70
74
  Dictionary mapping SC GID values to an iterable oif resource
71
75
  GID values. Resource GID values should correspond to GID
72
- values in teh HDF5 file, so any GID map must be applied
76
+ values in the HDF5 file, so any GID map must be applied
73
77
  before initializing :class`BespokeMultiPlantData`.
74
78
  """
75
79
  self.res_fpath = res_fpath
@@ -95,12 +99,7 @@ class BespokeMultiPlantData:
95
99
  }
96
100
 
97
101
  start_time = time.time()
98
- if "*" in self.res_fpath:
99
- handler = MultiYearWindResource
100
- else:
101
- handler = WindResource
102
-
103
- with handler(self.res_fpath) as res:
102
+ with MultiYearWindResource(self.res_fpath) as res:
104
103
  self._wind_dirs = {
105
104
  hh: res[f"winddirection_{hh}m", :, gids]
106
105
  for hh, gids in self.hh_to_res_gids.items()
@@ -481,8 +480,7 @@ class BespokeSinglePlant:
481
480
  self._pre_loaded_data = pre_loaded_data
482
481
  self._outputs = {}
483
482
 
484
- Handler = self.get_wind_handler(res)
485
- res = res if not isinstance(res, str) else Handler(res)
483
+ res = res if not isinstance(res, str) else MultiYearWindResource(res)
486
484
 
487
485
  self._sc_point = AggSCPoint(
488
486
  gid,
@@ -1142,29 +1140,6 @@ class BespokeSinglePlant:
1142
1140
  lcoe_kwargs["capital_cost"] = lcoe_kwargs["capital_cost"] + bos
1143
1141
  return lcoe_kwargs
1144
1142
 
1145
- @staticmethod
1146
- def get_wind_handler(res):
1147
- """Get a wind resource handler for a resource filepath.
1148
-
1149
- Parameters
1150
- ----------
1151
- res : str
1152
- Resource filepath to wtk .h5 file. Can include * wildcards
1153
- for multi year resource.
1154
-
1155
- Returns
1156
- -------
1157
- handler : WindResource | MultiYearWindResource
1158
- Wind resource handler or multi year handler
1159
- """
1160
- handler = res
1161
- if isinstance(res, str):
1162
- if "*" in res:
1163
- handler = MultiYearWindResource
1164
- else:
1165
- handler = WindResource
1166
- return handler
1167
-
1168
1143
  @classmethod
1169
1144
  def check_dependencies(cls):
1170
1145
  """Check special dependencies for bespoke"""
@@ -1263,10 +1238,10 @@ class BespokeSinglePlant:
1263
1238
  self._outputs.update(means)
1264
1239
 
1265
1240
  self._meta[SupplyCurveField.MEAN_RES] = self.res_df["windspeed"].mean()
1266
- self._meta[SupplyCurveField.MEAN_CF_DC] = None
1267
- self._meta[SupplyCurveField.MEAN_CF_AC] = None
1268
- self._meta[SupplyCurveField.MEAN_LCOE] = None
1269
- self._meta[SupplyCurveField.SC_POINT_ANNUAL_ENERGY_MW] = None
1241
+ self._meta[SupplyCurveField.MEAN_CF_DC] = np.nan
1242
+ self._meta[SupplyCurveField.MEAN_CF_AC] = np.nan
1243
+ self._meta[SupplyCurveField.MEAN_LCOE] = np.nan
1244
+ self._meta[SupplyCurveField.SC_POINT_ANNUAL_ENERGY_MW] = np.nan
1270
1245
  # copy dataset outputs to meta data for supply curve table summary
1271
1246
  if "cf_mean-means" in self.outputs:
1272
1247
  self._meta.loc[:, SupplyCurveField.MEAN_CF_AC] = self.outputs[
@@ -1373,7 +1348,7 @@ class BespokeSinglePlant:
1373
1348
  # convert SAM system capacity in kW to reV supply curve cap in MW
1374
1349
  capacity_ac_mw = system_capacity_kw / 1e3
1375
1350
  self._meta[SupplyCurveField.CAPACITY_AC_MW] = capacity_ac_mw
1376
- self._meta[SupplyCurveField.CAPACITY_DC_MW] = None
1351
+ self._meta[SupplyCurveField.CAPACITY_DC_MW] = np.nan
1377
1352
 
1378
1353
  # add required ReEDS multipliers to meta
1379
1354
  baseline_cost = self.plant_optimizer.capital_cost_per_kw(
@@ -1382,32 +1357,45 @@ class BespokeSinglePlant:
1382
1357
  eos_mult = (self.plant_optimizer.capital_cost
1383
1358
  / self.plant_optimizer.capacity
1384
1359
  / baseline_cost)
1385
- reg_mult = self.sam_sys_inputs.get("capital_cost_multiplier", 1)
1360
+ reg_mult_cc = self.sam_sys_inputs.get(
1361
+ "capital_cost_multiplier", 1)
1362
+ reg_mult_foc = self.sam_sys_inputs.get(
1363
+ "fixed_operating_cost_multiplier", 1)
1364
+ reg_mult_voc = self.sam_sys_inputs.get(
1365
+ "variable_operating_cost_multiplier", 1)
1366
+ reg_mult_bos = self.sam_sys_inputs.get(
1367
+ "balance_of_system_cost_multiplier", 1)
1386
1368
 
1387
1369
  self._meta[SupplyCurveField.EOS_MULT] = eos_mult
1388
- self._meta[SupplyCurveField.REG_MULT] = reg_mult
1370
+ self._meta[SupplyCurveField.REG_MULT] = reg_mult_cc
1389
1371
 
1390
- cap_cost = (
1391
- self.plant_optimizer.capital_cost
1392
- + self.plant_optimizer.balance_of_system_cost
1393
- )
1394
1372
  self._meta[SupplyCurveField.COST_SITE_OCC_USD_PER_AC_MW] = (
1395
- cap_cost / capacity_ac_mw
1373
+ (self.plant_optimizer.capital_cost
1374
+ + self.plant_optimizer.balance_of_system_cost)
1375
+ / capacity_ac_mw
1396
1376
  )
1397
1377
  self._meta[SupplyCurveField.COST_BASE_OCC_USD_PER_AC_MW] = (
1398
- cap_cost / eos_mult / reg_mult / capacity_ac_mw
1378
+ (self.plant_optimizer.capital_cost / eos_mult / reg_mult_cc
1379
+ + self.plant_optimizer.balance_of_system_cost / reg_mult_bos)
1380
+ / capacity_ac_mw
1399
1381
  )
1400
1382
  self._meta[SupplyCurveField.COST_SITE_FOC_USD_PER_AC_MW] = (
1401
- self.plant_optimizer.fixed_operating_cost / capacity_ac_mw
1383
+ self.plant_optimizer.fixed_operating_cost
1384
+ / capacity_ac_mw
1402
1385
  )
1403
1386
  self._meta[SupplyCurveField.COST_BASE_FOC_USD_PER_AC_MW] = (
1404
- self.plant_optimizer.fixed_operating_cost / capacity_ac_mw
1387
+ self.plant_optimizer.fixed_operating_cost
1388
+ / reg_mult_foc
1389
+ / capacity_ac_mw
1405
1390
  )
1406
1391
  self._meta[SupplyCurveField.COST_SITE_VOC_USD_PER_AC_MW] = (
1407
- self.plant_optimizer.variable_operating_cost / capacity_ac_mw
1392
+ self.plant_optimizer.variable_operating_cost
1393
+ / capacity_ac_mw
1408
1394
  )
1409
1395
  self._meta[SupplyCurveField.COST_BASE_VOC_USD_PER_AC_MW] = (
1410
- self.plant_optimizer.variable_operating_cost / capacity_ac_mw
1396
+ self.plant_optimizer.variable_operating_cost
1397
+ / reg_mult_voc
1398
+ / capacity_ac_mw
1411
1399
  )
1412
1400
  self._meta[SupplyCurveField.FIXED_CHARGE_RATE] = (
1413
1401
  self.plant_optimizer.fixed_charge_rate
@@ -1489,8 +1477,9 @@ class BespokeWindPlants(BaseAggregation):
1489
1477
  ws_bins=(0.0, 20.0, 5.0), wd_bins=(0.0, 360.0, 45.0),
1490
1478
  excl_dict=None, area_filter_kernel='queen', min_area=None,
1491
1479
  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):
1480
+ pre_extract_inclusions=False, eos_mult_baseline_cap_mw=200,
1481
+ prior_run=None, gid_map=None, bias_correct=None,
1482
+ pre_load_data=False):
1494
1483
  """reV bespoke analysis class.
1495
1484
 
1496
1485
  Much like generation, ``reV`` bespoke analysis runs SAM
@@ -1521,14 +1510,15 @@ class BespokeWindPlants(BaseAggregation):
1521
1510
  uniquely defined (i.e.only appear once and in a single
1522
1511
  input file).
1523
1512
  res_fpath : str
1524
- Filepath to wind resource data in NREL WTK format. This
1525
- input can be path to a single resource HDF5 file or a path
1526
- including a wildcard input like ``/h5_dir/prefix*suffix`` to
1527
- run bespoke on multiple years of resource data. The former
1528
- must be readable by
1529
- :py:class:`rex.renewable_resource.WindResource` while the
1530
- latter must be readable by
1531
- or :py:class:`rex.multi_year_resource.MultiYearWindResource`
1513
+ Unix shell style path to wind resource HDF5 file in NREL WTK
1514
+ format. Can also be a path including a wildcard input like
1515
+ ``/h5_dir/prefix*suffix`` to run bespoke on multiple years
1516
+ of resource data. Can also be an explicit list of resource
1517
+ HDF5 file paths, which themselves can contain wildcards. If
1518
+ multiple files are specified in this way, they must have the
1519
+ same coordinates but can have different time indices (i.e.
1520
+ different years). This input must be readable by
1521
+ :py:class:`rex.multi_year_resource.MultiYearWindResource`
1532
1522
  (i.e. the resource data conform to the
1533
1523
  `rex data format <https://tinyurl.com/3fy7v5kx>`_). This
1534
1524
  means the data file(s) must contain a 1D ``time_index``
@@ -1618,7 +1608,7 @@ class BespokeWindPlants(BaseAggregation):
1618
1608
  multiple sites can be specified to evaluate ``reV`` at
1619
1609
  multiple specific locations. A string pointing to a project
1620
1610
  points CSV file may also be specified. Typically, the CSV
1621
- contains two columns:
1611
+ contains the following columns:
1622
1612
 
1623
1613
  - ``gid``: Integer specifying the supply curve GID of
1624
1614
  each site.
@@ -1635,7 +1625,7 @@ class BespokeWindPlants(BaseAggregation):
1635
1625
  site-specific capital cost value for each location). Columns
1636
1626
  that do not correspond to a config key may also be included,
1637
1627
  but they will be ignored. The CSV file input can also have
1638
- these extra columns:
1628
+ these extra, optional columns:
1639
1629
 
1640
1630
  - ``capital_cost_multiplier``
1641
1631
  - ``fixed_operating_cost_multiplier``
@@ -1855,6 +1845,13 @@ class BespokeWindPlants(BaseAggregation):
1855
1845
  the `excl_dict` input. It is typically faster to compute
1856
1846
  the inclusion mask on the fly with parallel workers.
1857
1847
  By default, ``False``.
1848
+ eos_mult_baseline_cap_mw : int | float, optional
1849
+ Baseline plant capacity (MW) used to calculate economies of
1850
+ scale (EOS) multiplier from the `capital_cost_function`. EOS
1851
+ multiplier is calculated as the $-per-kW of the wind plant
1852
+ divided by the $-per-kW of a plant with this baseline
1853
+ capacity. By default, `200` (MW), which aligns the baseline
1854
+ with ATB assumptions. See here: https://tinyurl.com/y85hnu6h.
1858
1855
  prior_run : str, optional
1859
1856
  Optional filepath to a bespoke output HDF5 file belonging to
1860
1857
  a prior run. If specified, this module will only run the
@@ -1980,6 +1977,7 @@ class BespokeWindPlants(BaseAggregation):
1980
1977
  self._ws_bins = ws_bins
1981
1978
  self._wd_bins = wd_bins
1982
1979
  self._data_layers = data_layers
1980
+ self._eos_mult_baseline_cap_mw = eos_mult_baseline_cap_mw
1983
1981
  self._prior_meta = self._parse_prior_run(prior_run)
1984
1982
  self._gid_map = BespokeSinglePlant._parse_gid_map(gid_map)
1985
1983
  self._bias_correct = Gen._parse_bc(bias_correct)
@@ -2119,8 +2117,7 @@ class BespokeWindPlants(BaseAggregation):
2119
2117
  )
2120
2118
 
2121
2119
  # just check that this file exists, cannot check res_fpath if *glob
2122
- Handler = BespokeSinglePlant.get_wind_handler(self._res_fpath)
2123
- with Handler(self._res_fpath) as f:
2120
+ with MultiYearWindResource(self._res_fpath) as f:
2124
2121
  assert any(f.dsets)
2125
2122
 
2126
2123
  def _pre_load_data(self, pre_load_data):
@@ -2453,8 +2450,8 @@ class BespokeWindPlants(BaseAggregation):
2453
2450
  area_filter_kernel='queen', min_area=None,
2454
2451
  resolution=64, excl_area=0.0081, data_layers=None,
2455
2452
  gids=None, exclusion_shape=None, slice_lookup=None,
2456
- prior_meta=None, gid_map=None, bias_correct=None,
2457
- pre_loaded_data=None):
2453
+ eos_mult_baseline_cap_mw=200, prior_meta=None,
2454
+ gid_map=None, bias_correct=None, pre_loaded_data=None):
2458
2455
  """
2459
2456
  Standalone serial method to run bespoke optimization.
2460
2457
  See BespokeWindPlants docstring for parameter description.
@@ -2480,14 +2477,13 @@ class BespokeWindPlants(BaseAggregation):
2480
2477
  exclusion_shape = sc.exclusions.shape
2481
2478
 
2482
2479
  cls._check_inclusion_mask(inclusion_mask, gids, exclusion_shape)
2483
- Handler = BespokeSinglePlant.get_wind_handler(res_fpath)
2484
2480
 
2485
2481
  # pre-extract handlers so they are not repeatedly initialized
2486
2482
  file_kwargs = {
2487
2483
  "excl_dict": excl_dict,
2488
2484
  "area_filter_kernel": area_filter_kernel,
2489
2485
  "min_area": min_area,
2490
- "h5_handler": Handler,
2486
+ "h5_handler": MultiYearWindResource,
2491
2487
  }
2492
2488
 
2493
2489
  with AggFileHandler(excl_fpath, res_fpath, **file_kwargs) as fh:
@@ -2520,6 +2516,7 @@ class BespokeWindPlants(BaseAggregation):
2520
2516
  excl_area=excl_area,
2521
2517
  data_layers=data_layers,
2522
2518
  exclusion_shape=exclusion_shape,
2519
+ eos_mult_baseline_cap_mw=eos_mult_baseline_cap_mw,
2523
2520
  prior_meta=prior_meta,
2524
2521
  gid_map=gid_map,
2525
2522
  bias_correct=bias_correct,
@@ -2612,6 +2609,7 @@ class BespokeWindPlants(BaseAggregation):
2612
2609
  gids=gid,
2613
2610
  exclusion_shape=self.shape,
2614
2611
  slice_lookup=copy.deepcopy(self.slice_lookup),
2612
+ eos_mult_baseline_cap_mw=self._eos_mult_baseline_cap_mw,
2615
2613
  prior_meta=self._get_prior_meta(gid),
2616
2614
  gid_map=self._gid_map,
2617
2615
  bias_correct=self._get_bc_for_gid(gid),
@@ -2676,6 +2674,7 @@ class BespokeWindPlants(BaseAggregation):
2676
2674
  afk = self._area_filter_kernel
2677
2675
  wlm = self._wake_loss_multiplier
2678
2676
  i_bc = self._get_bc_for_gid(gid)
2677
+ ebc = self._eos_mult_baseline_cap_mw
2679
2678
 
2680
2679
  si = self.run_serial(self._excl_fpath,
2681
2680
  self._res_fpath,
@@ -2700,6 +2699,7 @@ class BespokeWindPlants(BaseAggregation):
2700
2699
  excl_area=self._excl_area,
2701
2700
  data_layers=self._data_layers,
2702
2701
  slice_lookup=slice_lookup,
2702
+ eos_mult_baseline_cap_mw=ebc,
2703
2703
  prior_meta=prior_meta,
2704
2704
  gid_map=self._gid_map,
2705
2705
  bias_correct=i_bc,
reV/econ/econ.py CHANGED
@@ -76,25 +76,36 @@ class Econ(BaseGen):
76
76
  (or slice) representing the GIDs of multiple sites can be
77
77
  specified to evaluate reV at multiple specific locations.
78
78
  A string pointing to a project points CSV file may also be
79
- specified. Typically, the CSV contains two columns:
79
+ specified. Typically, the CSV contains the following
80
+ columns:
80
81
 
81
- - ``gid``: Integer specifying the GID of each site.
82
+ - ``gid``: Integer specifying the generation GID of each
83
+ site.
82
84
  - ``config``: Key in the `sam_files` input dictionary
83
85
  (see below) corresponding to the SAM configuration to
84
86
  use for each particular site. This value can also be
85
87
  ``None`` (or left out completely) if you specify only
86
88
  a single SAM configuration file as the `sam_files`
87
89
  input.
88
-
89
- The CSV file may also contain site-specific inputs by
90
+ - ``capital_cost_multiplier``: This is an *optional*
91
+ multiplier input that, if included, will be used to
92
+ regionally scale the ``capital_cost`` input in the SAM
93
+ config. If you include this column in your CSV, you
94
+ *do not* need to specify ``capital_cost``, unless you
95
+ would like that value to vary regionally and
96
+ independently of the multiplier (i.e. the multiplier
97
+ will still be applied on top of the ``capital_cost``
98
+ input).
99
+
100
+ The CSV file may also contain other site-specific inputs by
90
101
  including a column named after a config keyword (e.g. a
91
- column called ``capital_cost`` may be included to specify a
92
- site-specific capital cost value for each location). Columns
93
- that do not correspond to a config key may also be included,
94
- but they will be ignored. A DataFrame following the same
95
- guidelines as the CSV input (or a dictionary that can be
96
- used to initialize such a DataFrame) may be used for this
97
- input as well.
102
+ column called ``wind_turbine_rotor_diameter`` may be
103
+ included to specify a site-specific turbine diameter for
104
+ each location). Columns that do not correspond to a config
105
+ key may also be included, but they will be ignored. A
106
+ DataFrame following the same guidelines as the CSV input
107
+ (or a dictionary that can be used to initialize such a
108
+ DataFrame) may be used for this input as well.
98
109
  sam_files : dict | str
99
110
  A dictionary mapping SAM input configuration ID(s) to SAM
100
111
  configuration(s). Keys are the SAM config ID(s) which
@@ -212,11 +212,7 @@ class EconomiesOfScale:
212
212
  if cost_per_mw is None:
213
213
  return None
214
214
 
215
- cost = cap * cost_per_mw
216
- if cost > 0:
217
- return cost
218
-
219
- return None
215
+ return cap * cost_per_mw
220
216
 
221
217
  @property
222
218
  def raw_capital_cost(self):
@@ -278,7 +274,7 @@ class EconomiesOfScale:
278
274
  Fixed operating cost from input data arg
279
275
  """
280
276
  foc_from_cap = self._cost_from_cap(
281
- SupplyCurveField.COST_BASE_FOC_USD_PER_AC_MW
277
+ SupplyCurveField.COST_SITE_FOC_USD_PER_AC_MW
282
278
  )
283
279
  if foc_from_cap is not None:
284
280
  return foc_from_cap
@@ -297,7 +293,7 @@ class EconomiesOfScale:
297
293
  Variable operating cost from input data arg
298
294
  """
299
295
  voc_from_cap = self._cost_from_cap(
300
- SupplyCurveField.COST_BASE_VOC_USD_PER_AC_MW
296
+ SupplyCurveField.COST_SITE_VOC_USD_PER_AC_MW
301
297
  )
302
298
  if voc_from_cap is not None:
303
299
  return voc_from_cap
@@ -171,7 +171,7 @@ class Gen(BaseGen):
171
171
  multiple sites can be specified to evaluate reV at multiple
172
172
  specific locations. A string pointing to a project points
173
173
  CSV file may also be specified. Typically, the CSV contains
174
- two columns:
174
+ the following columns:
175
175
 
176
176
  - ``gid``: Integer specifying the generation GID of each
177
177
  site.
@@ -181,16 +181,25 @@ class Gen(BaseGen):
181
181
  ``None`` (or left out completely) if you specify only
182
182
  a single SAM configuration file as the `sam_files`
183
183
  input.
184
-
185
- The CSV file may also contain site-specific inputs by
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
186
195
  including a column named after a config keyword (e.g. a
187
- column called ``capital_cost`` may be included to specify a
188
- site-specific capital cost value for each location). Columns
189
- that do not correspond to a config key may also be included,
190
- but they will be ignored. A DataFrame following the same
191
- guidelines as the CSV input (or a dictionary that can be
192
- used to initialize such a DataFrame) may be used for this
193
- 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.
194
203
 
195
204
  .. Note:: By default, the generation GID of each site is
196
205
  assumed to match the resource GID to be evaluated for that
@@ -227,10 +236,10 @@ class Gen(BaseGen):
227
236
  info on the allowed and/or required SAM config file inputs.
228
237
  resource_file : str
229
238
  Filepath to resource data. This input can be path to a
230
- single resource HDF5 file, a path to a directory containing
231
- data spread across multiple HDF5 files, or a path including
232
- a wildcard input like ``/h5_dir/prefix*suffix``. In all
233
- cases, the resource data must be readable by
239
+ single resource HDF5 file or a path including a wildcard
240
+ input like ``/h5_dir/prefix*suffix`` (i.e. if your datasets
241
+ for a single year are spread out over multiple files). In
242
+ all cases, the resource data must be readable by
234
243
  :py:class:`rex.resource.Resource`
235
244
  or :py:class:`rex.multi_file_resource.MultiFileResource`.
236
245
  (i.e. the resource data conform to the
@@ -244,8 +253,14 @@ class Gen(BaseGen):
244
253
  consideration, and its shape must be a multiple of 8760.
245
254
 
246
255
  .. Note:: If executing ``reV`` from the command line, this
247
- path can contain brackets ``{}`` that will be filled in by
248
- the `analysis_years` input.
256
+ input string can contain brackets ``{}`` that will be
257
+ filled in by the `analysis_years` input. Alternatively,
258
+ this input can be a list of explicit files to process. In
259
+ this case, the length of the list must match the length of
260
+ the `analysis_years` input exactly, and the path are
261
+ assumed to align with the `analysis_years` (i.e. the first
262
+ path corresponds to the first analysis year, the second
263
+ path corresponds to the second analysis year, and so on).
249
264
 
250
265
  .. Important:: If you are using custom resource data (i.e.
251
266
  not NSRDB/WTK/Sup3rCC, etc.), ensure the following:
@@ -103,8 +103,9 @@ class MultiYearGroup:
103
103
 
104
104
  if "lcoe_fcr" in dsets:
105
105
  for dset in LCOE_REQUIRED_OUTPUTS:
106
- if dset not in pass_through_dsets:
106
+ if dset not in pass_through_dsets and dset in dsets:
107
107
  pass_through_dsets.append(dset)
108
+
108
109
  if "dc_ac_ratio" in dsets:
109
110
  if "dc_ac_ratio" not in pass_through_dsets:
110
111
  pass_through_dsets.append("dc_ac_ratio")
@@ -839,11 +840,6 @@ def my_collect_groups(out_fpath, groups, clobber=True):
839
840
  dset, group=group['group'])
840
841
 
841
842
  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
843
  for dset in pass_through_dsets:
848
844
  MultiYear.pass_through(out_fpath, group['source_files'],
849
845
  dset, group=group['group'])
@@ -50,12 +50,25 @@ def _preprocessor(config, out_dir):
50
50
  def _format_res_fpath(config):
51
51
  """Format res_fpath with year, if need be. """
52
52
  res_fpath = config.setdefault("res_fpath", None)
53
- if isinstance(res_fpath, str) and '{}' in res_fpath:
54
- for year in range(1998, 2018):
55
- if os.path.exists(res_fpath.format(year)):
56
- break
57
-
58
- config["res_fpath"] = res_fpath.format(year)
53
+ if isinstance(res_fpath, str):
54
+ if '{}' in res_fpath:
55
+ for year in range(1950, 2100):
56
+ if os.path.exists(res_fpath.format(year)):
57
+ break
58
+ else:
59
+ msg = ("Could not find any files that match the pattern"
60
+ "{!r}".format(res_fpath.format("<year>")))
61
+ logger.error(msg)
62
+ raise FileNotFoundError(msg)
63
+
64
+ res_fpath = res_fpath.format(year)
65
+
66
+ elif not os.path.exists(res_fpath):
67
+ msg = "Could not find resource file: {!r}".format(res_fpath)
68
+ logger.error(msg)
69
+ raise FileNotFoundError(msg)
70
+
71
+ config["res_fpath"] = res_fpath
59
72
 
60
73
  return config
61
74
 
@@ -2188,10 +2188,10 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
2188
2188
  def _compute_cost_per_ac_mw(self, dset):
2189
2189
  """Compute a cost per AC MW for a given input. """
2190
2190
  if self._sam_system_capacity <= 0:
2191
- return 0
2191
+ return None
2192
2192
 
2193
2193
  if dset not in self.gen.datasets:
2194
- return 0
2194
+ return None
2195
2195
 
2196
2196
  sam_cost = self.exclusion_weighted_mean(self.gen[dset])
2197
2197
  sam_cost_per_mw = sam_cost / self._sam_system_capacity
@@ -2391,10 +2391,11 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
2391
2391
  summary[SupplyCurveField.RAW_LCOE] = eos.raw_lcoe
2392
2392
  summary[SupplyCurveField.MEAN_LCOE] = eos.scaled_lcoe
2393
2393
  summary[SupplyCurveField.EOS_MULT] = eos.capital_cost_scalar
2394
- summary[SupplyCurveField.COST_SITE_OCC_USD_PER_AC_MW] = (
2395
- summary[SupplyCurveField.COST_SITE_OCC_USD_PER_AC_MW]
2396
- * summary[SupplyCurveField.EOS_MULT]
2397
- )
2394
+ cost = summary[SupplyCurveField.COST_SITE_OCC_USD_PER_AC_MW]
2395
+ if cost is not None:
2396
+ summary[SupplyCurveField.COST_SITE_OCC_USD_PER_AC_MW] = (
2397
+ cost * summary[SupplyCurveField.EOS_MULT]
2398
+ )
2398
2399
  return summary
2399
2400
 
2400
2401
  @classmethod
@@ -2537,6 +2538,10 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
2537
2538
  if cap_cost_scale is not None:
2538
2539
  summary = point.economies_of_scale(cap_cost_scale, summary)
2539
2540
 
2541
+ for arg, val in summary.items():
2542
+ if val is None:
2543
+ summary[arg] = np.nan
2544
+
2540
2545
  return summary
2541
2546
 
2542
2547
 
@@ -18,7 +18,6 @@ from rex.multi_file_resource import MultiFileResource
18
18
  from rex.resource import Resource
19
19
  from rex.utilities.execution import SpawnProcessPool
20
20
 
21
- from reV.generation.base import BaseGen
22
21
  from reV.handlers.exclusions import ExclusionLayers
23
22
  from reV.supply_curve.aggregation import (
24
23
  AbstractAggFileHandler,
@@ -1410,7 +1409,7 @@ class SupplyCurveAggregation(BaseAggregation):
1410
1409
  ``multi-year``, ``collect``, or ``econ``. However, note
1411
1410
  that duplicate executions of any of these commands within
1412
1411
  the pipeline may invalidate this parsing, meaning the
1413
- `econ_fpath` input will have to be specified manually.
1412
+ `gen_fpath` input will have to be specified manually.
1414
1413
 
1415
1414
  By default, ``None``.
1416
1415
  args : tuple | list, optional
@@ -1116,8 +1116,7 @@ class SupplyCurve:
1116
1116
 
1117
1117
  return table
1118
1118
 
1119
- # pylint: disable=C901
1120
- def _full_sort(
1119
+ def _full_sort( # noqa: C901
1121
1120
  self,
1122
1121
  trans_table,
1123
1122
  trans_costs=None,
@@ -1289,7 +1288,7 @@ class SupplyCurve:
1289
1288
 
1290
1289
  for col in _REQUIRED_OUTPUT_COLS:
1291
1290
  if col not in self._trans_table:
1292
- self._trans_table[col] = None
1291
+ self._trans_table[col] = np.nan
1293
1292
  if col not in columns:
1294
1293
  columns.append(col)
1295
1294
 
@@ -185,7 +185,7 @@ class TechMapping:
185
185
  excl_row_slices, excl_col_slices,
186
186
  coord_labels=(LATITUDE, LONGITUDE)):
187
187
  """
188
- Extract the exclusion coordinates for teh desired gids for TechMapping.
188
+ Extract the exclusion coordinates for the desired gids for TechMapping.
189
189
 
190
190
  Parameters
191
191
  ----------
reV/version.py CHANGED
@@ -2,4 +2,4 @@
2
2
  reV Version number
3
3
  """
4
4
 
5
- __version__ = "0.9.0"
5
+ __version__ = "0.9.4"