Carving¶
The carving module implements the three geometric strategies used to remove voxels from the maximum buildable volume:
- Sun-ray carving (
carve_with_sun_rays) -- traces rays along actual sun vectors at specific dates/times. Used by thetime-basedmode. - Sky-patch carving (
carve_with_sky_patch_rays) -- traces rays toward Tregenza sky patches weighted by irradiance, heating benefit, daylight luminance, or radiative cooling potential. Used byirradiance,benefit,daylight, andradiative_coolingmodes. - Plane carving (
carve_with_planes) -- removes voxels above a tilted cut plane at a fixed angle from each test surface. Used by thetilted_planemode.
All three accept a voxel grid, surface sample points with normals, and mode-specific parameters. They return a per-voxel score array that downstream thresholding converts into a binary carving mask.
urbansolarcarver.carving
¶
UrbanSolarCarver — Carving utilities¶
Purpose
Convert point/normal samples on analysis surfaces into ray sets, march those through a voxelized envelope, and remove the voxels they visit. Supports:
• Time-based solar carving (sun vectors from EPW for specific datetimes) • Sky-patch-weighted carving (Tregenza patches + mode-specific weights) • Tilted-plane daylight carving (single or per-octant plane angle) • Simple directional carving (global vector, per-point normals, or +Z)
Raytracing contract
All carving is executed via trace_multi_hit_grid, which:
• samples at cell centers: distances = (0.5 + k) * voxel_size
• maps world → grid with floor(), not round()
This avoids lattice aliasing, especially for the plane method.
Coordinate/scale conventions
grid_origin is the world-space min corner for grid index [0,0,0].
grid_extent is the physical cube size (meters). grid_resolution is D.
The tracer receives scale = grid_extent and a per-ray step of voxel_size.
Determinism and batching
Given a fixed config, carving is deterministic. Rays are processed in batches
(config.ray_batch_size) on the device of the input voxel grid.
SkyPatchCarvingResult
¶
Bases: NamedTuple
Return type for :func:carve_with_sky_patch_rays.
validate_inputs(config)
¶
Validate file paths and supported modes.
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If any required input file is missing. |
ValueError
|
If |
sample_period(config)
¶
Build analysis datetimes and HOYs via Ladybug's AnalysisPeriod.
Returns:
| Name | Type | Description |
|---|---|---|
datetimes |
list[datetime]
|
Start→end at 1-hour steps using config start/end. |
hoys |
list[int]
|
Hour-of-year indices (1..8760) aligned to |
load_meshes(config)
¶
Load the maximum envelope mesh and the insolation sampling surface.
Returns:
| Name | Type | Description |
|---|---|---|
envelope_mesh |
Trimesh
|
|
insolation_mesh |
Trimesh
|
|
carve_with_sun_rays(voxel_grid, grid_origin, grid_extent, grid_resolution, sample_points, sample_normals, config, datetimes, return_counts=False)
¶
Perform time-based carving of a voxel grid using time-based sun vectors.
This function constructs a classical solar envelope (or fan) by tracing rays for a set of sun directions derived from weather data and explicit datetimes. No scoring, normalization, or thresholding is applied. Any voxel intersected by any ray is removed.
Core procedure
1. Compute sun vectors for the provided datetimes from the EPW file, then filter
by minimum altitude.
2. For each surface sample point with outward normal, build rays toward the sun
directions that lie in its visible hemisphere (facing-mask from normals).
3. March points along each ray in fixed steps equal to voxel_size up to
ray_length.
4. Map sampled world positions to grid indices and zero all intersected voxels.
5. Return the carved grid and the full set of ray origins and directions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
voxel_grid
|
(Tensor, shape(X, Y, Z))
|
Input occupancy grid on CPU or GPU. |
required |
grid_origin
|
(array_like, shape(3))
|
Minimum corner of the voxel volume in world coordinates. |
required |
grid_extent
|
float
|
Physical span of the cubic grid (meters). |
required |
grid_resolution
|
int or (3,)
|
Number of voxels along each axis (must represent a cube). |
required |
sample_points
|
(Tensor or ndarray, shape(R, 3))
|
Coordinates of R surface evaluation points. |
required |
sample_normals
|
(Tensor or ndarray, shape(R, 3))
|
Outward unit normals at each sample point. |
required |
config
|
user_config
|
Configuration containing: - epw_path: path to EPW file used for sun positions. - min_altitude: degrees above horizon for filtering sun vectors. - voxel_size: step size for the marcher. - ray_length: maximum traced distance along each ray. - ray_batch_size: rays processed per batch. |
required |
datetimes
|
Sequence[datetime]
|
Wall-clock timestamps for which sun vectors are generated. |
required |
return_counts
|
bool
|
If True, return a 4th element: per-voxel hit counts (int32 array). |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
carved_grid |
(Tensor, shape(X, Y, Z))
|
Voxel grid after removing all cells intersected by any traced ray. |
ray_origins |
(ndarray, shape(N, 3))
|
World-space starting points for all emitted rays after facing cull. |
ray_directions |
(ndarray, shape(N, 3))
|
Unit direction vectors for the emitted rays. |
counts |
(ndarray, shape(V), optional)
|
Per-voxel hit counts (flat). Only returned when return_counts is True. |
carve_with_sky_patch_rays(voxel_grid, grid_origin, grid_extent, grid_resolution, sample_points, sample_normals, config, hoys)
¶
Perform sky-patch-weighted carving of a voxel grid using mode-specific weight metrics.
This function assigns a weight to each Tregenza sky patch according to the selected mode. Available modes draw weights from: • Global horizontal + diffuse irradiance data. • Passive solar benefit indices (e.g. from Ladybug). • Daylighting metrics under a CIE overcast sky. • Radiative cooling potential (Bliss-K anisotropy and dew-point adjustment).
Core procedure (identical across modes): 1. Subdivide the hemisphere into angular bins (Tregenza patches). 2. Cast rays from each sample point p with outward normal n toward each patch center. 3. Trace rays through the voxel volume, accumulating per-voxel weights based on patch assignments. 4. Normalize the raw score distribution if requested (linear or percentile scaling). 5. Select a threshold via fixed value, head/tail breaks, or carve_fraction 6. Apply binary mask to remove voxels above the threshold, yielding the carved grid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
voxel_grid
|
(Tensor, shape(X, Y, Z))
|
Input occupancy or density values for each grid cell. |
required |
grid_origin
|
(array_like, shape(3))
|
Minimum corner of the voxel volume in world coordinates. |
required |
grid_extent
|
float
|
Physical span of each grid axis (meters). |
required |
grid_resolution
|
int or (3,)
|
Number of voxels along each axis (uniform or per-axis). |
required |
sample_points
|
(Tensor, shape(R, 3))
|
Coordinates of R surface evaluation points. |
required |
sample_normals
|
(Tensor, shape(R, 3))
|
Outward-facing unit normals at each sample point. |
required |
config
|
user_config
|
Configuration containing: - mode: weight mode selector. - epw_path: EPW file for irradiance/weather. - dew_point_celsius: dew-point temperature for cooling mode. - bliss_k: anisotropy scaling for cooling. - balance_temperature, balance_offset: tuning for energy-balance modes. - ray_batch_size: rays processed per batch. - voxel_size, ray_length: tracer resolution and extent. |
required |
hoys
|
Sequence[int]
|
Hours-of-year indices mapping to EPW or other time-series input. |
required |
Returns:
| Type | Description |
|---|---|
SkyPatchCarvingResult
|
NamedTuple with fields: - ray_origins (N, 3): world-space ray start points. - ray_directions (N, 3): unit ray vectors. - raw_voxel_scores (XYZ,): accumulated patch weights per voxel. - patch_weights (P,): weight per Tregenza sky patch. |
carve_with_planes(voxel_grid, grid_origin, grid_extent, grid_resolution, sample_points, sample_normals, config, return_counts=False)
¶
Tilted-plane daylight carving.
For each sample: 1) Project the surface normal to the XY plane → n_xy 2) Select angle α (single value or per-octant table) 3) Form d = cos(α)·n_xy + sin(α)·ẑ and march this direction Every visited voxel is deleted.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
voxel_grid
|
|
required | |
grid_origin
|
|
required | |
grid_extent
|
|
required | |
grid_resolution
|
|
required | |
sample_points
|
See :func: |
required | |
sample_normals
|
See :func: |
required | |
config
|
user_config
|
Must contain |
required |
return_counts
|
bool
|
If True, return a 4th element: per-voxel hit counts (int32 array). |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
carved_grid |
(Tensor, shape(D, D, D))
|
|
ray_origins |
(ndarray, shape(N, 3))
|
|
ray_directions |
(ndarray, shape(N, 3))
|
|
counts |
(ndarray, shape(V), optional)
|
Only returned when return_counts is True. |
Notes
• The underlying tracer uses half-voxel steps and floor indexing to avoid checkerboarding on regular planar lattices.
carve_directional(voxel_grid, grid_origin, grid_extent, grid_resolution, sample_points, sample_normals, config, mode='vector', direction=None)
¶
Simple directional carving primitive (geometry-only, no scoring).
March rays from sample_points and delete all visited voxels.
Unlike :func:carve_with_sun_rays and :func:carve_with_planes,
return_counts is not supported here because this mode produces
a binary mask without score accumulation.
Direction can be: • "vector" — one global world-space vector for all points • "normals" — the per-point surface normal • "positive_z" — +Z for all points (vertical clearance)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mode
|
Literal['vector', 'normals', 'positive_z']
|
Direction policy. |
'vector'
|
direction
|
tuple[float, float, float] | None
|
Required when |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
carved_grid |
(Tensor, shape(D, D, D))
|
|
ray_origins |
(ndarray, shape(N, 3))
|
|
ray_directions |
(ndarray, shape(N, 3))
|
|
Notes
• This is geometry-only; it ignores EPW and weights. • Useful for vertical setbacks ("positive_z") or view corridors along a fixed vector.
carve_above_columns(mask, voxel_grid, min_consecutive=1)
¶
Carve occupied voxels above the lowest sufficiently-carved run per column.
For each (x, y) column, scans bottom-to-top (z=0 upward) for runs of consecutive carved (False) voxels:
- Runs with length >= min_consecutive trigger carve-above: all occupied voxels above the run are carved. The first (lowest) qualifying run triggers; scanning stops for that column.
- Runs with length < min_consecutive are treated as noise and patched back to kept (True).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mask
|
ndarray (D, D, D) bool
|
Thresholding mask. True = keep, False = carved. Not mutated. |
required |
voxel_grid
|
ndarray (D, D, D) bool
|
Original envelope occupancy. True = occupied. |
required |
min_consecutive
|
int
|
Minimum consecutive carved voxels to trigger carve-above. Shorter runs are patched (filled back in). |
1
|
Returns:
| Type | Description |
|---|---|
ndarray (D, D, D) bool
|
Modified mask with short runs patched and voxels above qualifying runs carved. |