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.
- {NREL_reV-0.8.7.dist-info → NREL_reV-0.8.9.dist-info}/METADATA +12 -10
- {NREL_reV-0.8.7.dist-info → NREL_reV-0.8.9.dist-info}/RECORD +38 -38
- {NREL_reV-0.8.7.dist-info → NREL_reV-0.8.9.dist-info}/WHEEL +1 -1
- reV/SAM/SAM.py +182 -133
- reV/SAM/econ.py +18 -14
- reV/SAM/generation.py +608 -419
- reV/SAM/windbos.py +93 -79
- reV/bespoke/bespoke.py +690 -445
- reV/bespoke/place_turbines.py +6 -6
- reV/config/project_points.py +220 -140
- reV/econ/econ.py +165 -113
- reV/econ/economies_of_scale.py +57 -34
- reV/generation/base.py +310 -183
- reV/generation/generation.py +298 -190
- reV/handlers/exclusions.py +16 -15
- reV/handlers/multi_year.py +12 -9
- reV/handlers/outputs.py +6 -5
- reV/hybrids/hybrid_methods.py +28 -30
- reV/hybrids/hybrids.py +304 -188
- reV/nrwal/nrwal.py +262 -168
- reV/qa_qc/cli_qa_qc.py +14 -10
- reV/qa_qc/qa_qc.py +217 -119
- reV/qa_qc/summary.py +228 -146
- reV/rep_profiles/rep_profiles.py +349 -230
- reV/supply_curve/aggregation.py +349 -188
- reV/supply_curve/competitive_wind_farms.py +90 -48
- reV/supply_curve/exclusions.py +138 -85
- reV/supply_curve/extent.py +75 -50
- reV/supply_curve/points.py +536 -309
- reV/supply_curve/sc_aggregation.py +366 -225
- reV/supply_curve/supply_curve.py +505 -308
- reV/supply_curve/tech_mapping.py +144 -82
- reV/utilities/__init__.py +199 -16
- reV/utilities/pytest_utils.py +8 -4
- reV/version.py +1 -1
- {NREL_reV-0.8.7.dist-info → NREL_reV-0.8.9.dist-info}/LICENSE +0 -0
- {NREL_reV-0.8.7.dist-info → NREL_reV-0.8.9.dist-info}/entry_points.txt +0 -0
- {NREL_reV-0.8.7.dist-info → NREL_reV-0.8.9.dist-info}/top_level.txt +0 -0
reV/supply_curve/points.py
CHANGED
@@ -2,26 +2,30 @@
|
|
2
2
|
"""
|
3
3
|
reV supply curve points frameworks.
|
4
4
|
"""
|
5
|
-
|
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
|
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
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
"""
|
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 = (
|
176
|
-
|
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__(
|
196
|
-
|
197
|
-
|
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 = (
|
249
|
-
|
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(
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
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 = (
|
316
|
-
|
317
|
-
|
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(
|
347
|
-
|
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[
|
363
|
-
lons = self.exclusions.excl_h5[
|
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 = (
|
442
|
-
|
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 =
|
509
|
-
|
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(
|
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(
|
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
|
-
|
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(
|
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(
|
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(
|
616
|
-
|
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 = {
|
653
|
-
|
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(
|
661
|
-
|
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 = {
|
698
|
-
|
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
|
-
|
722
|
-
|
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 = {
|
747
|
-
|
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 = {
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
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 = (
|
784
|
-
|
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 = (
|
793
|
-
|
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 ==
|
798
|
-
data = method_func[
|
799
|
-
elif method in [
|
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(
|
892
|
+
excl_fp = attrs.get("fpath", self._excl_fpath)
|
832
893
|
if excl_fp != self._excl_fpath:
|
833
|
-
fh = ExclusionLayers(attrs[
|
894
|
+
fh = ExclusionLayers(attrs["fpath"])
|
834
895
|
else:
|
835
896
|
fh = self.exclusions.excl_h5
|
836
897
|
|
837
|
-
raw = fh[attrs[
|
838
|
-
nodata = fh.get_nodata_value(attrs[
|
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 =
|
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 = (
|
850
|
-
|
851
|
-
|
852
|
-
|
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(
|
856
|
-
|
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__(
|
869
|
-
|
870
|
-
|
871
|
-
|
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__(
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
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 = (
|
931
|
-
|
932
|
-
|
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(
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
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 =
|
986
|
-
|
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
|
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
|
-
|
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[
|
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[
|
1138
|
+
country = self.h5.meta.loc[
|
1139
|
+
self.h5_gid_set, ResourceMetaField.COUNTRY
|
1140
|
+
].values
|
1051
1141
|
country = country[iloc]
|
1052
1142
|
|
1053
|
-
elif
|
1054
|
-
country = self.h5.meta.loc[
|
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
|
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[
|
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[
|
1161
|
+
state = self.h5.meta.loc[
|
1162
|
+
self.h5_gid_set, ResourceMetaField.STATE
|
1163
|
+
].values
|
1068
1164
|
state = state[iloc]
|
1069
1165
|
|
1070
|
-
elif
|
1071
|
-
state = self.h5.meta.loc[
|
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
|
1081
|
-
county = self.h5.meta.loc[
|
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
|
1091
|
-
elevation = self.h5.meta.loc[
|
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
|
-
|
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[
|
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[
|
1208
|
+
timezone = self.h5.meta.loc[
|
1209
|
+
self.h5_gid_set, ResourceMetaField.TIMEZONE
|
1210
|
+
].values
|
1104
1211
|
timezone = timezone[iloc]
|
1105
1212
|
|
1106
|
-
elif
|
1107
|
-
timezone = self.h5.meta.loc[
|
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
|
-
|
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[
|
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[
|
1233
|
+
offshore = self.h5.meta.loc[
|
1234
|
+
self.h5_gid_set, ResourceMetaField.OFFSHORE
|
1235
|
+
].values
|
1122
1236
|
offshore = offshore[iloc]
|
1123
1237
|
|
1124
|
-
elif
|
1125
|
-
offshore = self.h5.meta.loc[
|
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 = [
|
1142
|
-
|
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 = {
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
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(
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
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 = {
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
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(
|
1375
|
+
if agg_method.lower().startswith("mean"):
|
1243
1376
|
agg_method = point.exclusion_weighted_mean
|
1244
|
-
elif agg_method.lower().startswith((
|
1377
|
+
elif agg_method.lower().startswith(("sum", "agg")):
|
1245
1378
|
agg_method = point.aggregate
|
1246
|
-
elif
|
1379
|
+
elif "wind_dir" in agg_method.lower():
|
1247
1380
|
agg_method = point.mean_wind_dirs
|
1248
1381
|
else:
|
1249
|
-
msg = (
|
1250
|
-
|
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 = {
|
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 = {
|
1269
|
-
|
1270
|
-
def __init__(
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
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__(
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
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(
|
1377
|
-
|
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 = (
|
1381
|
-
|
1382
|
-
|
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(
|
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 = [
|
1480
|
-
|
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
|
-
|
1499
|
-
if self.
|
1500
|
-
|
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
|
-
|
1520
|
-
if self.
|
1521
|
-
|
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
|
-
|
1541
|
-
if self.
|
1542
|
-
|
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 = (
|
1582
|
-
|
1583
|
-
|
1584
|
-
|
1585
|
-
|
1586
|
-
|
1587
|
-
|
1588
|
-
|
1589
|
-
|
1590
|
-
|
1591
|
-
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1595
|
-
|
1596
|
-
|
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[
|
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(
|
1687
|
-
|
1688
|
-
|
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 = (
|
1696
|
-
|
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[
|
1701
|
-
|
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(
|
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[
|
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(
|
1737
|
-
|
1738
|
-
|
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 = (
|
1747
|
-
|
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[
|
1752
|
-
|
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 = (
|
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 = (
|
1824
|
-
|
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 = (
|
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 = (
|
1850
|
-
|
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 = (
|
1913
|
-
|
1914
|
-
|
1915
|
-
|
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 = (
|
1959
|
-
|
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 (
|
1978
|
-
|
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 = (
|
1982
|
-
|
2170
|
+
rex = (rex < np.min(self._res_class_bin)) | (
|
2171
|
+
rex >= np.max(self._res_class_bin)
|
2172
|
+
)
|
1983
2173
|
|
1984
|
-
boolean_exclude =
|
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 = {
|
2005
|
-
|
2006
|
-
|
2007
|
-
|
2008
|
-
|
2009
|
-
|
2010
|
-
|
2011
|
-
|
2012
|
-
|
2013
|
-
|
2014
|
-
|
2015
|
-
|
2016
|
-
|
2017
|
-
|
2018
|
-
|
2019
|
-
|
2020
|
-
|
2021
|
-
|
2022
|
-
|
2023
|
-
|
2024
|
-
|
2025
|
-
|
2026
|
-
|
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[
|
2032
|
-
ARGS[
|
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[
|
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(
|
2047
|
-
|
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[
|
2074
|
-
summary[
|
2075
|
-
summary[
|
2076
|
-
summary[
|
2077
|
-
if
|
2078
|
-
scaled_costs = (
|
2079
|
-
|
2080
|
-
|
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(
|
2086
|
-
|
2087
|
-
|
2088
|
-
|
2089
|
-
|
2090
|
-
|
2091
|
-
|
2092
|
-
|
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 = {
|
2180
|
-
|
2181
|
-
|
2182
|
-
|
2183
|
-
|
2184
|
-
|
2185
|
-
|
2186
|
-
|
2187
|
-
|
2188
|
-
|
2189
|
-
|
2190
|
-
|
2191
|
-
|
2192
|
-
|
2193
|
-
|
2194
|
-
|
2195
|
-
|
2196
|
-
|
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:
|