The power per unit area from the sun that hits a surface. Measured in watts per square meter (W/m²).
Clouds, temperature, and atmospheric conditions significantly affect solar panel performance.
Tilt angle and azimuth direction optimize energy capture throughout the year.
Solar panels convert sunlight into electrical energy through the photovoltaic effect.
Accurate solar energy prediction requires complex modeling of weather patterns, panel positioning, and system components. This is where pvlib becomes essential!
Simulates photovoltaic (PV) energy systems with validated, peer-reviewed models.
Provides standardized models for solar resource assessment and energy forecasting.
Trusted by thousands of researchers and engineers worldwide.
Used in academic research, commercial projects, and policy development.
What you're seeing:
The Python version of pvlib began, porting functionality from the original MATLAB version developed at Sandia National Laboratories.
First official release on PyPI, making it accessible to the global Python community and sparking its growth.
A top-tier scientific package, ranking in the top 1% on PyPI by downloads and supported by NumFOCUS and a global community of over 100 contributors.
'pvlib allows me to easily compare my novel irradiance model against established benchmarks, saving months of work.'
'I rely on pvlib for bankable energy yield assessments of utility-scale solar farms. It's the industry standard.'
'It's an incredible tool for learning the fundamentals of PV system performance. The documentation is fantastic.'
pvlib transforms weather data into precise power predictions through a sophisticated modeling pipeline. Here's how it works:
Global Horizontal Irradiance (GHI), temperature, wind speed, and humidity from weather stations or satellite data.
Calculate precise sun position (azimuth, elevation) for any location and time using astronomical algorithms.
Convert GHI to Plane-of-Array (POA) irradiance accounting for panel tilt, orientation, and diffuse/direct components.
Estimate cell temperature from ambient conditions, as it significantly affects module efficiency.
Convert irradiance to DC power using module models, then to AC power through inverter models.
Maximum control over each calculation step
import pvlib
import pandas as pd
# Step-by-step solar calculations
times = pd.date_range('2025-01-01', periods=24, freq='H', tz='Australia/Sydney')
lat, lon = -33.8688, 151.2093 # Sydney coordinates
# 1. Solar position
solpos = pvlib.solarposition.get_solarposition(times, lat, lon)
# 2. Irradiance components
dni_extra = pvlib.irradiance.get_extra_radiation(times)
airmass = pvlib.atmosphere.get_relative_airmass(solpos['apparent_zenith'])
# 3. Plane-of-array irradiance
total_irrad = pvlib.irradiance.get_total_irradiance(
surface_tilt=30, surface_azimuth=180,
solar_zenith=solpos['apparent_zenith'],
solar_azimuth=solpos['azimuth'],
dni=900, ghi=1000, dhi=100 # Example values
)
# 4. Cell temperature
cell_temp = pvlib.temperature.sapm_cell(
poa_global=total_irrad['poa_global'],
temp_air=25, wind_speed=1,
a=-3.56, b=-0.075, deltaT=3 # SAPM parameters
)
print(f"Peak POA irradiance: {total_irrad['poa_global'].max():.1f} W/m²")
Organize system components as objects
from pvlib.location import Location
from pvlib.pvsystem import PVSystem, FixedMount, Array
from pvlib.temperature import TEMPERATURE_MODEL_PARAMETERS
# Define location object
location = Location(
latitude=-33.8688, longitude=151.2093, # Sydney
name='Sydney', altitude=58, tz='Australia/Sydney'
)
# Define system components
temperature_params = TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_glass']
mount = FixedMount(surface_tilt=30, surface_azimuth=180)
array = Array(
mount=mount,
module_parameters={'pdc0': 320, 'gamma_pdc': -0.004},
temperature_model_parameters=temperature_params,
modules_per_string=10, strings=4
)
system = PVSystem(
arrays=[array],
inverter_parameters={'pdc0': 12000, 'eta_inv_nom': 0.96}
)
# Use object methods
clearsky = location.get_clearsky(times)
solpos = location.get_solarposition(times)
# Calculate system performance
dc_power = system.pvwatts_dc(
g_poa_effective=clearsky['ghi'] * 0.9, # Simplified
temp_cell=25
)
print(f"System configuration: {system.num_arrays} array(s)")
print(f"Total module capacity: {array.module_parameters['pdc0'] * 40} W")
Complete simulation with one function call
from pvlib.modelchain import ModelChain
from pvlib.pvsystem import PVSystem, FixedMount, Array
from pvlib.location import Location
import pandas as pd
# Quick setup with ModelChain
location = Location(-33.8688, 151.2093, tz='Australia/Sydney') # Sydney
# Define system
array = Array(
mount=FixedMount(surface_tilt=30, surface_azimuth=180),
module_parameters={'pdc0': 320, 'gamma_pdc': -0.004},
temperature_model_parameters={'a': -3.56, 'b': -0.075, 'deltaT': 3}
)
system = PVSystem(
arrays=[array],
inverter_parameters={'pdc0': 12000, 'eta_inv_nom': 0.96}
)
# Create ModelChain and run simulation
mc = ModelChain(system, location, aoi_model='physical', spectral_model='no_loss')
# Sample weather data
weather = pd.DataFrame({
'ghi': [0, 200, 600, 1000, 800, 400, 100, 0],
'dni': [0, 300, 800, 900, 700, 500, 200, 0],
'dhi': [0, 100, 150, 100, 200, 150, 50, 0],
'temp_air': [18, 20, 24, 28, 26, 22, 19, 17],
'wind_speed': [2, 3, 2, 1, 2, 3, 2, 2]
}, index=pd.date_range('2025-08-20 06:00', periods=8, freq='2H', tz='Australia/Sydney'))
# Run complete simulation
mc.run_model(weather)
# Access results
print(f"Daily AC energy: {mc.results.ac.sum()/1000:.1f} kWh")
print(f"Peak AC power: {mc.results.ac.max():.0f} W")
print(f"Peak cell temperature: {mc.results.cell_temperature.max():.1f}°C")
# Results are automatically stored in mc.results with:
# - ac: AC power output
# - dc: DC power output
# - cell_temperature: Cell temperatures
# - aoi: Angle of incidence
# - And much more...
# Complete PV System Simulation with pvlib
import pvlib
import pandas as pd
import numpy as np
# 1. Define location (Sydney, Australia)
location = pvlib.location.Location(
latitude=-33.8688, longitude=151.2093,
name='Sydney', altitude=58, tz='Australia/Sydney'
)
# 2. Create time series for one day
times = pd.date_range('2025-08-20 06:00', '2025-08-20 18:00',
freq='30min', tz='Australia/Sydney')
# 3. Get clear sky irradiance data
clearsky = location.get_clearsky(times)
# 4. Define PV system parameters
system_size_kw = 5.0 # System size in kW
tilt_angle = 30 # Panel tilt angle in degrees
# Module parameters (typical silicon PV)
module_params = {
'pdc0': 320, # DC power at STC (W)
'gamma_pdc': -0.004, # Power temperature coefficient (/°C)
}
# 5. Create PV system
from pvlib.pvsystem import PVSystem, FixedMount, Array
from pvlib.modelchain import ModelChain
mount = FixedMount(surface_tilt=tilt_angle, surface_azimuth=180)
array = Array(
mount=mount,
module_parameters=module_params,
temperature_model_parameters={'a': -3.56, 'b': -0.075, 'deltaT': 3}
)
# Calculate number of modules for desired system size
modules_needed = int(system_size_kw * 1000 / module_params['pdc0'])
system = PVSystem(
arrays=[array],
inverter_parameters={'pdc0': system_size_kw * 1000, 'eta_inv_nom': 0.96}
)
# 6. Run simulation
mc = ModelChain(system, location,
aoi_model='physical', spectral_model='no_loss')
# Create weather data with temperature
weather = clearsky.copy()
weather['temp_air'] = 25 + 5 * np.sin((times.hour - 12) / 6 * np.pi)
weather['wind_speed'] = 2.0
# Run the simulation
mc.run_model(weather)
# 7. Extract and display results
ac_power_kw = mc.results.ac / 1000 # Convert to kW
daily_energy = ac_power_kw.sum() * 0.5 # kWh (30min intervals)
peak_power = ac_power_kw.max()
capacity_factor = (daily_energy / (system_size_kw * 12)) * 100 # 12h day
print(f"System Size: {system_size_kw} kW")
print(f"Peak Power: {peak_power:.2f} kW")
print(f"Daily Energy: {daily_energy:.2f} kWh")
print(f"Capacity Factor: {capacity_factor:.1f}%")
🖱️ Mouse Controls: Left-click + drag to rotate, wheel to zoom, right-click + drag to pan