Skip to content

Commit 5f85c32

Browse files
authored
Merge branch 'main' into normalize
2 parents 104bb23 + a14e95e commit 5f85c32

File tree

17 files changed

+451
-60
lines changed

17 files changed

+451
-60
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
"""
2+
Use different Perez coefficients with the ModelChain
3+
====================================================
4+
5+
This example demonstrates how to customize the ModelChain
6+
to use site-specific Perez transposition coefficients.
7+
"""
8+
9+
# %%
10+
# The :py:class:`pvlib.modelchain.ModelChain` object provides a useful method
11+
# for easily constructing a PV system model with a simple, unified interface.
12+
# However, a user may want to customize the steps
13+
# in the system model in various ways.
14+
# One such example is during the irradiance transposition step.
15+
# The Perez model perform very well on field data, but
16+
# it requires a set of fitted coefficients from various sites.
17+
# It has been noted that these coefficients can be specific to
18+
# various climates, so users may see improved model accuracy
19+
# when using a site-specific set of coefficients.
20+
# However, the base :py:class:`~pvlib.modelchain.ModelChain`
21+
# only supports the default coefficients.
22+
# This example shows how the :py:class:`~pvlib.modelchain.ModelChain` can
23+
# be adjusted to use a different set of Perez coefficients.
24+
25+
import pandas as pd
26+
from pvlib.pvsystem import PVSystem
27+
from pvlib.modelchain import ModelChain
28+
from pvlib.temperature import TEMPERATURE_MODEL_PARAMETERS
29+
from pvlib import iotools, location, irradiance
30+
import pvlib
31+
import os
32+
import matplotlib.pyplot as plt
33+
34+
# load in TMY weather data from North Carolina included with pvlib
35+
PVLIB_DIR = pvlib.__path__[0]
36+
DATA_FILE = os.path.join(PVLIB_DIR, 'data', '723170TYA.CSV')
37+
38+
tmy, metadata = iotools.read_tmy3(DATA_FILE, coerce_year=1990,
39+
map_variables=True)
40+
41+
weather_data = tmy[['ghi', 'dhi', 'dni', 'temp_air', 'wind_speed']]
42+
43+
loc = location.Location.from_tmy(metadata)
44+
45+
#%%
46+
# Now, let's set up a standard PV model using the ``ModelChain``
47+
48+
surface_tilt = metadata['latitude']
49+
surface_azimuth = 180
50+
51+
# define an example module and inverter
52+
sandia_modules = pvlib.pvsystem.retrieve_sam('SandiaMod')
53+
cec_inverters = pvlib.pvsystem.retrieve_sam('cecinverter')
54+
sandia_module = sandia_modules['Canadian_Solar_CS5P_220M___2009_']
55+
cec_inverter = cec_inverters['ABB__MICRO_0_25_I_OUTD_US_208__208V_']
56+
57+
temp_params = TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_glass']
58+
59+
# define the system and ModelChain
60+
system = PVSystem(arrays=None,
61+
surface_tilt=surface_tilt,
62+
surface_azimuth=surface_azimuth,
63+
module_parameters=sandia_module,
64+
inverter_parameters=cec_inverter,
65+
temperature_model_parameters=temp_params)
66+
67+
mc = ModelChain(system, location=loc)
68+
69+
# %%
70+
# Now, let's calculate POA irradiance values outside of the ``ModelChain``.
71+
# We do this for both the default Perez coefficients and the desired
72+
# alternative Perez coefficients. This enables comparison at the end.
73+
74+
# Cape Canaveral seems like the most likely match for climate
75+
model_perez = 'capecanaveral1988'
76+
77+
solar_position = loc.get_solarposition(times=weather_data.index)
78+
dni_extra = irradiance.get_extra_radiation(weather_data.index)
79+
80+
POA_irradiance = irradiance.get_total_irradiance(
81+
surface_tilt=surface_tilt,
82+
surface_azimuth=surface_azimuth,
83+
dni=weather_data['dni'],
84+
ghi=weather_data['ghi'],
85+
dhi=weather_data['dhi'],
86+
solar_zenith=solar_position['apparent_zenith'],
87+
solar_azimuth=solar_position['azimuth'],
88+
model='perez',
89+
dni_extra=dni_extra)
90+
91+
POA_irradiance_new_perez = irradiance.get_total_irradiance(
92+
surface_tilt=surface_tilt,
93+
surface_azimuth=surface_azimuth,
94+
dni=weather_data['dni'],
95+
ghi=weather_data['ghi'],
96+
dhi=weather_data['dhi'],
97+
solar_zenith=solar_position['apparent_zenith'],
98+
solar_azimuth=solar_position['azimuth'],
99+
model='perez',
100+
model_perez=model_perez,
101+
dni_extra=dni_extra)
102+
103+
# %%
104+
# Now, run the ``ModelChain`` with both sets of irradiance data and compare
105+
# (note that to use POA irradiance as input to the ModelChain the method
106+
# `.run_model_from_poa` is used):
107+
108+
mc.run_model_from_poa(POA_irradiance)
109+
ac_power_default = mc.results.ac
110+
111+
mc.run_model_from_poa(POA_irradiance_new_perez)
112+
ac_power_new_perez = mc.results.ac
113+
114+
start, stop = '1990-05-05 06:00:00', '1990-05-05 19:00:00'
115+
plt.plot(ac_power_default.loc[start:stop],
116+
label="Default Composite Perez Model")
117+
plt.plot(ac_power_new_perez.loc[start:stop],
118+
label="Cape Canaveral Perez Model")
119+
plt.xticks(rotation=90)
120+
plt.ylabel("AC Power ($W$)")
121+
plt.legend()
122+
plt.tight_layout()
123+
plt.show()
124+
# %%
125+
# Note that there is a small, but noticeable difference from the default
126+
# coefficients that may add up over longer periods of time.

docs/examples/iv-modeling/plot_singlediode.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@
114114
# mark the MPP
115115
plt.plot([v_mp], [i_mp], ls='', marker='o', c='k')
116116

117+
plt.xlim(left=0)
118+
plt.ylim(bottom=0)
117119
plt.legend(loc=(1.0, 0))
118120
plt.xlabel('Module voltage [V]')
119121
plt.ylabel('Module current [A]')

docs/sphinx/source/reference/effects_on_pv_system_output/spectrum.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ Spectrum
1818
spectrum.spectral_factor_jrc
1919
spectrum.sr_to_qe
2020
spectrum.qe_to_sr
21+
spectrum.average_photon_energy

docs/sphinx/source/whatsnew/v0.11.1.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,19 @@ Deprecations
1010

1111
Enhancements
1212
~~~~~~~~~~~~
13+
* Add new function to calculate the average photon energy,
14+
:py:func:`pvlib.spectrum.average_photon_energy`.
15+
(:issue:`2135`, :pull:`2140`)
1316
* Add new losses function that accounts for non-uniform irradiance on bifacial
1417
modules, :py:func:`pvlib.bifacial.power_mismatch_deline`.
1518
(:issue:`2045`, :pull:`2046`)
1619
* Add new parameters for min/max absolute air mass to
1720
:py:func:`pvlib.spectrum.spectral_factor_firstsolar`.
1821
(:issue:`2086`, :pull:`2100`)
22+
* Add ``roll_utc_offset`` and ``coerce_year`` arguments to
23+
:py:func:`pvlib.iotools.get_pvgis_tmy` to allow user to specify time zone,
24+
rotate indices of TMY to begin at midnight, and force indices to desired
25+
year. (:issue:`2139`, :pull:`2138`)
1926
* Restructured the pvlib/spectrum folder by breaking up the contents of
2027
pvlib/spectrum/mismatch.py into pvlib/spectrum/mismatch.py,
2128
pvlib/spectrum/irradiance.py, and
@@ -42,6 +49,10 @@ Documentation
4249
* Added gallery example on calculating cell temperature for
4350
floating PV. (:pull:`2110`)
4451

52+
* Added gallery example demonstrating how to use
53+
different Perez coefficients in a ModelChain.
54+
(:issue:`2127`, :pull:`2148`)
55+
4556
* Removed unused "times" input from dni_et() function (:issue:`2105`)
4657

4758
Requirements
@@ -57,4 +68,10 @@ Contributors
5768
* Echedey Luis (:ghuser:`echedey-ls`)
5869
* Rajiv Daxini (:ghuser:`RDaxini`)
5970
* Scott Nelson (:ghuser:`scttnlsn`)
71+
* Mark A. Mikofski (:ghuser:`mikofski`)
72+
* Ben Pierce (:ghuser:`bgpierc`)
6073
* Jose Meza (:ghuser:`JoseMezaMendieta`)
74+
* Carlos Cárdenas-Bravo (:ghuser:`cardenca`)
75+
* Marcos R. Escudero (:ghuser:`marc-resc`)
76+
* Bernat Nicolau (:ghuser:`BernatNicolau`)
77+
* Eduardo Sarquis (:ghuser:`EduardoSarquis`)

pvlib/clearsky.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -941,7 +941,7 @@ def bird(zenith, airmass_relative, aod380, aod500, precipitable_water,
941941
zenith is never explicitly defined in the report, since the purpose
942942
was to compare existing clear sky models with "rigorous radiative
943943
transfer models" (RTM) it is possible that apparent zenith was
944-
obtained as output from the RTM. However, the implentation presented
944+
obtained as output from the RTM. However, the implementation presented
945945
in PVLIB is tested against the NREL Excel implementation by Daryl
946946
Myers which uses an analytical expression for solar zenith instead
947947
of apparent zenith.

pvlib/iotools/pvgis.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
import json
1919
from pathlib import Path
2020
import requests
21+
import numpy as np
2122
import pandas as pd
23+
import pytz
2224
from pvlib.iotools import read_epw, parse_epw
23-
import warnings
24-
from pvlib._deprecation import pvlibDeprecationWarning
2525

2626
URL = 'https://re.jrc.ec.europa.eu/api/'
2727

@@ -390,9 +390,33 @@ def read_pvgis_hourly(filename, pvgis_format=None, map_variables=True):
390390
raise ValueError(err_msg)
391391

392392

393+
def _coerce_and_roll_tmy(tmy_data, tz, year):
394+
"""
395+
Assumes ``tmy_data`` input is UTC, converts from UTC to ``tz``, rolls
396+
dataframe so timeseries starts at midnight, and forces all indices to
397+
``year``. Only works for integer ``tz``, but ``None`` and ``False`` are
398+
re-interpreted as zero / UTC.
399+
"""
400+
if tz:
401+
tzname = pytz.timezone(f'Etc/GMT{-tz:+d}')
402+
else:
403+
tz = 0
404+
tzname = pytz.timezone('UTC')
405+
new_index = pd.DatetimeIndex([
406+
timestamp.replace(year=year, tzinfo=tzname)
407+
for timestamp in tmy_data.index],
408+
name=f'time({tzname})')
409+
new_tmy_data = pd.DataFrame(
410+
np.roll(tmy_data, tz, axis=0),
411+
columns=tmy_data.columns,
412+
index=new_index)
413+
return new_tmy_data
414+
415+
393416
def get_pvgis_tmy(latitude, longitude, outputformat='json', usehorizon=True,
394417
userhorizon=None, startyear=None, endyear=None,
395-
map_variables=True, url=URL, timeout=30):
418+
map_variables=True, url=URL, timeout=30,
419+
roll_utc_offset=None, coerce_year=None):
396420
"""
397421
Get TMY data from PVGIS.
398422
@@ -424,6 +448,13 @@ def get_pvgis_tmy(latitude, longitude, outputformat='json', usehorizon=True,
424448
base url of PVGIS API, append ``tmy`` to get TMY endpoint
425449
timeout : int, default 30
426450
time in seconds to wait for server response before timeout
451+
roll_utc_offset: int, optional
452+
Use to specify a time zone other than the default UTC zero and roll
453+
dataframe by ``roll_utc_offset`` so it starts at midnight on January
454+
1st. Ignored if ``None``, otherwise will force year to ``coerce_year``.
455+
coerce_year: int, optional
456+
Use to force indices to desired year. Will default to 1990 if
457+
``coerce_year`` is not specified, but ``roll_utc_offset`` is specified.
427458
428459
Returns
429460
-------
@@ -510,6 +541,11 @@ def get_pvgis_tmy(latitude, longitude, outputformat='json', usehorizon=True,
510541
if map_variables:
511542
data = data.rename(columns=VARIABLE_MAP)
512543

544+
if not (roll_utc_offset is None and coerce_year is None):
545+
# roll_utc_offset is specified, but coerce_year isn't
546+
coerce_year = coerce_year or 1990
547+
data = _coerce_and_roll_tmy(data, roll_utc_offset, coerce_year)
548+
513549
return data, months_selected, inputs, meta
514550

515551

pvlib/irradiance.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,8 +395,8 @@ def get_sky_diffuse(surface_tilt, surface_azimuth,
395395
Raises
396396
------
397397
ValueError
398-
If model is one of ``'haydavies'``, ``'reindl'``, or ``'perez'`` and
399-
``dni_extra`` is not specified.
398+
If model is one of ``'haydavies'``, ``'reindl'``, ``'perez'``, or
399+
``'perez_driesse'`` and ``dni_extra`` is not specified.
400400
401401
Notes
402402
-----

pvlib/modelchain.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1474,7 +1474,7 @@ def prepare_inputs_from_poa(self, data):
14741474
data : DataFrame, or tuple or list of DataFrame
14751475
Contains plane-of-array irradiance data. Required column names
14761476
include ``'poa_global'``, ``'poa_direct'`` and ``'poa_diffuse'``.
1477-
Columns with weather-related data are ssigned to the
1477+
Columns with weather-related data are assigned to the
14781478
``weather`` attribute. If columns for ``'temp_air'`` and
14791479
``'wind_speed'`` are not provided, air temperature of 20 C and wind
14801480
speed of 0 m/s are assumed.

pvlib/pvsystem.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,7 @@ def scale_voltage_current_power(self, data):
841841
@_unwrap_single_value
842842
def pvwatts_dc(self, g_poa_effective, temp_cell):
843843
"""
844-
Calcuates DC power according to the PVWatts model using
844+
Calculates DC power according to the PVWatts model using
845845
:py:func:`pvlib.pvsystem.pvwatts_dc`, `self.module_parameters['pdc0']`,
846846
and `self.module_parameters['gamma_pdc']`.
847847
@@ -1550,7 +1550,7 @@ def calcparams_desoto(effective_irradiance, temp_cell,
15501550
Light-generated current in amperes
15511551
15521552
saturation_current : numeric
1553-
Diode saturation curent in amperes
1553+
Diode saturation current in amperes
15541554
15551555
resistance_series : numeric
15561556
Series resistance in ohms

pvlib/solarposition.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -717,11 +717,11 @@ def ephemeris(time, latitude, longitude, pressure=101325, temperature=12):
717717
718718
* apparent_elevation : apparent sun elevation accounting for
719719
atmospheric refraction.
720+
This is the complement of the apparent zenith angle.
720721
* elevation : actual elevation (not accounting for refraction)
721722
of the sun in decimal degrees, 0 = on horizon.
722723
The complement of the zenith angle.
723724
* azimuth : Azimuth of the sun in decimal degrees East of North.
724-
This is the complement of the apparent zenith angle.
725725
* apparent_zenith : apparent sun zenith accounting for atmospheric
726726
refraction.
727727
* zenith : Solar zenith angle

0 commit comments

Comments
 (0)