Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
#
# In this example we look at a single point in time and consider a full range
# of possible GHI and POA global values as shown in figures 3 and 4 of [1]_.
# Then we use :py:meth:`pvlib.irradiance.ghi_from_poa_driesse_2023` to estimate
# Then we use :py:meth:`pvlib.irradiance.ghi_from_poa_driesse_2024` to estimate
# the original GHI from POA global.
#
# References
Expand All @@ -45,7 +45,7 @@

from pvlib.irradiance import (erbs_driesse,
get_total_irradiance,
ghi_from_poa_driesse_2023,
ghi_from_poa_driesse_2024,
)

matplotlib.rcParams['axes.grid'] = True
Expand Down Expand Up @@ -92,7 +92,7 @@

poa_test = 200

ghi_hat = ghi_from_poa_driesse_2023(surface_tilt, surface_azimuth,
ghi_hat = ghi_from_poa_driesse_2024(surface_tilt, surface_azimuth,
solar_zenith, solar_azimuth,
poa_test,
dni_extra,
Expand Down Expand Up @@ -156,7 +156,7 @@
# out, other times not.
#

result = ghi_from_poa_driesse_2023(surface_tilt, surface_azimuth,
result = ghi_from_poa_driesse_2024(surface_tilt, surface_azimuth,
solar_zenith, solar_azimuth,
poa_global,
dni_extra,
Expand Down
6 changes: 3 additions & 3 deletions docs/examples/irradiance-transposition/plot_rtranpose_year.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
# Recovering GHI from POA irradiance is termed "reverse transposition."
#
# In this example we start with a TMY file and calculate POA global irradiance.
# Then we use :py:meth:`pvlib.irradiance.ghi_from_poa_driesse_2023` to estimate
# Then we use :py:meth:`pvlib.irradiance.ghi_from_poa_driesse_2024` to estimate
# the original GHI from POA global. Details of the method found in [1]_.
#
# Another method for reverse tranposition called GTI-DIRINT is also
Expand Down Expand Up @@ -49,7 +49,7 @@
from pvlib import iotools, location
from pvlib.irradiance import (get_extra_radiation,
get_total_irradiance,
ghi_from_poa_driesse_2023,
ghi_from_poa_driesse_2024,
aoi,
)

Expand Down Expand Up @@ -114,7 +114,7 @@

start = time.process_time()

df['ghi_rev'] = ghi_from_poa_driesse_2023(TILT, ORIENT,
df['ghi_rev'] = ghi_from_poa_driesse_2024(TILT, ORIENT,
solpos.apparent_zenith,
solpos.azimuth,
df.poa_global,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ Reverse transposition models
.. autosummary::
:toctree: ../generated/

irradiance.ghi_from_poa_driesse_2023
irradiance.ghi_from_poa_driesse_2024
irradiance.gti_dirint
26 changes: 19 additions & 7 deletions docs/sphinx/source/whatsnew/v0.15.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,17 @@ Breaking Changes

Deprecations
~~~~~~~~~~~~
* Rename :py:func:`!pvlib.irradiance.ghi_from_poa_driesse_2023` to
:py:func:`~pvlib.irradiance.ghi_from_poa_driesse_2024`. The year now reflects
the publication date. The old name will be removed in v0.17.0.
(:issue:`2774`, :pull:`2777`)


Bug fixes
~~~~~~~~~
* Added test coverage for :py:func:`pvlib.irradiance.dirint` with
``np.array`` and ``pd.Series`` inputs.
(:issue:`2751`, :pull:`2752`)
* Corrects a bug in :py:func:`pvlib.temperature.fuentes`. If inputs were
data type integer, users can expect modeled cell temperature values to
increase slightly.
Expand All @@ -26,16 +33,18 @@ Bug fixes
represent the end of the averaging interval, consistent with ERA5
conventions. (:issue:`2772`, :pull:`2773`)

* :py:func:`pvlib.iotools.read_nsrdb_psm4` now parses the file header with the
:py:mod:`csv` module instead of a naive ``str.split(',')``, so quoted column
names containing commas (e.g. the material names in spectral-on-demand files)
are no longer split into spurious columns. (:issue:`2736`, :pull:`2771`)

Enhancements
~~~~~~~~~~~~
* Add the ``front_side_fraction`` parameter to
:py:func:`pvlib.snow.loss_townsend` to support Townsend snow-loss
workflows for bifacial systems. (:issue:`2755`, :pull:`2756`)

* Added mapping of the parameter ``"albedo"`` in
:py:func:`~pvlib.iotools.get_nasa_power` when ``map_variables=True``
(:pull:`2753`)
* Add the following parameters to :py:func:`~pvlib.iotools.get_nasa_power`
when ``map_variables=True``: ``temp_dew``, ``precipitable_water``,
``relative_humidity``, ``ghi_extra``, ``dhi_clear``, ``longwave_down``,
and ``albedo``.
(:issue:`2731`, :pull:`2753`, :pull:`2762`)


Documentation
Expand Down Expand Up @@ -67,7 +76,10 @@ Maintenance
Contributors
~~~~~~~~~~~~
* :ghuser:`Omesh37`
* :ghuser:`gaoflow`
* Cliff Hansen (:ghuser:`cwhanse`)
* :ghuser:`shethkajal7`
* Arthur Onno (:ghuser:`ArthurOnnoTerabase`)
* Adam R. Jensen (:ghuser:`AdamRJensen`)
* Karl Hill (:ghuser:`karlhillx`)
* Rajiv Daxini (:ghuser:`RDaxini`)
22 changes: 21 additions & 1 deletion pvlib/iotools/nasa_power.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,19 @@
'ALLSKY_SFC_SW_DWN': 'ghi',
'ALLSKY_SFC_SW_DIFF': 'dhi',
'ALLSKY_SFC_SW_DNI': 'dni',
'ALLSKY_SRF_ALB': 'albedo',
'ALLSKY_SFC_LW_DWN': 'longwave_down',
'CLRSKY_SFC_SW_DIFF': 'dhi_clear',
'CLRSKY_SFC_SW_DNI': 'dni_clear',
'CLRSKY_SFC_SW_DWN': 'ghi_clear',
'PS': 'pressure',
'RH2M': 'relative_humidity',
'T2M': 'temp_air',
'T2MDEW': 'temp_dew',
'TQV': 'precipitable_water',
'TOA_SW_DWN': 'ghi_extra',
'WS2M': 'wind_speed_2m',
'WS10M': 'wind_speed',
'ALLSKY_SRF_ALB': 'albedo',
}


Expand Down Expand Up @@ -82,6 +90,12 @@ def get_nasa_power(latitude, longitude, start, end,
requests.HTTPError
Raises an error when an incorrect request is made.

Notes
-----
When ``map_variables=True`` the following unit conversions are applied:
pressure is converted from kPa to Pa, and precipitable water
is converted from kg/m² (mm) to cm.

Returns
-------
data : pd.DataFrame
Expand Down Expand Up @@ -150,5 +164,11 @@ def get_nasa_power(latitude, longitude, start, end,
# Rename according to pvlib convention
if map_variables:
df = df.rename(columns=VARIABLE_MAP)
# PS is returned in kPa; convert to Pa for pvlib compatibility.
if 'pressure' in df.columns:
df['pressure'] = df['pressure'] * 1000
# TQV is returned in kg/m^2 (=mm); convert to cm for compatibility
if 'precipitable_water' in df.columns:
df['precipitable_water'] = df['precipitable_water'] / 10

return df, meta
12 changes: 9 additions & 3 deletions pvlib/iotools/psm4.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-GOES-full-disc-v4-0-0-download/
"""

import csv
import io
from urllib.parse import urljoin
import requests
Expand Down Expand Up @@ -723,11 +724,16 @@ def read_nsrdb_psm4(filename, map_variables=True):
<https://web.archive.org/web/20170207203107/https://sam.nrel.gov/sites/default/files/content/documents/pdf/wfcsv.pdf>`_
"""
with tools._file_context_manager(filename) as fbuf:
# The first 3 header lines are parsed with the csv module rather than a
# naive str.split(',') so that quoted fields containing commas are kept
# intact. Spectral-on-demand files, for instance, have column names
# like '"GaAs (Bauhuis et al., 2009)"' whose embedded commas would
# otherwise be split into spurious columns (see GH #2736).
# The first 2 lines of the response are headers with metadata
metadata_fields = fbuf.readline().split(',')
metadata_values = fbuf.readline().split(',')
metadata_fields = next(csv.reader([fbuf.readline()]))
metadata_values = next(csv.reader([fbuf.readline()]))
# get the column names so we can set the dtypes
columns = fbuf.readline().split(',')
columns = next(csv.reader([fbuf.readline()]))
columns[-1] = columns[-1].strip() # strip trailing newline
# Since the header has so many columns, excel saves blank cols in the
# data below the header lines.
Expand Down
97 changes: 73 additions & 24 deletions pvlib/irradiance.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from pvlib import atmosphere, solarposition, tools
import pvlib # used to avoid dni name collision in complete_irradiance

from pvlib._deprecation import pvlibDeprecationWarning
from pvlib._deprecation import pvlibDeprecationWarning, deprecated
import warnings


Expand Down Expand Up @@ -132,7 +132,9 @@ def _handle_extra_radiation_types(datetime_or_doy, epoch_year):
# a better way to do it.
if isinstance(datetime_or_doy, pd.DatetimeIndex):
to_doy = tools._pandas_to_doy # won't be evaluated unless necessary
def to_datetimeindex(x): return x # noqa: E306

def to_datetimeindex(x):
return x # noqa: E306
to_output = partial(pd.Series, index=datetime_or_doy)
elif isinstance(datetime_or_doy, pd.Timestamp):
to_doy = tools._pandas_to_doy
Expand All @@ -146,12 +148,14 @@ def to_datetimeindex(x): return x # noqa: E306
tools._datetimelike_scalar_to_datetimeindex
to_output = tools._scalar_out
elif np.isscalar(datetime_or_doy): # ints and floats of various types
def to_doy(x): return x # noqa: E306
def to_doy(x):
return x # noqa: E306
to_datetimeindex = partial(tools._doy_to_datetimeindex,
epoch_year=epoch_year)
to_output = tools._scalar_out
else: # assume that we have an array-like object of doy
def to_doy(x): return x # noqa: E306
def to_doy(x):
return x # noqa: E306
to_datetimeindex = partial(tools._doy_to_datetimeindex,
epoch_year=epoch_year)
to_output = tools._array_out
Expand Down Expand Up @@ -873,7 +877,7 @@ def haydavies(surface_tilt, surface_azimuth, dhi, dni, dni_extra,


def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra,
solar_zenith, solar_azimuth):
solar_zenith, solar_azimuth, return_components=False):
r'''
Determine the diffuse irradiance from the sky on a tilted surface using
the Reindl (1990) model.
Expand Down Expand Up @@ -912,10 +916,27 @@ def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra,
solar_azimuth : numeric
Solar azimuth angles. See :term:`solar_azimuth`. [°]

return_components : bool, default False
Flag used to decide whether to return the calculated diffuse components
or not.

Returns
-------
poa_sky_diffuse : numeric
The sky diffuse component of the solar radiation. [Wm⁻²]
numeric, Dict, or DataFrame
Return type controlled by ``return_components`` argument.
If ``return_components=False``, `sky_diffuse` is returned.
If ``return_components=True``, `diffuse_components` is returned.

sky_diffuse : numeric
The sky diffuse component of the solar radiation on a tilted
surface.

diffuse_components : Dict (array input) or DataFrame (Series input)
Keys/columns are:
* poa_sky_diffuse: Total sky diffuse
* poa_isotropic
* poa_circumsolar
* poa_horizon

Notes
-----
Expand All @@ -939,8 +960,12 @@ def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra,
Implementation is based on Loutzenhiser et al.
(2007) [3]_, Equation 8. The beam and ground reflectance portion of the
equation have been removed, therefore the model described here generates
ONLY the diffuse radiation from the sky and circumsolar, so the form of the
equation varies slightly from Equation 8 in [3]_.
ONLY the diffuse radiation from the sky, circumsolar, and horizon
brightening, so the form of the equation varies slightly from Equation 8
in [3]_.

For clarity, the horizon component in `reindl` corresponds to the term
added on top of the `hay–davies` formulation, on which `reindl` builds.

References
----------
Expand Down Expand Up @@ -973,16 +998,31 @@ def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra,
HB = dni * cos_solar_zenith
HB = np.maximum(HB, 0)

# these are the () and [] sub-terms of the second term of eqn 8
term1 = 1 - AI
term2 = 0.5 * (1 + tools.cosd(surface_tilt))
SVF = (1 + tools.cosd(surface_tilt)) / 2

with np.errstate(invalid='ignore', divide='ignore'):
hb_to_ghi = np.where(ghi == 0, 0, np.divide(HB, ghi))
term3 = 1 + np.sqrt(hb_to_ghi) * (tools.sind(0.5 * surface_tilt)**3)
sky_diffuse = dhi * (AI * Rb + term1 * term2 * term3)
sky_diffuse = np.maximum(sky_diffuse, 0)
h = np.sqrt(hb_to_ghi) * (tools.sind(surface_tilt / 2) ** 3)

return sky_diffuse
term1 = (1 - AI) * SVF
term2 = AI * Rb
term3 = (1 - AI) * SVF * h

sky_diffuse = dhi * (term1 + term2 + term3)

if return_components:
diffuse_components = {
'poa_sky_diffuse': sky_diffuse,
'poa_isotropic': dhi * term1,
'poa_circumsolar': dhi * term2,
'poa_horizon': dhi * term3
}

if isinstance(sky_diffuse, pd.Series):
diffuse_components = pd.DataFrame(diffuse_components)
return diffuse_components
else:
return sky_diffuse


def king(surface_tilt, dhi, ghi, solar_zenith):
Expand Down Expand Up @@ -1442,7 +1482,7 @@ def _poa_from_ghi(surface_tilt, surface_azimuth,
Transposition function that includes decomposition of GHI using the
continuous Erbs-Driesse model.

Helper function for ghi_from_poa_driesse_2023.
Helper function for ghi_from_poa_driesse_2024.
'''
# Contributed by Anton Driesse (@adriesse), PV Performance Labs. Nov., 2023

Expand All @@ -1468,7 +1508,7 @@ def _ghi_from_poa(surface_tilt, surface_azimuth,
'''
Reverse transposition function that uses the scalar bisection from scipy.

Helper function for ghi_from_poa_driesse_2023.
Helper function for ghi_from_poa_driesse_2024.
'''
# Contributed by Anton Driesse (@adriesse), PV Performance Labs. Nov., 2023

Expand Down Expand Up @@ -1512,7 +1552,7 @@ def poa_error(ghi):
return ghi, conv, niter


def ghi_from_poa_driesse_2023(surface_tilt, surface_azimuth,
def ghi_from_poa_driesse_2024(surface_tilt, surface_azimuth,
solar_zenith, solar_azimuth,
poa_global,
dni_extra, airmass=None, albedo=0.25,
Expand Down Expand Up @@ -1614,6 +1654,14 @@ def ghi_from_poa_driesse_2023(surface_tilt, surface_azimuth,
return ghi


ghi_from_poa_driesse_2023 = deprecated(
since="0.15.2",
name="pvlib.irradiance.ghi_from_poa_driesse_2023",
alternative="pvlib.irradiance.ghi_from_poa_driesse_2024",
removal="0.17.0",
)(ghi_from_poa_driesse_2024)


def clearsky_index(ghi, ghi_clear, max_clearsky_index=2.0):
"""
Calculate the clearsky index.
Expand Down Expand Up @@ -1964,14 +2012,14 @@ def dirint(ghi, solar_zenith, times, pressure=101325., use_delta_kt_prime=True,

Returns
-------
dni : array-like
The modeled direct normal irradiance, as provided by the
DIRINT model. [Wm⁻²]
dni : pd.Series
Estimated direct normal irradiance. [Wm⁻²]

Notes
-----
DIRINT model requires time series data (ie. one of the inputs must
be a vector of length > 2).
The DIRINT model was developed for time series data with length > 2.
The implementation in pvlib assumes the data are periodic which may
affect the first and last DNI values.

References
----------
Expand Down Expand Up @@ -2109,6 +2157,7 @@ def _dirint_bins(times, kt_prime, zenith, w, delta_kt_prime):
-------
tuple of kt_prime_bin, zenith_bin, w_bin, delta_kt_prime_bin
"""

# @wholmgren: the following bin assignments use MATLAB's 1-indexing.
# Later, we'll subtract 1 to conform to Python's 0-indexing.

Expand Down
Loading