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
@@ -2,26 +2,30 @@
2
2
  """
3
3
  reV supply curve points frameworks.
4
4
  """
5
- from abc import ABC
5
+
6
6
  import logging
7
+ from abc import ABC
8
+ from warnings import warn
9
+
7
10
  import numpy as np
8
11
  import pandas as pd
9
- from warnings import warn
12
+ from rex.multi_time_resource import MultiTimeResource
13
+ from rex.resource import BaseResource, Resource
14
+ from rex.utilities.utilities import jsonify_dict
10
15
 
11
16
  from reV.econ.economies_of_scale import EconomiesOfScale
12
17
  from reV.econ.utilities import lcoe_fcr
13
- from reV.handlers.exclusions import ExclusionLayers
18
+ from reV.handlers.exclusions import LATITUDE, LONGITUDE, ExclusionLayers
14
19
  from reV.supply_curve.exclusions import ExclusionMask, ExclusionMaskFromDict
15
- from reV.utilities.exceptions import (SupplyCurveInputError,
16
- EmptySupplyCurvePointError,
17
- InputWarning,
18
- FileInputError,
19
- DataShapeError,
20
- OutputWarning)
21
-
22
- from rex.resource import Resource, BaseResource
23
- from rex.multi_time_resource import MultiTimeResource
24
- from rex.utilities.utilities import jsonify_dict
20
+ from reV.utilities import ResourceMetaField, SupplyCurveField
21
+ from reV.utilities.exceptions import (
22
+ DataShapeError,
23
+ EmptySupplyCurvePointError,
24
+ FileInputError,
25
+ InputWarning,
26
+ OutputWarning,
27
+ SupplyCurveInputError,
28
+ )
25
29
 
26
30
  logger = logging.getLogger(__name__)
27
31
 
@@ -48,7 +52,8 @@ class AbstractSupplyCurvePoint(ABC):
48
52
  self._gid = gid
49
53
  self._resolution = resolution
50
54
  self._rows, self._cols = self._parse_slices(
51
- gid, resolution, exclusion_shape)
55
+ gid, resolution, exclusion_shape
56
+ )
52
57
 
53
58
  @staticmethod
54
59
  def _ordered_unique(seq):
@@ -98,7 +103,7 @@ class AbstractSupplyCurvePoint(ABC):
98
103
 
99
104
  @property
100
105
  def gid(self):
101
- """supply curve point gid"""
106
+ """Supply curve point gid"""
102
107
  return self._gid
103
108
 
104
109
  @property
@@ -172,8 +177,10 @@ class AbstractSupplyCurvePoint(ABC):
172
177
  row = loc[0][0]
173
178
  col = loc[1][0]
174
179
  except IndexError as exc:
175
- msg = ('Gid {} out of bounds for extent shape {} and '
176
- 'resolution {}.'.format(gid, shape, resolution))
180
+ msg = (
181
+ "Gid {} out of bounds for extent shape {} and "
182
+ "resolution {}.".format(gid, shape, resolution)
183
+ )
177
184
  raise IndexError(msg) from exc
178
185
 
179
186
  if row + 1 != nrows:
@@ -192,9 +199,18 @@ class AbstractSupplyCurvePoint(ABC):
192
199
  class SupplyCurvePoint(AbstractSupplyCurvePoint):
193
200
  """Generic single SC point based on exclusions, resolution, and techmap"""
194
201
 
195
- def __init__(self, gid, excl, tm_dset, excl_dict=None, inclusion_mask=None,
196
- resolution=64, excl_area=None, exclusion_shape=None,
197
- close=True):
202
+ def __init__(
203
+ self,
204
+ gid,
205
+ excl,
206
+ tm_dset,
207
+ excl_dict=None,
208
+ inclusion_mask=None,
209
+ resolution=64,
210
+ excl_area=None,
211
+ exclusion_shape=None,
212
+ close=True,
213
+ ):
198
214
  """
199
215
  Parameters
200
216
  ----------
@@ -245,8 +261,10 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
245
261
  self._incl_mask = inclusion_mask
246
262
  self._incl_mask_flat = None
247
263
  if inclusion_mask is not None:
248
- msg = ('Bad inclusion mask input shape of {} with stated '
249
- 'resolution of {}'.format(inclusion_mask.shape, resolution))
264
+ msg = (
265
+ "Bad inclusion mask input shape of {} with stated "
266
+ "resolution of {}".format(inclusion_mask.shape, resolution)
267
+ )
250
268
  assert len(inclusion_mask.shape) == 2, msg
251
269
  assert inclusion_mask.shape[0] <= resolution, msg
252
270
  assert inclusion_mask.shape[1] <= resolution, msg
@@ -282,11 +300,12 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
282
300
  excl_fpath = excl.excl_h5.h5_file
283
301
  exclusions = excl
284
302
  else:
285
- raise SupplyCurveInputError('SupplyCurvePoints needs an '
286
- 'exclusions file path, or '
287
- 'ExclusionMask handler but '
288
- 'received: {}'
289
- .format(type(excl)))
303
+ raise SupplyCurveInputError(
304
+ "SupplyCurvePoints needs an "
305
+ "exclusions file path, or "
306
+ "ExclusionMask handler but "
307
+ "received: {}".format(type(excl))
308
+ )
290
309
 
291
310
  return excl_fpath, exclusions
292
311
 
@@ -312,9 +331,12 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
312
331
  res_gids = res_gids.astype(np.int32).flatten()
313
332
 
314
333
  if (res_gids != -1).sum() == 0:
315
- emsg = ('Supply curve point gid {} has no viable exclusion points '
316
- 'based on exclusions file: "{}"'
317
- .format(self._gid, self._excl_fpath))
334
+ emsg = (
335
+ "Supply curve point gid {} has no viable exclusion points "
336
+ 'based on exclusions file: "{}"'.format(
337
+ self._gid, self._excl_fpath
338
+ )
339
+ )
318
340
  raise EmptySupplyCurvePointError(emsg)
319
341
 
320
342
  return res_gids
@@ -343,8 +365,9 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
343
365
  ExclusionMask h5 handler object.
344
366
  """
345
367
  if self._excls is None:
346
- self._excls = ExclusionMaskFromDict(self._excl_fpath,
347
- layers_dict=self._excl_dict)
368
+ self._excls = ExclusionMaskFromDict(
369
+ self._excl_fpath, layers_dict=self._excl_dict
370
+ )
348
371
 
349
372
  return self._excls
350
373
 
@@ -359,8 +382,8 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
359
382
  """
360
383
 
361
384
  if self._centroid is None:
362
- lats = self.exclusions.excl_h5['latitude', self.rows, self.cols]
363
- lons = self.exclusions.excl_h5['longitude', self.rows, self.cols]
385
+ lats = self.exclusions.excl_h5[LATITUDE, self.rows, self.cols]
386
+ lons = self.exclusions.excl_h5[LONGITUDE, self.rows, self.cols]
364
387
  self._centroid = (lats.mean(), lons.mean())
365
388
 
366
389
  return self._centroid
@@ -438,8 +461,12 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
438
461
  self._incl_mask[out_of_extent] = 0.0
439
462
 
440
463
  if self._incl_mask.max() > 1:
441
- w = ('Exclusions data max value is > 1: {}'
442
- .format(self._incl_mask.max()), InputWarning)
464
+ w = (
465
+ "Exclusions data max value is > 1: {}".format(
466
+ self._incl_mask.max()
467
+ ),
468
+ InputWarning,
469
+ )
443
470
  logger.warning(w)
444
471
  warn(w)
445
472
 
@@ -505,8 +532,9 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
505
532
  """
506
533
 
507
534
  if all(self.include_mask_flat[self.bool_mask] == 0):
508
- msg = ('Supply curve point gid {} is completely excluded!'
509
- .format(self._gid))
535
+ msg = "Supply curve point gid {} is completely excluded!".format(
536
+ self._gid
537
+ )
510
538
  raise EmptySupplyCurvePointError(msg)
511
539
 
512
540
  def exclusion_weighted_mean(self, arr, drop_nan=True):
@@ -530,18 +558,18 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
530
558
  """
531
559
 
532
560
  if len(arr.shape) == 2:
533
- x = arr[:, self._gids[self.bool_mask]].astype('float32')
561
+ x = arr[:, self._gids[self.bool_mask]].astype("float32")
534
562
  incl = self.include_mask_flat[self.bool_mask]
535
563
  x *= incl
536
564
  mean = x.sum(axis=1) / incl.sum()
537
565
 
538
566
  else:
539
- x = arr[self._gids[self.bool_mask]].astype('float32')
567
+ x = arr[self._gids[self.bool_mask]].astype("float32")
540
568
  incl = self.include_mask_flat[self.bool_mask]
541
569
 
542
570
  if np.isnan(x).all():
543
571
  return np.nan
544
- elif drop_nan and np.isnan(x).any():
572
+ if drop_nan and np.isnan(x).any():
545
573
  nan_mask = np.isnan(x)
546
574
  x = x[~nan_mask]
547
575
  incl = incl[~nan_mask]
@@ -600,10 +628,10 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
600
628
  Sum of arr masked by the binary exclusions
601
629
  """
602
630
  if len(arr.shape) == 2:
603
- x = arr[:, self._gids[self.bool_mask]].astype('float32')
631
+ x = arr[:, self._gids[self.bool_mask]].astype("float32")
604
632
  ax = 1
605
633
  else:
606
- x = arr[self._gids[self.bool_mask]].astype('float32')
634
+ x = arr[self._gids[self.bool_mask]].astype("float32")
607
635
  ax = 0
608
636
 
609
637
  x *= self.include_mask_flat[self.bool_mask]
@@ -612,8 +640,17 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
612
640
  return agg
613
641
 
614
642
  @classmethod
615
- def sc_mean(cls, gid, excl, tm_dset, data, excl_dict=None, resolution=64,
616
- exclusion_shape=None, close=True):
643
+ def sc_mean(
644
+ cls,
645
+ gid,
646
+ excl,
647
+ tm_dset,
648
+ data,
649
+ excl_dict=None,
650
+ resolution=64,
651
+ exclusion_shape=None,
652
+ close=True,
653
+ ):
617
654
  """
618
655
  Compute exclusions weight mean for the sc point from data
619
656
 
@@ -649,16 +686,29 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
649
686
  ndarray
650
687
  Exclusions weighted means of data for supply curve point
651
688
  """
652
- kwargs = {"excl_dict": excl_dict, "resolution": resolution,
653
- "exclusion_shape": exclusion_shape, "close": close}
689
+ kwargs = {
690
+ "excl_dict": excl_dict,
691
+ "resolution": resolution,
692
+ "exclusion_shape": exclusion_shape,
693
+ "close": close,
694
+ }
654
695
  with cls(gid, excl, tm_dset, **kwargs) as point:
655
696
  means = point.exclusion_weighted_mean(data)
656
697
 
657
698
  return means
658
699
 
659
700
  @classmethod
660
- def sc_sum(cls, gid, excl, tm_dset, data, excl_dict=None, resolution=64,
661
- exclusion_shape=None, close=True):
701
+ def sc_sum(
702
+ cls,
703
+ gid,
704
+ excl,
705
+ tm_dset,
706
+ data,
707
+ excl_dict=None,
708
+ resolution=64,
709
+ exclusion_shape=None,
710
+ close=True,
711
+ ):
662
712
  """
663
713
  Compute the aggregate (sum) of data for the sc point
664
714
 
@@ -694,8 +744,12 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
694
744
  ndarray
695
745
  Sum / aggregation of data for supply curve point
696
746
  """
697
- kwargs = {"excl_dict": excl_dict, "resolution": resolution,
698
- "exclusion_shape": exclusion_shape, "close": close}
747
+ kwargs = {
748
+ "excl_dict": excl_dict,
749
+ "resolution": resolution,
750
+ "exclusion_shape": exclusion_shape,
751
+ "close": close,
752
+ }
699
753
  with cls(gid, excl, tm_dset, **kwargs) as point:
700
754
  agg = point.aggregate(data)
701
755
 
@@ -718,9 +772,8 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
718
772
  """
719
773
  if not data.size:
720
774
  return None
721
- else:
722
- # pd series is more flexible with non-numeric than stats mode
723
- return pd.Series(data).mode().values[0]
775
+ # pd series is more flexible with non-numeric than stats mode
776
+ return pd.Series(data).mode().values[0]
724
777
 
725
778
  @staticmethod
726
779
  def _categorize(data, incl_mult):
@@ -743,8 +796,10 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
743
796
  total inclusions
744
797
  """
745
798
 
746
- data = {category: float(incl_mult[(data == category)].sum())
747
- for category in np.unique(data)}
799
+ data = {
800
+ category: float(incl_mult[(data == category)].sum())
801
+ for category in np.unique(data)
802
+ }
748
803
  data = jsonify_dict(data)
749
804
 
750
805
  return data
@@ -770,18 +825,22 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
770
825
  data : float | int | str | None
771
826
  Result of applying method to data.
772
827
  """
773
- method_func = {'mode': cls._mode,
774
- 'mean': np.mean,
775
- 'max': np.max,
776
- 'min': np.min,
777
- 'sum': np.sum,
778
- 'category': cls._categorize}
828
+ method_func = {
829
+ "mode": cls._mode,
830
+ "mean": np.mean,
831
+ "max": np.max,
832
+ "min": np.min,
833
+ "sum": np.sum,
834
+ "category": cls._categorize,
835
+ }
779
836
 
780
837
  if data is not None:
781
838
  method = method.lower()
782
839
  if method not in method_func:
783
- e = ('Cannot recognize data layer agg method: '
784
- '"{}". Can only {}'.format(method, list(method_func)))
840
+ e = (
841
+ "Cannot recognize data layer agg method: "
842
+ '"{}". Can only {}'.format(method, list(method_func))
843
+ )
785
844
  logger.error(e)
786
845
  raise ValueError(e)
787
846
 
@@ -789,14 +848,16 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
789
848
  data = data.flatten()
790
849
 
791
850
  if data.shape != incl_mult.shape:
792
- e = ('Cannot aggregate data with shape that doesnt '
793
- 'match excl mult!')
851
+ e = (
852
+ "Cannot aggregate data with shape that doesnt "
853
+ "match excl mult!"
854
+ )
794
855
  logger.error(e)
795
856
  raise DataShapeError(e)
796
857
 
797
- if method == 'category':
798
- data = method_func['category'](data, incl_mult)
799
- elif method in ['mean', 'sum']:
858
+ if method == "category":
859
+ data = method_func["category"](data, incl_mult)
860
+ elif method in ["mean", "sum"]:
800
861
  data = data * incl_mult
801
862
  data = method_func[method](data)
802
863
  else:
@@ -828,32 +889,36 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
828
889
 
829
890
  if data_layers is not None:
830
891
  for name, attrs in data_layers.items():
831
- excl_fp = attrs.get('fpath', self._excl_fpath)
892
+ excl_fp = attrs.get("fpath", self._excl_fpath)
832
893
  if excl_fp != self._excl_fpath:
833
- fh = ExclusionLayers(attrs['fpath'])
894
+ fh = ExclusionLayers(attrs["fpath"])
834
895
  else:
835
896
  fh = self.exclusions.excl_h5
836
897
 
837
- raw = fh[attrs['dset'], self.rows, self.cols]
838
- nodata = fh.get_nodata_value(attrs['dset'])
898
+ raw = fh[attrs["dset"], self.rows, self.cols]
899
+ nodata = fh.get_nodata_value(attrs["dset"])
839
900
 
840
901
  data = raw.flatten()[self.bool_mask]
841
902
  incl_mult = self.include_mask_flat[self.bool_mask].copy()
842
903
 
843
904
  if nodata is not None:
844
- valid_data_mask = (data != nodata)
905
+ valid_data_mask = data != nodata
845
906
  data = data[valid_data_mask]
846
907
  incl_mult = incl_mult[valid_data_mask]
847
908
 
848
909
  if not data.size:
849
- m = ('Data layer "{}" has no valid data for '
850
- 'SC point gid {} because of exclusions '
851
- 'and/or nodata values in the data layer.'
852
- .format(name, self._gid))
910
+ m = (
911
+ 'Data layer "{}" has no valid data for '
912
+ "SC point gid {} because of exclusions "
913
+ "and/or nodata values in the data layer.".format(
914
+ name, self._gid
915
+ )
916
+ )
853
917
  logger.debug(m)
854
918
 
855
- data = self._agg_data_layer_method(data, incl_mult,
856
- attrs['method'])
919
+ data = self._agg_data_layer_method(
920
+ data, incl_mult, attrs["method"]
921
+ )
857
922
  summary[name] = data
858
923
 
859
924
  if excl_fp != self._excl_fpath:
@@ -865,10 +930,21 @@ class SupplyCurvePoint(AbstractSupplyCurvePoint):
865
930
  class AggregationSupplyCurvePoint(SupplyCurvePoint):
866
931
  """Generic single SC point to aggregate data from an h5 file."""
867
932
 
868
- def __init__(self, gid, excl, agg_h5, tm_dset,
869
- excl_dict=None, inclusion_mask=None,
870
- resolution=64, excl_area=None, exclusion_shape=None,
871
- close=True, gen_index=None, apply_exclusions=True):
933
+ def __init__(
934
+ self,
935
+ gid,
936
+ excl,
937
+ agg_h5,
938
+ tm_dset,
939
+ excl_dict=None,
940
+ inclusion_mask=None,
941
+ resolution=64,
942
+ excl_area=None,
943
+ exclusion_shape=None,
944
+ close=True,
945
+ gen_index=None,
946
+ apply_exclusions=True,
947
+ ):
872
948
  """
873
949
  Parameters
874
950
  ----------
@@ -911,13 +987,17 @@ class AggregationSupplyCurvePoint(SupplyCurvePoint):
911
987
  Flag to apply exclusions to the resource / generation gid's on
912
988
  initialization.
913
989
  """
914
- super().__init__(gid, excl, tm_dset,
915
- excl_dict=excl_dict,
916
- inclusion_mask=inclusion_mask,
917
- resolution=resolution,
918
- excl_area=excl_area,
919
- exclusion_shape=exclusion_shape,
920
- close=close)
990
+ super().__init__(
991
+ gid,
992
+ excl,
993
+ tm_dset,
994
+ excl_dict=excl_dict,
995
+ inclusion_mask=inclusion_mask,
996
+ resolution=resolution,
997
+ excl_area=excl_area,
998
+ exclusion_shape=exclusion_shape,
999
+ close=close,
1000
+ )
921
1001
 
922
1002
  self._h5_fpath, self._h5 = self._parse_h5_file(agg_h5)
923
1003
 
@@ -927,9 +1007,12 @@ class AggregationSupplyCurvePoint(SupplyCurvePoint):
927
1007
  self._h5_gids = self._gids
928
1008
 
929
1009
  if (self._h5_gids != -1).sum() == 0:
930
- emsg = ('Supply curve point gid {} has no viable exclusion '
931
- 'points based on exclusions file: "{}"'
932
- .format(self._gid, self._excl_fpath))
1010
+ emsg = (
1011
+ "Supply curve point gid {} has no viable exclusion "
1012
+ 'points based on exclusions file: "{}"'.format(
1013
+ self._gid, self._excl_fpath
1014
+ )
1015
+ )
933
1016
  raise EmptySupplyCurvePointError(emsg)
934
1017
 
935
1018
  if apply_exclusions:
@@ -962,11 +1045,12 @@ class AggregationSupplyCurvePoint(SupplyCurvePoint):
962
1045
  elif issubclass(h5.__class__, MultiTimeResource):
963
1046
  h5_fpath = h5.h5_files
964
1047
  else:
965
- raise SupplyCurveInputError('SupplyCurvePoints needs a '
966
- '.h5 file path, or '
967
- 'Resource handler but '
968
- 'received: {}'
969
- .format(type(h5)))
1048
+ raise SupplyCurveInputError(
1049
+ "SupplyCurvePoints needs a "
1050
+ ".h5 file path, or "
1051
+ "Resource handler but "
1052
+ "received: {}".format(type(h5))
1053
+ )
970
1054
 
971
1055
  return h5_fpath, h5
972
1056
 
@@ -982,8 +1066,9 @@ class AggregationSupplyCurvePoint(SupplyCurvePoint):
982
1066
  self._h5_gids[exclude] = -1
983
1067
 
984
1068
  if (self._gids != -1).sum() == 0:
985
- msg = ('Supply curve point gid {} is completely excluded!'
986
- .format(self._gid))
1069
+ msg = "Supply curve point gid {} is completely excluded!".format(
1070
+ self._gid
1071
+ )
987
1072
  raise EmptySupplyCurvePointError(msg)
988
1073
 
989
1074
  def close(self):
@@ -1032,7 +1117,7 @@ class AggregationSupplyCurvePoint(SupplyCurvePoint):
1032
1117
  _h5 : Resource
1033
1118
  Resource h5 handler object.
1034
1119
  """
1035
- if self._h5 is None and '*' in self._h5_fpath:
1120
+ if self._h5 is None and "*" in self._h5_fpath:
1036
1121
  self._h5 = MultiTimeResource(self._h5_fpath)
1037
1122
  elif self._h5 is None:
1038
1123
  self._h5 = Resource(self._h5_fpath)
@@ -1043,15 +1128,22 @@ class AggregationSupplyCurvePoint(SupplyCurvePoint):
1043
1128
  def country(self):
1044
1129
  """Get the SC point country based on the resource meta data."""
1045
1130
  country = None
1046
- if 'country' in self.h5.meta and self.county is not None:
1131
+ county_not_none = self.county is not None
1132
+ if ResourceMetaField.COUNTRY in self.h5.meta and county_not_none:
1047
1133
  # make sure country and county are coincident
1048
- counties = self.h5.meta.loc[self.h5_gid_set, 'county'].values
1134
+ counties = self.h5.meta.loc[
1135
+ self.h5_gid_set, ResourceMetaField.COUNTY
1136
+ ].values
1049
1137
  iloc = np.where(counties == self.county)[0][0]
1050
- country = self.h5.meta.loc[self.h5_gid_set, 'country'].values
1138
+ country = self.h5.meta.loc[
1139
+ self.h5_gid_set, ResourceMetaField.COUNTRY
1140
+ ].values
1051
1141
  country = country[iloc]
1052
1142
 
1053
- elif 'country' in self.h5.meta:
1054
- country = self.h5.meta.loc[self.h5_gid_set, 'country'].mode()
1143
+ elif ResourceMetaField.COUNTRY in self.h5.meta:
1144
+ country = self.h5.meta.loc[
1145
+ self.h5_gid_set, ResourceMetaField.COUNTRY
1146
+ ].mode()
1055
1147
  country = country.values[0]
1056
1148
 
1057
1149
  return country
@@ -1060,15 +1152,21 @@ class AggregationSupplyCurvePoint(SupplyCurvePoint):
1060
1152
  def state(self):
1061
1153
  """Get the SC point state based on the resource meta data."""
1062
1154
  state = None
1063
- if 'state' in self.h5.meta and self.county is not None:
1155
+ if ResourceMetaField.STATE in self.h5.meta and self.county is not None:
1064
1156
  # make sure state and county are coincident
1065
- counties = self.h5.meta.loc[self.h5_gid_set, 'county'].values
1157
+ counties = self.h5.meta.loc[
1158
+ self.h5_gid_set, ResourceMetaField.COUNTY
1159
+ ].values
1066
1160
  iloc = np.where(counties == self.county)[0][0]
1067
- state = self.h5.meta.loc[self.h5_gid_set, 'state'].values
1161
+ state = self.h5.meta.loc[
1162
+ self.h5_gid_set, ResourceMetaField.STATE
1163
+ ].values
1068
1164
  state = state[iloc]
1069
1165
 
1070
- elif 'state' in self.h5.meta:
1071
- state = self.h5.meta.loc[self.h5_gid_set, 'state'].mode()
1166
+ elif ResourceMetaField.STATE in self.h5.meta:
1167
+ state = self.h5.meta.loc[
1168
+ self.h5_gid_set, ResourceMetaField.STATE
1169
+ ].mode()
1072
1170
  state = state.values[0]
1073
1171
 
1074
1172
  return state
@@ -1077,8 +1175,10 @@ class AggregationSupplyCurvePoint(SupplyCurvePoint):
1077
1175
  def county(self):
1078
1176
  """Get the SC point county based on the resource meta data."""
1079
1177
  county = None
1080
- if 'county' in self.h5.meta:
1081
- county = self.h5.meta.loc[self.h5_gid_set, 'county'].mode()
1178
+ if ResourceMetaField.COUNTY in self.h5.meta:
1179
+ county = self.h5.meta.loc[
1180
+ self.h5_gid_set, ResourceMetaField.COUNTY
1181
+ ].mode()
1082
1182
  county = county.values[0]
1083
1183
 
1084
1184
  return county
@@ -1087,8 +1187,10 @@ class AggregationSupplyCurvePoint(SupplyCurvePoint):
1087
1187
  def elevation(self):
1088
1188
  """Get the SC point elevation based on the resource meta data."""
1089
1189
  elevation = None
1090
- if 'elevation' in self.h5.meta:
1091
- elevation = self.h5.meta.loc[self.h5_gid_set, 'elevation'].mean()
1190
+ if ResourceMetaField.ELEVATION in self.h5.meta:
1191
+ elevation = self.h5.meta.loc[
1192
+ self.h5_gid_set, ResourceMetaField.ELEVATION
1193
+ ].mean()
1092
1194
 
1093
1195
  return elevation
1094
1196
 
@@ -1096,15 +1198,22 @@ class AggregationSupplyCurvePoint(SupplyCurvePoint):
1096
1198
  def timezone(self):
1097
1199
  """Get the SC point timezone based on the resource meta data."""
1098
1200
  timezone = None
1099
- if 'timezone' in self.h5.meta and self.county is not None:
1201
+ county_not_none = self.county is not None
1202
+ if ResourceMetaField.TIMEZONE in self.h5.meta and county_not_none:
1100
1203
  # make sure timezone flag and county are coincident
1101
- counties = self.h5.meta.loc[self.h5_gid_set, 'county'].values
1204
+ counties = self.h5.meta.loc[
1205
+ self.h5_gid_set, ResourceMetaField.COUNTY
1206
+ ].values
1102
1207
  iloc = np.where(counties == self.county)[0][0]
1103
- timezone = self.h5.meta.loc[self.h5_gid_set, 'timezone'].values
1208
+ timezone = self.h5.meta.loc[
1209
+ self.h5_gid_set, ResourceMetaField.TIMEZONE
1210
+ ].values
1104
1211
  timezone = timezone[iloc]
1105
1212
 
1106
- elif 'timezone' in self.h5.meta:
1107
- timezone = self.h5.meta.loc[self.h5_gid_set, 'timezone'].mode()
1213
+ elif ResourceMetaField.TIMEZONE in self.h5.meta:
1214
+ timezone = self.h5.meta.loc[
1215
+ self.h5_gid_set, ResourceMetaField.TIMEZONE
1216
+ ].mode()
1108
1217
  timezone = timezone.values[0]
1109
1218
 
1110
1219
  return timezone
@@ -1114,15 +1223,22 @@ class AggregationSupplyCurvePoint(SupplyCurvePoint):
1114
1223
  """Get the SC point offshore flag based on the resource meta data
1115
1224
  (if offshore column is present)."""
1116
1225
  offshore = None
1117
- if 'offshore' in self.h5.meta and self.county is not None:
1226
+ county_not_none = self.county is not None
1227
+ if ResourceMetaField.OFFSHORE in self.h5.meta and county_not_none:
1118
1228
  # make sure offshore flag and county are coincident
1119
- counties = self.h5.meta.loc[self.h5_gid_set, 'county'].values
1229
+ counties = self.h5.meta.loc[
1230
+ self.h5_gid_set, ResourceMetaField.COUNTY
1231
+ ].values
1120
1232
  iloc = np.where(counties == self.county)[0][0]
1121
- offshore = self.h5.meta.loc[self.h5_gid_set, 'offshore'].values
1233
+ offshore = self.h5.meta.loc[
1234
+ self.h5_gid_set, ResourceMetaField.OFFSHORE
1235
+ ].values
1122
1236
  offshore = offshore[iloc]
1123
1237
 
1124
- elif 'offshore' in self.h5.meta:
1125
- offshore = self.h5.meta.loc[self.h5_gid_set, 'offshore'].mode()
1238
+ elif ResourceMetaField.OFFSHORE in self.h5.meta:
1239
+ offshore = self.h5.meta.loc[
1240
+ self.h5_gid_set, ResourceMetaField.OFFSHORE
1241
+ ].mode()
1126
1242
  offshore = offshore.values[0]
1127
1243
 
1128
1244
  return offshore
@@ -1138,8 +1254,10 @@ class AggregationSupplyCurvePoint(SupplyCurvePoint):
1138
1254
  -------
1139
1255
  gid_counts : list
1140
1256
  """
1141
- gid_counts = [self.include_mask_flat[(self._h5_gids == gid)].sum()
1142
- for gid in self.h5_gid_set]
1257
+ gid_counts = [
1258
+ self.include_mask_flat[(self._h5_gids == gid)].sum()
1259
+ for gid in self.h5_gid_set
1260
+ ]
1143
1261
 
1144
1262
  return gid_counts
1145
1263
 
@@ -1153,28 +1271,41 @@ class AggregationSupplyCurvePoint(SupplyCurvePoint):
1153
1271
  pandas.Series
1154
1272
  List of supply curve point's meta data
1155
1273
  """
1156
- meta = {'sc_point_gid': self.sc_point_gid,
1157
- 'source_gids': self.h5_gid_set,
1158
- 'gid_counts': self.gid_counts,
1159
- 'n_gids': self.n_gids,
1160
- 'area_sq_km': self.area,
1161
- 'latitude': self.latitude,
1162
- 'longitude': self.longitude,
1163
- 'country': self.country,
1164
- 'state': self.state,
1165
- 'county': self.county,
1166
- 'elevation': self.elevation,
1167
- 'timezone': self.timezone,
1168
- }
1274
+ meta = {
1275
+ SupplyCurveField.SC_POINT_GID: self.sc_point_gid,
1276
+ SupplyCurveField.SOURCE_GIDS: self.h5_gid_set,
1277
+ SupplyCurveField.GID_COUNTS: self.gid_counts,
1278
+ SupplyCurveField.N_GIDS: self.n_gids,
1279
+ SupplyCurveField.AREA_SQ_KM: self.area,
1280
+ SupplyCurveField.LATITUDE: self.latitude,
1281
+ SupplyCurveField.LONGITUDE: self.longitude,
1282
+ SupplyCurveField.COUNTRY: self.country,
1283
+ SupplyCurveField.STATE: self.state,
1284
+ SupplyCurveField.COUNTY: self.county,
1285
+ SupplyCurveField.ELEVATION: self.elevation,
1286
+ SupplyCurveField.TIMEZONE: self.timezone,
1287
+ }
1169
1288
  meta = pd.Series(meta)
1170
1289
 
1171
1290
  return meta
1172
1291
 
1173
1292
  @classmethod
1174
- def run(cls, gid, excl, agg_h5, tm_dset, *agg_dset, agg_method='mean',
1175
- excl_dict=None, inclusion_mask=None,
1176
- resolution=64, excl_area=None,
1177
- exclusion_shape=None, close=True, gen_index=None):
1293
+ def run(
1294
+ cls,
1295
+ gid,
1296
+ excl,
1297
+ agg_h5,
1298
+ tm_dset,
1299
+ *agg_dset,
1300
+ agg_method="mean",
1301
+ excl_dict=None,
1302
+ inclusion_mask=None,
1303
+ resolution=64,
1304
+ excl_area=None,
1305
+ exclusion_shape=None,
1306
+ close=True,
1307
+ gen_index=None,
1308
+ ):
1178
1309
  """
1179
1310
  Compute exclusions weight mean for the sc point from data
1180
1311
 
@@ -1228,30 +1359,34 @@ class AggregationSupplyCurvePoint(SupplyCurvePoint):
1228
1359
  Given datasets and meta data aggregated to supply curve points
1229
1360
  """
1230
1361
  if isinstance(agg_dset, str):
1231
- agg_dset = (agg_dset, )
1232
-
1233
- kwargs = {"excl_dict": excl_dict,
1234
- "inclusion_mask": inclusion_mask,
1235
- "resolution": resolution,
1236
- "excl_area": excl_area,
1237
- "exclusion_shape": exclusion_shape,
1238
- "close": close,
1239
- "gen_index": gen_index}
1362
+ agg_dset = (agg_dset,)
1363
+
1364
+ kwargs = {
1365
+ "excl_dict": excl_dict,
1366
+ "inclusion_mask": inclusion_mask,
1367
+ "resolution": resolution,
1368
+ "excl_area": excl_area,
1369
+ "exclusion_shape": exclusion_shape,
1370
+ "close": close,
1371
+ "gen_index": gen_index,
1372
+ }
1240
1373
 
1241
1374
  with cls(gid, excl, agg_h5, tm_dset, **kwargs) as point:
1242
- if agg_method.lower().startswith('mean'):
1375
+ if agg_method.lower().startswith("mean"):
1243
1376
  agg_method = point.exclusion_weighted_mean
1244
- elif agg_method.lower().startswith(('sum', 'agg')):
1377
+ elif agg_method.lower().startswith(("sum", "agg")):
1245
1378
  agg_method = point.aggregate
1246
- elif 'wind_dir' in agg_method.lower():
1379
+ elif "wind_dir" in agg_method.lower():
1247
1380
  agg_method = point.mean_wind_dirs
1248
1381
  else:
1249
- msg = ('Aggregation method must be either mean, '
1250
- 'sum/aggregate, or wind_dir')
1382
+ msg = (
1383
+ "Aggregation method must be either mean, "
1384
+ "sum/aggregate, or wind_dir"
1385
+ )
1251
1386
  logger.error(msg)
1252
1387
  raise ValueError(msg)
1253
1388
 
1254
- out = {'meta': point.summary}
1389
+ out = {"meta": point.summary}
1255
1390
 
1256
1391
  for dset in agg_dset:
1257
1392
  ds = point.h5.open_dataset(dset)
@@ -1265,15 +1400,31 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
1265
1400
  respective generation and resource data."""
1266
1401
 
1267
1402
  # technology-dependent power density estimates in MW/km2
1268
- POWER_DENSITY = {'pv': 36, 'wind': 3}
1269
-
1270
- def __init__(self, gid, excl, gen, tm_dset, gen_index,
1271
- excl_dict=None, inclusion_mask=None,
1272
- res_class_dset=None, res_class_bin=None, excl_area=None,
1273
- power_density=None, cf_dset='cf_mean-means',
1274
- lcoe_dset='lcoe_fcr-means', h5_dsets=None, resolution=64,
1275
- exclusion_shape=None, close=False, friction_layer=None,
1276
- recalc_lcoe=True, apply_exclusions=True):
1403
+ POWER_DENSITY = {"pv": 36, "wind": 3}
1404
+
1405
+ def __init__(
1406
+ self,
1407
+ gid,
1408
+ excl,
1409
+ gen,
1410
+ tm_dset,
1411
+ gen_index,
1412
+ excl_dict=None,
1413
+ inclusion_mask=None,
1414
+ res_class_dset=None,
1415
+ res_class_bin=None,
1416
+ excl_area=None,
1417
+ power_density=None,
1418
+ cf_dset="cf_mean-means",
1419
+ lcoe_dset="lcoe_fcr-means",
1420
+ h5_dsets=None,
1421
+ resolution=64,
1422
+ exclusion_shape=None,
1423
+ close=False,
1424
+ friction_layer=None,
1425
+ recalc_lcoe=True,
1426
+ apply_exclusions=True,
1427
+ ):
1277
1428
  """
1278
1429
  Parameters
1279
1430
  ----------
@@ -1360,26 +1511,36 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
1360
1511
  self._friction_layer = friction_layer
1361
1512
  self._recalc_lcoe = recalc_lcoe
1362
1513
 
1363
- super().__init__(gid, excl, gen, tm_dset,
1364
- excl_dict=excl_dict,
1365
- inclusion_mask=inclusion_mask,
1366
- resolution=resolution,
1367
- excl_area=excl_area,
1368
- exclusion_shape=exclusion_shape,
1369
- close=close, apply_exclusions=False)
1514
+ super().__init__(
1515
+ gid,
1516
+ excl,
1517
+ gen,
1518
+ tm_dset,
1519
+ excl_dict=excl_dict,
1520
+ inclusion_mask=inclusion_mask,
1521
+ resolution=resolution,
1522
+ excl_area=excl_area,
1523
+ exclusion_shape=exclusion_shape,
1524
+ close=close,
1525
+ apply_exclusions=False,
1526
+ )
1370
1527
 
1371
1528
  self._res_gid_set = None
1372
1529
  self._gen_gid_set = None
1373
1530
 
1374
1531
  self._gen_fpath, self._gen = self._h5_fpath, self._h5
1375
1532
 
1376
- self._gen_gids, self._res_gids = self._map_gen_gids(self._gids,
1377
- gen_index)
1533
+ self._gen_gids, self._res_gids = self._map_gen_gids(
1534
+ self._gids, gen_index
1535
+ )
1378
1536
  self._gids = self._gen_gids
1379
1537
  if (self._gen_gids != -1).sum() == 0:
1380
- emsg = ('Supply curve point gid {} has no viable exclusion '
1381
- 'points based on exclusions file: "{}"'
1382
- .format(self._gid, self._excl_fpath))
1538
+ emsg = (
1539
+ "Supply curve point gid {} has no viable exclusion "
1540
+ 'points based on exclusions file: "{}"'.format(
1541
+ self._gid, self._excl_fpath
1542
+ )
1543
+ )
1383
1544
  raise EmptySupplyCurvePointError(emsg)
1384
1545
 
1385
1546
  if apply_exclusions:
@@ -1401,7 +1562,7 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
1401
1562
  Mean of flat_arr masked by the binary exclusions then weighted by
1402
1563
  the non-zero exclusions.
1403
1564
  """
1404
- x = flat_arr[self._gen_gids[self.bool_mask]].astype('float32')
1565
+ x = flat_arr[self._gen_gids[self.bool_mask]].astype("float32")
1405
1566
  incl = self.include_mask_flat[self.bool_mask]
1406
1567
  x *= incl
1407
1568
  mean = x.sum() / incl.sum()
@@ -1476,8 +1637,10 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
1476
1637
  gid_counts : list
1477
1638
  List of exclusion pixels in each resource/generation gid.
1478
1639
  """
1479
- gid_counts = [self.include_mask_flat[(self._res_gids == gid)].sum()
1480
- for gid in self.res_gid_set]
1640
+ gid_counts = [
1641
+ self.include_mask_flat[(self._res_gids == gid)].sum()
1642
+ for gid in self.res_gid_set
1643
+ ]
1481
1644
 
1482
1645
  return gid_counts
1483
1646
 
@@ -1495,10 +1658,9 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
1495
1658
  if isinstance(self._res_class_dset, np.ndarray):
1496
1659
  return self._res_class_dset
1497
1660
 
1498
- else:
1499
- if self._res_data is None:
1500
- if self._res_class_dset in self.gen.datasets:
1501
- self._res_data = self.gen[self._res_class_dset]
1661
+ if self._res_data is None:
1662
+ if self._res_class_dset in self.gen.datasets:
1663
+ self._res_data = self.gen[self._res_class_dset]
1502
1664
 
1503
1665
  return self._res_data
1504
1666
 
@@ -1516,10 +1678,9 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
1516
1678
  if isinstance(self._cf_dset, np.ndarray):
1517
1679
  return self._cf_dset
1518
1680
 
1519
- else:
1520
- if self._gen_data is None:
1521
- if self._cf_dset in self.gen.datasets:
1522
- self._gen_data = self.gen[self._cf_dset]
1681
+ if self._gen_data is None:
1682
+ if self._cf_dset in self.gen.datasets:
1683
+ self._gen_data = self.gen[self._cf_dset]
1523
1684
 
1524
1685
  return self._gen_data
1525
1686
 
@@ -1537,10 +1698,9 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
1537
1698
  if isinstance(self._lcoe_dset, np.ndarray):
1538
1699
  return self._lcoe_dset
1539
1700
 
1540
- else:
1541
- if self._lcoe_data is None:
1542
- if self._lcoe_dset in self.gen.datasets:
1543
- self._lcoe_data = self.gen[self._lcoe_dset]
1701
+ if self._lcoe_data is None:
1702
+ if self._lcoe_dset in self.gen.datasets:
1703
+ self._lcoe_data = self.gen[self._lcoe_dset]
1544
1704
 
1545
1705
  return self._lcoe_data
1546
1706
 
@@ -1578,22 +1738,31 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
1578
1738
  # year CF, but the output should be identical to the original LCOE and
1579
1739
  # so is not consequential).
1580
1740
  if self._recalc_lcoe:
1581
- required = ('fixed_charge_rate', 'capital_cost',
1582
- 'fixed_operating_cost', 'variable_operating_cost',
1583
- 'system_capacity')
1584
- if self.mean_h5_dsets_data is not None:
1585
- if all(k in self.mean_h5_dsets_data for k in required):
1586
- aep = (self.mean_h5_dsets_data['system_capacity']
1587
- * self.mean_cf * 8760)
1588
- # Note the AEP computation uses the SAM config
1589
- # `system_capacity`, so no need to scale `capital_cost`
1590
- # or `fixed_operating_cost` by anything
1591
- mean_lcoe = lcoe_fcr(
1592
- self.mean_h5_dsets_data['fixed_charge_rate'],
1593
- self.mean_h5_dsets_data['capital_cost'],
1594
- self.mean_h5_dsets_data['fixed_operating_cost'],
1595
- aep,
1596
- self.mean_h5_dsets_data['variable_operating_cost'])
1741
+ required = (
1742
+ "fixed_charge_rate",
1743
+ "capital_cost",
1744
+ "fixed_operating_cost",
1745
+ "variable_operating_cost",
1746
+ "system_capacity",
1747
+ )
1748
+ if self.mean_h5_dsets_data is not None and all(
1749
+ k in self.mean_h5_dsets_data for k in required
1750
+ ):
1751
+ aep = (
1752
+ self.mean_h5_dsets_data["system_capacity"]
1753
+ * self.mean_cf
1754
+ * 8760
1755
+ )
1756
+ # Note the AEP computation uses the SAM config
1757
+ # `system_capacity`, so no need to scale `capital_cost`
1758
+ # or `fixed_operating_cost` by anything
1759
+ mean_lcoe = lcoe_fcr(
1760
+ self.mean_h5_dsets_data["fixed_charge_rate"],
1761
+ self.mean_h5_dsets_data["capital_cost"],
1762
+ self.mean_h5_dsets_data["fixed_operating_cost"],
1763
+ aep,
1764
+ self.mean_h5_dsets_data["variable_operating_cost"],
1765
+ )
1597
1766
 
1598
1767
  # alternative if lcoe was not able to be re-calculated from
1599
1768
  # multi year mean CF
@@ -1679,26 +1848,31 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
1679
1848
  """
1680
1849
 
1681
1850
  if self._power_density is None:
1682
- tech = self.gen.meta['reV_tech'][0]
1851
+ tech = self.gen.meta["reV_tech"][0]
1683
1852
  if tech in self.POWER_DENSITY:
1684
1853
  self._power_density = self.POWER_DENSITY[tech]
1685
1854
  else:
1686
- warn('Could not recognize reV technology in generation meta '
1687
- 'data: "{}". Cannot lookup an appropriate power density '
1688
- 'to calculate SC point capacity.'.format(tech))
1855
+ warn(
1856
+ "Could not recognize reV technology in generation meta "
1857
+ 'data: "{}". Cannot lookup an appropriate power density '
1858
+ "to calculate SC point capacity.".format(tech)
1859
+ )
1689
1860
 
1690
1861
  elif isinstance(self._power_density, pd.DataFrame):
1691
1862
  self._pd_obj = self._power_density
1692
1863
 
1693
1864
  missing = set(self.res_gid_set) - set(self._pd_obj.index.values)
1694
1865
  if any(missing):
1695
- msg = ('Variable power density input is missing the '
1696
- 'following resource GIDs: {}'.format(missing))
1866
+ msg = (
1867
+ "Variable power density input is missing the "
1868
+ "following resource GIDs: {}".format(missing)
1869
+ )
1697
1870
  logger.error(msg)
1698
1871
  raise FileInputError(msg)
1699
1872
 
1700
- pds = self._pd_obj.loc[self._res_gids[self.bool_mask],
1701
- 'power_density'].values
1873
+ pds = self._pd_obj.loc[
1874
+ self._res_gids[self.bool_mask], "power_density"
1875
+ ].values
1702
1876
  pds = pds.astype(np.float32)
1703
1877
  pds *= self.include_mask_flat[self.bool_mask]
1704
1878
  denom = self.include_mask_flat[self.bool_mask].sum()
@@ -1724,18 +1898,20 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
1724
1898
  return None
1725
1899
 
1726
1900
  ilr = self.gen["dc_ac_ratio", self._gen_gids[self.bool_mask]]
1727
- ilr = ilr.astype('float32')
1901
+ ilr = ilr.astype("float32")
1728
1902
  weights = self.include_mask_flat[self.bool_mask]
1729
1903
  if self._power_density_ac is None:
1730
- tech = self.gen.meta['reV_tech'][0]
1904
+ tech = self.gen.meta["reV_tech"][0]
1731
1905
  if tech in self.POWER_DENSITY:
1732
1906
  power_density_ac = self.POWER_DENSITY[tech] / ilr
1733
1907
  power_density_ac *= weights
1734
1908
  power_density_ac = power_density_ac.sum() / weights.sum()
1735
1909
  else:
1736
- warn('Could not recognize reV technology in generation meta '
1737
- 'data: "{}". Cannot lookup an appropriate power density '
1738
- 'to calculate SC point capacity.'.format(tech))
1910
+ warn(
1911
+ "Could not recognize reV technology in generation meta "
1912
+ 'data: "{}". Cannot lookup an appropriate power density '
1913
+ "to calculate SC point capacity.".format(tech)
1914
+ )
1739
1915
  power_density_ac = None
1740
1916
 
1741
1917
  elif isinstance(self._power_density_ac, pd.DataFrame):
@@ -1743,13 +1919,16 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
1743
1919
 
1744
1920
  missing = set(self.res_gid_set) - set(self._pd_obj.index.values)
1745
1921
  if any(missing):
1746
- msg = ('Variable power density input is missing the '
1747
- 'following resource GIDs: {}'.format(missing))
1922
+ msg = (
1923
+ "Variable power density input is missing the "
1924
+ "following resource GIDs: {}".format(missing)
1925
+ )
1748
1926
  logger.error(msg)
1749
1927
  raise FileInputError(msg)
1750
1928
 
1751
- pds = self._pd_obj.loc[self._res_gids[self.bool_mask],
1752
- 'power_density'].values
1929
+ pds = self._pd_obj.loc[
1930
+ self._res_gids[self.bool_mask], "power_density"
1931
+ ].values
1753
1932
  power_density_ac = pds.astype(np.float32) / ilr
1754
1933
  power_density_ac *= weights
1755
1934
  power_density_ac = power_density_ac.sum() / weights.sum()
@@ -1816,12 +1995,14 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
1816
1995
  if self.mean_h5_dsets_data is None:
1817
1996
  return None
1818
1997
 
1819
- required = ('capital_cost', 'system_capacity')
1998
+ required = ("capital_cost", "system_capacity")
1820
1999
  if not all(k in self.mean_h5_dsets_data for k in required):
1821
2000
  return None
1822
2001
 
1823
- cap_cost_per_mw = (self.mean_h5_dsets_data['capital_cost']
1824
- / self.mean_h5_dsets_data['system_capacity'])
2002
+ cap_cost_per_mw = (
2003
+ self.mean_h5_dsets_data["capital_cost"]
2004
+ / self.mean_h5_dsets_data["system_capacity"]
2005
+ )
1825
2006
  return cap_cost_per_mw * self.capacity
1826
2007
 
1827
2008
  @property
@@ -1842,12 +2023,14 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
1842
2023
  if self.mean_h5_dsets_data is None:
1843
2024
  return None
1844
2025
 
1845
- required = ('fixed_operating_cost', 'system_capacity')
2026
+ required = ("fixed_operating_cost", "system_capacity")
1846
2027
  if not all(k in self.mean_h5_dsets_data for k in required):
1847
2028
  return None
1848
2029
 
1849
- fixed_cost_per_mw = (self.mean_h5_dsets_data['fixed_operating_cost']
1850
- / self.mean_h5_dsets_data['system_capacity'])
2030
+ fixed_cost_per_mw = (
2031
+ self.mean_h5_dsets_data["fixed_operating_cost"]
2032
+ / self.mean_h5_dsets_data["system_capacity"]
2033
+ )
1851
2034
  return fixed_cost_per_mw * self.capacity
1852
2035
 
1853
2036
  @property
@@ -1909,10 +2092,13 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
1909
2092
  _h5_dsets_data = self._h5_dsets
1910
2093
 
1911
2094
  elif self._h5_dsets is not None:
1912
- e = ('Cannot recognize h5_dsets input type, should be None, '
1913
- 'a list of dataset names, or a dictionary or '
1914
- 'pre-extracted data. Received: {} {}'
1915
- .format(type(self._h5_dsets), self._h5_dsets))
2095
+ e = (
2096
+ "Cannot recognize h5_dsets input type, should be None, "
2097
+ "a list of dataset names, or a dictionary or "
2098
+ "pre-extracted data. Received: {} {}".format(
2099
+ type(self._h5_dsets), self._h5_dsets
2100
+ )
2101
+ )
1916
2102
  logger.error(e)
1917
2103
  raise TypeError(e)
1918
2104
 
@@ -1955,8 +2141,10 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
1955
2141
  self._incl_mask = self._incl_mask.flatten()
1956
2142
 
1957
2143
  if (self._gen_gids != -1).sum() == 0:
1958
- msg = ('Supply curve point gid {} is completely excluded for res '
1959
- 'bin: {}'.format(self._gid, self._res_class_bin))
2144
+ msg = (
2145
+ "Supply curve point gid {} is completely excluded for res "
2146
+ "bin: {}".format(self._gid, self._res_class_bin)
2147
+ )
1960
2148
  raise EmptySupplyCurvePointError(msg)
1961
2149
 
1962
2150
  def _resource_exclusion(self, boolean_exclude):
@@ -1974,14 +2162,16 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
1974
2162
  outside of current resource class bin.
1975
2163
  """
1976
2164
 
1977
- if (self._res_class_dset is not None
1978
- and self._res_class_bin is not None):
1979
-
2165
+ if (
2166
+ self._res_class_dset is not None
2167
+ and self._res_class_bin is not None
2168
+ ):
1980
2169
  rex = self.res_data[self._gen_gids]
1981
- rex = ((rex < np.min(self._res_class_bin))
1982
- | (rex >= np.max(self._res_class_bin)))
2170
+ rex = (rex < np.min(self._res_class_bin)) | (
2171
+ rex >= np.max(self._res_class_bin)
2172
+ )
1983
2173
 
1984
- boolean_exclude = (boolean_exclude | rex)
2174
+ boolean_exclude = boolean_exclude | rex
1985
2175
 
1986
2176
  return boolean_exclude
1987
2177
 
@@ -2001,39 +2191,50 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
2001
2191
  Dictionary of summary outputs for this sc point.
2002
2192
  """
2003
2193
 
2004
- ARGS = {'res_gids': self.res_gid_set,
2005
- 'gen_gids': self.gen_gid_set,
2006
- 'gid_counts': self.gid_counts,
2007
- 'n_gids': self.n_gids,
2008
- 'mean_cf': self.mean_cf,
2009
- 'mean_lcoe': self.mean_lcoe,
2010
- 'mean_res': self.mean_res,
2011
- 'capacity': self.capacity,
2012
- 'area_sq_km': self.area,
2013
- 'latitude': self.latitude,
2014
- 'longitude': self.longitude,
2015
- 'country': self.country,
2016
- 'state': self.state,
2017
- 'county': self.county,
2018
- 'elevation': self.elevation,
2019
- 'timezone': self.timezone,
2020
- }
2021
-
2022
- extra_atts = ['capacity_ac', 'offshore', 'sc_point_capital_cost',
2023
- 'sc_point_fixed_operating_cost',
2024
- 'sc_point_annual_energy', 'sc_point_annual_energy_ac']
2025
- for attr in extra_atts:
2026
- value = getattr(self, attr)
2194
+ ARGS = {
2195
+ SupplyCurveField.LATITUDE: self.latitude,
2196
+ SupplyCurveField.LONGITUDE: self.longitude,
2197
+ SupplyCurveField.TIMEZONE: self.timezone,
2198
+ SupplyCurveField.COUNTRY: self.country,
2199
+ SupplyCurveField.STATE: self.state,
2200
+ SupplyCurveField.COUNTY: self.county,
2201
+ SupplyCurveField.ELEVATION: self.elevation,
2202
+ SupplyCurveField.RES_GIDS: self.res_gid_set,
2203
+ SupplyCurveField.GEN_GIDS: self.gen_gid_set,
2204
+ SupplyCurveField.GID_COUNTS: self.gid_counts,
2205
+ SupplyCurveField.N_GIDS: self.n_gids,
2206
+ SupplyCurveField.MEAN_CF: self.mean_cf,
2207
+ SupplyCurveField.MEAN_LCOE: self.mean_lcoe,
2208
+ SupplyCurveField.MEAN_RES: self.mean_res,
2209
+ SupplyCurveField.CAPACITY: self.capacity,
2210
+ SupplyCurveField.AREA_SQ_KM: self.area,
2211
+ }
2212
+
2213
+ extra_atts = {
2214
+ SupplyCurveField.CAPACITY_AC: self.capacity_ac,
2215
+ SupplyCurveField.OFFSHORE: self.offshore,
2216
+ SupplyCurveField.SC_POINT_CAPITAL_COST: self.sc_point_capital_cost,
2217
+ SupplyCurveField.SC_POINT_FIXED_OPERATING_COST: (
2218
+ self.sc_point_fixed_operating_cost
2219
+ ),
2220
+ SupplyCurveField.SC_POINT_ANNUAL_ENERGY: (
2221
+ self.sc_point_annual_energy
2222
+ ),
2223
+ SupplyCurveField.SC_POINT_ANNUAL_ENERGY_AC: (
2224
+ self.sc_point_annual_energy_ac
2225
+ ),
2226
+ }
2227
+ for attr, value in extra_atts.items():
2027
2228
  if value is not None:
2028
2229
  ARGS[attr] = value
2029
2230
 
2030
2231
  if self._friction_layer is not None:
2031
- ARGS['mean_friction'] = self.mean_friction
2032
- ARGS['mean_lcoe_friction'] = self.mean_lcoe_friction
2232
+ ARGS[SupplyCurveField.MEAN_FRICTION] = self.mean_friction
2233
+ ARGS[SupplyCurveField.MEAN_LCOE_FRICTION] = self.mean_lcoe_friction
2033
2234
 
2034
2235
  if self._h5_dsets is not None:
2035
2236
  for dset, data in self.mean_h5_dsets_data.items():
2036
- ARGS['mean_{}'.format(dset)] = data
2237
+ ARGS["mean_{}".format(dset)] = data
2037
2238
 
2038
2239
  if args is None:
2039
2240
  args = list(ARGS.keys())
@@ -2043,8 +2244,11 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
2043
2244
  if arg in ARGS:
2044
2245
  summary[arg] = ARGS[arg]
2045
2246
  else:
2046
- warn('Cannot find "{}" as an available SC self summary '
2047
- 'output', OutputWarning)
2247
+ warn(
2248
+ 'Cannot find "{}" as an available SC self summary '
2249
+ "output",
2250
+ OutputWarning,
2251
+ )
2048
2252
 
2049
2253
  return summary
2050
2254
 
@@ -2070,26 +2274,47 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
2070
2274
  """
2071
2275
 
2072
2276
  eos = EconomiesOfScale(cap_cost_scale, summary)
2073
- summary['raw_lcoe'] = eos.raw_lcoe
2074
- summary['mean_lcoe'] = eos.scaled_lcoe
2075
- summary['capital_cost_scalar'] = eos.capital_cost_scalar
2076
- summary['scaled_capital_cost'] = eos.scaled_capital_cost
2077
- if "sc_point_capital_cost" in summary:
2078
- scaled_costs = (summary["sc_point_capital_cost"]
2079
- * eos.capital_cost_scalar)
2080
- summary['scaled_sc_point_capital_cost'] = scaled_costs
2277
+ summary[SupplyCurveField.RAW_LCOE] = eos.raw_lcoe
2278
+ summary[SupplyCurveField.MEAN_LCOE] = eos.scaled_lcoe
2279
+ summary[SupplyCurveField.CAPITAL_COST_SCALAR] = eos.capital_cost_scalar
2280
+ summary[SupplyCurveField.SCALED_CAPITAL_COST] = eos.scaled_capital_cost
2281
+ if SupplyCurveField.SC_POINT_CAPITAL_COST in summary:
2282
+ scaled_costs = (
2283
+ summary[SupplyCurveField.SC_POINT_CAPITAL_COST]
2284
+ * eos.capital_cost_scalar
2285
+ )
2286
+ summary[SupplyCurveField.SCALED_SC_POINT_CAPITAL_COST] = (
2287
+ scaled_costs
2288
+ )
2081
2289
 
2082
2290
  return summary
2083
2291
 
2084
2292
  @classmethod
2085
- def summarize(cls, gid, excl_fpath, gen_fpath, tm_dset, gen_index,
2086
- excl_dict=None, inclusion_mask=None,
2087
- res_class_dset=None, res_class_bin=None,
2088
- excl_area=None, power_density=None,
2089
- cf_dset='cf_mean-means', lcoe_dset='lcoe_fcr-means',
2090
- h5_dsets=None, resolution=64, exclusion_shape=None,
2091
- close=False, friction_layer=None, args=None,
2092
- data_layers=None, cap_cost_scale=None, recalc_lcoe=True):
2293
+ def summarize(
2294
+ cls,
2295
+ gid,
2296
+ excl_fpath,
2297
+ gen_fpath,
2298
+ tm_dset,
2299
+ gen_index,
2300
+ excl_dict=None,
2301
+ inclusion_mask=None,
2302
+ res_class_dset=None,
2303
+ res_class_bin=None,
2304
+ excl_area=None,
2305
+ power_density=None,
2306
+ cf_dset="cf_mean-means",
2307
+ lcoe_dset="lcoe_fcr-means",
2308
+ h5_dsets=None,
2309
+ resolution=64,
2310
+ exclusion_shape=None,
2311
+ close=False,
2312
+ friction_layer=None,
2313
+ args=None,
2314
+ data_layers=None,
2315
+ cap_cost_scale=None,
2316
+ recalc_lcoe=True,
2317
+ ):
2093
2318
  """Get a summary dictionary of a single supply curve point.
2094
2319
 
2095
2320
  Parameters
@@ -2176,24 +2401,26 @@ class GenerationSupplyCurvePoint(AggregationSupplyCurvePoint):
2176
2401
  summary : dict
2177
2402
  Dictionary of summary outputs for this sc point.
2178
2403
  """
2179
- kwargs = {"excl_dict": excl_dict,
2180
- "inclusion_mask": inclusion_mask,
2181
- "res_class_dset": res_class_dset,
2182
- "res_class_bin": res_class_bin,
2183
- "excl_area": excl_area,
2184
- "power_density": power_density,
2185
- "cf_dset": cf_dset,
2186
- "lcoe_dset": lcoe_dset,
2187
- "h5_dsets": h5_dsets,
2188
- "resolution": resolution,
2189
- "exclusion_shape": exclusion_shape,
2190
- "close": close,
2191
- 'friction_layer': friction_layer,
2192
- 'recalc_lcoe': recalc_lcoe,
2193
- }
2194
-
2195
- with cls(gid, excl_fpath, gen_fpath, tm_dset, gen_index,
2196
- **kwargs) as point:
2404
+ kwargs = {
2405
+ "excl_dict": excl_dict,
2406
+ "inclusion_mask": inclusion_mask,
2407
+ "res_class_dset": res_class_dset,
2408
+ "res_class_bin": res_class_bin,
2409
+ "excl_area": excl_area,
2410
+ "power_density": power_density,
2411
+ "cf_dset": cf_dset,
2412
+ "lcoe_dset": lcoe_dset,
2413
+ "h5_dsets": h5_dsets,
2414
+ "resolution": resolution,
2415
+ "exclusion_shape": exclusion_shape,
2416
+ "close": close,
2417
+ "friction_layer": friction_layer,
2418
+ "recalc_lcoe": recalc_lcoe,
2419
+ }
2420
+
2421
+ with cls(
2422
+ gid, excl_fpath, gen_fpath, tm_dset, gen_index, **kwargs
2423
+ ) as point:
2197
2424
  summary = point.point_summary(args=args)
2198
2425
 
2199
2426
  if data_layers is not None: