NREL-reV 0.8.7__py3-none-any.whl → 0.8.9__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.
Files changed (38) hide show
  1. {NREL_reV-0.8.7.dist-info → NREL_reV-0.8.9.dist-info}/METADATA +12 -10
  2. {NREL_reV-0.8.7.dist-info → NREL_reV-0.8.9.dist-info}/RECORD +38 -38
  3. {NREL_reV-0.8.7.dist-info → NREL_reV-0.8.9.dist-info}/WHEEL +1 -1
  4. reV/SAM/SAM.py +182 -133
  5. reV/SAM/econ.py +18 -14
  6. reV/SAM/generation.py +608 -419
  7. reV/SAM/windbos.py +93 -79
  8. reV/bespoke/bespoke.py +690 -445
  9. reV/bespoke/place_turbines.py +6 -6
  10. reV/config/project_points.py +220 -140
  11. reV/econ/econ.py +165 -113
  12. reV/econ/economies_of_scale.py +57 -34
  13. reV/generation/base.py +310 -183
  14. reV/generation/generation.py +298 -190
  15. reV/handlers/exclusions.py +16 -15
  16. reV/handlers/multi_year.py +12 -9
  17. reV/handlers/outputs.py +6 -5
  18. reV/hybrids/hybrid_methods.py +28 -30
  19. reV/hybrids/hybrids.py +304 -188
  20. reV/nrwal/nrwal.py +262 -168
  21. reV/qa_qc/cli_qa_qc.py +14 -10
  22. reV/qa_qc/qa_qc.py +217 -119
  23. reV/qa_qc/summary.py +228 -146
  24. reV/rep_profiles/rep_profiles.py +349 -230
  25. reV/supply_curve/aggregation.py +349 -188
  26. reV/supply_curve/competitive_wind_farms.py +90 -48
  27. reV/supply_curve/exclusions.py +138 -85
  28. reV/supply_curve/extent.py +75 -50
  29. reV/supply_curve/points.py +536 -309
  30. reV/supply_curve/sc_aggregation.py +366 -225
  31. reV/supply_curve/supply_curve.py +505 -308
  32. reV/supply_curve/tech_mapping.py +144 -82
  33. reV/utilities/__init__.py +199 -16
  34. reV/utilities/pytest_utils.py +8 -4
  35. reV/version.py +1 -1
  36. {NREL_reV-0.8.7.dist-info → NREL_reV-0.8.9.dist-info}/LICENSE +0 -0
  37. {NREL_reV-0.8.7.dist-info → NREL_reV-0.8.9.dist-info}/entry_points.txt +0 -0
  38. {NREL_reV-0.8.7.dist-info → NREL_reV-0.8.9.dist-info}/top_level.txt +0 -0
reV/SAM/SAM.py CHANGED
@@ -3,25 +3,38 @@
3
3
 
4
4
  Wraps the NREL-PySAM library with additional reV features.
5
5
  """
6
+
6
7
  import copy
7
8
  import json
8
9
  import logging
9
- import numpy as np
10
10
  import os
11
- import pandas as pd
12
11
  from warnings import warn
13
- import PySAM.GenericSystem as generic
14
-
15
- from reV.utilities.exceptions import (SAMInputWarning, SAMInputError,
16
- SAMExecutionError, ResourceError)
17
12
 
18
- from rex.multi_file_resource import (MultiFileResource, MultiFileNSRDB,
19
- MultiFileWTK)
20
- from rex.renewable_resource import (WindResource, SolarResource, NSRDB,
21
- WaveResource, GeothermalResource)
13
+ import numpy as np
14
+ import pandas as pd
15
+ import PySAM.GenericSystem as generic
16
+ from rex.multi_file_resource import (
17
+ MultiFileNSRDB,
18
+ MultiFileResource,
19
+ MultiFileWTK,
20
+ )
22
21
  from rex.multi_res_resource import MultiResolutionResource
22
+ from rex.renewable_resource import (
23
+ NSRDB,
24
+ GeothermalResource,
25
+ SolarResource,
26
+ WaveResource,
27
+ WindResource,
28
+ )
23
29
  from rex.utilities.utilities import check_res_file
24
30
 
31
+ from reV.utilities import ResourceMetaField
32
+ from reV.utilities.exceptions import (
33
+ ResourceError,
34
+ SAMExecutionError,
35
+ SAMInputError,
36
+ SAMInputWarning,
37
+ )
25
38
 
26
39
  logger = logging.getLogger(__name__)
27
40
 
@@ -31,18 +44,19 @@ class SamResourceRetriever:
31
44
 
32
45
  # Mapping for reV technology and SAM module to h5 resource handler type
33
46
  # SolarResource is swapped for NSRDB if the res_file contains "nsrdb"
34
- RESOURCE_TYPES = {'geothermal': GeothermalResource,
35
- 'pvwattsv5': SolarResource,
36
- 'pvwattsv7': SolarResource,
37
- 'pvwattsv8': SolarResource,
38
- 'pvsamv1': SolarResource,
39
- 'tcsmoltensalt': SolarResource,
40
- 'solarwaterheat': SolarResource,
41
- 'troughphysicalheat': SolarResource,
42
- 'lineardirectsteam': SolarResource,
43
- 'windpower': WindResource,
44
- 'mhkwave': WaveResource
45
- }
47
+ RESOURCE_TYPES = {
48
+ "geothermal": GeothermalResource,
49
+ "pvwattsv5": SolarResource,
50
+ "pvwattsv7": SolarResource,
51
+ "pvwattsv8": SolarResource,
52
+ "pvsamv1": SolarResource,
53
+ "tcsmoltensalt": SolarResource,
54
+ "solarwaterheat": SolarResource,
55
+ "troughphysicalheat": SolarResource,
56
+ "lineardirectsteam": SolarResource,
57
+ "windpower": WindResource,
58
+ "mhkwave": WaveResource,
59
+ }
46
60
 
47
61
  @staticmethod
48
62
  def _get_base_handler(res_file, module):
@@ -69,15 +83,17 @@ class SamResourceRetriever:
69
83
  res_handler = SamResourceRetriever.RESOURCE_TYPES[module.lower()]
70
84
 
71
85
  except KeyError as e:
72
- msg = ('Cannot interpret what kind of resource handler the SAM '
73
- 'module or reV technology "{}" requires. Expecting one of '
74
- 'the following SAM modules or reV technologies: {}'
75
- .format(module,
76
- list(SamResourceRetriever.RESOURCE_TYPES.keys())))
86
+ msg = (
87
+ "Cannot interpret what kind of resource handler the SAM "
88
+ 'module or reV technology "{}" requires. Expecting one of '
89
+ "the following SAM modules or reV technologies: {}".format(
90
+ module, list(SamResourceRetriever.RESOURCE_TYPES.keys())
91
+ )
92
+ )
77
93
  logger.exception(msg)
78
94
  raise SAMExecutionError(msg) from e
79
95
 
80
- if res_handler == SolarResource and 'nsrdb' in res_file.lower():
96
+ if res_handler == SolarResource and "nsrdb" in res_file.lower():
81
97
  # Use NSRDB handler if definitely an NSRDB file
82
98
  res_handler = NSRDB
83
99
 
@@ -113,8 +129,9 @@ class SamResourceRetriever:
113
129
  return res_gids
114
130
 
115
131
  @classmethod
116
- def _make_res_kwargs(cls, res_handler, project_points, output_request,
117
- gid_map):
132
+ def _make_res_kwargs(
133
+ cls, res_handler, project_points, output_request, gid_map
134
+ ):
118
135
  """
119
136
  Make Resource.preloadSam args and kwargs
120
137
 
@@ -146,9 +163,9 @@ class SamResourceRetriever:
146
163
  kwargs = {}
147
164
  if res_handler in (SolarResource, NSRDB):
148
165
  # check for clearsky irradiation analysis for NSRDB
149
- kwargs['clearsky'] = project_points.sam_config_obj.clearsky
150
- kwargs['bifacial'] = project_points.sam_config_obj.bifacial
151
- kwargs['tech'] = project_points.tech
166
+ kwargs["clearsky"] = project_points.sam_config_obj.clearsky
167
+ kwargs["bifacial"] = project_points.sam_config_obj.bifacial
168
+ kwargs["tech"] = project_points.tech
152
169
 
153
170
  downscale = project_points.sam_config_obj.downscale
154
171
  # check for downscaling request
@@ -156,30 +173,34 @@ class SamResourceRetriever:
156
173
  # make sure that downscaling is only requested for NSRDB
157
174
  # resource
158
175
  if res_handler != NSRDB:
159
- msg = ('Downscaling was requested for a non-NSRDB '
160
- 'resource file. reV does not have this capability '
161
- 'at the current time. Please contact a developer '
162
- 'for more information on this feature.')
176
+ msg = (
177
+ "Downscaling was requested for a non-NSRDB "
178
+ "resource file. reV does not have this capability "
179
+ "at the current time. Please contact a developer "
180
+ "for more information on this feature."
181
+ )
163
182
  logger.warning(msg)
164
183
  warn(msg, SAMInputWarning)
165
184
  else:
166
185
  # pass through the downscaling request
167
- kwargs['downscale'] = downscale
186
+ kwargs["downscale"] = downscale
168
187
 
169
188
  elif res_handler == WindResource:
170
- args += (project_points.h, )
171
- kwargs['icing'] = project_points.sam_config_obj.icing
172
- if project_points.curtailment is not None:
173
- if project_points.curtailment.precipitation:
174
- # make precip rate available for curtailment analysis
175
- kwargs['precip_rate'] = True
189
+ args += (project_points.h,)
190
+ kwargs["icing"] = project_points.sam_config_obj.icing
191
+ if (
192
+ project_points.curtailment is not None
193
+ and project_points.curtailment.precipitation
194
+ ):
195
+ # make precip rate available for curtailment analysis
196
+ kwargs["precip_rate"] = True
176
197
 
177
198
  elif res_handler == GeothermalResource:
178
- args += (project_points.d, )
199
+ args += (project_points.d,)
179
200
 
180
201
  # Check for resource means
181
- if any(req.endswith('_mean') for req in output_request):
182
- kwargs['means'] = True
202
+ if any(req.endswith("_mean") for req in output_request):
203
+ kwargs["means"] = True
183
204
 
184
205
  return kwargs, args
185
206
 
@@ -218,9 +239,17 @@ class SamResourceRetriever:
218
239
  return res_handler, kwargs, res_file
219
240
 
220
241
  @classmethod
221
- def get(cls, res_file, project_points, module,
222
- output_request=('cf_mean', ), gid_map=None,
223
- lr_res_file=None, nn_map=None, bias_correct=None):
242
+ def get(
243
+ cls,
244
+ res_file,
245
+ project_points,
246
+ module,
247
+ output_request=("cf_mean",),
248
+ gid_map=None,
249
+ lr_res_file=None,
250
+ nn_map=None,
251
+ bias_correct=None,
252
+ ):
224
253
  """Get the SAM resource iterator object (single year, single file).
225
254
 
226
255
  Parameters
@@ -280,27 +309,30 @@ class SamResourceRetriever:
280
309
  """
281
310
 
282
311
  res_handler = cls._get_base_handler(res_file, module)
283
- kwargs, args = cls._make_res_kwargs(res_handler, project_points,
284
- output_request, gid_map)
312
+ kwargs, args = cls._make_res_kwargs(
313
+ res_handler, project_points, output_request, gid_map
314
+ )
285
315
 
286
316
  multi_h5_res, hsds = check_res_file(res_file)
287
317
  if multi_h5_res:
288
- res_handler, kwargs, res_file = cls._multi_file_mods(res_handler,
289
- kwargs,
290
- res_file)
318
+ res_handler, kwargs, res_file = cls._multi_file_mods(
319
+ res_handler, kwargs, res_file
320
+ )
291
321
  else:
292
- kwargs['hsds'] = hsds
322
+ kwargs["hsds"] = hsds
293
323
 
294
- kwargs['time_index_step'] = \
324
+ kwargs["time_index_step"] = (
295
325
  project_points.sam_config_obj.time_index_step
326
+ )
296
327
 
297
328
  if lr_res_file is None:
298
329
  res = res_handler.preload_SAM(res_file, *args, **kwargs)
299
330
  else:
300
- kwargs['handler_class'] = res_handler
301
- kwargs['nn_map'] = nn_map
302
- res = MultiResolutionResource.preload_SAM(res_file, lr_res_file,
303
- *args, **kwargs)
331
+ kwargs["handler_class"] = res_handler
332
+ kwargs["nn_map"] = nn_map
333
+ res = MultiResolutionResource.preload_SAM(
334
+ res_file, lr_res_file, *args, **kwargs
335
+ )
304
336
 
305
337
  if bias_correct is not None:
306
338
  res.bias_correct(bias_correct)
@@ -315,15 +347,15 @@ class Sam:
315
347
  PYSAM = generic
316
348
 
317
349
  # callable attributes to be ignored in the get/set logic
318
- IGNORE_ATTRS = ['assign', 'execute', 'export']
350
+ IGNORE_ATTRS = ["assign", "execute", "export"]
319
351
 
320
352
  def __init__(self):
321
353
  self._pysam = self.PYSAM.new()
322
354
  self._attr_dict = None
323
355
  self._inputs = []
324
356
  self.sam_sys_inputs = {}
325
- if 'constant' in self.input_list:
326
- self['constant'] = 0.0
357
+ if "constant" in self.input_list:
358
+ self["constant"] = 0.0
327
359
 
328
360
  def __getitem__(self, key):
329
361
  """Get the value of a PySAM attribute (either input or output).
@@ -359,24 +391,27 @@ class Sam:
359
391
  """
360
392
 
361
393
  if key not in self.input_list:
362
- msg = ('Could not set input key "{}". Attribute not '
363
- 'found in PySAM object: "{}"'
364
- .format(key, self.pysam))
394
+ msg = (
395
+ 'Could not set input key "{}". Attribute not '
396
+ 'found in PySAM object: "{}"'.format(key, self.pysam)
397
+ )
365
398
  logger.exception(msg)
366
399
  raise SAMInputError(msg)
367
- else:
368
- self.sam_sys_inputs[key] = value
369
- group = self._get_group(key, outputs=False)
370
- try:
371
- setattr(getattr(self.pysam, group), key, value)
372
- except Exception as e:
373
- msg = ('Could not set input key "{}" to '
374
- 'group "{}" in "{}".\n'
375
- 'Data is: {} ({})\n'
376
- 'Received the following error: "{}"'
377
- .format(key, group, self.pysam, value, type(value), e))
378
- logger.exception(msg)
379
- raise SAMInputError(msg) from e
400
+ self.sam_sys_inputs[key] = value
401
+ group = self._get_group(key, outputs=False)
402
+ try:
403
+ setattr(getattr(self.pysam, group), key, value)
404
+ except Exception as e:
405
+ msg = (
406
+ 'Could not set input key "{}" to '
407
+ 'group "{}" in "{}".\n'
408
+ "Data is: {} ({})\n"
409
+ 'Received the following error: "{}"'.format(
410
+ key, group, self.pysam, value, type(value), e
411
+ )
412
+ )
413
+ logger.exception(msg)
414
+ raise SAMInputError(msg) from e
380
415
 
381
416
  @property
382
417
  def pysam(self):
@@ -391,7 +426,7 @@ class Sam:
391
426
  -------
392
427
  PySAM.GenericSystem
393
428
  """
394
- obj = cls.PYSAM.default('GenericSystemNone')
429
+ obj = cls.PYSAM.default("GenericSystemNone")
395
430
  obj.execute()
396
431
 
397
432
  return obj
@@ -409,8 +444,9 @@ class Sam:
409
444
  """
410
445
  if self._attr_dict is None:
411
446
  keys = self._get_pysam_attrs(self.pysam)
412
- self._attr_dict = {k: self._get_pysam_attrs(getattr(self.pysam, k))
413
- for k in keys}
447
+ self._attr_dict = {
448
+ k: self._get_pysam_attrs(getattr(self.pysam, k)) for k in keys
449
+ }
414
450
 
415
451
  return self._attr_dict
416
452
 
@@ -425,7 +461,7 @@ class Sam:
425
461
  """
426
462
  if not any(self._inputs):
427
463
  for k, v in self.attr_dict.items():
428
- if k.lower() != 'outputs':
464
+ if k.lower() != "outputs":
429
465
  self._inputs += v
430
466
 
431
467
  return self._inputs
@@ -450,8 +486,7 @@ class Sam:
450
486
 
451
487
  temp = self.attr_dict
452
488
  if not outputs:
453
- temp = {k: v for (k, v) in temp.items()
454
- if k.lower() != 'outputs'}
489
+ temp = {k: v for (k, v) in temp.items() if k.lower() != "outputs"}
455
490
 
456
491
  for k, v in temp.items():
457
492
  if key in v:
@@ -474,8 +509,11 @@ class Sam:
474
509
  List of attrs belonging to obj with dunder attrs and IGNORE_ATTRS
475
510
  not included.
476
511
  """
477
- attrs = [a for a in dir(obj) if not a.startswith('__')
478
- and a not in self.IGNORE_ATTRS]
512
+ attrs = [
513
+ a
514
+ for a in dir(obj)
515
+ if not a.startswith("__") and a not in self.IGNORE_ATTRS
516
+ ]
479
517
  return attrs
480
518
 
481
519
  def execute(self):
@@ -506,19 +544,21 @@ class Sam:
506
544
  Filtered Input value associated with key.
507
545
  """
508
546
 
509
- if '.' in key:
510
- key = key.replace('.', '_')
547
+ if "." in key:
548
+ key = key.replace(".", "_")
511
549
 
512
- if ':constant' in key and 'adjust:' in key:
513
- key = key.replace('adjust:', '')
550
+ if ":constant" in key and "adjust:" in key:
551
+ key = key.replace("adjust:", "")
514
552
 
515
- if isinstance(value, str) and '[' in value and ']' in value:
553
+ if isinstance(value, str) and "[" in value and "]" in value:
516
554
  try:
517
555
  value = json.loads(value)
518
556
  except json.JSONDecodeError:
519
- msg = ('Found a weird SAM config input for "{}" that looks '
520
- 'like a stringified-list but could not run through '
521
- 'json.loads() so skipping: {}'.format(key, value))
557
+ msg = (
558
+ 'Found a weird SAM config input for "{}" that looks '
559
+ "like a stringified-list but could not run through "
560
+ "json.loads() so skipping: {}".format(key, value)
561
+ )
522
562
  logger.warning(msg)
523
563
  warn(msg)
524
564
 
@@ -541,8 +581,7 @@ class Sam:
541
581
  if k in self.input_list and v is not None:
542
582
  self[k] = v
543
583
  elif raise_warning:
544
- wmsg = ('Not setting input "{}" to: {}.'
545
- .format(k, v))
584
+ wmsg = 'Not setting input "{}" to: {}.'.format(k, v)
546
585
  warn(wmsg, SAMInputWarning)
547
586
  logger.warning(wmsg)
548
587
 
@@ -553,8 +592,9 @@ class RevPySam(Sam):
553
592
  DIR = os.path.dirname(os.path.realpath(__file__))
554
593
  MODULE = None
555
594
 
556
- def __init__(self, meta, sam_sys_inputs, output_request,
557
- site_sys_inputs=None):
595
+ def __init__(
596
+ self, meta, sam_sys_inputs, output_request, site_sys_inputs=None
597
+ ):
558
598
  """Initialize a SAM object.
559
599
 
560
600
  Parameters
@@ -567,7 +607,7 @@ class RevPySam(Sam):
567
607
  Site-agnostic SAM system model inputs arguments.
568
608
  output_request : list
569
609
  Requested SAM outputs (e.g., 'cf_mean', 'annual_energy',
570
- 'cf_profile', 'gen_profile', 'energy_yield', 'ppa_price',
610
+ , 'gen_profile', 'energy_yield', 'ppa_price',
571
611
  'lcoe_fcr').
572
612
  site_sys_inputs : dict
573
613
  Optional set of site-specific SAM system inputs to complement the
@@ -623,11 +663,13 @@ class RevPySam(Sam):
623
663
  Resource dataframe with all February 29th timesteps removed.
624
664
  """
625
665
 
626
- if hasattr(resource, 'index'):
627
- if (hasattr(resource.index, 'month')
628
- and hasattr(resource.index, 'day')):
629
- leap_day = ((resource.index.month == 2)
630
- & (resource.index.day == 29))
666
+ if hasattr(resource, "index"):
667
+ if hasattr(resource.index, "month") and hasattr(
668
+ resource.index, "day"
669
+ ):
670
+ leap_day = (resource.index.month == 2) & (
671
+ resource.index.day == 29
672
+ )
631
673
  resource = resource.drop(resource.index[leap_day])
632
674
 
633
675
  return resource
@@ -651,13 +693,15 @@ class RevPySam(Sam):
651
693
  arr : ndarray
652
694
  Truncated array of data such that there are 365 days
653
695
  """
654
- msg = ('A valid time_index must be supplied to ensure the proper '
655
- 'resource length! Instead {} was supplied'
656
- .format(type(time_index)))
696
+ msg = (
697
+ "A valid time_index must be supplied to ensure the proper "
698
+ "resource length! Instead {} was supplied".format(type(time_index))
699
+ )
657
700
  assert isinstance(time_index, pd.DatetimeIndex)
658
701
 
659
- msg = ('arr length {} does not match time_index length {}!'
660
- .format(len(arr), len(time_index)))
702
+ msg = "arr length {} does not match time_index length {}!".format(
703
+ len(arr), len(time_index)
704
+ )
661
705
  assert len(arr) == len(time_index)
662
706
 
663
707
  if time_index.is_leap_year.all():
@@ -669,7 +713,7 @@ class RevPySam(Sam):
669
713
  s = np.where(mask)[0][-1]
670
714
 
671
715
  freq = pd.infer_freq(time_index[:s])
672
- msg = 'frequencies do not match before and after 2/29'
716
+ msg = "frequencies do not match before and after 2/29"
673
717
  assert freq == pd.infer_freq(time_index[s + 1:]), msg
674
718
  else:
675
719
  freq = pd.infer_freq(time_index)
@@ -677,8 +721,10 @@ class RevPySam(Sam):
677
721
  freq = pd.infer_freq(time_index)
678
722
 
679
723
  if freq is None:
680
- msg = ('Resource time_index does not have a consistent time-step '
681
- '(frequency)!')
724
+ msg = (
725
+ "Resource time_index does not have a consistent time-step "
726
+ "(frequency)!"
727
+ )
682
728
  logger.error(msg)
683
729
  raise ResourceError(msg)
684
730
 
@@ -696,7 +742,7 @@ class RevPySam(Sam):
696
742
  @staticmethod
697
743
  def make_datetime(series):
698
744
  """Ensure that pd series is a datetime series with dt accessor"""
699
- if not hasattr(series, 'dt'):
745
+ if not hasattr(series, "dt"):
700
746
  series = pd.to_datetime(pd.Series(series))
701
747
 
702
748
  return series
@@ -727,7 +773,7 @@ class RevPySam(Sam):
727
773
  if t == 1.0:
728
774
  time_interval += 1
729
775
  break
730
- elif t == 0.0:
776
+ if t == 0.0:
731
777
  time_interval += 1
732
778
 
733
779
  return int(time_interval)
@@ -751,10 +797,11 @@ class RevPySam(Sam):
751
797
  location. Should include values for latitude, longitude, elevation,
752
798
  and timezone. Can be None for econ runs.
753
799
  """
754
-
755
800
  if isinstance(meta, pd.DataFrame):
756
- msg = ('Meta data must only be for a single site but received: {}'
757
- .format(meta))
801
+ msg = (
802
+ "Meta data must only be for a single site but received: "
803
+ f"{meta}"
804
+ )
758
805
  assert len(meta) == 1, msg
759
806
  meta = meta.iloc[0]
760
807
 
@@ -785,22 +832,20 @@ class RevPySam(Sam):
785
832
  """Returns true if SAM data is array-like. False if scalar."""
786
833
  if isinstance(val, (int, float, str)):
787
834
  return False
835
+ try:
836
+ len(val)
837
+ except TypeError:
838
+ return False
788
839
  else:
789
- try:
790
- len(val)
791
- except TypeError:
792
- return False
793
- else:
794
- return True
840
+ return True
795
841
 
796
842
  @classmethod
797
843
  def _is_hourly(cls, val):
798
844
  """Returns true if SAM data is hourly or sub-hourly. False otherise."""
799
845
  if not cls._is_arr_like(val):
800
846
  return False
801
- else:
802
- L = len(val)
803
- return L >= 8760
847
+ L = len(val)
848
+ return L >= 8760
804
849
 
805
850
  def outputs_to_utc_arr(self):
806
851
  """Convert array-like SAM outputs to UTC np.ndarrays"""
@@ -815,8 +860,11 @@ class RevPySam(Sam):
815
860
  output = output.astype(np.int32)
816
861
 
817
862
  if self._is_hourly(output):
818
- n_roll = int(-1 * self.meta['timezone']
819
- * self.time_interval)
863
+ n_roll = int(
864
+ -1
865
+ * self.meta[ResourceMetaField.TIMEZONE]
866
+ * self.time_interval
867
+ )
820
868
  output = np.roll(output, n_roll)
821
869
 
822
870
  self.outputs[key] = output
@@ -861,9 +909,10 @@ class RevPySam(Sam):
861
909
  try:
862
910
  self.pysam.execute()
863
911
  except Exception as e:
864
- msg = ('PySAM raised an error while executing: "{}"'
865
- .format(self.module))
912
+ msg = 'PySAM raised an error while executing: "{}"'.format(
913
+ self.module
914
+ )
866
915
  if self.site is not None:
867
- msg += ' for site {}'.format(self.site)
916
+ msg += " for site {}".format(self.site)
868
917
  logger.exception(msg)
869
918
  raise SAMExecutionError(msg) from e
reV/SAM/econ.py CHANGED
@@ -4,24 +4,27 @@
4
4
  Wraps the NREL-PySAM lcoefcr and singleowner modules with
5
5
  additional reV features.
6
6
  """
7
- from copy import deepcopy
8
7
  import logging
9
- import numpy as np
8
+ from copy import deepcopy
10
9
  from warnings import warn
10
+
11
+ import numpy as np
11
12
  import PySAM.Lcoefcr as PySamLCOE
12
13
  import PySAM.Singleowner as PySamSingleOwner
13
14
 
14
- from reV.SAM.defaults import DefaultSingleOwner, DefaultLCOE
15
15
  from reV.handlers.outputs import Outputs
16
- from reV.SAM.windbos import WindBos
16
+ from reV.SAM.defaults import DefaultLCOE, DefaultSingleOwner
17
17
  from reV.SAM.SAM import RevPySam
18
+ from reV.SAM.windbos import WindBos
18
19
  from reV.utilities.exceptions import SAMExecutionError
20
+ from reV.utilities import ResourceMetaField
19
21
 
20
22
  logger = logging.getLogger(__name__)
21
23
 
22
24
 
23
25
  class Economic(RevPySam):
24
26
  """Base class for SAM economic models."""
27
+
25
28
  MODULE = None
26
29
 
27
30
  def __init__(self, sam_sys_inputs, site_sys_inputs=None,
@@ -172,7 +175,7 @@ class Economic(RevPySam):
172
175
  with Outputs(cf_file) as cfh:
173
176
 
174
177
  # get the index location of the site in question
175
- site_gids = list(cfh.get_meta_arr('gid'))
178
+ site_gids = list(cfh.get_meta_arr(ResourceMetaField.GID))
176
179
  isites = [site_gids.index(s) for s in sites]
177
180
 
178
181
  # look for the cf_profile dataset
@@ -335,6 +338,7 @@ class Economic(RevPySam):
335
338
  class LCOE(Economic):
336
339
  """SAM LCOE model.
337
340
  """
341
+
338
342
  MODULE = 'lcoefcr'
339
343
  PYSAM = PySamLCOE
340
344
 
@@ -375,7 +379,7 @@ class LCOE(Economic):
375
379
 
376
380
  # get the cf_file meta data gid's to use as indexing tools
377
381
  with Outputs(cf_file) as cfh:
378
- site_gids = list(cfh.meta['gid'])
382
+ site_gids = list(cfh.meta[ResourceMetaField.GID])
379
383
 
380
384
  calc_aey = False
381
385
  if 'annual_energy' not in site_df:
@@ -463,6 +467,7 @@ class LCOE(Economic):
463
467
  class SingleOwner(Economic):
464
468
  """SAM single owner economic model.
465
469
  """
470
+
466
471
  MODULE = 'singleowner'
467
472
  PYSAM = PySamSingleOwner
468
473
 
@@ -497,14 +502,13 @@ class SingleOwner(Economic):
497
502
  """
498
503
 
499
504
  outputs = {}
500
- if inputs is not None:
501
- if 'total_installed_cost' in inputs:
502
- if isinstance(inputs['total_installed_cost'], str):
503
- if inputs['total_installed_cost'].lower() == 'windbos':
504
- wb = WindBos(inputs)
505
- inputs['total_installed_cost'] = \
506
- wb.total_installed_cost
507
- outputs = wb.output
505
+ if (inputs is not None
506
+ and 'total_installed_cost' in inputs
507
+ and isinstance(inputs['total_installed_cost'], str)
508
+ and inputs['total_installed_cost'].lower() == 'windbos'):
509
+ wb = WindBos(inputs)
510
+ inputs['total_installed_cost'] = wb.total_installed_cost
511
+ outputs = wb.output
508
512
  return inputs, outputs
509
513
 
510
514
  @staticmethod