NREL-reV 0.14.0__py3-none-any.whl → 0.14.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: NREL-reV
3
- Version: 0.14.0
3
+ Version: 0.14.2
4
4
  Summary: National Renewable Energy Laboratory's (NREL's) Renewable Energy Potential(V) Model: reV
5
5
  Author-email: Galen Maclaurin <galen.maclaurin@nrel.gov>
6
6
  Maintainer-email: Grant Buster <gbuster@nrel.gov>, Paul Pinchuk <ppinchuk@nrel.gov>
@@ -23,7 +23,7 @@ License-File: LICENSE
23
23
  Requires-Dist: NREL-gaps<0.9,>=0.8.0
24
24
  Requires-Dist: NREL-NRWAL<0.1,>=0.0.11
25
25
  Requires-Dist: NREL-PySAM~=7.0.0
26
- Requires-Dist: NREL-rex<0.4,>=0.3.2
26
+ Requires-Dist: NREL-rex<0.4,>=0.3.5
27
27
  Requires-Dist: numpy<3,>=2.0.2
28
28
  Requires-Dist: packaging<25,>=24.2
29
29
  Requires-Dist: plotly<7,>=6.0.1
@@ -1,13 +1,13 @@
1
- nrel_rev-0.14.0.dist-info/licenses/LICENSE,sha256=hDwoTANtan2ZpufBlXm5C3W_PJ-mCqItvlcobgjxL7k,1526
1
+ nrel_rev-0.14.2.dist-info/licenses/LICENSE,sha256=hDwoTANtan2ZpufBlXm5C3W_PJ-mCqItvlcobgjxL7k,1526
2
2
  reV/__init__.py,sha256=tXTpWu_qVo3uotfSw_TJ-gNbidGaIPPfUTwBlpCMJ-g,856
3
3
  reV/cli.py,sha256=u7G5M5moA7q8fCgC_1MB30Z7R14GNcngVf6eVNkfQU8,1682
4
- reV/version.py,sha256=MW2Xrj6zWXWLE73quVP27irsAbTU85t5oflVIQzGMVE,51
4
+ reV/version.py,sha256=iZ2NEm6j_ZMkwjscuehf0I9OUwsEHrSS3xbfjV9LCbM,51
5
5
  reV/SAM/SAM.py,sha256=3NK9rRaJzqH6wz7CU_5XguKCRhmoilOpDdFFsFgOaxQ,33163
6
6
  reV/SAM/__init__.py,sha256=LJqoncyKDY5ZP5WA4kboh561bce11F9Ge645Izah0EY,240
7
7
  reV/SAM/defaults.py,sha256=JQMJomX7wsbMzxKjx_IMnA_9QFsV2yWCyl_JToDVSJo,6703
8
8
  reV/SAM/econ.py,sha256=dFhtUXp5eozSsPyk0XXji0HGexKJnxoct8cwE1ApHVg,20704
9
9
  reV/SAM/generation.py,sha256=kbDiQ0a0VCrfPi6P1RSMKdjlbxuO3rgiR0-YSM1pyU8,85653
10
- reV/SAM/version_checker.py,sha256=q-eXsmSB08A5hSelNH7uAe_bqPpqjsxaiS3OBjXBs-0,3997
10
+ reV/SAM/version_checker.py,sha256=MBSPqHYmTyjXJLi1ziJ1sGS18AH9an-PZRcl-6IPJGw,3963
11
11
  reV/SAM/windbos.py,sha256=fnq1uxtSl-vtJth0gTlHkCSsmvDbRROCo9RGdqA7hK4,7504
12
12
  reV/SAM/defaults/USA AZ Phoenix Sky Harbor Intl Ap (TMY3).csv,sha256=8QorTX0ACjgPgNV7kLSpTHOqfY4E17gkkpKB-qseiFk,406896
13
13
  reV/SAM/defaults/USA CA Daggett (TMY2).csv,sha256=vVP-mk_cXj1GRHMbqsyZ60fTVMywTappzwp5-ILurFs,410050
@@ -16,7 +16,7 @@ reV/SAM/defaults/WY Southern-Flat Lands.srw,sha256=oSlFI7nnycW7SMnTXEXBXYkoh8rhV
16
16
  reV/SAM/defaults/geothermal.json,sha256=gnlxOBxxkbDDacIw6B9yP9iRkfmwj0WTWs8Zb71ytCo,7119
17
17
  reV/SAM/defaults/i_pvwattsv5.json,sha256=sF8rSe1bcqsKLXchvRxlp25dXOVe_q7rPJTf_vWR20Y,312
18
18
  reV/bespoke/__init__.py,sha256=vpXbyBUrUsTgK8UP_LafMjLiDg2CRG9WZLHPsOJoxek,109
19
- reV/bespoke/bespoke.py,sha256=573mIoJx8HXq5RH8HufjIhX20XjMJZbbR1kDGKpMzSw,112723
19
+ reV/bespoke/bespoke.py,sha256=ZMl4kz9oAaXpZaNa5EgiD43RvKiKl3xymRv-z4lDYmQ,113169
20
20
  reV/bespoke/cli_bespoke.py,sha256=b6Xu0GKpXqPX3qVJ6-z0FrO97uCsH_1dVOa4r6IvesQ,2911
21
21
  reV/bespoke/gradient_free.py,sha256=URWV1yiO2jyWk3_GOpfpLV_wlgJhXXGmTUwCB3WTV0Y,12015
22
22
  reV/bespoke/pack_turbs.py,sha256=Dcd9F8obF8LPztzeycB5kxa5hXKSCiz3jC1WeAFUx28,3508
@@ -28,7 +28,7 @@ reV/config/cli_project_points.py,sha256=6edOlLNOG-ZEbcpNS2MPfu-DXjcOTEh_MEvKOvQS
28
28
  reV/config/curtailment.py,sha256=1bH7xzxOmD4PwLKcXFNotMAa9iCfGBUm2DKTOXViCJg,5548
29
29
  reV/config/execution.py,sha256=hyf8W7XYUXE6tXBXs-4En7h_aDTYu8FzbslgBsKLJkQ,5046
30
30
  reV/config/output_request.py,sha256=Sj3L5hcypLTCtLnKAqS7GSaI4_Hpb28QLuWT9IMCvrc,4520
31
- reV/config/project_points.py,sha256=5cY-vMXlGfgjpUDf1OeVLuUJXXXwis365JdOaclMqoY,41740
31
+ reV/config/project_points.py,sha256=CSq7lHgWfJ1pq1XlWJ5eykmGGhumkxVcg__Ln9RR4-Y,41886
32
32
  reV/config/sam_config.py,sha256=xvvx2FTuliq0Sk-BjRE3I9zdDmIdwHVBnWtXCcsoc40,7998
33
33
  reV/econ/__init__.py,sha256=UId1LNaAP9lErCEXVce6JZf0qVRUvwNFOPrajdRevGo,130
34
34
  reV/econ/cli_econ.py,sha256=2KNy3JQD0EKjStaoD2r6nv3ELFw88h2E_up-UKj_sfE,4286
@@ -74,17 +74,17 @@ reV/rep_profiles/__init__.py,sha256=v6YZk7tGLpw5gnSckjOn22xazTnv10siCFfWQl9H_fQ,
74
74
  reV/rep_profiles/cli_rep_profiles.py,sha256=i3fRolT7HTzTQePXpNLDyxwgpRa7FEfHO1mOqiSufK4,4613
75
75
  reV/rep_profiles/rep_profiles.py,sha256=dJ2jcklbelYYNS3IUXIYHg8E0poXLOWrwdqeBBnqYIE,48271
76
76
  reV/supply_curve/__init__.py,sha256=dbf0cO0qmt1VhV8dnDddztrcpnwua9T2HBhM6tLKaP8,274
77
- reV/supply_curve/aggregation.py,sha256=dnnuDG9BEGEgY17uA8nSF3s6L6Q6Lxe94aS7AIKSlm0,40471
78
- reV/supply_curve/cli_sc_aggregation.py,sha256=qGfJyZjg4tFARG2dl-wAlHv9KjEp2u4wg1Boxz18Gf0,3347
77
+ reV/supply_curve/aggregation.py,sha256=DvIUmj0PbZ8NxgOSmpXvzn9wJnOiQO2Xm2w_WCRVK-U,40908
78
+ reV/supply_curve/cli_sc_aggregation.py,sha256=iDBHlUUPg-Ce4N4Rqb4scn8A_Ygq2q4i9v_ypbH-krs,3387
79
79
  reV/supply_curve/cli_supply_curve.py,sha256=e-XrHQIe4OqWTL6u-TUAyHrw7Alk7vkXQ2HoLbE3zTM,2163
80
- reV/supply_curve/cli_tech_mapping.py,sha256=ztNJhgk3WlSD5Gn7Rh3qBLMvX_pFkjuQT7qUK4s0P2w,1731
80
+ reV/supply_curve/cli_tech_mapping.py,sha256=Dirn4JOmu_3BIP7WgcRLAepsreKqmDhChHLPEUqcDAo,1735
81
81
  reV/supply_curve/competitive_wind_farms.py,sha256=eOjM72-4oWtsqxB7Wh2gnB2zVAt4LY3iPE_DqWdXbQ4,15795
82
82
  reV/supply_curve/exclusions.py,sha256=4-ZxTO5Vlu03vie0V_74uvdajQfCuC8FE96Pg8I4U_c,42950
83
83
  reV/supply_curve/extent.py,sha256=a31po753hXSxQ8lfcCvpE8hoKc4bY7MmYq0NO0jtdqA,17414
84
- reV/supply_curve/points.py,sha256=tWL_9hO5xBzDygk9TfDGotSTOgLNp9hEY7-2DJ01TrI,93336
85
- reV/supply_curve/sc_aggregation.py,sha256=0rxNPJgaK1eilSeRVF-IBRVAqE0UcM8gKo_SkLHdZl0,67738
84
+ reV/supply_curve/points.py,sha256=p9Kqzas1jX8uCXcvYX7o6mdUVsgeHCC_F9pjBaEypPI,93548
85
+ reV/supply_curve/sc_aggregation.py,sha256=l7uXs-grLb0enblPNsEDKccSP11WZ2Pah5baWtgtvzE,68067
86
86
  reV/supply_curve/supply_curve.py,sha256=9zhAA_9XSxE18j1Z9FuC71Wr3I0VuakfR5mt1_gHYuU,69931
87
- reV/supply_curve/tech_mapping.py,sha256=JAinwbh6UPzBX2qwf0EBDDYCanEfe-F6RgiE23oNBcU,18149
87
+ reV/supply_curve/tech_mapping.py,sha256=WP-o5Sk5q1Jz_F9Rpe77_fvTYGH8R4GnYDfRMSTuHB8,17804
88
88
  reV/utilities/__init__.py,sha256=HVb1P-ee-z2P6UTjyc0s0gYFK7XYM71BQLEMxj26apA,10834
89
89
  reV/utilities/_clean_readme.py,sha256=IFI9wGPX5nnLTNVLJzH8IOHq9unQlAlHRu4Namib0LA,709
90
90
  reV/utilities/cli_functions.py,sha256=1_T_sXz0Ct8lW-vOk3mMRcpD6NYsc9cGI7dEujIi9z4,3864
@@ -92,8 +92,8 @@ reV/utilities/curtailment.py,sha256=As902-2aLGnCiVEutYfAFIOwuV--_rCQhxGNOY9RB-4,
92
92
  reV/utilities/exceptions.py,sha256=f7sRGsbFLpmL6Caq_H1cD4GfVhnLMyvYUsLPA1UVDDE,3974
93
93
  reV/utilities/pytest_utils.py,sha256=spCw9yQ8KEYOkQZpCi9IEmaWIvIqHqbUPDXXNQJJ68U,3241
94
94
  reV/utilities/slots.py,sha256=xsw-JuUVZ0YeoCNuwP_HxGNxFMA4xRs1tuImXHIJqaU,2618
95
- nrel_rev-0.14.0.dist-info/METADATA,sha256=wZN_Wb9S2ZCiKDgYXrPAmEiywSA_-1o12-anLqwQvHs,10733
96
- nrel_rev-0.14.0.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
97
- nrel_rev-0.14.0.dist-info/entry_points.txt,sha256=4IfJtZm2iMJwrbC8J0Or7VjZWnFpvCaHYVpvSWfIwDA,616
98
- nrel_rev-0.14.0.dist-info/top_level.txt,sha256=S6YF2ZYgXUB6n28SY0K2H8YB9tMJdXQ9CyQbo6VC89M,4
99
- nrel_rev-0.14.0.dist-info/RECORD,,
95
+ nrel_rev-0.14.2.dist-info/METADATA,sha256=lIgkZ3PQLu5YOtLYAP62nRtNVAhxkwRuHGqS_2mSwDY,10733
96
+ nrel_rev-0.14.2.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
97
+ nrel_rev-0.14.2.dist-info/entry_points.txt,sha256=4IfJtZm2iMJwrbC8J0Or7VjZWnFpvCaHYVpvSWfIwDA,616
98
+ nrel_rev-0.14.2.dist-info/top_level.txt,sha256=S6YF2ZYgXUB6n28SY0K2H8YB9tMJdXQ9CyQbo6VC89M,4
99
+ nrel_rev-0.14.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.1)
2
+ Generator: setuptools (80.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -6,8 +6,9 @@ Created on Mon Feb 3 14:40:42 2020
6
6
  @author: gbuster
7
7
  """
8
8
  import logging
9
+ import importlib
9
10
  from warnings import warn
10
- from pkg_resources import get_distribution
11
+
11
12
  from packaging import version
12
13
  from reV.utilities.exceptions import PySAMVersionError, PySAMVersionWarning
13
14
 
@@ -105,7 +106,7 @@ class PySamVersionChecker:
105
106
  @property
106
107
  def pysam_version(self):
107
108
  """Get the PySAM distribution version"""
108
- return str(get_distribution('nrel-pysam')).split(' ')[1]
109
+ return importlib.metadata.version("nrel-pysam")
109
110
 
110
111
  @classmethod
111
112
  def run(cls, tech, parameters):
reV/bespoke/bespoke.py CHANGED
@@ -23,7 +23,7 @@ from rex.multi_year_resource import MultiYearWindResource
23
23
  from rex.utilities.bc_parse_table import parse_bc_table
24
24
  from rex.utilities.execution import SpawnProcessPool
25
25
  from rex.utilities.loggers import create_dirs, log_mem
26
- from rex.utilities.utilities import parse_year
26
+ from rex.utilities.utilities import parse_year, check_res_file
27
27
 
28
28
  from reV.config.output_request import SAMOutputRequest
29
29
  from reV.econ.utilities import lcoe_fcr
@@ -80,7 +80,8 @@ class BespokeMultiPlantData:
80
80
  Option to pre-load relative humidity data (useful for icing
81
81
  runs). If ``False``, relative humidities are not loaded.
82
82
  """
83
- self.res_fpath = res_fpath
83
+ self.res_fpath = ([res_fpath]
84
+ if isinstance(res_fpath, str) else res_fpath)
84
85
  self.sc_gid_to_hh = sc_gid_to_hh
85
86
  self.sc_gid_to_res_gid = sc_gid_to_res_gid
86
87
  self.hh_to_res_gids = {}
@@ -104,8 +105,9 @@ class BespokeMultiPlantData:
104
105
  hh: sorted(gids) for hh, gids in self.hh_to_res_gids.items()
105
106
  }
106
107
 
108
+ hsds = all(check_res_file(fp)[1] for fp in self.res_fpath)
107
109
  start_time = time.time()
108
- with MultiYearWindResource(self.res_fpath) as res:
110
+ with MultiYearWindResource(self.res_fpath, hsds=hsds) as res:
109
111
  self._wind_dirs = {
110
112
  hh: res[f"winddirection_{hh}m", :, gids]
111
113
  for hh, gids in self.hh_to_res_gids.items()
@@ -488,7 +490,9 @@ class BespokeSinglePlant:
488
490
  self._pre_loaded_data = pre_loaded_data
489
491
  self._outputs = {}
490
492
 
491
- res = res if not isinstance(res, str) else MultiYearWindResource(res)
493
+ if isinstance(res, str):
494
+ __, hsds = check_res_file(res)
495
+ res = MultiYearWindResource(res, hsds=hsds)
492
496
 
493
497
  self._sc_point = AggSCPoint(
494
498
  gid,
@@ -1972,7 +1976,8 @@ class BespokeWindPlants(BaseAggregation):
1972
1976
  pre_extract_inclusions=pre_extract_inclusions,
1973
1977
  )
1974
1978
 
1975
- self._res_fpath = res_fpath
1979
+ self._res_fpath = ([res_fpath]
1980
+ if isinstance(res_fpath, str) else res_fpath)
1976
1981
  self._obj_fun = objective_function
1977
1982
  self._cap_cost_fun = capital_cost_function
1978
1983
  self._foc_fun = fixed_operating_cost_function
@@ -2123,8 +2128,9 @@ class BespokeWindPlants(BaseAggregation):
2123
2128
  )
2124
2129
  )
2125
2130
 
2131
+ hsds = all(check_res_file(fp)[1] for fp in self._res_fpath)
2126
2132
  # just check that this file exists, cannot check res_fpath if *glob
2127
- with MultiYearWindResource(self._res_fpath) as f:
2133
+ with MultiYearWindResource(self._res_fpath, hsds=hsds) as f:
2128
2134
  assert any(f.dsets)
2129
2135
 
2130
2136
  def _pre_load_data(self, pre_load_data):
@@ -2494,6 +2500,7 @@ class BespokeWindPlants(BaseAggregation):
2494
2500
  "area_filter_kernel": area_filter_kernel,
2495
2501
  "min_area": min_area,
2496
2502
  "h5_handler": MultiYearWindResource,
2503
+ "hsds": all(check_res_file(fp)[1] for fp in res_fpath),
2497
2504
  }
2498
2505
 
2499
2506
  with AggFileHandler(excl_fpath, res_fpath, **file_kwargs) as fh:
@@ -464,8 +464,10 @@ class ProjectPoints:
464
464
  h_var = "wind_turbine_hub_ht"
465
465
  if self._h is None:
466
466
  if "wind" in self.tech:
467
- # wind technology, get a list of h values
468
- self._h = [self[site][1][h_var] for site in self.sites]
467
+ if h_var in self.df.columns:
468
+ self._h = self.df[h_var].values.tolist()
469
+ else:
470
+ self._h = [self[site][1][h_var] for site in self.sites]
469
471
 
470
472
  return self._h
471
473
 
@@ -837,7 +839,6 @@ class ProjectPoints:
837
839
  logger.error(msg)
838
840
  raise ConfigError(msg)
839
841
 
840
-
841
842
  unused_configs = set(curtail_configs) - set(df_configs)
842
843
  if unused_configs:
843
844
  msg = ("One or more curtailment configurations not found in "
@@ -1164,12 +1165,14 @@ class ProjectPoints:
1164
1165
  multi_h5_res, hsds = check_res_file(res_file)
1165
1166
  if multi_h5_res:
1166
1167
  res_cls = MultiFileResourceX
1168
+ res_kwargs = {}
1167
1169
  else:
1168
1170
  res_cls = ResourceX
1171
+ res_kwargs = {"hsds": hsds}
1169
1172
 
1170
1173
  logger.info("Extracting ProjectPoints for desired regions")
1171
1174
  points = []
1172
- with res_cls(res_file, hsds=hsds) as f:
1175
+ with res_cls(res_file, **res_kwargs) as f:
1173
1176
  meta = f.meta
1174
1177
  for region, region_col in regions.items():
1175
1178
  logger.debug("- {}: {}".format(region_col, region))
@@ -1181,7 +1184,7 @@ class ProjectPoints:
1181
1184
  if duplicates:
1182
1185
  msg = (
1183
1186
  "reV Cannot currently handle duplicate "
1184
- "Resource gids! The given regions containg the "
1187
+ "Resource gids! The given regions containing the "
1185
1188
  "same gids:\n{}".format(duplicates)
1186
1189
  )
1187
1190
  logger.error(msg)
@@ -9,6 +9,7 @@ import h5py
9
9
  import numpy as np
10
10
  import pandas as pd
11
11
  from rex.resource import Resource
12
+ from rex.utilities import check_res_file
12
13
  from rex.utilities.execution import SpawnProcessPool
13
14
  from rex.utilities.loggers import log_mem
14
15
 
@@ -113,6 +114,7 @@ class AggFileHandler(AbstractAggFileHandler):
113
114
  area_filter_kernel="queen",
114
115
  min_area=None,
115
116
  h5_handler=None,
117
+ **h5_handler_kwargs,
116
118
  ):
117
119
  """
118
120
  Parameters
@@ -136,6 +138,8 @@ class AggFileHandler(AbstractAggFileHandler):
136
138
  h5_handler : rex.Resource | None
137
139
  Optional special handler similar to the rex.Resource handler which
138
140
  is default.
141
+ **h5_handler_kwargs
142
+ Optional keyword-value pairs to pass to the h5 handler.
139
143
  """
140
144
  super().__init__(
141
145
  excl_fpath,
@@ -145,9 +149,9 @@ class AggFileHandler(AbstractAggFileHandler):
145
149
  )
146
150
 
147
151
  if h5_handler is None:
148
- self._h5 = Resource(h5_fpath)
152
+ self._h5 = Resource(h5_fpath, **h5_handler_kwargs)
149
153
  else:
150
- self._h5 = h5_handler(h5_fpath)
154
+ self._h5 = h5_handler(h5_fpath, **h5_handler_kwargs)
151
155
 
152
156
  @property
153
157
  def h5(self):
@@ -504,8 +508,9 @@ class BaseAggregation(ABC):
504
508
  generation run.
505
509
  """
506
510
 
511
+ __, hsds = check_res_file(gen_fpath)
507
512
  if gen_fpath.endswith(".h5"):
508
- with Resource(gen_fpath) as f:
513
+ with Resource(gen_fpath, hsds=hsds) as f:
509
514
  gen_index = f.meta
510
515
  elif gen_fpath.endswith(".csv"):
511
516
  gen_index = pd.read_csv(gen_fpath)
@@ -631,7 +636,8 @@ class Aggregation(BaseAggregation):
631
636
  )
632
637
  )
633
638
 
634
- if not os.path.exists(h5_fpath):
639
+ __, hsds = check_res_file(h5_fpath)
640
+ if not hsds and not os.path.exists(h5_fpath):
635
641
  raise FileNotFoundError(
636
642
  "Could not find required h5 file: " "{}".format(h5_fpath)
637
643
  )
@@ -645,7 +651,7 @@ class Aggregation(BaseAggregation):
645
651
  )
646
652
  )
647
653
 
648
- with Resource(h5_fpath) as f:
654
+ with Resource(h5_fpath, hsds=hsds) as f:
649
655
  for dset in self._agg_dsets:
650
656
  if dset not in f:
651
657
  raise FileInputError(
@@ -744,6 +750,7 @@ class Aggregation(BaseAggregation):
744
750
  "excl_dict": excl_dict,
745
751
  "area_filter_kernel": area_filter_kernel,
746
752
  "min_area": min_area,
753
+ "hsds": check_res_file(h5_fpath)[1],
747
754
  }
748
755
  dsets = (
749
756
  *agg_dset,
@@ -1011,7 +1018,9 @@ class Aggregation(BaseAggregation):
1011
1018
  chunks = {}
1012
1019
  dtypes = {}
1013
1020
  time_index = None
1014
- with Resource(h5_fpath) as f:
1021
+
1022
+ __, hsds = check_res_file(h5_fpath)
1023
+ with Resource(h5_fpath, hsds=hsds) as f:
1015
1024
  for dset, data in agg_out.items():
1016
1025
  dsets.append(dset)
1017
1026
  shape = data.shape
@@ -6,6 +6,7 @@ import os
6
6
  import logging
7
7
 
8
8
  from rex.multi_file_resource import MultiFileResource
9
+ from rex.utilities.utilities import check_res_file
9
10
  from gaps.cli import as_click_command, CLICommandFromClass
10
11
 
11
12
  from reV.supply_curve.sc_aggregation import SupplyCurveAggregation
@@ -32,7 +33,7 @@ def _preprocessor(config, out_dir):
32
33
  dict
33
34
  Updated config file.
34
35
  """
35
- config = _format_res_fpath(config)
36
+ config = _validate_res_fpath(config)
36
37
  _validate_tm(config)
37
38
 
38
39
  key_to_modules = {"gen_fpath": [ModuleName.MULTI_YEAR,
@@ -47,32 +48,37 @@ def _preprocessor(config, out_dir):
47
48
  return config
48
49
 
49
50
 
50
- def _format_res_fpath(config):
51
- """Format res_fpath with year, if need be. """
51
+ def _validate_res_fpath(config):
52
+ """Format res_fpath with year (if needed) and check for file existence"""
52
53
  res_fpath = config.setdefault("res_fpath", None)
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
54
+ if not isinstance(res_fpath, str):
55
+ return config
56
+
57
+ if '{}' in res_fpath:
58
+ config["res_fpath"] = _get_filepath_with_year(res_fpath)
59
+ else:
60
+ check_res_file(res_fpath)
72
61
 
73
62
  return config
74
63
 
75
64
 
65
+ def _get_filepath_with_year(res_fpath):
66
+ """Find first file that exists on disk with year filled in"""
67
+
68
+ for year in range(1950, 2100):
69
+ fp = res_fpath.format(year)
70
+ try:
71
+ check_res_file(fp)
72
+ except FileNotFoundError:
73
+ continue
74
+ return fp
75
+
76
+ msg = ("Could not find any files that match the pattern"
77
+ "{!r}".format(res_fpath.format("<year>")))
78
+ logger.error(msg)
79
+ raise FileNotFoundError(msg)
80
+
81
+
76
82
  def _validate_tm(config):
77
83
  """Check that tm_dset exists or that res_fpath is given (to generate tm)"""
78
84
  paths = config["excl_fpath"]
@@ -9,7 +9,7 @@ from gaps.cli import as_click_command, CLICommandFromClass
9
9
  from reV.supply_curve.tech_mapping import TechMapping
10
10
  from reV.utilities import ModuleName
11
11
  from reV.utilities.exceptions import ConfigError
12
- from reV.supply_curve.cli_sc_aggregation import _format_res_fpath
12
+ from reV.supply_curve.cli_sc_aggregation import _validate_res_fpath
13
13
 
14
14
  logger = logging.getLogger(__name__)
15
15
 
@@ -28,7 +28,7 @@ def _preprocessor(config):
28
28
  Updated config file.
29
29
  """
30
30
  _validate_excl_fpath(config)
31
- config = _format_res_fpath(config)
31
+ config = _validate_res_fpath(config)
32
32
  _validate_dset(config)
33
33
 
34
34
  return config
@@ -11,7 +11,7 @@ import numpy as np
11
11
  import pandas as pd
12
12
  from rex.multi_time_resource import MultiTimeResource
13
13
  from rex.resource import BaseResource, Resource
14
- from rex.utilities.utilities import jsonify_dict
14
+ from rex.utilities.utilities import jsonify_dict, check_res_file
15
15
 
16
16
  from reV.econ.economies_of_scale import EconomiesOfScale
17
17
  from reV.econ.utilities import lcoe_fcr
@@ -1169,9 +1169,11 @@ class AggregationSupplyCurvePoint(SupplyCurvePoint):
1169
1169
  Resource h5 handler object.
1170
1170
  """
1171
1171
  if self._h5 is None and "*" in self._h5_fpath:
1172
- self._h5 = MultiTimeResource(self._h5_fpath)
1172
+ __, hsds = check_res_file(self._h5_fpath)
1173
+ self._h5 = MultiTimeResource(self._h5_fpath, hsds=hsds)
1173
1174
  elif self._h5 is None:
1174
- self._h5 = Resource(self._h5_fpath)
1175
+ __, hsds = check_res_file(self._h5_fpath)
1176
+ self._h5 = Resource(self._h5_fpath, hsds=hsds)
1175
1177
 
1176
1178
  return self._h5
1177
1179
 
@@ -1643,7 +1645,8 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
1643
1645
  reV generation Resource object
1644
1646
  """
1645
1647
  if self._gen is None:
1646
- self._gen = Resource(self._gen_fpath, str_decode=False)
1648
+ __, hsds = check_res_file(self._gen_fpath)
1649
+ self._gen = Resource(self._gen_fpath, str_decode=False, hsds=hsds)
1647
1650
 
1648
1651
  return self._gen
1649
1652
 
@@ -569,15 +569,20 @@ class SupplyCurveAggregation(BaseAggregation):
569
569
  generation HDF5 output, or if `recalc_lcoe` is set to
570
570
  ``False``, the mean LCOE will be computed from the data
571
571
  stored under the `lcoe_dset` instead. By default, ``True``.
572
- zones_dset: str, optional
573
- Dataset name in `excl_fpath` containing the zones to be applied.
574
- If specified, supply curve aggregation will be performed separately
575
- for each discrete zone within each supply curve site. This option
576
- can be used for uses cases such as subdividing sites by parcel,
577
- such that each parcel within each site is output to a separate
578
- sc_gid. The input data layer should consist of unique integer
579
- values for each zone. Values of zero will be treated as excluded
580
- areas.
572
+ zones_dset : str, optional
573
+ Dataset name in `excl_fpath` containing the zones to be
574
+ applied. If specified, supply curve aggregation will be
575
+ performed separately for each discrete zone within each
576
+ supply curve site. This option can be used for uses cases
577
+ such as subdividing sites by parcel, such that each parcel
578
+ within each site is output to a separate ``sc_gid``. The
579
+ input data layer should consist of unique integer values for
580
+ each zone. Values of zero will be treated as excluded areas.
581
+ This functionality works best of you have on the order of
582
+ ~100,000 zones or less. If you have significantly more zones
583
+ than that, the supply curve aggregation computation may take
584
+ too long to run; consider performing an alternative
585
+ analysis. By default, ``None``.
581
586
 
582
587
  Examples
583
588
  --------
@@ -16,10 +16,10 @@ from warnings import warn
16
16
 
17
17
  import h5py
18
18
  import numpy as np
19
+ from scipy.spatial import cKDTree
19
20
  from rex.resource import Resource
20
21
  from rex.utilities.execution import SpawnProcessPool
21
- from rex.utilities.utilities import res_dist_threshold
22
- from scipy.spatial import cKDTree
22
+ from rex.utilities.utilities import check_res_file, res_dist_threshold
23
23
 
24
24
  from reV.supply_curve.extent import SupplyCurveExtent, LATITUDE, LONGITUDE
25
25
  from reV.utilities.exceptions import FileInputError, FileInputWarning
@@ -30,17 +30,13 @@ logger = logging.getLogger(__name__)
30
30
  class TechMapping:
31
31
  """Framework to create map between tech layer (exclusions), res, and gen"""
32
32
 
33
- def __init__(
34
- self, excl_fpath, res_fpath, sc_resolution=1200, dist_margin=1.05
35
- ):
33
+ def __init__(self, excl_fpath, sc_resolution=1200):
36
34
  """
37
35
  Parameters
38
36
  ----------
39
37
  excl_fpath : str
40
38
  Filepath to exclusions h5 file, must contain latitude and longitude
41
39
  arrays to allow for mapping to resource points
42
- res_fpath : str
43
- Filepath to .h5 resource file that we're mapping to.
44
40
  sc_resolution : int | None, optional
45
41
  Defines how many exclusion pixels are mapped at a time. Units
46
42
  indicate the length of one dimension, in pixels, of each square
@@ -52,22 +48,13 @@ class TechMapping:
52
48
  ``sc_resolution`` parameter works in other functionality within
53
49
  ``reV``.
54
50
 
55
- dist_margin : float, optional
56
- Extra margin to multiply times the computed distance between
57
- neighboring resource points, by default 1.05
58
51
  """
59
52
  self._excl_fpath = excl_fpath
60
- self._res_fpath = res_fpath
61
53
  self._check_fout()
62
54
 
63
- self._tree, self._dist_thresh = self._build_tree(
64
- self._res_fpath, dist_margin=dist_margin
65
- )
66
-
67
55
  with SupplyCurveExtent(
68
56
  self._excl_fpath, resolution=sc_resolution
69
57
  ) as sc:
70
- self._sc_resolution = sc.resolution
71
58
  self._gids = np.array(list(range(len(sc))), dtype=np.uint32)
72
59
  self._excl_shape = sc.exclusions.shape
73
60
  self._n_excl = np.prod(self._excl_shape)
@@ -82,19 +69,6 @@ class TechMapping:
82
69
  )
83
70
  )
84
71
 
85
- @property
86
- def distance_threshold(self):
87
- """Get the upper bound on NN distance between excl and res points.
88
-
89
- Returns
90
- -------
91
- float
92
- Estimate the distance between resource points. Calculated as half
93
- of the diagonal between closest resource points, with desired
94
- extra margin
95
- """
96
- return self._dist_thresh
97
-
98
72
  @staticmethod
99
73
  def _build_tree(res_fpath, dist_margin=1.05):
100
74
  """
@@ -118,7 +92,8 @@ class TechMapping:
118
92
  of the diagonal between closest resource points, with desired
119
93
  extra margin
120
94
  """
121
- with Resource(res_fpath) as f:
95
+ __, hsds = check_res_file(res_fpath)
96
+ with Resource(res_fpath, hsds=hsds) as f:
122
97
  lat_lons = f.lat_lon
123
98
 
124
99
  # pylint: disable=not-callable
@@ -320,12 +295,6 @@ class TechMapping:
320
295
  fillvalue=-1
321
296
  )
322
297
 
323
- if self._dist_thresh:
324
- f[dset].attrs["distance_threshold"] = self._dist_thresh
325
-
326
- if self._res_fpath:
327
- f[dset].attrs["src_res_fpath"] = self._res_fpath
328
-
329
298
  def _check_fout(self):
330
299
  """Check the TechMapping output file for cached data."""
331
300
  with h5py.File(self._excl_fpath, 'r') as f:
@@ -337,9 +306,8 @@ class TechMapping:
337
306
  logger.exception(emsg)
338
307
  raise FileInputError(emsg)
339
308
 
340
- def map_resource(
341
- self, dset, max_workers=None, batch_size=100
342
- ):
309
+ def map_resource(self, dset, res_fpath, dist_margin=1.05,
310
+ max_workers=None, batch_size=100):
343
311
  """
344
312
  Map all resource gids to exclusion gids. Save results to dset in
345
313
  exclusions h5 file.
@@ -349,6 +317,11 @@ class TechMapping:
349
317
  dset : str, optional
350
318
  Name of the output dataset in the exclusions H5 file to which the
351
319
  tech map will be saved.
320
+ res_fpath : str
321
+ Filepath to .h5 resource file that we're mapping to.
322
+ dist_margin : float, optional
323
+ Extra margin to multiply times the computed distance between
324
+ neighboring resource points, by default 1.05
352
325
  max_workers : int, optional
353
326
  Number of cores to run mapping on. None uses all available cpus,
354
327
  by default None
@@ -362,6 +335,17 @@ class TechMapping:
362
335
  """
363
336
  loggers = [__name__, "reV"]
364
337
 
338
+ logger.info(
339
+ f"Computing cKDtree for tech mapping using {res_fpath=!r} "
340
+ f"and {dist_margin=!r}"
341
+ )
342
+ tree, dist_thresh = self._build_tree(
343
+ res_fpath, dist_margin=dist_margin
344
+ )
345
+ with h5py.File(self._excl_fpath, "a") as f:
346
+ f[dset].attrs["distance_threshold"] = dist_thresh
347
+ f[dset].attrs["src_res_fpath"] = res_fpath
348
+
365
349
  n_jobs = len(self._gids)
366
350
  n_batches = ceil(n_jobs / batch_size)
367
351
  gid_batches = np.array_split(self._gids, n_batches)
@@ -389,8 +373,8 @@ class TechMapping:
389
373
  exe.submit(
390
374
  self.map_resource_gids,
391
375
  excl_coords,
392
- self._tree,
393
- self.distance_threshold,
376
+ tree,
377
+ dist_thresh,
394
378
  )
395
379
  ] = i
396
380
 
@@ -476,11 +460,12 @@ class TechMapping:
476
460
  out-of-memory errors. Values less than the number of workers can
477
461
  also lead to slower processing, due to poor load balancing.
478
462
  """
479
- kwargs = {"dist_margin": dist_margin, "sc_resolution": sc_resolution}
480
- mapper = cls(excl_fpath, res_fpath, **kwargs)
463
+ mapper = cls(excl_fpath, sc_resolution=sc_resolution)
481
464
  mapper.initialize_dataset(dset)
482
465
  mapper.map_resource(
483
- max_workers=max_workers,
484
466
  dset=dset,
467
+ res_fpath=res_fpath,
468
+ dist_margin=dist_margin,
469
+ max_workers=max_workers,
485
470
  batch_size=batch_size,
486
471
  )
reV/version.py CHANGED
@@ -2,4 +2,4 @@
2
2
  reV Version number
3
3
  """
4
4
 
5
- __version__ = "0.14.0"
5
+ __version__ = "0.14.2"