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_dirfor fast reuse. - Array mode (dsm is an ndarray): works in memory.
pixel_sizeis required;working_diris 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 |
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: |
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. |
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. |
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 |
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:
Custom configuration:
>>> config = ModelConfig(
... use_anisotropic_sky=True,
... human=HumanParams(abs_k=0.7, posture="standing"),
... )
Load from legacy 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 |
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
With surface metadata (recommended when using from_geotiff)¶
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. |
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.
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. |
None
|
figsize
|
tuple[float, float]
|
Figure size as |
(14, 10)
|
max_days
|
int
|
Maximum number of days to display. If the timeseries
spans more than this many days, only the first |
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 |
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.