"""
The ``cpvsystem`` module contains functions for modeling the output and
performance of CPV modules.
"""
import numpy as np
import pandas as pd
import pvlib
from pvlib.tools import _build_kwargs
[docs]class CPVSystem(pvlib.pvsystem.PVSystem):
"""
The CPVSystem class defines a set of CPV system attributes and modeling
functions. This class describes the collection and interactions of CPV
system components installed on a Dual Axis Tracker.
It inheritates from pvlib.pvsystem.PVSystem, modifying those methods that
are specific for a CPV system following the PVSyst implementation.
Besides, specific CPV utilization factors are added as defined in
https://doi.org/10.1063/1.3509185 as new methods and new specific CPV parameters
are passes to these methods.
The class supports basic system topologies consisting of:
* `N` total modules arranged in series
(`modules_per_string=N`, `strings_per_inverter=1`).
* `M` total modules arranged in parallel
(`modules_per_string=1`, `strings_per_inverter=M`).
* `NxM` total modules arranged in `M` strings of `N` modules each
(`modules_per_string=N`, `strings_per_inverter=M`).
The attributes should generally be things that don't change about
the system, such the type of module and the inverter. The instance
methods accept arguments for things that do change, such as
irradiance and temperature.
Parameters
----------
albedo : None or float, default None
The ground albedo. If ``None``, will attempt to use
``surface_type`` and ``irradiance.SURFACE_ALBEDOS``
to lookup albedo.
surface_type : None or string, default None
The ground surface type. See ``irradiance.SURFACE_ALBEDOS``
for valid values.
module : None or string, default None
The model name of the modules.
May be used to look up the module_parameters dictionary
via some other method.
module_parameters : None, dict or Series, default None
Module parameters as defined by the SAPM, CEC, or other.
modules_per_string: int or float, default 1
See system topology discussion above.
strings_per_inverter: int or float, default 1
See system topology discussion above.
inverter : None or string, default None
The model name of the inverters.
May be used to look up the inverter_parameters dictionary
via some other method.
inverter_parameters : None, dict or Series, default None
Inverter parameters as defined by the SAPM, CEC, or other.
racking_model : None or string, default 'freestanding'
Used for cell and module temperature calculations.
losses_parameters : None, dict or Series, default None
Losses parameters as defined by PVWatts or other.
name : None or string, default None
**kwargs
Arbitrary keyword arguments.
Included for compatibility, but not used.
"""
def __init__(self,
module=None, module_parameters=None,
temperature_model_parameters=None,
modules_per_string=1, strings_per_inverter=1,
inverter=None, inverter_parameters=None,
racking_model='freestanding',
losses_parameters=None, name=None, albedo=None,
surface_type=None, **kwargs):
self.name = name
self.module = module
if module_parameters is None:
self.module_parameters = {}
else:
self.module_parameters = module_parameters
if temperature_model_parameters is None:
self.temperature_model_parameters = {}
else:
self.temperature_model_parameters = temperature_model_parameters
self.modules_per_string = modules_per_string
self.strings_per_inverter = strings_per_inverter
self.inverter = inverter
if inverter_parameters is None:
self.inverter_parameters = {}
else:
self.inverter_parameters = inverter_parameters
if losses_parameters is None:
self.losses_parameters = {}
else:
self.losses_parameters = losses_parameters
self.racking_model = racking_model
self.surface_type = surface_type
if albedo is None:
self.albedo = pvlib.irradiance.SURFACE_ALBEDOS.get(
surface_type, 0.25)
else:
self.albedo = albedo
def __repr__(self):
attrs = ['name', 'module', 'inverter', 'racking_model']
return ('CPVSystem: \n ' + '\n '.join(
('{}: {}'.format(attr, getattr(self, attr)) for attr in attrs)))
[docs] def get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi,
dni_extra=None, airmass=None, model='haydavies',
**kwargs):
"""
Uses the :py:func:`irradiance.get_total_irradiance` function to
calculate the plane of array irradiance components on a Dual axis
tracker.
Parameters
----------
solar_zenith : float or Series.
Solar zenith angle.
solar_azimuth : float or Series.
Solar azimuth angle.
dni : float or Series
Direct Normal Irradiance
ghi : float or Series
Global horizontal irradiance
dhi : float or Series
Diffuse horizontal irradiance
dni_extra : None, float or Series, default None
Extraterrestrial direct normal irradiance
airmass : None, float or Series, default None
Airmass
model : String, default 'haydavies'
Irradiance model.
**kwargs
Passed to :func:`irradiance.total_irrad`.
Returns
-------
poa_irradiance : DataFrame
Column names are: ``total, beam, sky, ground``.
"""
# not needed for all models, but this is easier
if dni_extra is None:
dni_extra = pvlib.irradiance.get_extra_radiation(
solar_zenith.index)
if airmass is None:
airmass = pvlib.atmosphere.get_relative_airmass(solar_zenith)
return pvlib.irradiance.get_total_irradiance(90 - solar_zenith,
solar_azimuth,
solar_zenith, solar_azimuth,
dni, ghi, dhi,
dni_extra=dni_extra,
airmass=airmass,
model=model,
albedo=self.albedo,
**kwargs)
[docs] def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0):
"""
Uses :py:func:`pvsystem.pvsyst_celltemp` to calculate module
temperatures based on ``self.racking_model`` and the input parameters.
Parameters
----------
See pvsystem.pvsyst_celltemp for details
Returns
-------
See pvsystem.pvsyst_celltemp for details
"""
kwargs = _build_kwargs(['eta_m', 'alpha_absorption'],
self.module_parameters)
kwargs.update(_build_kwargs(['u_c', 'u_v'],
self.temperature_model_parameters))
return pvlib.temperature.pvsyst_cell(poa_global, temp_air, wind_speed,
**kwargs)
[docs] def get_am_util_factor(self, airmass, am_thld=None, am_uf_m_low=None, am_uf_m_high=None):
"""
Retrieves the utilization factor for airmass.
Parameters
----------
airmass : numeric
absolute airmass.
am_thld : numeric
limit between the two regression lines of the utilization factor.
am_uf_m_low : numeric
slope of the first regression line of the utilization factor
for airmass.
am_uf_m_high : numeric
slope of the second regression line of the utilization factor
for airmass.
Returns
-------
am_uf : numeric
the utilization factor for airmass.
"""
if am_thld is not None:
am_uf = get_simple_util_factor(x=airmass, thld=am_thld,
m_low=am_uf_m_low,
m_high=am_uf_m_high)
else:
am_uf = get_simple_util_factor(x=airmass, thld=self.module_parameters['am_thld'],
m_low=self.module_parameters['am_uf_m_low'] /
self.module_parameters['IscDNI_top'],
m_high=self.module_parameters['am_uf_m_high']/self.module_parameters['IscDNI_top'])
return am_uf
[docs] def get_tempair_util_factor(self, temp_air, ta_thld=None, ta_uf_m_low=None,
ta_uf_m_high=None):
"""
Retrieves the utilization factor for ambient temperature.
Parameters
----------
temp_air : numeric
Ambient dry bulb temperature in degrees C.
ta_thld : numeric
limit between the two regression lines of the utilization factor.
ta_uf_m_low : numeric
slope of the first regression line of the utilization factor
for ambient temperature.
ta_uf_m_high : numeric
slope of the second regression line of the utilization factor
for ambient temperature.
Returns
-------
ta_uf : numeric
the utilization factor for ambient temperature.
"""
if ta_thld is not None:
ta_uf = get_simple_util_factor(x=temp_air, thld=ta_thld,
m_low=ta_uf_m_low,
m_high=ta_uf_m_high)
else:
ta_uf = get_simple_util_factor(x=temp_air, thld=self.module_parameters['ta_thld'],
m_low=self.module_parameters['ta_uf_m_low'] /
self.module_parameters['IscDNI_top'],
m_high=self.module_parameters['ta_uf_m_high']/self.module_parameters['IscDNI_top'])
return ta_uf
[docs] def get_dni_util_factor(self, dni, dni_thld=None, dni_uf_m_low=None, dni_uf_m_high=None):
"""
Retrieves the utilization factor for DNI.
Parameters
----------
dni : numeric
Direct Normal Irradiance
dni_thld : numeric
limit between the two regression lines of the utilization factor.
dni_uf_m_low : numeric
slope of the first regression line of the utilization factor
for DNI.
dni_uf_m_low_uf_m_high : numeric
slope of the second regression line of the utilization factor
for DNI.
Returns
-------
dni_uf : numeric
the utilization factor for DNI.
"""
if dni_thld is not None:
dni_uf = get_simple_util_factor(x=dni, thld=dni_thld,
m_low=dni_uf_m_low,
m_high=dni_uf_m_high)
else:
dni_uf = get_simple_util_factor(x=dni, thld=self.module_parameters['dni_thld'],
m_low=self.module_parameters['dni_uf_m_low'] /
self.module_parameters['IscDNI_top'],
m_high=self.module_parameters['dni_uf_m_high']/self.module_parameters['IscDNI_top'])
return dni_uf
[docs] def get_global_utilization_factor(self, airmass_absolute, temp_air):
"""
Retrieves the global utilization factor (Air mass and Air temperature CPV effects)
Parameters
----------
airmass : numeric
absolute airmass.
temp_air : numeric
Ambient dry bulb temperature in degrees C.
Returns
-------
uf_global : numeric
the global utilization factor.
"""
uf_am = self.get_am_util_factor(airmass=airmass_absolute)
uf_ta = self.get_tempair_util_factor(temp_air=temp_air)
uf_global = (uf_am * self.module_parameters['weight_am'] +
uf_ta * self.module_parameters['weight_temp'])
return uf_global
[docs]class StaticCPVSystem(CPVSystem):
"""
The StaticCPVSystem class defines a set of Static CPV system attributes and
modeling functions. This class describes the collection and interactions of
Static CPV system components installed on a Fixed Panel or a Single Axis tracker.
It inheritates from CPVSystem, modifying those methods that
are specific for a Static CPV system following the PVSyst implementation.
The class supports basic system topologies consisting of:
* `N` total modules arranged in series
(`modules_per_string=N`, `strings_per_inverter=1`).
* `M` total modules arranged in parallel
(`modules_per_string=1`, `strings_per_inverter=M`).
* `NxM` total modules arranged in `M` strings of `N` modules each
(`modules_per_string=N`, `strings_per_inverter=M`).
The attributes should generally be things that don't change about
the system, such the type of module and the inverter. The instance
methods accept arguments for things that do change, such as
irradiance and temperature.
Parameters
----------
surface_tilt: float or array-like, default 0
Surface tilt angles in decimal degrees.
The tilt angle is defined as degrees from horizontal
(e.g. surface facing up = 0, surface facing horizon = 90)
surface_azimuth: float or array-like, default 180
Azimuth angle of the module surface.
North=0, East=90, South=180, West=270.
module : None or string, default None
The model name of the modules.
May be used to look up the module_parameters dictionary
via some other method.
module_parameters : None, dict or Series, default None
Module parameters as defined by the SAPM, CEC, or other.
modules_per_string: int or float, default 1
See system topology discussion above.
strings_per_inverter: int or float, default 1
See system topology discussion above.
inverter : None or string, default None
The model name of the inverters.
May be used to look up the inverter_parameters dictionary
via some other method.
inverter_parameters : None, dict or Series, default None
Inverter parameters as defined by the SAPM, CEC, or other.
racking_model : None or string, default 'freestanding'
Used for cell and module temperature calculations.
losses_parameters : None, dict or Series, default None
Losses parameters as defined by PVWatts or other.
in_singleaxis_tracker : None or bool, defult False
Conttros if the system is mounted in a NS single axis tracker
If true, it affects get_aoi() and get_irradiance()
name : None or string, default None
**kwargs
Arbitrary keyword arguments.
Included for compatibility, but not used.
"""
def __init__(self,
surface_tilt=0, surface_azimuth=180,
module=None, module_parameters=None,
temperature_model_parameters=None,
in_singleaxis_tracker=False,
b=None,
parameters_tracker=None,
modules_per_string=1, strings_per_inverter=1,
inverter=None, inverter_parameters=None,
racking_model='freestanding',
losses_parameters=None, name=None, **kwargs):
self.surface_tilt = surface_tilt
self.surface_azimuth = surface_azimuth
self.in_singleaxis_tracker = in_singleaxis_tracker
if parameters_tracker is None:
self.parameters_tracker = {}
else:
self.parameters_tracker = parameters_tracker
super().__init__(module, module_parameters, temperature_model_parameters, modules_per_string,
strings_per_inverter, inverter, inverter_parameters,
racking_model, losses_parameters, name, **kwargs)
def __repr__(self):
attrs = ['name', 'module', 'inverter', 'racking_model']
return ('StaticCPVSystem: \n ' + '\n '.join(
('{}: {}'.format(attr, getattr(self, attr)) for attr in attrs)))
[docs] def get_aoi(self, solar_zenith, solar_azimuth):
"""Get the angle of incidence on the system.
Parameters
----------
solar_zenith : float or Series.
Solar zenith angle.
solar_azimuth : float or Series.
Solar azimuth angle.
Returns
-------
aoi : Series
The angle of incidence
"""
if self.in_singleaxis_tracker:
aoi = pvlib.tracking.singleaxis(
solar_zenith, solar_azimuth, **self.parameters_tracker).aoi
else:
aoi = pvlib.irradiance.aoi(self.surface_tilt, self.surface_azimuth,
solar_zenith, solar_azimuth)
return aoi
[docs] def get_iam(self, aoi, iam_model):
"""
Determine the incidence angle modifier using the method specified by
``iam_model``.
Parameters for the selected IAM model are expected to be in
``StaticCPVSystem.module_parameters``.
Parameters
----------
aoi : numeric
The angle of incidence in degrees.
aoi_model : string
The IAM model to be used. Valid strings are 'ashrae' and 'interp'.
Returns
-------
iam : numeric
The AOI modifier.
Raises
------
AttributeError if `b` for 'ashrae' or `theta_ref` or `iam_red` for 'interp' are missing
ValueError if `iam_model` is not a valid model name.
"""
if iam_model == 'ashrae':
if self.module_parameters['b'] is None:
raise AttributeError(
'Missing IAM parameter (ASHRAE:b) in "module_parameters"')
else:
iam = pvlib.iam.ashrae(aoi, b=self.module_parameters['b'])
elif iam_model == 'interp':
if self.module_parameters['theta_ref'] is None or self.module_parameters['iam_ref'] is None:
raise AttributeError(
'Missing IAM parameter (interp:theta_ref or iam_red) in "module_parameters"')
else:
iam = pvlib.iam.interp(
aoi, self.module_parameters['theta_ref'], self.module_parameters['iam_ref'], method='linear')
else:
raise ValueError(iam_model + ' is not a valid IAM model')
return iam
[docs] def get_irradiance(self, solar_zenith, solar_azimuth, dni):
"""
Uses the :py:func:`pvlib.irradiance.beam_component` function to
calculate the beam component of the plane of array irradiance.
Parameters
----------
solar_zenith : float or Series.
Solar zenith angle.
solar_azimuth : float or Series.
Solar azimuth angle.
dni : float or Series
Direct Normal Irradiance
Returns
-------
dii : numeric
Direct (on the) Inclinated (plane) Irradiance
Beam component of the plane of array irradiance
"""
if self.in_singleaxis_tracker:
tracking_info = pvlib.tracking.singleaxis(
solar_zenith, solar_azimuth, **self.parameters_tracker)
surface_tilt = tracking_info.surface_tilt
surface_azimuth = tracking_info.surface_azimuth
else:
surface_tilt = self.surface_tilt
surface_azimuth = self.surface_azimuth
dii = pvlib.irradiance.beam_component(
surface_tilt,
surface_azimuth,
solar_zenith,
solar_azimuth,
dni)
return dii
[docs] def get_effective_irradiance(self, solar_zenith, solar_azimuth, dni):
"""
Calculates the effective irradiance (taking into account the IAM)
Parameters
----------
solar_zenith : float or Series
Solar zenith angle.
solar_azimuth : float or Series
Solar azimuth angle.
dni : float or Series
Direct Normal Irradiance
Returns
-------
dii_effective : float or Series
Effective Direct (on the) Inclinated (plane) Irradiance
Beam component of the plane of array irradiance plus the effect of AOI
"""
dii = self.get_irradiance(solar_zenith, solar_azimuth, dni)
aoi = self.get_aoi(solar_zenith, solar_azimuth)
dii_effective = dii * \
self.get_iam(aoi, iam_model=self.module_parameters['iam_model'])
return dii_effective
[docs]class StaticFlatPlateSystem(pvlib.pvsystem.PVSystem):
"""
The StaticFlatPlateSystem class defines a set of Static FlatPlate system attributes and
modeling functions. This class describes the collection and interactions of
Static FlatPlate system components installed on a Fixed Panel or a Single Axis tracker.
It inheritates from PVSystem, modifying those methods that
are specific for a Static FlatPlate system following the PVSyst implementation.
The class supports basic system topologies consisting of:
* `N` total modules arranged in series
(`modules_per_string=N`, `strings_per_inverter=1`).
* `M` total modules arranged in parallel
(`modules_per_string=1`, `strings_per_inverter=M`).
* `NxM` total modules arranged in `M` strings of `N` modules each
(`modules_per_string=N`, `strings_per_inverter=M`).
The attributes should generally be things that don't change about
the system, such the type of module and the inverter. The instance
methods accept arguments for things that do change, such as
irradiance and temperature.
Parameters
----------
surface_tilt: float or array-like, default 0
Surface tilt angles in decimal degrees.
The tilt angle is defined as degrees from horizontal
(e.g. surface facing up = 0, surface facing horizon = 90)
surface_azimuth: float or array-like, default 180
Azimuth angle of the module surface.
North=0, East=90, South=180, West=270.
module : None or string, default None
The model name of the modules.
May be used to look up the module_parameters dictionary
via some other method.
module_parameters : None, dict or Series, default None
Module parameters as defined by the SAPM, CEC, or other.
modules_per_string: int or float, default 1
See system topology discussion above.
strings_per_inverter: int or float, default 1
See system topology discussion above.
inverter : None or string, default None
The model name of the inverters.
May be used to look up the inverter_parameters dictionary
via some other method.
inverter_parameters : None, dict or Series, default None
Inverter parameters as defined by the SAPM, CEC, or other.
racking_model : None or string, default 'freestanding'
Used for cell and module temperature calculations.
losses_parameters : None, dict or Series, default None
Losses parameters as defined by PVWatts or other.
in_singleaxis_tracker : None or bool, defult False
Conttros if the system is mounted in a NS single axis tracker
If true, it affects get_aoi() and get_irradiance()
name : None or string, default None
**kwargs
Arbitrary keyword arguments.
Included for compatibility, but not used.
"""
def __init__(self,
surface_tilt=0, surface_azimuth=180,
module=None, module_parameters=None,
temperature_model_parameters=None,
modules_per_string=1,
in_singleaxis_tracker=False,
parameters_tracker=None,
strings_per_inverter=1,
inverter=None, inverter_parameters=None,
racking_model='freestanding',
losses_parameters=None, name=None, **kwargs):
self.in_singleaxis_tracker = in_singleaxis_tracker
if parameters_tracker is None:
self.parameters_tracker = {}
else:
self.parameters_tracker = parameters_tracker
super().__init__(surface_tilt=surface_tilt, surface_azimuth=surface_azimuth,
albedo=None, surface_type=None,
module=module, module_type='glass_polymer',
module_parameters=module_parameters,
temperature_model_parameters=temperature_model_parameters,
modules_per_string=modules_per_string, strings_per_inverter=strings_per_inverter,
inverter=inverter, inverter_parameters=inverter_parameters,
racking_model=racking_model, losses_parameters=losses_parameters, name=name,
**kwargs)
def __repr__(self):
attrs = ['name', 'module', 'inverter', 'racking_model']
return ('StaticFlatPlateSystem: \n ' + '\n '.join(
('{}: {}'.format(attr, getattr(self, attr)) for attr in attrs)))
[docs] def get_aoi(self, solar_zenith, solar_azimuth):
"""Get the angle of incidence on the system.
Parameters
----------
solar_zenith : float or Series.
Solar zenith angle.
solar_azimuth : float or Series.
Solar azimuth angle.
Returns
-------
aoi : Series
The angle of incidence
"""
if self.in_singleaxis_tracker:
aoi = pvlib.tracking.singleaxis(
solar_zenith, solar_azimuth, **self.parameters_tracker).aoi
else:
aoi = pvlib.irradiance.aoi(self.surface_tilt, self.surface_azimuth,
solar_zenith, solar_azimuth)
return aoi
[docs] def get_iam(self, aoi):
"""
Determines the angle of incidence modifier for the dii part
Parameters
----------
aoi : numeric
The angle of incidence in degrees.
Returns
-------
iam : numeric
The AOI modifier.
"""
if self.module_parameters['theta_ref'] is None or self.module_parameters['iam_ref'] is None:
raise AttributeError(
'Missing IAM parameter (interp:theta_ref or iam_ref) in "module_parameters"')
else:
iam = pvlib.iam.interp(
aoi, self.module_parameters['theta_ref'], self.module_parameters['iam_ref'], method='linear')
return iam
[docs] def get_spillage_iam(self, aoi):
"""
Determines the spillage using an specific IAM that accounts for the spot size.
Since the spot increases, IAM values shoud be > 1
Parameters
----------
aoi : numeric
The angle of incidence in degrees.
Returns
-------
iam : numeric
The AOI modifier.
"""
if self.module_parameters['theta_ref_spillage'] is None or self.module_parameters['iam_ref_spillage'] is None:
raise AttributeError(
'Missing IAM parameter (interp:theta_ref_spillage or iam_ref_spillage) in "module_parameters"')
else:
spillage_iam = pvlib.iam.interp(
aoi, self.module_parameters['theta_ref_spillage'], self.module_parameters['iam_ref_spillage'], method='linear')
return spillage_iam
[docs] def get_effective_irradiance(self, solar_zenith, solar_azimuth, dni=None,
ghi=None, dhi=None, dii=None, gii=None, dni_extra=None,
airmass=None, model='haydavies', spillage=0, **kwargs):
"""
Calculates the plane of array irradiance of a Static Flat Plate system
from dii and gii. If any is missing then is calculated from ghi, dhi and dhi
using corresponding pvlib function.
Internal `aoi_limit` parameter from `module_parameters` sets the limit
of tracking of the Static CPV system and therefore when the dii irradiance
is added to the poa_diffuse.
Spillage factor accounts for the dii fraction that is allowed to pass into the system
See https://doi.org/10.1002/pip.3387 for details
Parameters
----------
solar_zenith : float or Series.
Solar zenith angle.
solar_azimuth : float or Series.
Solar azimuth angle.
dni : numeric
Direct Normal Irradiance
ghi : numeric
Global horizontal irradiance
dhi : numeric
Diffuse horizontal irradiance
dii : numeric
Direct (on the) Inclinated (plane) Irradiance
gii : numeric
Global (on the) Inclinated (plane) Irradiance
dni_extra : None or numeric, default None
Extraterrestrial direct normal irradiance
airmass : None or numeric, default None
Airmass
model : String, default 'isotropic'
Irradiance model.
spillage : float
Percentage of dii allowed to pass into the system
Returns
-------
poa_flatplate_static : numeric
Plane of Array Irradiance
"""
# not needed for all models, but this is easier
if dni_extra is None:
dni_extra = pvlib.irradiance.get_extra_radiation(
solar_zenith.index)
if airmass is None:
airmass = pvlib.atmosphere.get_relative_airmass(solar_zenith)
if self.in_singleaxis_tracker:
tracking_info = pvlib.tracking.singleaxis(
solar_zenith, solar_azimuth, **self.parameters_tracker)
surface_tilt = tracking_info.surface_tilt
surface_azimuth = tracking_info.surface_azimuth
else:
surface_tilt = self.surface_tilt
surface_azimuth = self.surface_azimuth
if dii is None:
dii = pvlib.irradiance.beam_component(
surface_tilt,
surface_azimuth,
solar_zenith,
solar_azimuth,
dni)
if gii is None:
irr = pvlib.irradiance.get_total_irradiance(surface_tilt,
surface_azimuth,
solar_zenith, solar_azimuth,
dni, ghi, dhi,
dni_extra=dni_extra,
airmass=airmass,
model=model,
albedo=self.albedo,
**kwargs)
poa_diffuse = irr['poa_diffuse']
gii = irr['poa_global']
else:
poa_diffuse = gii - dii
aoi = self.get_aoi(solar_zenith, solar_azimuth)
dii_effective = dii * self.get_iam(aoi)
gii_effective = dii_effective + poa_diffuse
spillage_effective = spillage * self.get_spillage_iam(aoi)
poa_diffuse_dii_effective_spillage = poa_diffuse + (dii_effective * spillage_effective)
if 'aoi_limit' in self.module_parameters:
aoi_limit = self.module_parameters['aoi_limit']
else:
raise AttributeError(
'Missing "aoi_limit" parameter in "module_parameters"')
poa_flatplate_static_effective = pd.concat(
[poa_diffuse_dii_effective_spillage[aoi < aoi_limit], gii_effective[aoi > aoi_limit]]).sort_index()
return poa_flatplate_static_effective
[docs] def pvsyst_celltemp(self, poa_flatplate_static, temp_air, wind_speed=1.0):
"""
Uses :py:func:`pvsystem.pvsyst_celltemp` to calculate module
temperatures based on ``self.racking_model`` and the input parameters.
Parameters
----------
See pvsystem.pvsyst_celltemp for details
Returns
-------
See pvsystem.pvsyst_celltemp for details
"""
kwargs = _build_kwargs(['eta_m', 'alpha_absorption'],
self.module_parameters)
kwargs.update(_build_kwargs(['u_c', 'u_v'],
self.temperature_model_parameters))
return pvlib.temperature.pvsyst_cell(poa_flatplate_static, temp_air, wind_speed,
**kwargs)
[docs]class StaticHybridSystem():
"""
The StaticHybridSystem class defines a set of Static Hybrid system attributes and
modeling functions. This class describes the collection and interactions of
Static CPV system components installed on a Fixed Panel or a Single Axis tracker.
It is the composition of two subsystems: StaticCPVSystem and StaticFlatPlateSystem
The class supports basic system topologies consisting of:
* `N` total modules arranged in series
(`modules_per_string=N`, `strings_per_inverter=1`).
* `M` total modules arranged in parallel
(`modules_per_string=1`, `strings_per_inverter=M`).
* `NxM` total modules arranged in `M` strings of `N` modules each
(`modules_per_string=N`, `strings_per_inverter=M`).
The attributes should generally be things that don't change about
the system, such the type of module and the inverter. The instance
methods accept arguments for things that do change, such as
irradiance and temperature.
See https://doi.org/10.1002/pip.3387
Parameters
----------
surface_tilt: float or array-like, default 0
Surface tilt angles in decimal degrees.
The tilt angle is defined as degrees from horizontal
(e.g. surface facing up = 0, surface facing horizon = 90)
surface_azimuth: float or array-like, default 180
Azimuth angle of the module surface.
North=0, East=90, South=180, West=270.
module : None or string, default None
The model name of the modules.
May be used to look up the module_parameters dictionary
via some other method.
module_parameters : None, dict or Series, default None
Module parameters as defined by the SAPM, CEC, or other.
modules_per_string: int or float, default 1
See system topology discussion above.
strings_per_inverter: int or float, default 1
See system topology discussion above.
inverter : None or string, default None
The model name of the inverters.
May be used to look up the inverter_parameters dictionary
via some other method.
inverter_parameters : None, dict or Series, default None
Inverter parameters as defined by the SAPM, CEC, or other.
racking_model : None or string, default 'freestanding'
Used for cell and module temperature calculations.
losses_parameters : None, dict or Series, default None
Losses parameters as defined by PVWatts or other.
in_singleaxis_tracker : None or bool, defult False
Conttros if the system is mounted in a NS single axis tracker
If true, it affects get_aoi() and get_irradiance()
name : None or string, default None
**kwargs
Arbitrary keyword arguments.
Included for compatibility, but not used.
"""
def __init__(self,
surface_tilt=30,
surface_azimuth=180,
module_cpv=None,
module_parameters_cpv=None,
temperature_model_parameters_cpv=None,
module_flatplate=None,
module_parameters_flatplate=None,
temperature_model_parameters_flatplate=None,
in_singleaxis_tracker=False,
parameters_tracker=None,
modules_per_string=1,
strings_per_inverter=1,
inverter=None,
inverter_parameters=None,
racking_model="insulated",
losses_parameters=None,
name=None,
**kwargs):
self.name = name
self.surface_tilt = surface_tilt
self.surface_azimuth = surface_azimuth
# could tie these together with @property
self.module_cpv = module_cpv
self.module_flatplate = module_flatplate
self.in_singleaxis_tracker = in_singleaxis_tracker
if module_parameters_cpv is None:
self.module_parameters_cpv = {}
else:
self.module_parameters_cpv = module_parameters_cpv
if module_parameters_flatplate is None:
self.module_parameters_flatplate = {}
else:
self.module_parameters_flatplate = module_parameters_flatplate
if parameters_tracker is None:
self.parameters_tracker = {}
else:
self.parameters_tracker = parameters_tracker
self.modules_per_string = modules_per_string
self.strings_per_inverter = strings_per_inverter
self.inverter = inverter
if inverter_parameters is None:
self.inverter_parameters = {}
else:
self.inverter_parameters = inverter_parameters
if losses_parameters is None:
self.losses_parameters = {}
else:
self.losses_parameters = losses_parameters
self.racking_model = racking_model
self.static_cpv_sys = StaticCPVSystem(
surface_tilt=surface_tilt,
surface_azimuth=surface_azimuth,
module=module_cpv,
module_parameters=module_parameters_cpv,
temperature_model_parameters=temperature_model_parameters_cpv,
in_singleaxis_tracker=in_singleaxis_tracker,
modules_per_string=modules_per_string,
strings_per_inverter=strings_per_inverter,
inverter=inverter,
inverter_parameters=inverter_parameters,
racking_model=racking_model,
losses_parameters=losses_parameters,
name=name,
)
self.static_flatplate_sys = StaticFlatPlateSystem(
surface_tilt=surface_tilt,
surface_azimuth=surface_azimuth,
module=module_flatplate,
module_parameters=module_parameters_flatplate,
temperature_model_parameters=temperature_model_parameters_flatplate,
in_singleaxis_tracker=in_singleaxis_tracker,
modules_per_string=modules_per_string,
strings_per_inverter=strings_per_inverter,
inverter=inverter,
inverter_parameters=inverter_parameters,
racking_model=racking_model,
losses_parameters=losses_parameters,
name=name,
)
def __repr__(self):
attrs = ['name', 'module_cpv', 'module_flatplate',
'inverter', 'racking_model']
return ('StaticHybridSystem: \n ' + '\n '.join(
('{}: {}'.format(attr, getattr(self, attr)) for attr in attrs)))
[docs] def get_effective_irradiance(self, solar_zenith, solar_azimuth, dni,
ghi=None, dhi=None, dii=None, gii=None, dni_extra=None,
airmass=None, model='haydavies', spillage=0, **kwargs):
"""
Calculates the effective irradiance (taking into account the IAM)
TO BE VALIDATED
Parameters
----------
solar_zenith : float or Series.
Solar zenith angle.
solar_azimuth : float or Series.
Solar azimuth angle.
dni : numeric
Direct Normal Irradiance
ghi : numeric
Global horizontal irradiance
dhi : numeric
Diffuse horizontal irradiance
dii : numeric
Direct (on the) Inclinated (plane) Irradiance
gii : numeric
Global (on the) Inclinated (plane) Irradiance
dni_extra : None or numeric, default None
Extraterrestrial direct normal irradiance
airmass : None or numeric, default None
Airmass
model : String, default 'isotropic'
Irradiance model.
spillage : float
Percentage of dii allowed to pass into the system
Returns
-------
dii_effective : float or Series
Effective Direct (on the) Inclinated (plane) Irradiance [StaticCPVSystem]
Beam component of the plane of array irradiance plus the effect of AOI
poa_flatplate_static_effective : float or Series
Effective Direct (on the) Inclinated (plane) Irradiance [StaticFlatPlateSystem]
Plane of array irradiance plus the effect of AOI
"""
dii_effective = self.static_cpv_sys.get_effective_irradiance(
solar_zenith, solar_azimuth, dni)
aoi = self.static_flatplate_sys.get_aoi(solar_zenith, solar_azimuth)
poa_flatplate_static_effective = self.static_flatplate_sys.get_effective_irradiance(solar_zenith,
solar_azimuth,
aoi=aoi,
dii=dii,
gii=gii,
ghi=ghi,
dhi=dhi,
dni=dni,
model=model,
spillage=spillage,
**kwargs
)
return dii_effective, poa_flatplate_static_effective
[docs] def pvsyst_celltemp(self, dii, poa_flatplate_static, temp_air, wind_speed=1.0):
"""
Uses :py:func:`pvsystem.pvsyst_celltemp` to calculate module
temperatures based on ``self.racking_model`` and the input parameters.
Parameters
----------
dii : numeric
Direct (on the) Inclinated (plane) Irradiance [StaticCPVSystem]
poa_flatplate_static : numeric
Plane of Array Irradiance [StaticFlatPlateSystem]
See pvsystem.pvsyst_celltemp for details
Returns
-------
See pvsystem.pvsyst_celltemp for details
"""
celltemp_cpv = self.static_cpv_sys.pvsyst_celltemp(
dii, temp_air, wind_speed)
celltemp_flatplate = self.static_flatplate_sys.pvsyst_celltemp(
poa_flatplate_static, temp_air, wind_speed)
return celltemp_cpv, celltemp_flatplate
[docs] def calcparams_pvsyst(self, dii, poa_flatplate_static, temp_cell_cpv, temp_cell_flatplate):
"""
Use the :py:func:`calcparams_pvsyst` function, the input
parameters and ``self.module_parameters`` to calculate the
module currents and resistances.
Parameters
----------
dii : numeric
Direct (on the) Inclinated (plane) Irradiance [StaticCPVSystem]
poa_flatplate_static : numeric
Plane of Array Irradiance [StaticFlatPlateSystem]
temp_cell : float or Series
The average cell temperature of cells within a module in C.
Returns
-------
See pvsystem.calcparams_pvsyst for details
"""
diode_parameters_cpv = self.static_cpv_sys.calcparams_pvsyst(
dii, temp_cell_cpv)
diode_parameters_flatplate = self.static_flatplate_sys.calcparams_pvsyst(
poa_flatplate_static, temp_cell_flatplate)
return diode_parameters_cpv, diode_parameters_flatplate
[docs] def singlediode(self, diode_parameters_cpv, diode_parameters_flatplate, ivcurve_pnts=None):
"""Wrapper around the :py:func:`singlediode` function.
Parameters
----------
See pvsystem.singlediode for details [StaticCPVSystem & StaticFlatPlateSystem()]
Returns
-------
See pvsystem.singlediode for details
"""
dc_cpv = self.static_cpv_sys.singlediode(*diode_parameters_cpv,
ivcurve_pnts=ivcurve_pnts)
dc_flatplate = self.static_flatplate_sys.singlediode(*diode_parameters_flatplate,
ivcurve_pnts=ivcurve_pnts)
return dc_cpv, dc_flatplate
[docs] def get_global_utilization_factor_cpv(self, airmass_absolute, temp_air):
"""
Retrieves the global utilization factor (Air mass and Air temperature CPV effects)
for the StaticCPVSystem subsystem
Parameters
----------
airmass : numeric
absolute airmass.
temp_air : numeric
Ambient dry bulb temperature in degrees C.
Returns
-------
uf_global : numeric
the global utilization factor.
"""
uf_am = self.static_cpv_sys.get_am_util_factor(
airmass=airmass_absolute)
uf_ta = self.static_cpv_sys.get_tempair_util_factor(temp_air=temp_air)
uf_global = (uf_am * self.static_cpv_sys.module_parameters['weight_am'] +
uf_ta * self.static_cpv_sys.module_parameters['weight_temp'])
return uf_global
[docs]def get_simple_util_factor(x, thld, m_low, m_high):
"""
Retrieves the utilization factor for a variable.
Parameters
----------
x : numeric / pd.Series
variable value(s) for the utilization factor calc.
thld : numeric
limit between the two regression lines of the utilization factor.
m_low : numeric
slope of the first regression line of the utilization factor.
m_high : numeric
slope of the second regression line of the utilization factor.
Returns
-------
single_uf : numeric
utilization factor for the x variable.
"""
simple_uf = pd.Series(dtype='float64')
if isinstance(x, (int, float)):
simple_uf = 1 + (x - thld) * m_low
else:
def f(value):
if value <= thld:
s = 1 + (value - thld) * m_low
else:
s = 1 + (value - thld) * m_high
return s
simple_uf = x.apply(f)
return simple_uf