Skip to content

Data Classes

SurfaceData

solweig.SurfaceData dataclass

Surface/terrain data for SOLWEIG calculations.

Only dsm is required. Other rasters are optional and will be treated as absent if not provided.

Attributes:

Name Type Description
dsm NDArray[floating]

Digital Surface Model (elevation in meters). Required.

cdsm NDArray[floating] | None

Canopy Digital Surface Model (vegetation heights). Optional.

dem NDArray[floating] | None

Digital Elevation Model (ground elevation). Optional.

tdsm NDArray[floating] | None

Trunk Digital Surface Model (trunk zone heights). Optional.

land_cover NDArray[integer] | None

Land cover classification grid (UMEP standard IDs). Optional. IDs: 0=paved, 1=asphalt, 2=buildings, 5=grass, 6=bare_soil, 7=water. When provided, albedo and emissivity are derived from land cover.

wall_height NDArray[floating] | None

Preprocessed wall heights (meters). Optional. If not provided, computed during preparation from DSM.

wall_aspect NDArray[floating] | None

Preprocessed wall aspects (degrees, 0=N). Optional. If not provided, computed during preparation from DSM.

svf SvfArrays | None

Preprocessed Sky View Factor arrays. Optional. If not provided, must be prepared explicitly before calculate() (e.g. via SurfaceData.prepare() or compute_svf()).

shadow_matrices ShadowArrays | None

Preprocessed shadow matrices for anisotropic sky. Optional.

pixel_size float

Pixel size in meters. Default 1.0.

trunk_ratio float

Ratio for auto-generating TDSM from CDSM. Default 0.25.

dsm_relative bool

Whether DSM contains relative heights (above ground) rather than absolute elevations. Default False. If True, DEM is required and preprocess() converts DSM to absolute via DSM + DEM.

cdsm_relative bool

Whether CDSM contains relative heights. Default True. If True and preprocess() is not called, a warning is issued.

tdsm_relative bool

Whether TDSM contains relative heights. Default True. If True and preprocess() is not called, a warning is issued.

Note

Albedo and emissivity are derived internally from land_cover using standard UMEP parameters. They cannot be directly specified.

Note

max_height is auto-computed from dsm as: np.nanmax(dsm) - np.nanmin(dsm)

Height Conventions

Each raster layer can independently use relative or absolute heights. The per-layer flags (dsm_relative, cdsm_relative, tdsm_relative) control the convention for each layer.

Relative Heights (height above ground): - CDSM/TDSM: vegetation height above ground (e.g., 6m tree) - DSM: building/surface height above ground (requires DEM) - Typical range: 0-40m for CDSM, 0-10m for TDSM - Must call preprocess() before calculations

Absolute Heights (elevation above sea level): - Values in the same vertical reference system - Example: DSM=127m, CDSM=133m means 6m vegetation - No preprocessing needed

The internal algorithms (Rust) always use absolute heights. The preprocess() method converts relative → absolute using: dsm_absolute = dem + dsm_relative (requires DEM) cdsm_absolute = base + cdsm_relative tdsm_absolute = base + tdsm_relative where base = DEM if available, else base = DSM.

Example

Relative CDSM (common case):

surface = SurfaceData(dsm=dsm, cdsm=cdsm_rel) surface.preprocess() # Converts CDSM to absolute

Absolute CDSM:

surface = SurfaceData(dsm=dsm, cdsm=cdsm_abs, cdsm_relative=False)

Mixed: absolute DSM, relative CDSM, absolute TDSM:

surface = SurfaceData( dsm=dsm, cdsm=cdsm, tdsm=tdsm, cdsm_relative=True, tdsm_relative=False, ) surface.preprocess() # Only converts CDSM

Relative DSM (requires DEM):

surface = SurfaceData(dsm=ndsm, dem=dem, dsm_relative=True) surface.preprocess() # Converts DSM to absolute via DEM + nDSM

max_height property

Auto-compute maximum height difference for shadow buffer calculation.

Considers both DSM (buildings) and CDSM (vegetation) since both cast shadows. Returns max elevation minus ground level.

This property is conservative by design for shadow buffer sizing: CDSM is included whenever present, independent of current per-call vegetation switches.

shape property

Return DSM shape (rows, cols).

crs property

Return CRS as WKT string, or None if not set.

valid_mask property

Return computed valid mask, or None if not yet computed.

prepare(dsm, working_dir=None, cdsm=None, dem=None, tdsm=None, land_cover=None, wall_height=None, wall_aspect=None, svf_dir=None, bbox=None, pixel_size=None, trunk_ratio=0.25, dsm_relative=False, cdsm_relative=True, tdsm_relative=True, force_recompute=False, feedback=None) classmethod

Prepare surface data for SOLWEIG calculations.

Loads inputs, computes walls, SVF, and shadow matrices, and returns a ready-to-use :class:SurfaceData. This is the only setup step needed before calling :func:calculate.

Accepts either file paths (GeoTIFF) or numpy arrays:

  • File mode (dsm is a path): loads and aligns rasters, caches results in working_dir for fast reuse.
  • Array mode (dsm is an ndarray): works in memory. pixel_size is required; working_dir is not needed.

Parameters:

Name Type Description Default
dsm str | Path | NDArray[floating]

DSM as a GeoTIFF path or numpy array (required).

required
working_dir str | Path | None

Cache directory (required for file mode).

None
cdsm str | Path | NDArray[floating] | None

Canopy height model (tree tops). Optional.

None
dem str | Path | NDArray[floating] | None

Ground elevation model. Optional.

None
tdsm str | Path | NDArray[floating] | None

Trunk height model. Optional; auto-generated from CDSM if not provided (using trunk_ratio).

None
land_cover str | Path | NDArray[integer] | None

Land cover classification grid. Optional.

None
wall_height str | Path | NDArray[floating] | None

Pre-computed wall heights. Optional; computed from DSM if not provided.

None
wall_aspect str | Path | NDArray[floating] | None

Pre-computed wall aspects. Optional; computed from DSM if not provided.

None
svf_dir str | Path | None

Directory with existing SVF files (file mode only).

None
bbox list[float] | None

Bounding box [minx, miny, maxx, maxy] (file mode only).

None
pixel_size float | None

Pixel size in meters. Required for array mode; extracted from GeoTIFF in file mode.

None
trunk_ratio float

Trunk-to-canopy height ratio for auto TDSM. Default 0.25.

0.25
dsm_relative bool

DSM values are height above ground (not elevation). Default False.

False
cdsm_relative bool

CDSM values are height above ground. Default True.

True
tdsm_relative bool

TDSM values are height above ground. Default True.

True
force_recompute bool

Recompute walls/SVF even if cached (file mode only).

False
feedback Any

QGIS QgsProcessingFeedback for progress/cancellation.

None

Returns:

Type Description
SurfaceData

SurfaceData ready for :func:calculate.

Example::

# From GeoTIFF files
surface = SurfaceData.prepare(dsm="dsm.tif", working_dir="cache/")

# From numpy arrays
surface = SurfaceData.prepare(dsm=dsm_array, pixel_size=1.0)

preprocess()

Convert layers from relative to absolute heights based on per-layer flags.

Converts each layer that is flagged as relative (dsm_relative, cdsm_relative, tdsm_relative) to absolute heights. Layers already flagged as absolute are left unchanged.

This method: 1. Converts DSM from relative to absolute if dsm_relative=True (requires DEM: dsm_absolute = dem + dsm_relative) 2. Auto-generates TDSM from CDSM * trunk_ratio if TDSM is not provided 3. Converts CDSM from relative to absolute if cdsm_relative=True 4. Converts TDSM from relative to absolute if tdsm_relative=True 5. Zeros out vegetation pixels with height < 0.1m

Note

This method modifies arrays in-place and clears the per-layer relative flags once conversion is done.

compute_svf()

Compute SVF and shadow matrices, storing them on this instance.

Only needed when constructing SurfaceData manually. :meth:prepare calls this automatically.

Example::

surface = SurfaceData(dsm=dsm, pixel_size=1.0)
surface.preprocess()
surface.compute_svf()
result = calculate(surface, weather)

fill_nan(tolerance=0.1)

Fill NaN in surface layers using DEM as ground reference.

NaN in DSM/CDSM/TDSM means "no data, assume ground level." After filling, values within tolerance of ground are clamped to exactly the ground value to avoid shadow/SVF noise from resampling jitter.

Fill rules
  • DSM NaN → DEM value (if DEM provided, else left as NaN)
  • CDSM NaN → base value (DEM if available, else DSM)
  • TDSM NaN → base value (DEM if available, else DSM)
  • DEM NaN → not filled (DEM is the ground-truth baseline)

Works identically for relative and absolute height conventions.

Parameters:

Name Type Description Default
tolerance float

Height difference (m) below which a surface pixel is considered "at ground" and clamped. Default 0.1 m.

0.1

compute_valid_mask()

Compute combined valid mask: True where ALL ground-reference layers have finite data.

A pixel is valid only if DSM (and DEM/walls if provided) have finite values. CDSM/TDSM are excluded — NaN vegetation means "at ground", not "invalid pixel". Call fill_nan() before this to fill vegetation NaN with ground values.

Returns:

Type Description
NDArray[bool_]

Boolean array with same shape as DSM. True = valid pixel.

apply_valid_mask()

Set NaN in ALL layers where ANY layer has nodata.

Ensures consistent nodata across all surface arrays. Must call compute_valid_mask() first (or it will be called automatically).

crop_to_valid_bbox()

Crop all arrays to minimum bounding box of valid pixels.

Eliminates edge NaN bands to reduce wasted computation. Updates geotransform to reflect the new origin.

Returns:

Type Description
tuple[int, int, int, int]

(row_start, row_end, col_start, col_end) of the crop window.

save_cleaned(output_dir)

Save cleaned, aligned rasters to disk for inspection.

Writes all present layers to output_dir/cleaned/ as GeoTIFFs.

Parameters:

Name Type Description Default
output_dir str | Path

Parent directory. Files are saved under output_dir/cleaned/.

required

get_buffer_pool()

Get or create a buffer pool for this surface.

The buffer pool provides pre-allocated numpy arrays that can be reused across timesteps during timeseries calculations. This reduces memory allocation overhead and GC pressure.

Returns:

Type Description
BufferPool

BufferPool sized to this surface's grid dimensions.

Example

pool = surface.get_buffer_pool() temp = pool.get_zeros("ani_lum") # First call allocates temp = pool.get_zeros("ani_lum") # Second call reuses same memory

clear_buffers()

Clear the buffer pool to free memory.

Call this after completing a timeseries calculation to release the pre-allocated arrays.

get_land_cover_properties(params=None)

Derive surface properties from land cover grid.

Parameters:

Name Type Description Default
params SimpleNamespace | None

Optional loaded parameters from JSON file (via load_params()). When provided, land cover properties are read from the params. When None, uses built-in defaults matching parametersforsolweig.json.

None

Returns:

Type Description
NDArray[floating]

Tuple of (albedo_grid, emissivity_grid, tgk_grid, tstart_grid, tmaxlst_grid).

NDArray[floating]

If land_cover is None, returns defaults.

Land cover parameters from Lindberg et al. 2008, 2016 (parametersforsolweig.json): - TgK (Ts_deg): Temperature coefficient for surface heating - Tstart: Temperature offset at sunrise - TmaxLST: Hour of maximum local surface temperature


Location

solweig.Location dataclass

Geographic location for sun position calculations.

Attributes:

Name Type Description
latitude float

Latitude in degrees (north positive).

longitude float

Longitude in degrees (east positive).

altitude float

Altitude above sea level in meters. Default 0.

utc_offset int

UTC offset in hours. Default 0.

from_dsm_crs(dsm_path, utc_offset=0, altitude=0.0) classmethod

Extract location from DSM raster's CRS by converting center point to WGS84.

Parameters:

Name Type Description Default
dsm_path str | Path

Path to DSM GeoTIFF file with valid CRS.

required
utc_offset int

UTC offset in hours. Must be provided by user.

0
altitude float

Altitude above sea level in meters. Default 0.

0.0

Returns:

Type Description
Location

Location object with lat/lon from DSM center point.

Raises:

Type Description
ValueError

If DSM has no CRS or CRS conversion fails.

Example

location = Location.from_dsm_crs("dsm.tif", utc_offset=2)

from_surface(surface, utc_offset=None, altitude=0.0) classmethod

Extract location from SurfaceData's CRS by converting center point to WGS84.

This avoids reloading the DSM raster when you already have loaded SurfaceData.

Parameters:

Name Type Description Default
surface SurfaceData

SurfaceData instance loaded from GeoTIFF.

required
utc_offset int | None

UTC offset in hours. If not provided, defaults to 0 with a warning. Always provide this explicitly for correct sun position calculations.

None
altitude float

Altitude above sea level in meters. Default 0.

0.0

Returns:

Type Description
Location

Location object with lat/lon from DSM center point.

Raises:

Type Description
ValueError

If surface has no CRS metadata.

ImportError

If pyproj is not installed.

Example

surface = SurfaceData.from_geotiff("dsm.tif") location = Location.from_surface(surface, utc_offset=2) # Athens: UTC+2

from_epw(path) classmethod

Extract location from an EPW weather file header.

The EPW LOCATION line contains latitude, longitude, timezone offset, and elevation — everything needed for a complete Location.

Parameters:

Name Type Description Default
path str | Path

Path to the EPW file.

required

Returns:

Type Description
Location

Location with lat, lon, utc_offset, and altitude from the EPW header.

Raises:

Type Description
FileNotFoundError

If the EPW file doesn't exist.

ValueError

If the EPW header is malformed.

Example

location = Location.from_epw("madrid.epw")

Location(latitude=40.45, longitude=-3.55, altitude=667.0, utc_offset=1)

to_sun_position_dict()

Convert to dict format expected by sun_position module.


Weather

solweig.Weather dataclass

Weather/meteorological data for a single timestep.

Only basic measurements are required. Derived values (sun position, direct/diffuse radiation split) are computed automatically.

Attributes:

Name Type Description
datetime datetime

Date and time of measurement (end of interval).

ta float

Air temperature in °C.

rh float

Relative humidity in % (0-100).

global_rad float

Global solar radiation in W/m².

ws float

Wind speed in m/s. Default 1.0.

pressure float

Atmospheric pressure in hPa. Default 1013.25.

timestep_minutes float

Data timestep in minutes. Default 60.0. Sun position is computed at datetime - timestep/2 to represent the center of the measurement interval.

measured_direct_rad float | None

Optional measured direct beam radiation in W/m². If provided with measured_diffuse_rad, these override the computed values.

measured_diffuse_rad float | None

Optional measured diffuse radiation in W/m². If provided with measured_direct_rad, these override the computed values.

Auto-computed (after calling compute_derived()): sun_altitude: Sun altitude angle in degrees. Initial: 0.0. sun_azimuth: Sun azimuth angle in degrees. Initial: 0.0. sun_zenith: Sun zenith angle in degrees. Initial: 90.0. direct_rad: Direct beam radiation in W/m². Initial: 0.0. diffuse_rad: Diffuse radiation in W/m². Initial: 0.0. clearness_index: Clearness index (0-1). Initial: 1.0. altmax: Maximum sun altitude for the day in degrees. Initial: 45.0.

is_daytime property

Check if sun is above horizon.

compute_derived(location)

Compute derived values: sun position and radiation split.

Must be called before using sun_altitude, sun_azimuth, direct_rad, or diffuse_rad.

Sun position is calculated at the center of the measurement interval (datetime - timestep/2), which is standard for meteorological data where measurements are averaged over the interval.

Parameters:

Name Type Description Default
location Location

Geographic location for sun position calculation.

required

from_values(ta, rh, global_rad, datetime=None, ws=1.0, **kwargs) classmethod

Quick factory for creating Weather with minimal required values.

Useful for testing and single-timestep calculations where you just need to specify the essential parameters.

Parameters:

Name Type Description Default
ta float

Air temperature in °C.

required
rh float

Relative humidity in % (0-100).

required
global_rad float

Global solar radiation in W/m².

required
datetime datetime | None

Date and time. If None, uses current time.

None
ws float

Wind speed in m/s. Default 1.0.

1.0
**kwargs Any

Additional Weather parameters (pressure, etc.)

{}

Returns:

Type Description
Weather

Weather object ready for calculation.

Example
Quick weather for testing

weather = Weather.from_values(ta=25, rh=50, global_rad=800)

With specific datetime

weather = Weather.from_values( ta=30, rh=60, global_rad=900, datetime=datetime(2025, 7, 15, 14, 0) )

from_epw(path, start=None, end=None, hours=None, year=None) classmethod

Load weather data from an EnergyPlus Weather (EPW) file.

Parameters:

Name Type Description Default
path str | Path

Path to the EPW file.

required
start str | datetime | None

Start date/datetime. Can be: - ISO date string "YYYY-MM-DD" or "MM-DD" (for TMY with year=None) - datetime object If None, uses first date in file.

None
end str | datetime | None

End date/datetime (inclusive). Same format as start. If None, uses same as start (single day).

None
hours list[int] | None

List of hours to include (0-23). If None, includes all hours.

None
year int | None

Year override for TMY files. If None and start/end use MM-DD format, matches any year in the file.

None

Returns:

Type Description
list[Weather]

List of Weather objects for each timestep in the requested range.

Raises:

Type Description
FileNotFoundError

If the EPW file doesn't exist.

ValueError

If requested dates are outside the EPW file's date range.

Example
Load a single day

weather_list = Weather.from_epw("weather.epw", start="2023-07-15", end="2023-07-15")

Load with specific hours only (daylight hours)

weather_list = Weather.from_epw( "weather.epw", start="2023-07-15", end="2023-07-16", hours=[6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] )

TMY file (year-agnostic)

weather_list = Weather.from_epw("tmy.epw", start="07-15", end="07-15")

from_umep_met(paths, resample_hourly=True, start=None, end=None) classmethod

Load weather data from UMEP/SUEWS meteorological forcing files.

The UMEP met format is space-separated with columns: %iy, id, it, imin, Q*, QH, QE, Qs, Qf, Wind, RH, Td, press, rain, Kdn, snow, ldown, fcld, wuh, xsmd, lai_hr, Kdiff, Kdir, Wd

Missing values are encoded as -999.

Parameters:

Name Type Description Default
paths str | Path | list[str | Path]

Path(s) to UMEP met file(s). Multiple files are concatenated (e.g., one per month).

required
resample_hourly bool

If True, keep only on-the-hour rows (imin=0). Default True since SOLWEIG works best with hourly data.

True
start str | datetime | None

Start date filter as "YYYY-MM-DD" or datetime. Optional.

None
end str | datetime | None

End date filter (inclusive) as "YYYY-MM-DD" or datetime. Optional.

None

Returns:

Type Description
list[Weather]

List of Weather objects sorted by datetime.

Example
Single file

weather = Weather.from_umep_met("metdata_10min_july.txt")

Multiple monthly files

weather = Weather.from_umep_met([ "metdata_10min_may.txt", "metdata_10min_june.txt", ])


HumanParams

solweig.HumanParams dataclass

Human body parameters for thermal comfort calculations.

These parameters affect how radiation is absorbed by a person. Default values represent a standard reference person.

Attributes:

Name Type Description
posture str

Body posture ("standing" or "sitting"). Default "standing".

abs_k float

Shortwave absorption coefficient. Default 0.7.

abs_l float

Longwave absorption coefficient. Default 0.97.

PET-specific parameters (used when "pet" is in outputs): age: Age in years. Default 35. weight: Body weight in kg. Default 75. height: Body height in meters. Default 1.75. sex: Biological sex (1=male, 2=female). Default 1. activity: Metabolic activity in W. Default 80. clothing: Clothing insulation in clo. Default 0.9.


ModelConfig

solweig.ModelConfig dataclass

Model configuration for SOLWEIG calculations.

Groups all computational settings in one typed object. Pure configuration - no paths or data.

Attributes:

Name Type Description
use_anisotropic_sky bool

Use Perez anisotropic sky model for diffuse radiation. Default True. Requires precomputed shadow matrices.

human HumanParams | None

Human body parameters for Tmrt calculations.

material_params SimpleNamespace | None

Optional material properties from JSON file.

outputs list[str]

Which outputs to save in timeseries calculations. Default ["tmrt"].

physics SimpleNamespace | None

Physics parameters (vegetation, posture geometry). Optional.

materials SimpleNamespace | None

Material properties (albedo, emissivity). Optional.

max_shadow_distance_m float

Maximum shadow reach in metres. Default 1000.0. Caps horizontal shadow ray distance and serves as tile overlap buffer for automatic tiled processing. On mountainous terrain, increase this to capture terrain shadows across valleys.

tile_workers int | None

Number of workers for tiled orchestration. If None, picks an adaptive default based on CPU count.

tile_queue_depth int | None

Extra queued tile tasks beyond active workers. If None, defaults to one queue slot per worker when prefetching is enabled.

prefetch_tiles bool | None

Whether to prefetch tile tasks beyond active workers. If None, runtime chooses automatically based on memory pressure.

Note

UTCI and PET are computed inline when requested via outputs=["utci"].

Examples:

Basic usage with defaults:

>>> config = ModelConfig.defaults()
>>> config.save("my_config.json")

Custom configuration:

>>> config = ModelConfig(
...     use_anisotropic_sky=True,
...     human=HumanParams(abs_k=0.7, posture="standing"),
... )

Load from legacy JSON:

>>> config = ModelConfig.from_json("parametersforsolweig.json")

__post_init__()

Validate configuration fields.

Note: human is intentionally left as None here. Default HumanParams() is instantiated by calculate() when no human params are provided.

defaults() classmethod

Create a ModelConfig with recommended defaults.

Returns:

Type Description
ModelConfig

ModelConfig with use_anisotropic_sky=True and all other

ModelConfig

fields at their dataclass defaults.

from_json(path) classmethod

Load configuration from legacy JSON parameters file.

Parameters:

Name Type Description Default
path str | Path

Path to parametersforsolweig.json

required

Returns:

Type Description
ModelConfig

ModelConfig with settings extracted from JSON

Example

config = ModelConfig.from_json("parametersforsolweig.json") config.human.abs_k # From Tmrt_params 0.7

save(path)

Save configuration to JSON file.

Parameters:

Name Type Description Default
path str | Path

Output path for JSON file

required
Example

config = ModelConfig.defaults() config.save("my_settings.json")

load(path) classmethod

Load configuration from JSON file.

Parameters:

Name Type Description Default
path str | Path

Path to JSON configuration file

required

Returns:

Type Description
ModelConfig

ModelConfig loaded from file

Example

config = ModelConfig.load("my_settings.json") results = calculate(surface, weather, config=config)


SolweigResult

solweig.SolweigResult dataclass

Results from a SOLWEIG calculation.

All output grids have the same shape as the input DSM.

Attributes:

Name Type Description
tmrt NDArray[floating]

Mean Radiant Temperature grid (°C).

utci NDArray[floating] | None

Universal Thermal Climate Index grid (°C). Optional.

pet NDArray[floating] | None

Physiological Equivalent Temperature grid (°C). Optional.

shadow NDArray[floating] | None

Shadow fraction (1.0=sunlit, 0.0=fully shaded, float).

kdown NDArray[floating] | None

Downwelling shortwave radiation (W/m²).

kup NDArray[floating] | None

Upwelling shortwave radiation (W/m²).

ldown NDArray[floating] | None

Downwelling longwave radiation (W/m²).

lup NDArray[floating] | None

Upwelling longwave radiation (W/m²).

state ThermalState | None

Thermal state for multi-timestep chaining. Optional. When state parameter was passed to calculate(), this contains the updated state for the next timestep.

to_geotiff(output_dir, timestamp=None, outputs=None, surface=None, transform=None, crs_wkt=None)

Save results to GeoTIFF files.

Creates one GeoTIFF file per output variable per timestep. Filename pattern: {output}{YYYYMMDD}.tif

Parameters:

Name Type Description Default
output_dir str | Path

Directory to write GeoTIFF files.

required
timestamp datetime | None

Timestamp for filename. If None, uses current time.

None
outputs list[str] | None

List of outputs to save. Options: "tmrt", "utci", "pet", "shadow", "kdown", "kup", "ldown", "lup". Default: ["tmrt"] (only save Mean Radiant Temperature).

None
surface SurfaceData | None

SurfaceData object (if loaded via from_geotiff, contains CRS/transform). If provided and transform/crs_wkt not specified, uses surface metadata.

None
transform list[float] | None

GDAL-style geotransform [x_origin, pixel_width, 0, y_origin, 0, -pixel_height]. If None, attempts to use surface metadata, otherwise uses identity transform.

None
crs_wkt str | None

Coordinate reference system in WKT format. If None, attempts to use surface metadata, otherwise no CRS set.

None
Example

surface, precomputed = SurfaceData.from_geotiff("dsm.tif", svf_dir="svf/") result = solweig.calculate(surface, location, weather, precomputed=precomputed) result.to_geotiff("output/", timestamp=weather.datetime, surface=surface)

Without surface metadata (explicit transform/CRS)

result.to_geotiff( ... "output/", ... timestamp=datetime(2025, 7, 15, 12, 0), ... outputs=["tmrt", "utci", "pet"], ... transform=[0, 1, 0, 0, 0, -1], ... crs_wkt="EPSG:32633", ... )

compute_utci(weather_or_ta, rh=None, wind=None)

Compute UTCI (Universal Thermal Climate Index) from this result's Tmrt.

Can be called with either a Weather object or individual values

utci = result.compute_utci(weather) utci = result.compute_utci(ta=25.0, rh=50.0, wind=2.0)

Parameters:

Name Type Description Default
weather_or_ta Weather | float

Either a Weather object, or air temperature in °C.

required
rh float | None

Relative humidity in % (required if weather_or_ta is float).

None
wind float | None

Wind speed at 10m height in m/s. Default 1.0 if not provided.

None

Returns:

Type Description
NDArray[floating]

UTCI grid (°C) with same shape as tmrt.

Example

result = solweig.calculate(surface, location, weather)

Pattern A: Pass weather object (convenient)

utci = result.compute_utci(weather)

Pattern B: Pass individual values (explicit)

utci = result.compute_utci(25.0, rh=50.0, wind=2.0)

compute_pet(weather_or_ta, rh=None, wind=None, human=None)

Compute PET (Physiological Equivalent Temperature) from this result's Tmrt.

Can be called with either a Weather object or individual values

pet = result.compute_pet(weather) pet = result.compute_pet(ta=25.0, rh=50.0, wind=2.0)

Parameters:

Name Type Description Default
weather_or_ta Weather | float

Either a Weather object, or air temperature in °C.

required
rh float | None

Relative humidity in % (required if weather_or_ta is float).

None
wind float | None

Wind speed at 10m height in m/s. Default 1.0 if not provided.

None
human HumanParams | None

Human body parameters. Uses defaults if not provided.

None

Returns:

Type Description
NDArray[floating]

PET grid (°C) with same shape as tmrt.

Note

PET uses an iterative solver and is ~50× slower than UTCI.

Example

result = solweig.calculate(surface, location, weather)

Pattern A: Pass weather object (convenient)

pet = result.compute_pet(weather)

Pattern B: Pass individual values with custom human params

pet = result.compute_pet( 25.0, rh=50.0, wind=2.0, human=HumanParams(weight=70, height=1.65) )


TimeseriesSummary

solweig.TimeseriesSummary dataclass

Aggregated summary from a SOLWEIG timeseries calculation.

All grids have the same shape as the input DSM (rows, cols).

Attributes:

Name Type Description
tmrt_mean NDArray[floating]

Mean Tmrt across all timesteps (°C).

tmrt_max NDArray[floating]

Per-pixel maximum Tmrt (°C).

tmrt_min NDArray[floating]

Per-pixel minimum Tmrt (°C).

tmrt_day_mean NDArray[floating]

Mean Tmrt during daytime (°C). NaN where no daytime data.

tmrt_night_mean NDArray[floating]

Mean Tmrt during nighttime (°C). NaN where no nighttime data.

utci_mean NDArray[floating]

Mean UTCI across all timesteps (°C).

utci_max NDArray[floating]

Per-pixel maximum UTCI (°C).

utci_min NDArray[floating]

Per-pixel minimum UTCI (°C).

utci_day_mean NDArray[floating]

Mean UTCI during daytime (°C). NaN where no daytime data.

utci_night_mean NDArray[floating]

Mean UTCI during nighttime (°C). NaN where no nighttime data.

sun_hours NDArray[floating]

Hours of direct sun per pixel.

shade_hours NDArray[floating]

Hours of shade per pixel.

utci_hours_above dict[float, NDArray[floating]]

Threshold (°C) → grid of hours exceeding that UTCI value.

n_timesteps int

Total number of timesteps processed.

n_daytime int

Number of daytime timesteps.

n_nighttime int

Number of nighttime timesteps.

shadow_available bool

Whether shadow data was available for sun/shade hours.

heat_thresholds_day list[float]

Daytime UTCI thresholds used.

heat_thresholds_night list[float]

Nighttime UTCI thresholds used.

timeseries Timeseries | None

Per-timestep scalar timeseries (spatial means over time).

__len__()

Return number of timesteps processed.

report()

Return a human-readable summary report.

Includes spatial statistics, threshold exceedance, timeseries ranges, and links to saved GeoTIFF files when available.

Returns:

Type Description
str

Multi-line report string.

empty() classmethod

Create an empty summary for zero-timestep runs.

plot(save_path=None, figsize=(14, 10), max_days=5)

Plot the per-timestep timeseries as a multi-panel figure.

Requires matplotlib. If save_path is provided, the figure is saved to that path instead of being shown interactively.

For long simulations the plot is truncated to the first max_days days so that individual diurnal cycles remain readable.

Parameters:

Name Type Description Default
save_path str | Path | None

File path to save the figure (e.g. "summary.png"). If None, calls plt.show().

None
figsize tuple[float, float]

Figure size as (width, height) in inches.

(14, 10)
max_days int

Maximum number of days to display. If the timeseries spans more than this many days, only the first max_days are plotted. Set to 0 to plot all data.

5

Raises:

Type Description
RuntimeError

If no timeseries data is available.

to_geotiff(output_dir, surface=None)

Save all summary grids to GeoTIFF files in output_dir/summary/.

Parameters:

Name Type Description Default
output_dir str | Path

Base output directory.

required
surface SurfaceData | None

SurfaceData for CRS/transform metadata. Falls back to the internal _surface reference if not provided.

None

Timeseries

solweig.Timeseries dataclass

Per-timestep scalar timeseries extracted during the calculation loop.

Each field is a 1-D array of length n_timesteps, holding the spatial mean (or fraction) for that metric at each timestep. Useful for plotting how conditions evolve over the simulation period.

Attributes:

Name Type Description
datetime list[datetime]

Timestamp per step.

ta NDArray[floating]

Air temperature per step (°C) — from weather input.

rh NDArray[floating]

Relative humidity per step (%) — from weather input.

ws NDArray[floating]

Wind speed per step (m/s) — from weather input.

global_rad NDArray[floating]

Global solar radiation per step (W/m²) — from weather input.

direct_rad NDArray[floating]

Direct beam radiation per step (W/m²).

diffuse_rad NDArray[floating]

Diffuse radiation per step (W/m²).

sun_altitude NDArray[floating]

Sun altitude angle per step (°).

tmrt_mean NDArray[floating]

Spatial mean Tmrt per step (°C).

utci_mean NDArray[floating]

Spatial mean UTCI per step (°C).

sun_fraction NDArray[floating]

Fraction of sunlit pixels per step (0–1). NaN when shadow unavailable.

diffuse_fraction NDArray[floating]

Diffuse fraction per step (0–1). 0 = clear sky, 1 = fully overcast.

clearness_index NDArray[floating]

Clearness index per step. Higher = clearer sky. 0 at night.

is_daytime NDArray[bool_]

Day/night flag per step.


PrecomputedData

solweig.PrecomputedData dataclass

Optional container for externally supplied SVF/shadow data.

Most users don't need this — :meth:SurfaceData.prepare computes everything. Use PrecomputedData only when loading SVF or shadow matrices from files produced by a previous run or an external tool.

Example::

svf = SvfArrays.from_zip("cache/svfs.zip")
shadows = ShadowArrays.from_npz("cache/shadowmats.npz")
precomputed = PrecomputedData(svf=svf, shadow_matrices=shadows)

result = calculate(surface=surface, weather=weather, precomputed=precomputed)

prepare(walls_dir=None, svf_dir=None) classmethod

Prepare preprocessing data from directories.

Loads preprocessing files if they exist. If files don't exist, the corresponding data will be None.

All parameters are optional.

Parameters:

Name Type Description Default
walls_dir str | Path | None

Directory containing wall preprocessing files: - wall_hts.tif: Wall heights (meters) - wall_aspects.tif: Wall aspects (degrees, 0=N)

None
svf_dir str | Path | None

Directory containing SVF preprocessing files: - svfs.zip: SVF arrays (required if svf_dir provided) - shadowmats.npz: Shadow matrices for anisotropic sky (optional)

None

Returns:

Type Description
PrecomputedData

PrecomputedData with loaded arrays. Missing data is set to None.

Example
Prepare all preprocessing

precomputed = PrecomputedData.prepare( walls_dir="preprocessed/walls", svf_dir="preprocessed/svf", )

Prepare only SVF

precomputed = PrecomputedData.prepare(svf_dir="preprocessed/svf")

Nothing prepared (SVF must be provided before calculate())

precomputed = PrecomputedData.prepare()


ThermalState

solweig.models.state.ThermalState dataclass

Thermal state for multi-timestep calculations.

Carries forward surface temperature history between timesteps to model thermal inertia of ground and walls (TsWaveDelay_2015a).

This enables accurate time-series simulations where surface temperatures depend on accumulated heating throughout the day.

Attributes:

Name Type Description
tgmap1 NDArray[floating]

Upwelling longwave history (center view).

tgmap1_e NDArray[floating]

Upwelling longwave history (east view).

tgmap1_s NDArray[floating]

Upwelling longwave history (south view).

tgmap1_w NDArray[floating]

Upwelling longwave history (west view).

tgmap1_n NDArray[floating]

Upwelling longwave history (north view).

tgout1 NDArray[floating]

Ground temperature output history.

firstdaytime float

Flag for first morning timestep (1.0=first, 0.0=subsequent).

timeadd float

Accumulated time for thermal delay function.

timestep_dec float

Decimal time between steps (fraction of day).

Example

Manual state management for custom time loops

state = ThermalState.initial(dsm.shape) for weather in weather_list: result = calculate(..., state=state) state = result.state

initial(shape) classmethod

Create initial state for first timestep.

Parameters:

Name Type Description Default
shape tuple[int, int]

Grid shape (rows, cols) matching the DSM.

required

Returns:

Type Description
ThermalState

ThermalState with zero-initialized arrays.

copy()

Create a deep copy of this state.


TileSpec

solweig.TileSpec dataclass

Specification for a single tile with overlap regions.

Attributes:

Name Type Description
row_start, row_end

Core tile row bounds (without overlap).

col_start, col_end

Core tile column bounds (without overlap).

row_start_full, row_end_full

Full tile row bounds (with overlap).

col_start_full, col_end_full

Full tile column bounds (with overlap).

overlap_top, overlap_bottom

Vertical overlap in pixels.

overlap_left, overlap_right

Horizontal overlap in pixels.

core_shape property

Shape of core tile (without overlap).

full_shape property

Shape of full tile (with overlap).

core_slice property

Slices for extracting core from full tile result.

write_slice property

Slices for writing core to global output.

read_slice property

Slices for reading full tile from global input.