Skip to content

Pipeline

The pipeline is USC's main execution model. It runs in three stages -- preprocessing, thresholding, exporting -- each producing a manifest that the next stage consumes. This design lets you re-run thresholding with different parameters without recomputing scores.

run_pipeline() chains all three stages in one call. For finer control, call each stage individually.

from urbansolarcarver import load_config, run_pipeline

# One-shot
result = run_pipeline(load_config("config.yaml"), out_dir="outputs")

# Decomposed
from urbansolarcarver import preprocessing, thresholding, exporting
from pathlib import Path

cfg = load_config("config.yaml")
pre  = preprocessing(cfg, Path("outputs/preprocessing"))
thr  = thresholding(pre, cfg, Path("outputs/thresholding"))
exp  = exporting(thr, cfg, Path("outputs/exporting"))

run_pipeline

urbansolarcarver.api

Public facade. Re-export the staged API and config helpers.

run_pipeline(cfg, out_dir)

Run the full carving pipeline in one call.

Convenience wrapper around the three stages. For finer control (e.g. re-thresholding without re-carving), call each stage directly.

Returns the ExportingResult with the final mesh path.

Preprocessing

Voxelizes the max volume, samples the test surfaces, generates rays toward sky patches (or sun vectors), traces them through the voxel grid, and writes per-voxel obstruction scores to scores.npy.

urbansolarcarver.api_core.preprocessing

PreprocessingResult(out_dir, volume_path, volume_shape, dtype, hash, device_info='') dataclass

Immutable record returned by :func:preprocessing.

Carries paths to persisted artifacts and grid metadata so that downstream stages (thresholding, exporting) can locate them without re-computation. The manifest_path property points to the JSON manifest that fully describes this preprocessing run; the object also implements __fspath__ so it can be passed directly as a path-like to :func:thresholding.

preprocessing(cfg, out_dir)

First stage of the 3-stage envelope pipeline: per-voxel obstruction scoring.

Converts a maximum-volume mesh and a set of insolation test surfaces into a 3-D scalar field where each voxel carries a score indicating how much it obstructs the surrounding context. The pipeline steps are:

  1. Voxelization -- the maximum-volume envelope mesh is discretised into a regular Boolean grid at the resolution specified by cfg.voxel_size.
  2. Surface sampling -- insolation test surfaces are point-sampled (density controlled by cfg.grid_step), yielding test points with associated outward normals. The test surface PLY file may contain multiple disjoint mesh components (e.g. separate facade panels, a ground plane and a courtyard, or a PV array). All components are sampled and their ray contributions aggregated into a single score grid.
  3. Ray casting -- for every test point, rays are cast through the voxel grid according to the configured mode. Each voxel that blocks a ray accumulates a weight that depends on the mode:

  4. time-based: sun-position rays for the configured analysis period; scores are integer violation counts (number of sun-hours blocked).

  5. tilted_plane: geometric plane-intersection test; scores are also violation counts.
  6. sky-patch modes (irradiance, benefit, daylight, radiative_cooling): rays are sampled from a discretised sky hemisphere weighted by radiance or irradiance; scores are weighted sums reflecting the total sky contribution obstructed by the voxel.

Parameters:

Name Type Description Default
cfg user_config | str | Path

A validated user_config instance, or a filesystem path to a YAML / JSON configuration file that will be loaded and validated.

required
out_dir str | Path

Root output directory. All stage artifacts are written directly into this directory (no subdirectory is created).

required

Returns:

Type Description
PreprocessingResult

Frozen dataclass with paths and metadata for the completed stage.

Persisted artifacts

scores.npy 3-D float32 array (same shape as the voxel grid) with raw obstruction scores. voxel_grid.npy Boolean occupancy grid so downstream stages can skip re-voxelization. manifest.json Machine-readable manifest (PreprocessingManifest) recording grid origin, extent, voxel size, score semantics, and file paths. diagnostics/ Per-stage diagnostics: score histogram, sky-patch weight visualisation (if applicable), summary statistics, and wall/CPU timings.

Thresholding

Converts the continuous score array into a binary carving mask. Supports head-tail breaks, a fixed carve-fraction, or a numeric threshold. Writes mask.npy.

urbansolarcarver.api_core.thresholding

ThresholdingResult(out_dir, mask_path, hash, upstream, upstream_manifest, threshold_method='', threshold_value=0.0, voxels_kept=0, voxels_removed=0, retention_pct=0.0) dataclass

Immutable record returned by :func:thresholding.

Stores the path to the binary mask and the provenance chain back to the upstream preprocessing run (via upstream hash and upstream_manifest path). Implements __fspath__ so it can be passed directly as a path-like to :func:exporting.

thresholding(volume, cfg, out_dir)

thresholding(volume: 'PreprocessingResult', cfg: Union[user_config, str, Path], out_dir: Union[str, Path]) -> ThresholdingResult
thresholding(volume: Union[str, Path], cfg: Union[user_config, str, Path], out_dir: Union[str, Path]) -> ThresholdingResult

Second stage of the 3-stage pipeline: score-to-mask binarisation.

Loads the per-voxel obstruction scores produced by :func:preprocessing and applies a threshold strategy to produce a Boolean mask indicating which voxels to retain in the final envelope (True = keep, False = carve away).

For violation_count scores (time-based / tilted_plane modes), a numeric threshold from the config is applied directly.

Threshold strategies (set via cfg.threshold):

  • Numeric value -- a literal float cutoff; voxels with score <= threshold are kept.
  • "headtail" -- Head/tail breaks (Jiang 2013): iteratively splits the distribution at the arithmetic mean until the head proportion falls below 40 %, targeting heavy-tailed score distributions common in urban solar access studies.
  • "carve_fraction" (default) -- cumulative-score cutoff: sorts voxels by descending score and finds the cutoff that accounts for cfg.carve_fraction of the total score mass.

Score smoothing (cfg.score_smoothing):

Before thresholding, a Gaussian blur can be applied to the continuous score volume to smooth resolution-dependent noise. This produces a cleaner binary mask (and carved mesh) at fine voxel sizes.

  • None (default) -- auto: 1.1 × voxel_size metres.
  • 0 -- disabled, no smoothing.
  • Positive float -- explicit radius in metres. Rule of thumb: keep close to voxel_size (1.0–1.2×). Values above 2× voxel_size over-smooth and round features.

Only applied to weighted-score modes (irradiance, benefit, daylight, radiative_cooling). Violation-count modes (time-based, tilted_plane) are unaffected.

Future: target-based thresholding (binary search on carve_fraction to achieve a physical performance target in kWh/m²/year) requires iterative re-simulation and is not yet implemented. Use the performance metrics in diagnostics/summary.json to manually iterate on carve_fraction.

Parameters:

Name Type Description Default
volume PreprocessingResult | str | Path

Either the :class:PreprocessingResult returned by the previous stage, or a path to its output directory / manifest.json.

required
cfg user_config | str | Path

Validated config or path to config file.

required
out_dir str | Path

Root output directory; a thresholding/ subdirectory is created.

required

Returns:

Type Description
ThresholdingResult

Frozen dataclass carrying the mask path and provenance metadata.

Persisted artifacts

mask.npy 3-D Boolean array (same shape as the voxel grid). manifest.json ThresholdingManifest recording the resolved threshold value, upstream hash, and file paths. diagnostics/ Score histogram with threshold line overlay, voxel retention statistics, and wall/CPU timings.

Exporting

Reconstructs a triangle mesh from the binary mask -- either cubic voxel faces or SDF-smoothed marching cubes with Taubin polishing. Writes the final carved_mesh.ply.

urbansolarcarver.api_core.exporting

Exporting stage — convert a carved binary mask into a triangle mesh.

Reads the thresholding manifest, reconstructs a smoothed mesh from the binary voxel field, and writes it to disk (PLY/OBJ/STL/GLB). This is the final pipeline stage.

ExportingResult(out_dir, export_path, retention_pct=0.0, mesh_volume_m3=None, vertices=0, faces=0) dataclass

Immutable record returned by :func:exporting.

Holds the filesystem path to the exported triangle mesh and mesh statistics (retention percentage, volume, vertex/face counts). Implements __fspath__ so the result can be used directly as a path-like pointing to the export directory.

exporting(threshold_manifest, cfg, out_dir)

exporting(threshold_manifest: 'ThresholdingResult', cfg: Union[user_config, str, Path], out_dir: Union[str, Path]) -> ExportingResult
exporting(threshold_manifest: Union[str, Path], cfg: Union[user_config, str, Path], out_dir: Union[str, Path]) -> ExportingResult

Third stage of the 3-stage pipeline: mesh extraction from the binary mask.

Loads the Boolean voxel mask produced by :func:thresholding and the occupancy grid persisted during :func:preprocessing, combines them (logical AND), and converts the resulting carved voxel field into a triangle mesh suitable for downstream CAD / BIM / visualisation workflows.

Mesh extraction proceeds through :func:~urbansolarcarver.grid.finalize_mesh, which supports two strategies (selected by cfg.mesh_method):

  • cubic -- axis-aligned cuboid faces are emitted for every occupied voxel; fast but produces staircase geometry.
  • sdf (default) -- the binary field is converted to a signed-distance field, optionally smoothed (Gaussian sigma controlled by cfg.sdf_smooth), and iso-surfaced with Marching Cubes, yielding a smoother envelope. Small disconnected fragments below cfg.min_component_fraction of the largest component are removed.

Parameters:

Name Type Description Default
threshold_manifest ThresholdingResult | str | Path

Either the :class:ThresholdingResult from the previous stage, or a path to its output directory / manifest.json.

required
cfg user_config | str | Path

Validated config or path to config file.

required
out_dir str | Path

Root output directory; an exporting/ subdirectory is created.

required

Returns:

Type Description
ExportingResult

Frozen dataclass with the exported mesh path and provenance metadata.

Persisted artifacts

export.{ply,obj,stl,glb} Triangle mesh in the format specified by cfg.final_mesh_format (default ply). diagnostics/ Mesh statistics (vertex/face counts, watertightness, volume, bounding box), voxel retention percentage, and wall/CPU timings.