The one thing every interior user needs and does not have: a way to turn the FLAT mesh shape the rest of this SDK produces into the NESTED entity shape the interior models require.
client.buildings.get_area() returns AreaBuildings whose .buildings is a Dict[str, DotBimMesh] — the flat {coordinates, indices} shape used by the six outdoor grid models. Interior models need {"geometry": {"payload": {"coordinates": …, "indices": …}}}. Passing the flat form is not an error server-side: the entity is read as having no geometry, skipped, and the analysis runs against an empty occluder, returning a confident non-physical result.
So the natural thing to do — fetch neighbouring buildings and drop them into context_geometry — is exactly the thing that silently produces a wrong answer. These helpers exist so that path is one obvious call instead of a trap.
to_interior_entity
to_interior_entity( mesh: Any, *, category: Optional[str] = None, opening_factor: Optional[float] = None, position: Optional[Mapping[str, float]] = None, rotation: Optional[Mapping[str, float]] = None, ) -> Dict[str, Any]
Wrap a flat mesh in the nested interior entity shape.
Accepts a DotBimMesh, a plain {"coordinates": …, "indices": …} dict, or an already-nested entity (returned with its extras applied), so it is safe to call on anything.
Parameters mirror the wire contract rather than tidying it up:
category Tags what an entity IS. Required by two of daylight-factor's four tiers: the floors and buildings tiers cluster storeys from barriers carrying category="floor" and then read their geometry.position/rotation strictly — plain barriers give a billed rejection, and categorised-but-untransformed slabs give a billed 500. The sensor_points and sensor_surfaces tiers ignore it. opening_factor daylight-factor only; emitted as openingFactor (camelCase, the only spelling the worker reads). Visible-light transmittance in [0, 1]. position / rotation Read strictly wherever an entity is categorised, and their absence is a 500 rather than a validation error. Defaulted to identity when a category is given.
interior_entities
interior_entities( meshes: Any, *, category: Optional[str] = None, opening_factor: Optional[float] = None, ) -> Dict[str, Dict[str, Any]]
Convert a whole map of flat meshes into interior entities.
Takes the AreaBuildings returned by client.buildings.get_area(), its .buildings mapping, or any {id: mesh} dict, and returns a map ready to pass as context_geometry / barriers / spatial_volumes.
The intended use, and the reason this exists:
area = client.buildings.get_area(site_polygon)
request = DaylightFactorModelRequest(
analysis_type=AnalysesName.daylight_factor,
barriers=my_room_barriers,
sensor_points=my_points,
context_geometry=interior_entities(area), # neighbours as occluders
)Without the conversion those neighbours are silently dropped and the room reads far brighter than it is.