Skip to content

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 the time-based mode.
  • 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 by irradiance, benefit, daylight, and radiative_cooling modes.
  • Plane carving (carve_with_planes) -- removes voxels above a tilted cut plane at a fixed angle from each test surface. Used by the tilted_plane mode.

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 config.mode is not one of: {'time-based','irradiance','benefit','daylight','radiative_cooling','tilted_plane'}.

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 datetimes.

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:carve_with_sun_rays for shared parameter descriptions.

required
sample_normals

See :func:carve_with_sun_rays for shared parameter descriptions.

required
config user_config

Must contain tilted_plane_angle_deg — a single angle in degrees, or an 8-element list [N, NE, E, SE, S, SW, W, NW] for octant lookup.

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 mode=="vector". Ignored otherwise.

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.