Introduction to the pvlib Python Package

Solar Energy Modeling with Python

SPREE UNSW

School of Photovoltaic and Renewable Energy Engineering

Python Package
Open Source
Research Grade
POWER
☀️

Solar Irradiance

The power per unit area from the sun that hits a surface. Measured in watts per square meter (W/m²).

Solar Panel
🌤️

Weather Impact

Clouds, temperature, and atmospheric conditions significantly affect solar panel performance.

Clear Sky
100%
Partly Cloudy
70%
Overcast
30%
📐

Panel Positioning

Tilt angle and azimuth direction optimize energy capture throughout the year.

Ground Optimal Tilt Sun Rays

Power Generation

Solar panels convert sunlight into electrical energy through the photovoltaic effect.

☀️
Sunlight
📱
Solar Cell
Electricity

💡 Key Insight

Accurate solar energy prediction requires complex modeling of weather patterns, panel positioning, and system components. This is where pvlib becomes essential!

A powerful, open-source toolbox for solar energy simulation.

High Accuracy Simulation

Simulates photovoltaic (PV) energy systems with validated, peer-reviewed models.

🎯

Reliable Benchmarking

Provides standardized models for solar resource assessment and energy forecasting.

🌍

Global Community

Trusted by thousands of researchers and engineers worldwide.

🔬

Research Grade

Used in academic research, commercial projects, and policy development.

Interactive Solar Simulation

Power Output: Variable with sun position

What you're seeing:

  • ☀️ Sun position affects irradiance angle and intensity
  • ⚡ Power output varies throughout the day
  • ☁️ Clouds can temporarily reduce performance
  • 📊 pvlib models all these factors precisely

2013: The Genesis

The Python version of pvlib began, porting functionality from the original MATLAB version developed at Sandia National Laboratories.

2015: Public Debut

First official release on PyPI, making it accessible to the global Python community and sparking its growth.

Present Day: Industry Standard

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:

1
GHI

Weather Data Input

Global Horizontal Irradiance (GHI), temperature, wind speed, and humidity from weather stations or satellite data.

Sources: NSRDB, PVGIS, TMY files
2
Solar Position Azimuth Elevation

Solar Position

Calculate precise sun position (azimuth, elevation) for any location and time using astronomical algorithms.

Algorithms: SPA, NREL, PSA
3
POA Irradiance

Irradiance Transposition

Convert GHI to Plane-of-Array (POA) irradiance accounting for panel tilt, orientation, and diffuse/direct components.

Models: Hay-Davies, Perez, Isotropic
4
Cell Temp

Cell Temperature

Estimate cell temperature from ambient conditions, as it significantly affects module efficiency.

Models: SAPM, Pvsyst, Faiman
5
DC AC Power

Power Conversion

Convert irradiance to DC power using module models, then to AC power through inverter models.

Models: CEC, SAPM, PVWatts

🎯 Key Benefits

  • Modular Design: Use individual functions or complete chains
  • Model Choice: Multiple validated algorithms for each step
  • Real-world Accuracy: Accounts for all major loss mechanisms
  • Flexible Input: Works with various weather data sources

📊 Applications

  • Energy yield assessment
  • Financial modeling
  • Performance monitoring
  • Research & development

🔧 Procedural Approach

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²")

🏗️ Object-Oriented Approach

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")

⚡ ModelChain Approach

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...

🎛️ Simulation Parameters

📍 Location

🔧 System Configuration

📊 Results Summary

Peak Power: -- kW
Daily Energy: -- kWh
Capacity Factor: -- %

🐍 Complete PV System Simulation

Complete PV Simulation Code Open in Google Colab
# 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}%")

⚡ AC Power Analysis

  • Bell Curve Pattern: Power follows sun's daily path
  • Temperature Impact: Higher heat reduces output
  • Inverter Role: Converts DC to AC, tracks maximum power
  • Daily Energy: Area under power curve
  • Peak Time: Usually 1-2 hours after solar noon
  • Grid Connection: AC matches grid frequency and voltage

☀️ Solar Irradiance Analysis

  • GHI vs POA: Horizontal vs tilted panel irradiance
  • Peak at Solar Noon: Maximum around 12:00-13:00
  • Direct + Diffuse: Sunlight and sky radiation
  • Weather Effects: Clouds reduce irradiance
  • Solar Angles: Sun position affects intensity
  • Seasonal Variation: Summer peaks higher than winter

📊 System Efficiency Analysis

  • Inverter Efficiency: ~96% DC to AC conversion
  • Module Efficiency: Silicon cells 15-22%
  • Temperature Loss: Heat reduces efficiency
  • System Losses: Shading, soiling, wiring
  • MPPT Tracking: Optimizes power point continuously
  • Performance Ratio: Real vs theoretical output

🎛️ System Controls

📍 Location

🔧 Panel Orientation

📏 System Size

☀️ Sun Position

📊 System Information

Total Panels: 12
System Capacity: 3.84 kW
Sun Elevation: 45.0°
Sun Azimuth: 0.0°
Angle of Incidence: 15.0°

🖱️ Mouse Controls: Left-click + drag to rotate, wheel to zoom, right-click + drag to pan

⚡ Live Generation Profile

Daily Energy: 28.5 kWh
Peak Power: 3.2 kW
Capacity Factor: 18.7%
Annual Yield: 5,847 kWh

📈 Community Stats

100+
Contributors
2M+
Downloads/month
1000+
GitHub Stars
10+
Years Active