Skip to content

Timeseries Calculations

For multi-timestep simulations (hours, days, or longer), pass a list of weather objects to calculate(). Thermal state is carried between timesteps, and results are saved to disk as they are computed.

Thermal State Management

Ground and wall temperatures depend on accumulated heating from previous hours (thermal inertia). When given multiple weather timesteps, calculate() manages this via a ThermalState object, producing accurate ground-level longwave radiation values.

Basic Timeseries

import solweig

# Load surface
surface = solweig.SurfaceData.prepare(
    dsm="data/dsm.tif",
    working_dir="cache/",
)

# Load weather from EPW file
weather_list = solweig.Weather.from_epw(
    "data/weather.epw",
    start="2025-07-01",
    end="2025-07-03",
)
location = solweig.Location.from_epw("data/weather.epw")

# Calculate all timesteps — results saved to output/
summary = solweig.calculate(
    surface=surface,
    location=location,
    weather=weather_list,
    output_dir="output/",
    outputs=["tmrt", "shadow"],
)

print(f"Processed {len(summary)} timesteps")

This creates timestamped GeoTIFFs and summary grids:

output/
├── tmrt/
│   ├── tmrt_20250701_0000.tif
│   ├── tmrt_20250701_0100.tif
│   └── ...
├── shadow/
│   ├── shadow_20250701_0000.tif
│   └── ...
├── summary/
│   ├── tmrt_mean.tif
│   ├── tmrt_max.tif
│   ├── utci_mean.tif
│   └── ...
└── run_metadata.json            # All parameters for reproducibility

If only summary statistics are required (mean/max/min Tmrt, UTCI, sun hours), omit outputs — no per-timestep files are written, but the summary grids are still saved to output/summary/.

summary = solweig.calculate(
    surface=surface,
    location=location,
    weather=weather_list,
    output_dir="output/",  # Summary grids + metadata saved here
)

Inspecting Results

Summary report

report() returns a text summary of the run:

summary = solweig.calculate(
    surface=surface,
    weather=weather_list,
    output_dir="output/",
    outputs=["tmrt", "shadow"],
)
print(summary.report())

In Jupyter notebooks, placing summary as the last expression in a cell renders the report via _repr_html_.

Per-timestep timeseries

summary.timeseries contains 1-D arrays of spatial means at each timestep, providing an overview of how conditions evolved over the simulation:

ts = summary.timeseries
print(ts.datetime)       # timestamps
print(ts.ta)             # air temperature per step
print(ts.tmrt_mean)      # spatial mean Tmrt per step
print(ts.utci_mean)      # spatial mean UTCI per step
print(ts.sun_fraction)   # fraction of sunlit pixels per step

Plotting

plot() produces a multi-panel figure showing temperature, radiation, sun exposure, and meteorological inputs over time:

summary.plot()                                  # interactive display
summary.plot(save_path="output/timeseries.png") # save to file

Requires matplotlib (pip install matplotlib).

Per-Timestep UTCI and PET

UTCI and PET summary grids (mean, max, min, day/night averages) are included in the returned TimeseriesSummary. To save per-timestep UTCI or PET GeoTIFFs, include them in outputs:

summary = solweig.calculate(
    surface=surface,
    weather=weather_list,
    output_dir="output/",
    outputs=["tmrt", "utci"],  # per-timestep Tmrt + UTCI saved to disk
)

The indices are computed inline during the main loop (UTCI uses a Rust polynomial implementation; PET uses an iterative solver).

Memory Management for Long Simulations

Processing in chunks

For extended simulations (weeks or months), processing in daily chunks is recommended:

for chunk_start in range(0, len(weather_list), 24):
    chunk = weather_list[chunk_start:chunk_start + 24]
    results = solweig.calculate(
        surface=surface,
        location=location,
        weather=chunk,
        output_dir=f"output/",
    )

Performance

Grid size Surface prep (SVF) Per timestep 72 timesteps
100x100 ~5 s ~10 ms ~1 s
200x200 ~67 s ~20 ms ~2 s
500x500 ~10 min ~100 ms ~8 s

SVF is computed by SurfaceData.prepare(). For file workflows, a persistent working_dir avoids recomputing SVF on each run.

Run Metadata

calculate() saves a run_metadata.json file recording all parameters used. This can be loaded for reproducibility:

metadata = solweig.load_run_metadata("output/run_metadata.json")
print(f"Version: {metadata['solweig_version']}")
print(f"Timesteps: {metadata['timeseries']['timesteps']}")
print(f"Date range: {metadata['timeseries']['start']} to {metadata['timeseries']['end']}")