voxcity.simulator_gpu.solar.integrationยถ

VoxCity GPU-accelerated Solar Irradiance Integration Package

This package provides GPU-accelerated solar irradiance calculations for VoxCity voxel grids using Taichi. It supports:

  • Ground-level (horizontal) solar irradiance maps

  • Building surface irradiance with directional faces

  • Volumetric 3D radiation fields for thermal comfort analysis

  • Cumulative time-integrated irradiance and sunlight hours

  • Sky patch discretization (Tregenza, Reinhart, uniform, Fibonacci)

  • Surface reflections (optional)

Modules:
  • utils: Common helper functions

  • caching: Cache infrastructure for models and calculators

  • ground: Ground-level solar irradiance functions

  • building: Building surface irradiance functions

  • volumetric: Volumetric 3D radiation field functions

Public API:

The package re-exports all public functions for backward compatibility. Import directly from this package:

from voxcity.simulator_gpu.solar.integration import (

get_global_solar_irradiance_map, get_cumulative_global_solar_irradiance, get_sunlight_hours, get_building_solar_irradiance, get_volumetric_solar_irradiance_map, # โ€ฆ etc

)

Submodulesยถ

Attributesยถ

Classesยถ

ArrayWithMetadata

NumPy array subclass that can hold metadata.

Functionsยถ

get_location_from_voxcity(โ†’ Tuple[float, float])

Extract latitude/longitude from VoxCity object or return defaults.

convert_voxel_data_to_arrays(โ†’ Tuple[numpy.ndarray, ...)

Convert VoxCity voxel codes to is_solid and LAD arrays using vectorized operations.

compute_valid_ground_vectorized(โ†’ Tuple[numpy.ndarray, ...)

Compute valid ground mask and ground k-levels using vectorized operations.

compute_sun_direction(โ†’ Tuple[float, float, float, float])

Compute sun direction vector from azimuth and elevation angles.

parse_time_period(โ†’ Tuple[datetime.datetime, ...)

Parse start and end time strings.

filter_df_to_period(df, start_time, end_time, tz)

Filter weather DataFrame to specified time period and convert to UTC.

load_epw_data(โ†’ Tuple)

Load EPW weather data, optionally downloading the nearest file.

get_solar_positions_astral(times, lon, lat)

Compute solar azimuth and elevation for given times and location using Astral.

extract_terrain_following_slice(โ†’ numpy.ndarray)

Extract a terrain-following 2D slice from a 3D flux field (vectorized).

accumulate_terrain_following_slice(โ†’ None)

Accumulate terrain-following values from a 3D flux field into a 2D map (vectorized, in-place).

compute_boundary_vertical_mask(โ†’ numpy.ndarray)

Compute mask for vertical faces on domain boundary.

apply_computation_mask_to_faces(โ†’ numpy.ndarray)

Apply 2D computation mask to mesh face values.

Package Contentsยถ

voxcity.simulator_gpu.solar.integration.get_location_from_voxcity(voxcity, default_lat: float = 1.35, default_lon: float = 103.82) Tuple[float, float][source]ยถ

Extract latitude/longitude from VoxCity object or return defaults.

Parameters:
  • voxcity โ€“ VoxCity object with extras containing rectangle_vertices

  • default_lat โ€“ Default latitude if not found (Singapore)

  • default_lon โ€“ Default longitude if not found (Singapore)

Returns:

Tuple of (origin_lat, origin_lon)

voxcity.simulator_gpu.solar.integration.convert_voxel_data_to_arrays(voxel_data: numpy.ndarray, default_lad: float = 1.0) Tuple[numpy.ndarray, numpy.ndarray][source]ยถ

Convert VoxCity voxel codes to is_solid and LAD arrays using vectorized operations.

This is 10-100x faster than triple-nested Python loops for large grids.

Parameters:
  • voxel_data โ€“ 3D array of VoxCity voxel class codes

  • default_lad โ€“ Default Leaf Area Density for tree voxels (mยฒ/mยณ)

Returns:

Tuple of (is_solid, lad) numpy arrays with same shape as voxel_data

voxcity.simulator_gpu.solar.integration.compute_valid_ground_vectorized(voxel_data: numpy.ndarray) Tuple[numpy.ndarray, numpy.ndarray][source]ยถ

Compute valid ground mask and ground k-levels using vectorized operations.

Valid ground cells are those where: - The transition from solid to air/tree occurs - The solid below is not water (7,8,9) or building/underground (negative codes)

Parameters:

voxel_data โ€“ 3D array of VoxCity voxel class codes (ni, nj, nk)

Returns:

Tuple of (valid_ground 2D bool array, ground_k 2D int array) ground_k[i,j] = -1 means no valid ground found

voxcity.simulator_gpu.solar.integration.compute_sun_direction(azimuth_degrees_ori: float, elevation_degrees: float, rotation_angle: float = 0) Tuple[float, float, float, float][source]ยถ

Compute sun direction vector from azimuth and elevation angles.

Parameters:
  • azimuth_degrees_ori โ€“ Solar azimuth in VoxCity convention (0=North, clockwise)

  • elevation_degrees โ€“ Solar elevation in degrees above horizon

  • rotation_angle โ€“ Grid rotation angle in degrees (clockwise, from voxcity.extras)

Returns:

Tuple of (sun_dir_x, sun_dir_y, sun_dir_z, cos_zenith)

voxcity.simulator_gpu.solar.integration.parse_time_period(start_time: str, end_time: str) Tuple[datetime.datetime, datetime.datetime][source]ยถ

Parse start and end time strings.

Parameters:
  • start_time โ€“ Start time in format โ€˜MM-DD HH:MM:SSโ€™

  • end_time โ€“ End time in format โ€˜MM-DD HH:MM:SSโ€™

Returns:

Tuple of (start_dt, end_dt) datetime objects

Raises:

ValueError โ€“ If time format is invalid

voxcity.simulator_gpu.solar.integration.filter_df_to_period(df, start_time: str, end_time: str, tz: float)[source]ยถ

Filter weather DataFrame to specified time period and convert to UTC.

Parameters:
  • df โ€“ pandas DataFrame with datetime index

  • start_time โ€“ Start time in format โ€˜MM-DD HH:MM:SSโ€™

  • end_time โ€“ End time in format โ€˜MM-DD HH:MM:SSโ€™

  • tz โ€“ Timezone offset in hours

Returns:

Tuple of (df_period_utc, df with hour_of_year column)

Raises:

ValueError โ€“ If time format is invalid or no data in period

voxcity.simulator_gpu.solar.integration.load_epw_data(epw_file_path: str | None = None, download_nearest_epw: bool = False, voxcity=None, **kwargs) Tuple[source]ยถ

Load EPW weather data, optionally downloading the nearest file.

Parameters:
  • epw_file_path โ€“ Path to EPW file (required if download_nearest_epw=False)

  • download_nearest_epw โ€“ If True, download nearest EPW based on location

  • voxcity โ€“ VoxCity object (needed for location when downloading)

  • **kwargs โ€“ Additional parameters (output_dir, max_distance, rectangle_vertices)

Returns:

Tuple of (df, lon, lat, tz) where df is the weather DataFrame

Raises:
  • ValueError โ€“ If EPW file not provided and download_nearest_epw=False

  • ImportError โ€“ If required modules not available

voxcity.simulator_gpu.solar.integration.get_solar_positions_astral(times, lon: float, lat: float)[source]ยถ

Compute solar azimuth and elevation for given times and location using Astral.

Parameters:
  • times โ€“ Pandas DatetimeIndex of times (should be timezone-aware, preferably UTC)

  • lon โ€“ Longitude in degrees

  • lat โ€“ Latitude in degrees

Returns:

DataFrame indexed by times with columns [โ€˜azimuthโ€™, โ€˜elevationโ€™] in degrees

voxcity.simulator_gpu.solar.integration.extract_terrain_following_slice(flux_3d: numpy.ndarray, ground_k: numpy.ndarray, height_offset_k: int, is_solid: numpy.ndarray) numpy.ndarray[source]ยถ

Extract a terrain-following 2D slice from a 3D flux field (vectorized).

For each (i,j), extracts the value at ground_k[i,j] + height_offset_k. Cells that are solid at the extraction point, have no valid ground, or are above the domain are marked as NaN.

Parameters:
  • flux_3d โ€“ 3D array of flux values (ni, nj, nk)

  • ground_k โ€“ 2D array of ground k-levels (ni, nj), -1 means no valid ground

  • height_offset_k โ€“ Number of cells above ground to extract

  • is_solid โ€“ 3D array marking solid cells (ni, nj, nk)

Returns:

2D array of extracted values (ni, nj) with NaN for invalid cells

voxcity.simulator_gpu.solar.integration.accumulate_terrain_following_slice(cumulative_map: numpy.ndarray, flux_3d: numpy.ndarray, ground_k: numpy.ndarray, height_offset_k: int, is_solid: numpy.ndarray, weight: float = 1.0) None[source]ยถ

Accumulate terrain-following values from a 3D flux field into a 2D map (vectorized, in-place).

For each (i,j), adds flux_3d[i,j,k_extract] * weight to cumulative_map[i,j] where k_extract = ground_k[i,j] + height_offset_k.

Parameters:
  • cumulative_map โ€“ 2D array to accumulate into (ni, nj), modified in-place

  • flux_3d โ€“ 3D array of flux values (ni, nj, nk)

  • ground_k โ€“ 2D array of ground k-levels (ni, nj), -1 means no valid ground

  • height_offset_k โ€“ Number of cells above ground to extract

  • is_solid โ€“ 3D array marking solid cells (ni, nj, nk)

  • weight โ€“ Multiplier for values before accumulating (e.g., time_step_hours)

voxcity.simulator_gpu.solar.integration.compute_boundary_vertical_mask(mesh_face_centers: numpy.ndarray, mesh_face_normals: numpy.ndarray, grid_bounds: numpy.ndarray, boundary_epsilon: float) numpy.ndarray[source]ยถ

Compute mask for vertical faces on domain boundary.

Parameters:
  • mesh_face_centers โ€“ (N, 3) array of face center coordinates

  • mesh_face_normals โ€“ (N, 3) array of face normal vectors

  • grid_bounds โ€“ (2, 3) array of [[min_x, min_y, min_z], [max_x, max_y, max_z]]

  • boundary_epsilon โ€“ Tolerance for boundary detection

Returns:

Boolean mask (N,) - True for vertical boundary faces

voxcity.simulator_gpu.solar.integration.apply_computation_mask_to_faces(values: numpy.ndarray, mesh_face_centers: numpy.ndarray, computation_mask: numpy.ndarray, meshsize: float, grid_shape: Tuple[int, int]) numpy.ndarray[source]ยถ

Apply 2D computation mask to mesh face values.

Parameters:
  • values โ€“ (N,) array of face values

  • mesh_face_centers โ€“ (N, 3) array of face center coordinates

  • computation_mask โ€“ 2D boolean mask matching grid_shape

  • meshsize โ€“ Grid cell size

  • grid_shape โ€“ (ny_vc, nx_vc) grid dimensions

Returns:

Modified values array with NaN for masked-out faces

voxcity.simulator_gpu.solar.integration.VOXCITY_GROUND_CODE = -1ยถ
voxcity.simulator_gpu.solar.integration.VOXCITY_TREE_CODE = -2ยถ
voxcity.simulator_gpu.solar.integration.VOXCITY_BUILDING_CODE = -3ยถ