Frequency-domain admittance method for computing outdoor surface temperatures, based on Beckett, Owens, and Acred (SimBuild 2026).
The method decomposes the sol-air driving temperature into Fourier harmonics, applies a material-specific transfer matrix at each frequency, and reconstructs the surface temperature via inverse FFT. Geometry is read from STL facets, while the thermal solution remains a per-surface admittance calculation with no thermal mesh or warm-up period.
uv pip install -e .
uv run python examples/neighborhood.pyThis simulates a full year of hourly surface temperatures for a 12-building neighborhood in Atlanta and produces the figures shown below.
The default geometry is loaded from:
data/neighborhood_buildings.stldata/neighborhood_ground.stldata/neighborhood_surfaces.json
The driving signal for each surface is the sol-air temperature, which combines convective, radiative, and solar heat transfer into a single equivalent temperature:
where
The convective coefficient follows the DOE-2 linear wind model:
Sky temperature is back-calculated from the EPW horizontal infrared radiation field:
Each material layer is characterised by a
where
For a multi-layer assembly, the total transfer matrix is:
where $\mathbf{M}{si}$ is the internal surface resistance matrix and the product runs from the innermost to outermost layer. The external surface resistance $R{so}$ is excluded from the matrix because it is applied as a boundary condition in the solver.
The sol-air temperature is decomposed into Fourier harmonics via FFT:
At each harmonic
where
where
Solar position and irradiance transposition are handled by pvlib. Weather data comes from standard EPW files.
Building and ground facets are loaded from STL files and assigned assemblies, absorptivity, emissivity, and boundary conditions through data/neighborhood_surfaces.json.
Occlusion is handled in two places:
- Direct beam solar is blocked when the sun ray from a facet centroid intersects another STL triangle.
- Diffuse solar and longwave radiant temperature use pyViewFactor view factors with visibility and obstruction checks. Sky view is computed by complementarity against the modeled building and ground facets.
For the checked-in 5 m STL mesh, direct-shadow masks and pyViewFactor view factors are computed on each original rectangular parent surface and applied to its 5 m child triangles. This keeps the annual example practical while preserving the finer rendering and per-triangle thermal solve.
Surface temperatures over a full diurnal cycle on the hottest day of the year in Atlanta. The animation loops continuously, showing how surfaces heat rapidly after sunrise, peak in the afternoon, and cool through the night.
Surface temperatures at the peak summer hour for a 12-building neighborhood in Atlanta. Buildings have brick walls and concrete roofs; ground surfaces include concrete streets, grass lawns, and brick courtyards.
Selected surface temperatures over 3 summer days compared to air temperature. Dark surfaces (roofs,
Hour-of-day vs day-of-year heatmap for a concrete street surface (
The example loads STL files generated from 12 box-shaped buildings of varying height (5--15 m), footprint, and rotation (0--60 deg), arranged on a ~200 m grid with concrete streets, grass lawns, and brick courtyards. Each STL triangle is simulated as its own surface. The checked-in default subdivides rectangular faces to a 5 m target panel size, for 956 building facets and 2,222 ground facets.
| Surface type | Material layers | |
|---|---|---|
| Concrete street | 1 m subsoil + 200 mm concrete | 0.65 |
| Brick paving | 1 m subsoil + 100 mm sand + 65 mm brick | 0.70 |
| Grass | 1 m subsoil + 300 mm topsoil + 50 mm sod | 0.75 |
| Brick wall | 13 mm plaster + 150 mm block + 25 mm air gap + 102 mm brick | 0.70 |
| Concrete roof | 13 mm plaster + 150 mm concrete + 35 mm insulation + 15 mm screed + 1 mm membrane | 0.85 |
surface_temps/
constants.py Material properties, physical constants
materials.py Layer and Assembly transfer matrix computation
weather.py EPW loading via pvlib
solar.py Sol-air temperature, sky temperature, irradiance transposition
radiation.py Direct sun obstruction and pyViewFactor sky/ground/building factors
convection.py Wind-dependent convective heat transfer coefficient
admittance.py Core FFT/IFFT solver
geometry.py STL/JSON geometry import plus procedural default generator source
surfaces.py Simulation orchestrator
plotting.py 2D time-series, heatmaps, 3D neighborhood visualization
uv run pytestTests verify transfer matrix determinant = 1, steady-state and sinusoidal response correctness, temperature bounds, orientation effects (south walls warmer than north), and absorptivity ordering. They also verify STL loading, JSON mapping, view-factor sky obstruction, and lower temperatures for directly shaded surfaces.
- numpy
- pvlib
- pandas
- matplotlib
- pyviewfactor
- pyvista
- numba
- tqdm
Beckett, O., Owens, S., and Acred, A. (2026). "Applying Frequency Domain Methods for Calculating Outdoor Surface Temperatures." Proceedings of the 12th National Conference of IBPSA-USA, Minneapolis, MN.



