Skip to content
Open
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
19819e4
refactor geometry_generator with compas (failed because of breps)
yiqiaowang-arch Jul 17, 2025
8e3c792
update gitignore to ignore vscode settings
yiqiaowang-arch Aug 4, 2025
3c81499
Merge remote-tracking branch 'upstream/master' into refactor_geo_gen_…
yiqiaowang-arch Aug 4, 2025
58e4dd6
Merge branch 'master' into refactor_geo_gen_compas
yiqiaowang-arch Aug 5, 2025
3e08fda
calc_solid with compas_occ (unfinished)
yiqiaowang-arch Aug 5, 2025
805c9a1
Merge remote-tracking branch 'upstream/master' into refactor_geo_gen_…
yiqiaowang-arch Aug 7, 2025
65409ae
refactor geometry_generator with compas (failed because of breps)
yiqiaowang-arch Sep 2, 2025
a268437
Merge remote-tracking branch 'upstream/master' into refactor_geo_gen_…
yiqiaowang-arch Sep 5, 2025
b33f5cc
Merge branch 'master' into refactor_geo_gen_compas
yiqiaowang-arch Sep 28, 2025
a619be9
wip
yiqiaowang-arch Sep 29, 2025
f2cdcd7
refactor geometry generator with compas
yiqiaowang-arch Sep 30, 2025
2598c0b
change saved geometry from BrepEdge to Polygon
yiqiaowang-arch Sep 30, 2025
59aa236
add functionality to test if point is in Brep
yiqiaowang-arch Sep 30, 2025
c17fea7
convert BuildingGeometry into dataclass
yiqiaowang-arch Sep 30, 2025
cd436fb
replace py4design with compas in radiance
yiqiaowang-arch Sep 30, 2025
bb26f0c
update example code for compas
yiqiaowang-arch Sep 30, 2025
1ab0f42
Merge remote-tracking branch 'upstream/master' into refactor_geo_gen_…
yiqiaowang-arch Sep 30, 2025
189a0d1
add sensor area calculation functionality
yiqiaowang-arch Oct 1, 2025
fdc77f6
move BuildingGeometry into a separate file
yiqiaowang-arch Oct 1, 2025
26377db
formatting
yiqiaowang-arch Oct 1, 2025
29e561a
replace py4design with compas
yiqiaowang-arch Oct 1, 2025
4ffe701
bugfix: now return world coord when grid is too large
yiqiaowang-arch Oct 2, 2025
47da97a
bug fix
yiqiaowang-arch Oct 2, 2025
0a02717
bug fix: now directions of faces are correctly considered
yiqiaowang-arch Oct 2, 2025
9cb5a74
Merge remote-tracking branch 'upstream/master' into refactor_geo_gen_…
yiqiaowang-arch Oct 3, 2025
6b6db54
clean up temp files
yiqiaowang-arch Oct 3, 2025
14f6a78
more stable sensor area calculator
yiqiaowang-arch Oct 4, 2025
ba0a9dc
enabling multiprocessing by avoiding pickling OCCBreps by returning o…
yiqiaowang-arch Oct 4, 2025
010aebd
Add compas, compas_occ, compas-libigl dependencies
reyery Oct 4, 2025
30288bb
improve docstrings in geometry_generator
yiqiaowang-arch Oct 4, 2025
59e660c
Merge branch 'refactor_geo_gen_compas' of https://github.com/yiqiaowa…
yiqiaowang-arch Oct 4, 2025
d2f4f72
move polygon directly to world coordinates as a whole
yiqiaowang-arch Oct 7, 2025
f608773
try to fix boolean union problem
yiqiaowang-arch Oct 7, 2025
8fbc3b9
Revert "try to fix boolean union problem"
yiqiaowang-arch Oct 7, 2025
bd49c09
remove boolean union when creating building brep
yiqiaowang-arch Oct 7, 2025
7270933
more robust normal calculation in case footprint in concave
yiqiaowang-arch Oct 7, 2025
e9db222
simplified point-in-solid check assuming buildings are all vertical e…
yiqiaowang-arch Oct 7, 2025
3ae8670
Merge remote-tracking branch 'upstream/master' into refactor_geo_gen_…
yiqiaowang-arch Oct 7, 2025
c68bf30
refactor for sensor area calculator for robustness
yiqiaowang-arch Oct 7, 2025
f6d270a
more accurate (and slightly more expensive) way of identifying potent…
yiqiaowang-arch Oct 7, 2025
acf503b
fix type linting and comments
yiqiaowang-arch Oct 8, 2025
b883a6e
Merge remote-tracking branch 'upstream/master' into refactor_geo_gen_…
yiqiaowang-arch Oct 8, 2025
17f2f4b
Merge remote-tracking branch 'upstream/master' into refactor_geo_gen_…
yiqiaowang-arch Oct 9, 2025
7f08337
remove compas_occ from pixi
yiqiaowang-arch Oct 9, 2025
cc4ce14
Avoid creating tiny patches by filtering out geometries with area les…
yiqiaowang-arch Oct 9, 2025
49eb97d
Merge branch 'master' into refactor_geo_gen_compas
yiqiaowang-arch Dec 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions cea/resources/radiation/building_geometry_radiation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
from __future__ import annotations

import os
import pickle
from dataclasses import MISSING, dataclass, field, fields
from typing import TYPE_CHECKING, Any, NamedTuple

if TYPE_CHECKING:
from compas.geometry import Polygon, Vector


SURFACE_TYPES = ["walls", "windows", "roofs", "undersides"]
SURFACE_DIRECTION_LABELS = {
"windows_east",
"windows_west",
"windows_south",
"windows_north",
"walls_east",
"walls_west",
"walls_south",
"walls_north",
"roofs_top",
"undersides_bottom",
}


class SurfaceGroup(NamedTuple):
faces: list[Polygon]
orientations: list[str]
normals: list[Vector]
intersects: list[bool]


@dataclass(slots=True)
class BuildingGeometryForRadiation(object):
name: str
footprint: list[Polygon] # footprint means the building's projection on the ground,
walls: list[Polygon]
roofs: list[Polygon]

# optional slots
terrain_elevation: float = field(
default=0.0
) # elevation of the building footprint's middle point.
windows: list[Polygon] = field(default_factory=list)
orientation_windows: list[str] = field(default_factory=list)
normals_windows: list[Vector] = field(default_factory=list)
intersect_windows: list[bool] = field(default_factory=list)

orientation_walls: list[str] = field(default_factory=list)
normals_walls: list[Vector] = field(default_factory=list)
intersect_walls: list[bool] = field(default_factory=list)

orientation_roofs: list[str] = field(default_factory=list)
normals_roofs: list[Vector] = field(default_factory=list)
intersect_roofs: list[bool] = field(default_factory=list)

undersides: list[Polygon] = field(
default_factory=list
) # undersides means the bottom surface of the building in case it is elevated above the ground.
orientation_undersides: list[str] = field(default_factory=list)
normals_undersides: list[Vector] = field(default_factory=list)
intersect_undersides: list[bool] = field(default_factory=list)

def group(self, srf_type: str) -> SurfaceGroup:
mapping_dict = {
"walls": (
self.walls,
self.orientation_walls,
self.normals_walls,
self.intersect_walls,
),
"windows": (
self.windows,
self.orientation_windows,
self.normals_windows,
self.intersect_windows,
),
"roofs": (
self.roofs,
self.orientation_roofs,
self.normals_roofs,
self.intersect_roofs,
),
"undersides": (
self.undersides,
self.orientation_undersides,
self.normals_undersides,
self.intersect_undersides,
),
}
return SurfaceGroup(*mapping_dict[srf_type])

def __getstate__(self) -> list[Any]:
# Use fields(type(self)) so static type checkers recognize a dataclass type
return [getattr(self, f.name) for f in fields(type(self))]

def __setstate__(self, data: list[Any]) -> None:
for f, v in zip(fields(type(self)), data):
setattr(self, f.name, v)

@classmethod
def from_dict(cls, d: dict[str, Any]) -> "BuildingGeometryForRadiation":
fs = fields(cls)
valid = {f.name for f in fs}
# Only pass known, non-None keys so defaults kick in for optionals
kwargs = {k: v for k, v in d.items() if k in valid and v is not None}

# Required = fields with no default and no default_factory
required = [
f.name for f in fs if f.default is MISSING and f.default_factory is MISSING
]
missing = [name for name in required if name not in kwargs]
if missing:
raise ValueError(f"Missing required field(s): {', '.join(missing)}")

return cls(**kwargs)

@classmethod
def load(cls, pickle_location: str) -> "BuildingGeometryForRadiation":
with open(pickle_location, "rb") as fp:
state = pickle.load(fp)

if not isinstance(state, list):
raise TypeError(f"Expected a list state, got {type(state).__name__}")

obj = cls.__new__(cls) # bypass __init__
obj.__setstate__(state) # restore using your dataclass fields order
return obj

def save(self, pickle_location: str) -> str:
directory = os.path.dirname(pickle_location)
if directory:
os.makedirs(directory, exist_ok=True)
with open(pickle_location, "wb") as f:
pickle.dump(self.__getstate__(), f)
Comment thread
yiqiaowang-arch marked this conversation as resolved.
return pickle_location
Loading