Multi-site simulations
GSEE can compute many sites at once on a vectorized core that operates on (time, site) arrays. Two entry points wrap this:
gsee.api.run_sites(data, ...)for an arbitrary list of sites (e.g. a portfolio of PV systems), andgsee.api.run_grid(data, ...)for a regular latitude/longitude grid (e.g. reanalysis data), which is stacked to a flat site list internally and unstacked again.
Input data format
run_sites expects an xarray.Dataset with dimensions time and site:
time: uniformly spaced timestep starts, interpreted as UTC; hourly or finer (sub-hourly data must supplydiffuse_fraction)- coordinates
lat(site)andlon(site)in degrees - variables
global_horizontal(W/m2) anddiffuse_fraction, and optionallytemperature(degrees Celsius; 20 °C is assumed if absent)
import numpy as np
import pandas as pd
import xarray as xr
dataset = xr.Dataset(
{
"global_horizontal": (("time", "site"), ghi_array),
"diffuse_fraction": (("time", "site"), diffuse_fraction_array),
"temperature": (("time", "site"), temperature_array),
},
coords={
"time": pd.date_range("2019-01-01", "2019-12-31 23:00", freq="1h"),
"site": ["a", "b", "c"],
"lat": ("site", [47.4, -33.9, 78.3]),
"lon": ("site", [8.5, 18.4, 15.5]),
},
)
run_grid expects the same variables over (time, lat, lon) instead.
Running
import gsee.api
result = gsee.api.run_sites(
dataset,
tilt=30, # degrees
azim=180, # degrees, 180 = towards equator
tracking=0, # 0 = fixed, 1 = 1-axis, 2 = 2-axis
capacity=1000, # W
)
result["pv"] # (time, site) power output in W
All the options of gsee.pv.run_model are available (technology, inverter_capacity, use_inverter, system_loss, ... — see PV models). The results are physically equivalent to running gsee.pv.run_model per site. However, sunrise and sunset times come from a vectorized hour-angle method rather than the iterative SPA routine, so the numerical results are not 1:1 the same.
Per-site parameters
tilt, azim, capacity, inverter_capacity and albedo accept either a scalar (same for all sites) or an array with one value per site. For tilt, a callable evaluated per site latitude is also possible:
result = gsee.api.run_sites(
dataset,
tilt=gsee.pv.optimal_tilt, # function of latitude
azim=180,
tracking=0,
capacity=np.array([1000.0, 5000.0, 2000.0]), # per site
)
Sites on the southern hemisphere are handled automatically (the azimuth convention flips per site).
Performance and memory
Several options control how large runs execute:
- Site chunking (
chunk_size): sites are processed in chunks (by default sized to ~64 MB arrays), so the full(time, site)intermediate arrays are not loaded at once. - Parallel execution (
workers=N): chunks run onNworker processes, with a bounded number of chunks in flight. - Single precision (
dtype="float32"): halves memory use for inputs and everything following after the solar position computation (which always runs in float64 for accuracy).
In addition, input data is loaded one site chunk at a time. You can pass a lazily loaded Dataset such as from xr.open_dataset("data.nc", chunks={"lat": 10, "lon": 10}). Also possible is an open zarr store. Inputs stream from disk instead of being read into memory at the start.
data = xr.open_dataset("merra2_year.nc", chunks={"lat": 20, "lon": 20})
result = gsee.api.run_grid(
data,
tilt=gsee.pv.optimal_tilt,
azim=180,
tracking=0,
capacity=1000,
workers=8,
dtype="float32",
)
Speeding up single-site runs
The vectorized solar position core can also accelerate the classic single-site run_model path, which spends most of its time computing solar positions:
angles = gsee.api.sun_angles_frame(data.index, coords)
result = gsee.pv.run_model(data, coords=coords, angles=angles, ...)
This is useful when running run_model repeatedly over the same time index (e.g. multiple configurations for the same site and year can re-use angles after it has been computed once).