CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pvlib

A comprehensive toolbox for modeling and simulating photovoltaic energy systems.

Pending
Overview
Eval results
Files

iotools.mddocs/

Weather Data I/O

Access and read weather data from multiple sources including TMY files, NSRDB, PVGIS, SURFRAD, and other meteorological databases. Comprehensive tools for retrieving and parsing weather data for photovoltaic modeling.

Capabilities

TMY Data Sources

Read and retrieve Typical Meteorological Year data from various formats.

def read_tmy2(filename):
    """
    Read TMY2 weather data files.
    
    Parameters:
    - filename: str, path to TMY2 file
    
    Returns:
    tuple: (data, metadata) where data is DataFrame with weather data
    and metadata is dict with station information
    """

def read_tmy3(filename, coerce_year=None, map_variables=True, encoding=None):
    """
    Read TMY3 weather data files.
    
    Parameters:
    - filename: str, path to TMY3 file
    - coerce_year: int, force all data to specific year
    - map_variables: bool, map to standard pvlib names
    - encoding: str, file encoding
    
    Returns:
    tuple: (data, metadata) where data is DataFrame with weather data
    """

def read_epw(filename, coerce_year=None):
    """
    Read EnergyPlus Weather (EPW) files.
    
    Parameters:
    - filename: str, path to EPW file
    - coerce_year: int, force all data to specific year
    
    Returns:
    tuple: (data, metadata) where data is DataFrame with hourly weather data
    """

def parse_epw(filename, coerce_year=None):
    """
    Parse EnergyPlus Weather files with detailed error handling.
    
    Parameters:
    - filename: str, path to EPW file
    - coerce_year: int, force all data to specific year
    
    Returns:
    tuple: (data, metadata) with parsed weather data
    """

NSRDB (National Solar Radiation Database)

Access high-quality satellite-derived solar irradiance data.

def get_psm3(latitude, longitude, api_key, email, names='tmy', interval=60, 
             leap_day=False, full_name='pvlib python', affiliation='pvlib python',
             reason='pvlib python', mailing_list=False, utc=True, 
             map_variables=True, attributes=(), timeout=30):
    """
    Get NSRDB PSM3 data via API.
    
    Parameters:
    - latitude: numeric, latitude in degrees
    - longitude: numeric, longitude in degrees  
    - api_key: str, NREL API key
    - email: str, email address for API access
    - names: str or list, data years ('tmy', year, or list of years)
    - interval: int, time interval in minutes (30 or 60)
    - leap_day: bool, include leap day in data
    - utc: bool, return timestamps in UTC
    - map_variables: bool, map to standard pvlib names
    - attributes: list, additional data attributes to retrieve
    - timeout: int, request timeout in seconds
    
    Returns:
    tuple: (data, metadata) with solar irradiance and meteorological data
    """

def read_psm3(filename, map_variables=True):
    """
    Read PSM3 files downloaded from NSRDB.
    
    Parameters:
    - filename: str, path to PSM3 CSV file
    - map_variables: bool, map to standard pvlib names
    
    Returns:
    tuple: (data, metadata) with parsed PSM3 data
    """

def get_nsrdb_psm4_tmy(latitude, longitude, api_key, email, year='tmy',
                       attributes=['ghi', 'dni', 'dhi', 'temp_air', 'wind_speed'],
                       names=None, map_variables=True, leap_day=False, 
                       interval=60, full_name='pvlib python',
                       affiliation='pvlib python', reason='pvlib python',
                       mailing_list=False, timeout=30):
    """
    Get NSRDB PSM4 TMY data.
    
    Parameters:
    - latitude: numeric, latitude in degrees
    - longitude: numeric, longitude in degrees
    - api_key: str, NREL API key
    - email: str, email address
    - year: str or int, TMY year or specific year
    - attributes: list, weather variables to retrieve
    - map_variables: bool, map to standard pvlib names
    - leap_day: bool, include leap day
    - interval: int, time interval in minutes
    - timeout: int, request timeout in seconds
    
    Returns:
    tuple: (data, metadata) with PSM4 TMY data
    """

PVGIS Data Access

Retrieve data from the European Commission's Photovoltaic Geographical Information System.

def get_pvgis_tmy(latitude, longitude, outputformat='json', usehorizon=True,
                  userhorizon=None, startyear=None, endyear=None,
                  url='https://re.jrc.ec.europa.eu/api/v5_2/', 
                  map_variables=True, timeout=30):
    """
    Get PVGIS TMY data via API.
    
    Parameters:
    - latitude: numeric, latitude in degrees (-90 to 90)
    - longitude: numeric, longitude in degrees (-180 to 180)
    - outputformat: str, output format ('json', 'csv', 'basic')
    - usehorizon: bool, consider horizon shading
    - userhorizon: list, user-defined horizon profile
    - startyear: int, start year for TMY calculation
    - endyear: int, end year for TMY calculation
    - url: str, PVGIS API base URL
    - map_variables: bool, map to standard pvlib names
    - timeout: int, request timeout in seconds
    
    Returns:
    tuple: (data, metadata, inputs) with TMY data and metadata
    """

def get_pvgis_hourly(latitude, longitude, start=None, end=None, 
                     raddatabase=None, components=True, surface_tilt=0,
                     surface_azimuth=180, outputformat='json',
                     usehorizon=True, userhorizon=None, 
                     pvcalculation=False, peakpower=None,
                     pvtechchoice='crystSi', mountingplace='free',
                     loss=0, trackingtype=0, tilt=None, azim=None,
                     url='https://re.jrc.ec.europa.eu/api/v5_2/',
                     map_variables=True, timeout=30):
    """
    Get PVGIS hourly data via API.
    
    Parameters:
    - latitude: numeric, latitude in degrees
    - longitude: numeric, longitude in degrees
    - start: datetime-like, start date (YYYY or YYYY-MM-DD)
    - end: datetime-like, end date (YYYY or YYYY-MM-DD)
    - raddatabase: str, radiation database ('PVGIS-SARAH2', 'PVGIS-NSRDB', etc.)
    - components: bool, include irradiance components
    - surface_tilt: numeric, surface tilt angle in degrees
    - surface_azimuth: numeric, surface azimuth in degrees
    - outputformat: str, output format ('json', 'csv')
    - usehorizon: bool, consider horizon shading
    - pvcalculation: bool, include PV power calculation
    - peakpower: numeric, PV system peak power in kW
    - pvtechchoice: str, PV technology choice
    - mountingplace: str, mounting type ('free', 'building')
    - loss: numeric, system loss percentage
    - timeout: int, request timeout in seconds
    
    Returns:
    tuple: (data, metadata, inputs) with hourly data
    """

def read_pvgis_tmy(filename, pvgis_format=None, map_variables=True):
    """
    Read PVGIS TMY files.
    
    Parameters:
    - filename: str, path to PVGIS file
    - pvgis_format: str, file format ('json', 'csv', 'basic')
    - map_variables: bool, map to standard pvlib names
    
    Returns:
    tuple: (data, metadata, inputs) with parsed PVGIS data
    """

def get_pvgis_horizon(latitude, longitude, url='https://re.jrc.ec.europa.eu/api/v5_2/', **kwargs):
    """
    Get horizon profile from PVGIS.
    
    Parameters:
    - latitude: numeric, latitude in degrees
    - longitude: numeric, longitude in degrees
    - url: str, PVGIS API base URL
    
    Returns:
    pandas.DataFrame with horizon elevation angles
    """

SURFRAD and SRML Networks

Access ground-based radiation measurement networks.

def read_surfrad(filename, map_variables=True):
    """
    Read SURFRAD (Surface Radiation) data files.
    
    Parameters:
    - filename: str, path to SURFRAD file
    - map_variables: bool, map to standard pvlib names
    
    Returns:
    pandas.DataFrame with SURFRAD measurements
    """

def read_srml(filename, map_variables=True):
    """
    Read SRML (Solar Radiation Monitoring Laboratory) files.
    
    Parameters:
    - filename: str, path to SRML file
    - map_variables: bool, map to standard pvlib names
    
    Returns:
    pandas.DataFrame with SRML measurements
    """

def get_srml(station, start, end, filetype='PO', map_variables=True,
             url='http://solardat.uoregon.edu/SolarRadiationBasics.php'):
    """
    Get SRML data via web scraping.
    
    Parameters:
    - station: str, SRML station identifier
    - start: datetime-like, start date
    - end: datetime-like, end date  
    - filetype: str, file type ('PO' for processed)
    - map_variables: bool, map to standard pvlib names
    - url: str, SRML base URL
    
    Returns:
    pandas.DataFrame with SRML data
    """

MIDC and BSRN Networks

Access additional radiation measurement networks.

def read_midc(filename, variable_map={}, raw_data=False, **kwargs):
    """
    Read MIDC (Measurement and Instrumentation Data Center) files.
    
    Parameters:
    - filename: str, path to MIDC file
    - variable_map: dict, custom variable name mapping
    - raw_data: bool, return raw data without processing
    
    Returns:
    pandas.DataFrame with MIDC measurements
    """

def read_midc_raw_data_from_nrel(site, start, end, variable_map={}, **kwargs):
    """
    Get MIDC data directly from NREL servers.
    
    Parameters:
    - site: str, MIDC site identifier
    - start: datetime-like, start date
    - end: datetime-like, end date
    - variable_map: dict, custom variable mapping
    
    Returns:
    pandas.DataFrame with MIDC data
    """

def get_bsrn(station, start, end, username, password, url=None, **kwargs):
    """
    Get BSRN (Baseline Surface Radiation Network) data.
    
    Parameters:
    - station: str, BSRN station identifier
    - start: datetime-like, start date
    - end: datetime-like, end date
    - username: str, BSRN account username
    - password: str, BSRN account password
    - url: str, BSRN data server URL
    
    Returns:
    pandas.DataFrame with BSRN measurements
    """

def read_bsrn(filename, logical_records=('0100',)):
    """
    Read BSRN data files.
    
    Parameters:
    - filename: str, path to BSRN file
    - logical_records: tuple, logical record types to read
    
    Returns:
    pandas.DataFrame with BSRN data
    """

CAMS and SolarAnywhere

Access satellite-based and commercial data sources.

def get_cams(latitude, longitude, start, end, email, identifier='mcclear',
             time_step='PT01H', time_reference='UT', verbose=False,
             map_variables=True, timeout=30):
    """
    Get CAMS (Copernicus Atmosphere Monitoring Service) data.
    
    Parameters:
    - latitude: numeric, latitude in degrees
    - longitude: numeric, longitude in degrees
    - start: datetime-like, start date (YYYY-MM-DD)
    - end: datetime-like, end date (YYYY-MM-DD)
    - email: str, email for data request
    - identifier: str, data service identifier
    - time_step: str, time resolution ('PT01H', 'PT15M')
    - time_reference: str, time reference ('UT', 'TST')
    - verbose: bool, print request details
    - map_variables: bool, map to standard pvlib names
    - timeout: int, request timeout in seconds
    
    Returns:
    pandas.DataFrame with CAMS irradiance data
    """

def read_cams(filename, integrated=False, label=None, map_variables=True):
    """
    Read CAMS data files.
    
    Parameters:
    - filename: str, path to CAMS file
    - integrated: bool, data is time-integrated
    - label: str, data label for multi-file datasets
    - map_variables: bool, map to standard pvlib names
    
    Returns:
    pandas.DataFrame with parsed CAMS data
    """

def get_solaranywhere(latitude, longitude, api_key, start=None, end=None,
                      time_zone='UTC', spatial_resolution='1km',
                      temporal_resolution='15min', irradiance_type='beam+diffuse',
                      weather_data_format='csv', url=None, **kwargs):
    """
    Get SolarAnywhere satellite irradiance data.
    
    Parameters:
    - latitude: numeric, latitude in degrees
    - longitude: numeric, longitude in degrees
    - api_key: str, SolarAnywhere API key
    - start: datetime-like, start date
    - end: datetime-like, end date
    - time_zone: str, time zone identifier
    - spatial_resolution: str, spatial resolution ('1km', '10km')
    - temporal_resolution: str, temporal resolution ('15min', 'hour')
    - irradiance_type: str, irradiance components to retrieve
    - weather_data_format: str, output format ('csv', 'json')
    
    Returns:
    pandas.DataFrame with SolarAnywhere data
    """

def read_solaranywhere(filename, map_variables=True, encoding='iso-8859-1'):
    """
    Read SolarAnywhere data files.
    
    Parameters:
    - filename: str, path to SolarAnywhere file
    - map_variables: bool, map to standard pvlib names
    - encoding: str, file encoding
    
    Returns:
    pandas.DataFrame with SolarAnywhere data
    """

Climate Data Sources

Access precipitation and climate data for soiling and snow modeling.

def read_crn(filename, map_variables=True):
    """
    Read Climate Reference Network (CRN) files.
    
    Parameters:
    - filename: str, path to CRN file
    - map_variables: bool, map to standard pvlib names
    
    Returns:
    pandas.DataFrame with CRN climate data
    """

def get_acis_prism(latitude, longitude, start, end, map_variables=True, **kwargs):
    """
    Get ACIS PRISM precipitation data.
    
    Parameters:
    - latitude: numeric, latitude in degrees
    - longitude: numeric, longitude in degrees
    - start: datetime-like, start date
    - end: datetime-like, end date
    - map_variables: bool, map to standard pvlib names
    
    Returns:
    pandas.DataFrame with precipitation data
    """

def get_acis_station_data(station, start, end, trace_val=0.001, 
                          map_variables=True, **kwargs):
    """
    Get ACIS station-specific data.
    
    Parameters:
    - station: str, station identifier
    - start: datetime-like, start date  
    - end: datetime-like, end date
    - trace_val: numeric, value for trace precipitation
    - map_variables: bool, map to standard pvlib names
    
    Returns:
    pandas.DataFrame with station weather data
    """

def get_acis_available_stations(latitude_range, longitude_range,
                                start_date=None, end_date=None, **kwargs):
    """
    Get list of available ACIS weather stations.
    
    Parameters:
    - latitude_range: tuple, (min_lat, max_lat)
    - longitude_range: tuple, (min_lon, max_lon)
    - start_date: datetime-like, earliest data date
    - end_date: datetime-like, latest data date
    
    Returns:
    list of dict with station information
    """

Usage Examples

Loading TMY Data

import pvlib
from pvlib import iotools
import pandas as pd

# Read TMY3 file
data, metadata = iotools.read_tmy3('path/to/tmy3_file.csv')
print(f"Location: {metadata['Name']}, {metadata['State']}")
print(f"Latitude: {metadata['Latitude']:.2f}, Longitude: {metadata['Longitude']:.2f}")
print(f"Elevation: {metadata['Elevation']:.1f} m")

# Display first few rows
print(data.head())

# Read EnergyPlus Weather file
epw_data, epw_meta = iotools.read_epw('weather_file.epw')
print(f"Data shape: {epw_data.shape}")
print(f"Available columns: {epw_data.columns.tolist()}")

Accessing NSRDB Data

import pvlib
from pvlib import iotools
import matplotlib.pyplot as plt

# Get PSM3 TMY data from NSRDB
api_key = 'your_nrel_api_key'
email = 'your_email@domain.com'
lat, lon = 39.7555, -105.2211  # Golden, CO

# Retrieve TMY data
data, metadata = iotools.get_psm3(
    latitude=lat, 
    longitude=lon,
    api_key=api_key,
    email=email,
    names='tmy',
    attributes=['ghi', 'dni', 'dhi', 'temp_air', 'wind_speed'],
    map_variables=True
)

print(f"TMY data retrieved for {metadata['Location ID']}")
print(f"Available variables: {data.columns.tolist()}")

# Plot annual irradiance profile
fig, axes = plt.subplots(2, 1, figsize=(12, 8))

# Daily totals
daily_ghi = data['ghi'].resample('D').sum() / 1000  # kWh/m²/day
daily_ghi.plot(ax=axes[0], title='Daily Global Horizontal Irradiance')
axes[0].set_ylabel('GHI (kWh/m²/day)')

# Monthly averages
monthly_dni = data['dni'].resample('M').mean()
monthly_dhi = data['dhi'].resample('M').mean()
monthly_ghi = data['ghi'].resample('M').mean()

axes[1].plot(monthly_ghi.index.month, monthly_ghi, label='GHI', marker='o')
axes[1].plot(monthly_dni.index.month, monthly_dni, label='DNI', marker='s')
axes[1].plot(monthly_dhi.index.month, monthly_dhi, label='DHI', marker='^')
axes[1].set_xlabel('Month')
axes[1].set_ylabel('Irradiance (W/m²)')
axes[1].legend()
axes[1].set_title('Monthly Average Irradiance Components')

plt.tight_layout()
plt.show()

Retrieving PVGIS Data

import pvlib
from pvlib import iotools
import numpy as np
import pandas as pd

# Location in Europe (Munich, Germany)
lat, lon = 48.1351, 11.5820

# Get PVGIS TMY data
pvgis_data, pvgis_meta, pvgis_inputs = iotools.get_pvgis_tmy(
    latitude=lat,
    longitude=lon,
    usehorizon=True,
    outputformat='json'
)

print(f"PVGIS TMY data for coordinates: {lat:.2f}, {lon:.2f}")
print(f"Data period: {pvgis_inputs['meteo_data']['year_min']} - {pvgis_inputs['meteo_data']['year_max']}")
print(f"Available variables: {pvgis_data.columns.tolist()}")

# Get hourly data for specific period
start_date = 2020
end_date = 2020

hourly_data, hourly_meta, hourly_inputs = iotools.get_pvgis_hourly(
    latitude=lat,
    longitude=lon,
    start=start_date,
    end=end_date,
    raddatabase='PVGIS-SARAH2',
    components=True,
    surface_tilt=30,
    surface_azimuth=180
)

# Compare TMY vs actual year
comparison_months = []
for month in range(1, 13):
    tmy_month = pvgis_data[pvgis_data.index.month == month]['ghi'].mean()
    actual_month = hourly_data[hourly_data.index.month == month]['ghi'].mean()
    
    comparison_months.append({
        'month': month,
        'tmy_ghi': tmy_month,
        'actual_ghi': actual_month,
        'difference': actual_month - tmy_month
    })

comparison_df = pd.DataFrame(comparison_months)
print("\nTMY vs 2020 Comparison (Monthly Average GHI):")
print(comparison_df.round(2))

# Get horizon data
horizon = iotools.get_pvgis_horizon(lat, lon)
print(f"\nHorizon profile with {len(horizon)} data points")
print(f"Max horizon elevation: {horizon['horizon_elevation'].max():.1f}°")

Working with Multiple Data Sources

import pvlib
from pvlib import iotools
import pandas as pd
import matplotlib.pyplot as plt

# Compare data from multiple sources for same location
lat, lon = 36.0544, -112.1401  # Grand Canyon, AZ
year = 2020

# Source 1: NSRDB PSM3
nsrdb_data, nsrdb_meta = iotools.get_psm3(
    lat, lon, api_key='your_key', email='your_email',
    names=year, map_variables=True
)

# Source 2: PVGIS (if available for location)
try:
    pvgis_data, pvgis_meta, pvgis_inputs = iotools.get_pvgis_hourly(
        lat, lon, start=year, end=year
    )
    pvgis_available = True
except:
    pvgis_available = False
    print("PVGIS data not available for this location")

# Source 3: SolarAnywhere (requires API key)
try:
    sa_data = iotools.get_solaranywhere(
        lat, lon, 
        api_key='your_solaranywhere_key',
        start=f'{year}-01-01',
        end=f'{year}-12-31'
    )
    sa_available = True
except:
    sa_available = False
    print("SolarAnywhere data not available")

# Compare monthly statistics
monthly_stats = []

for month in range(1, 13):
    stats = {'month': month}
    
    # NSRDB statistics
    nsrdb_month = nsrdb_data[nsrdb_data.index.month == month]
    stats['nsrdb_ghi_avg'] = nsrdb_month['ghi'].mean()
    stats['nsrdb_dni_avg'] = nsrdb_month['dni'].mean()
    stats['nsrdb_temp_avg'] = nsrdb_month['temp_air'].mean()
    
    # PVGIS statistics (if available)
    if pvgis_available:
        pvgis_month = pvgis_data[pvgis_data.index.month == month]
        stats['pvgis_ghi_avg'] = pvgis_month['ghi'].mean()
        stats['pvgis_temp_avg'] = pvgis_month['temp_air'].mean()
    
    monthly_stats.append(stats)

stats_df = pd.DataFrame(monthly_stats)
print("\nMonthly Comparison Between Data Sources:")
print(stats_df.round(2))

# Plot comparison
if pvgis_available:
    fig, axes = plt.subplots(2, 1, figsize=(12, 8))
    
    axes[0].plot(stats_df['month'], stats_df['nsrdb_ghi_avg'], 
                'o-', label='NSRDB PSM3', linewidth=2)
    axes[0].plot(stats_df['month'], stats_df['pvgis_ghi_avg'], 
                's-', label='PVGIS', linewidth=2)
    axes[0].set_ylabel('GHI (W/m²)')
    axes[0].set_title('Monthly Average Global Horizontal Irradiance')
    axes[0].legend()
    axes[0].grid(True)
    
    axes[1].plot(stats_df['month'], stats_df['nsrdb_temp_avg'], 
                'o-', label='NSRDB', linewidth=2)
    axes[1].plot(stats_df['month'], stats_df['pvgis_temp_avg'], 
                's-', label='PVGIS', linewidth=2)
    axes[1].set_xlabel('Month')
    axes[1].set_ylabel('Temperature (°C)')
    axes[1].set_title('Monthly Average Air Temperature')
    axes[1].legend()
    axes[1].grid(True)
    
    plt.tight_layout()
    plt.show()

Processing Downloaded Files

import pvlib
from pvlib import iotools
import glob
import pandas as pd

# Process multiple TMY files in a directory
tmy_files = glob.glob('weather_data/*.csv')
weather_summary = []

for file in tmy_files:
    try:
        # Try reading as TMY3 first
        data, metadata = iotools.read_tmy3(file)
        
        # Calculate annual statistics
        annual_stats = {
            'file': file,
            'location': metadata.get('Name', 'Unknown'),
            'latitude': metadata.get('Latitude', None),
            'longitude': metadata.get('Longitude', None),
            'annual_ghi': data['ghi'].sum() / 1000,  # kWh/m²/year
            'annual_dni': data['dni'].sum() / 1000,
            'avg_temp': data['temp_air'].mean(),
            'max_temp': data['temp_air'].max(),
            'min_temp': data['temp_air'].min(),
            'avg_wind': data['wind_speed'].mean(),
            'max_wind': data['wind_speed'].max()
        }
        weather_summary.append(annual_stats)
        
    except Exception as e:
        print(f"Error reading {file}: {e}")

# Create summary dataframe
summary_df = pd.DataFrame(weather_summary)
summary_df = summary_df.round(2)

print("Weather Data Summary:")
print(summary_df.to_string(index=False))

# Find locations with highest solar resource
top_solar = summary_df.nlargest(3, 'annual_ghi')
print("\nTop 3 locations by annual GHI:")
print(top_solar[['location', 'latitude', 'longitude', 'annual_ghi']])

Error Handling and Data Validation

import pvlib
from pvlib import iotools
import pandas as pd
import numpy as np

def validate_weather_data(data, location_name="Unknown"):
    """
    Validate weather data for common issues.
    """
    print(f"\nValidating weather data for: {location_name}")
    
    issues = []
    
    # Check for missing data
    missing_data = data.isnull().sum()
    if missing_data.sum() > 0:
        issues.append(f"Missing data found: {missing_data[missing_data > 0].to_dict()}")
    
    # Check for negative irradiance
    if 'ghi' in data.columns:
        negative_ghi = (data['ghi'] < 0).sum()
        if negative_ghi > 0:
            issues.append(f"Negative GHI values: {negative_ghi}")
    
    # Check for unrealistic temperatures
    if 'temp_air' in data.columns:
        extreme_temps = ((data['temp_air'] < -50) | (data['temp_air'] > 60)).sum()
        if extreme_temps > 0:
            issues.append(f"Extreme temperature values: {extreme_temps}")
    
    # Check for unrealistic wind speeds
    if 'wind_speed' in data.columns:
        high_wind = (data['wind_speed'] > 50).sum()  # > 50 m/s is very rare
        if high_wind > 0:
            issues.append(f"Unusually high wind speeds: {high_wind}")
    
    # Check time series continuity
    if isinstance(data.index, pd.DatetimeIndex):
        time_gaps = pd.Series(data.index).diff().dropna()
        expected_freq = time_gaps.mode()[0] if len(time_gaps) > 0 else pd.Timedelta(hours=1)
        large_gaps = (time_gaps > expected_freq * 1.5).sum()
        if large_gaps > 0:
            issues.append(f"Time series gaps found: {large_gaps}")
    
    if issues:
        print("Issues found:")
        for issue in issues:
            print(f"  - {issue}")
    else:
        print("✓ No major issues detected")
    
    return len(issues) == 0

# Example usage with error handling
def robust_data_loading(source, **kwargs):
    """
    Robustly load weather data with fallback options.
    """
    try:
        if source == 'nsrdb':
            data, metadata = iotools.get_psm3(**kwargs)
            location_name = metadata.get('Location ID', 'NSRDB Location')
            
        elif source == 'pvgis':
            data, metadata, inputs = iotools.get_pvgis_tmy(**kwargs)
            location_name = f"PVGIS ({kwargs.get('latitude')}, {kwargs.get('longitude')})"
            
        elif source == 'tmy3':
            data, metadata = iotools.read_tmy3(**kwargs)
            location_name = metadata.get('Name', 'TMY3 Location')
            
        else:
            raise ValueError(f"Unknown source: {source}")
        
        # Validate the loaded data
        is_valid = validate_weather_data(data, location_name)
        
        if not is_valid:
            print(f"Warning: Data quality issues detected for {location_name}")
        
        return data, metadata, is_valid
        
    except Exception as e:
        print(f"Error loading data from {source}: {e}")
        return None, None, False

# Test robust loading
lat, lon = 40.0150, -105.2705  # Boulder, CO

# Try multiple sources with fallback
for source in ['nsrdb', 'pvgis', 'tmy3']:
    if source == 'nsrdb':
        kwargs = {
            'latitude': lat, 'longitude': lon,
            'api_key': 'DEMO_KEY', 'email': 'test@example.com',
            'names': 'tmy'
        }
    elif source == 'pvgis':
        kwargs = {'latitude': lat, 'longitude': lon}
    else:
        kwargs = {'filename': 'local_tmy_file.csv'}
        
    data, metadata, valid = robust_data_loading(source, **kwargs)
    
    if data is not None and valid:
        print(f"✓ Successfully loaded valid data from {source}")
        break
    else:
        print(f"✗ Failed to load valid data from {source}")

Install with Tessl CLI

npx tessl i tessl/pypi-pvlib@0.13.2

docs

atmosphere.md

bifacial.md

clearsky.md

iam.md

index.md

inverter.md

iotools.md

irradiance.md

losses.md

pvsystem.md

solar-position.md

spectrum.md

temperature.md

tile.json