PySAM with API (API Key Required)#
import pvdeg
from pvdeg import TEST_DATA_DIR
import pandas as pd
import os
import pickle
import xarray as xr
Pysam#
rundown on pysam…
https://nrel-pysam.readthedocs.io/en/main/inputs-from-sam.html
PVGIS#
Only works with PVGIS
weather_pvgis, meta_pvgis = pvdeg.weather.get(
database="PVGIS", id=(25.783388, -81.189029)
)
results = pvdeg.pysam.pysam(
weather_df=weather_pvgis,
meta=meta_pvgis,
pv_model="pvsamv1",
pv_model_default="FlatPlatePVCommercial",
)
results["annual_energy"]
Local Geospatial#
Using PySAM with geospatial data requires proper formatting of the weather DataFrame to match PySAM’s expectations.
GEO_META = pd.read_csv(os.path.join(TEST_DATA_DIR, "summit-meta.csv"), index_col=0)
GEO_WEATHER = xr.open_dataset(os.path.join(TEST_DATA_DIR, "summit-weather.nc"))
Local Geospatial - PySAM Integration#
The geospatial weather data is in half-hourly format (17520 timesteps) but PySAM expects hourly data (8760 timesteps). The wrapper function below handles this conversion automatically.
# this is just a wrapper to grab the result we want
def pysam_annual_energy(
weather_df, meta, pv_model="pvsamv1", pv_model_default="FlatPlatePVCommercial"
):
# Drop the gid column if present (added by geospatial conversion)
weather_df = weather_df.drop(columns=["gid"])
# Resample half-hourly data to hourly (PySAM expects hourly)
weather_df = weather_df.resample("h").mean()
results = pvdeg.pysam.pysam(
weather_df=weather_df,
meta=meta,
pv_model=pv_model,
pv_model_default=pv_model_default,
)
return results["annual_energy"]
# Select a small subset (2 gids) for demonstration to avoid timeouts
# PySAM calculations are computationally expensive
subset_gids = GEO_META.index[:2]
GEO_META_SUB = GEO_META.loc[subset_gids]
GEO_WEATHER_SUB = GEO_WEATHER.sel(gid=subset_gids)
template = pvdeg.geospatial.output_template(
ds_gids=GEO_WEATHER_SUB,
shapes={
"Annual Energy": ("gid",),
},
)
geo_res = pvdeg.geospatial.analysis(
weather_ds=GEO_WEATHER_SUB,
meta_df=GEO_META_SUB,
func=pysam_annual_energy,
template=template,
)
geo_res
NSRDB API#
weather_db = "PSM4"
weather_id = (25.783388, -80.189029)
weather_arg = {"api_key": "DEMO_KEY", "email": "user@mail.com", "map_variables": True}
weather_df, meta = pvdeg.weather.get(weather_db, weather_id, **weather_arg)
Geospatial Scenario#
location_grabber = pvdeg.GeospatialScenario()
location_grabber.addLocation(country="United States", downsample_factor=80)
location_grabber.plot_coords()
geo_weather, geo_meta = location_grabber.geospatial_data
# Select a small subset (2 gids) for demonstration to avoid timeouts
# PySAM calculations are computationally expensive
subset_gids = geo_meta.index[:2]
geo_meta_sub = geo_meta.loc[subset_gids]
geo_weather_sub = geo_weather.sel(gid=subset_gids)
template = pvdeg.geospatial.output_template(
ds_gids=geo_weather_sub,
shapes={
"pysam_annual_energy": ("gid",),
},
)
geo_res = pvdeg.geospatial.analysis(
weather_ds=geo_weather_sub,
meta_df=geo_meta_sub,
func=pysam_annual_energy, # using wrapper from before
template=template,
)
pvdeg.geospatial.plot_sparse_analysis(geo_res, data_var="pysam_annual_energy")
# Check weather data time dimension compatibility
if "time" in GEO_WEATHER.dims:
times = pd.to_datetime(GEO_WEATHER["time"].values)
years = times.year
unique_years = set(years)
if len(unique_years) != 1:
print(
f"Warning: Weather data contains multiple years: {unique_years}. Pysam expects a single year."
)
hours_per_year = times.size / len(unique_years)
if hours_per_year not in [8760, 8784]:
print(
f"Warning: Unexpected number of timesteps per year: {hours_per_year}. Expected 8760 or 8784."
)
else:
print("Warning: No 'time' dimension found in weather data. Pysam may fail.")