Skip to content

Commit

Permalink
Added some lifetime SAM key descriptions to the base pv class and ref…
Browse files Browse the repository at this point in the history
…actored some items in .
  • Loading branch information
WilliamsTravis committed Dec 22, 2023
1 parent 2d6c5fb commit 2c682e2
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 94 deletions.
49 changes: 34 additions & 15 deletions reV/SAM/generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,29 @@
Wraps the NREL-PySAM pvwattsv5, windpower, and tcsmolensalt modules with
additional reV features.
"""
from abc import ABC, abstractmethod
import copy
import os
import logging
import numpy as np
import pandas as pd

from abc import ABC, abstractmethod
from tempfile import TemporaryDirectory
from warnings import warn

import numpy as np
import pandas as pd
import PySAM.Geothermal as PySamGeothermal
import PySAM.LinearFresnelDsgIph as PySamLds
import PySAM.MhkWave as PySamMhkWave
import PySAM.Pvsamv1 as PySamDetailedPv
import PySAM.Pvwattsv5 as PySamPv5
import PySAM.Pvwattsv7 as PySamPv7
import PySAM.Pvwattsv8 as PySamPv8
import PySAM.Pvsamv1 as PySamDetailedPv
import PySAM.Windpower as PySamWindPower
import PySAM.TcsmoltenSalt as PySamCSP
import PySAM.Swh as PySamSwh
import PySAM.TcsmoltenSalt as PySamCSP
import PySAM.TroughPhysicalProcessHeat as PySamTpph
import PySAM.LinearFresnelDsgIph as PySamLds
import PySAM.MhkWave as PySamMhkWave
import PySAM.Windpower as PySamWindPower

from reV.losses import ScheduledLossesMixin, PowerCurveLossesMixin
from reV.SAM.defaults import (DefaultGeothermal,
DefaultPvWattsv5,
DefaultPvWattsv8,
Expand All @@ -34,12 +37,11 @@
DefaultTroughPhysicalProcessHeat,
DefaultLinearFresnelDsgIph,
DefaultMhkWave)
from reV.SAM.econ import LCOE, SingleOwner
from reV.SAM.SAM import RevPySam
from reV.utilities.curtailment import curtail
from reV.utilities.exceptions import (SAMInputWarning, SAMExecutionError,
InputError)
from reV.utilities.curtailment import curtail
from reV.SAM.SAM import RevPySam
from reV.SAM.econ import LCOE, SingleOwner
from reV.losses import ScheduledLossesMixin, PowerCurveLossesMixin

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -770,9 +772,26 @@ def __init__(self, resource, meta, sam_sys_inputs, site_sys_inputs=None,
"""Initialize a SAM solar object.
See the PySAM :py:class:`~PySAM.Pvwattsv8.Pvwattsv8` (or older
version model) documentation for the configuration keys required
in the `sam_sys_inputs` config. You may also include the
following ``reV``-specific keys:
version model) or :py:class:`~PySAM.Pvsamv1.Pvsamv1` documentation for
the configuration keys required in the `sam_sys_inputs` config for the
respective models. Some notable keys include the following to enable a
lifetime simulation (non-exhaustive):
- ``system_use_lifetime_output`` : Integer flag indicating whether
or not to run a full lifetime model (0 for off, 1 for on). If
running a lifetime model, the resource file will be repeated
for the number of years specified as the lifetime of the
plant and a performance degradation term will be used to
simulate reduced performance over time.
- ``analysis_period`` : Integer representing the number of years
to include in the lifetime of the model generator. Required if
``system_use_lifetime_output``=1.
- ``dc_degradation`` : List of percentage values representing the
annual DC degradation of capacity factors. Maybe a single value
that will be compound each year or a vector of yearly rates.
Required if ``system_use_lifetime_output``=1.
You may also include the following ``reV``-specific keys:
- ``reV_outages`` : Specification for ``reV``-scheduled
stochastic outage losses. For example::
Expand Down
152 changes: 73 additions & 79 deletions reV/generation/generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,39 @@
"""
reV generation module.
"""
import os
import copy
import json
import logging

import os
import pprint

import numpy as np
import pandas as pd

from reV.generation.base import BaseGen
from reV.utilities.exceptions import (
ConfigError,
InputError,
ProjectPointsValueError,
)
from reV.SAM.generation import (
Geothermal,
PvWattsv5,
PvWattsv7,
PvWattsv8,
PvSamv1,
TcsMoltenSalt,
WindPower,
SolarWaterHeat,
TroughPhysicalHeat,
LinearDirectSteam,
MhkWave
)
from reV.SAM.generation import (Geothermal,
MhkWave,
LinearDirectSteam,
PvSamv1,
PvWattsv5,
PvWattsv7,
PvWattsv8,
SolarWaterHeat,
TcsMoltenSalt,
TroughPhysicalHeat,
WindPower)
from reV.utilities import ModuleName

from rex.resource import Resource
from reV.utilities.exceptions import (ConfigError,
InputError,
ProjectPointsValueError)
from rex.multi_file_resource import MultiFileResource
from rex.multi_res_resource import MultiResolutionResource
from rex.resource import Resource
from rex.utilities.utilities import check_res_file

logger = logging.getLogger(__name__)


ATTR_DIR = os.path.dirname(os.path.realpath(__file__))
ATTR_DIR = os.path.join(ATTR_DIR, 'output_attributes')
with open(os.path.join(ATTR_DIR, 'other.json'), 'r') as f:
Expand All @@ -57,19 +53,18 @@ class Gen(BaseGen):
"""Gen"""

# Mapping of reV technology strings to SAM generation objects
OPTIONS = {
'geothermal': Geothermal,
'pvwattsv5': PvWattsv5,
'pvwattsv7': PvWattsv7,
'pvwattsv8': PvWattsv8,
'pvsamv1': PvSamv1,
'tcsmoltensalt': TcsMoltenSalt,
'solarwaterheat': SolarWaterHeat,
'troughphysicalheat': TroughPhysicalHeat,
'lineardirectsteam': LinearDirectSteam,
'windpower': WindPower,
'mhkwave': MhkWave
}
OPTIONS = {'geothermal': Geothermal,
'lineardirectsteam': LinearDirectSteam,
'mhkwave': MhkWave,
'pvsamv1': PvSamv1,
'pvwattsv5': PvWattsv5,
'pvwattsv7': PvWattsv7,
'pvwattsv8': PvWattsv8,
'solarwaterheat': SolarWaterHeat,
'tcsmoltensalt': TcsMoltenSalt,
'troughphysicalheat': TroughPhysicalHeat,
'windpower': WindPower}

"""reV technology options."""

# Mapping of reV generation outputs to scale factors and units.
Expand Down Expand Up @@ -519,52 +514,51 @@ def handle_lifetime_index(self, ti):
else:
lifetime_periods.append(1)

if any(ltp > 1 for ltp in lifetime_periods):
# Collect variables to check that this will work
n_unique_periods = len(np.unique(lifetime_periods))
array_vars = [
var for var, attrs in GEN_ATTRS.items()
if attrs['type'] == 'array'
]
valid_vars = ['gen_profile', 'cf_profile', 'cf_profile_ac']
invalid_vars = set(array_vars) - set(valid_vars)
invalid_requests = [var for var in self.output_request
if var in invalid_vars]

if n_unique_periods != 1:
# Only one time index, so lifetime outputs all need to match
msg = ('reV cannot handle multiple analysis_periods when '
'modeling with `system_use_lifetime_output` set '
'to 1. Found {} different analysis_periods in the SAM '
'configs'.format(n_unique_periods))
logger.error(msg)
raise ConfigError(msg)

elif invalid_requests:
# SAM does not output full lifetime for all array variables
msg = (
'reV can only handle the following output arrays '
'when modeling with `system_use_lifetime_output` set '
'to 1: {}. Try running without {}.'.format(
', '.join(valid_vars), ', '.join(invalid_requests)
)
)
logger.error(msg)
raise ConfigError(msg)
if not any(ltp > 1 for ltp in lifetime_periods):
return ti

else:
sam_meta = self.sam_metas[next(iter(self.sam_metas))]
analysis_period = sam_meta["analysis_period"]
logger.info('reV generation running with a full system '
'life of {} years.'.format(analysis_period))

old_end = ti[-1]
new_end = old_end + pd.DateOffset(
years=analysis_period - 1
# Only one time index may be passed, check that lifetime periods match
n_unique_periods = len(np.unique(lifetime_periods))
if n_unique_periods != 1:
msg = ('reV cannot handle multiple analysis_periods when '
'modeling with `system_use_lifetime_output` set '
'to 1. Found {} different analysis_periods in the SAM '
'configs'.format(n_unique_periods))
logger.error(msg)
raise ConfigError(msg)

# Collect requested variables to check for lifetime compatibility
array_vars = [
var for var, attrs in GEN_ATTRS.items()
if attrs['type'] == 'array'
]
valid_vars = ['gen_profile', 'cf_profile', 'cf_profile_ac']
invalid_vars = set(array_vars) - set(valid_vars)
invalid_requests = [var for var in self.output_request
if var in invalid_vars]

if invalid_requests:
# SAM does not output full lifetime for all array variables
msg = (
'reV can only handle the following output arrays '
'when modeling with `system_use_lifetime_output` set '
'to 1: {}. Try running without {}.'.format(
', '.join(valid_vars), ', '.join(invalid_requests)
)
step = old_end - ti[-2]
time_extension = pd.date_range(old_end, new_end, freq=step)
ti = time_extension.union(ti)
)
logger.error(msg)
raise ConfigError(msg)

sam_meta = self.sam_metas[next(iter(self.sam_metas))]
analysis_period = sam_meta["analysis_period"]
logger.info('reV generation running with a full system '
'life of {} years.'.format(analysis_period))

old_end = ti[-1]
new_end = old_end + pd.DateOffset(years=analysis_period - 1)
step = old_end - ti[-2]
time_extension = pd.date_range(old_end, new_end, freq=step)
ti = time_extension.union(ti)

return ti

Expand Down

0 comments on commit 2c682e2

Please sign in to comment.