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:
- Voxelization -- the maximum-volume envelope mesh is discretised into
a regular Boolean grid at the resolution specified by
cfg.voxel_size. - 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. -
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:
-
time-based: sun-position rays for the configured analysis period; scores are integer violation counts (number of sun-hours blocked). tilted_plane: geometric plane-intersection test; scores are also violation counts.- 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 |
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)
¶
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 <= thresholdare 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 forcfg.carve_fractionof 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_sizemetres.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_sizeover-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: |
required |
cfg
|
user_config | str | Path
|
Validated config or path to config file. |
required |
out_dir
|
str | Path
|
Root output directory; a |
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)
¶
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 belowcfg.min_component_fractionof the largest component are removed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
threshold_manifest
|
ThresholdingResult | str | Path
|
Either the :class: |
required |
cfg
|
user_config | str | Path
|
Validated config or path to config file. |
required |
out_dir
|
str | Path
|
Root output directory; an |
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.