Source code for honeybee_radiance.properties.room

# coding=utf-8
"""Room Radiance Properties."""
import math

from ladybug_geometry.geometry3d.pointvector import Vector3D
from honeybee.facetype import Floor, Wall
from honeybee.typing import clean_rad_string
from honeybee.checkdup import is_equivalent

from ..sensorgrid import SensorGrid
from ..view import View
from ..modifierset import ModifierSet
from ..lib.modifiersets import generic_modifier_set_visible


[docs]class RoomRadianceProperties(object): """Radiance Properties for Honeybee Room. Args: host: A honeybee_core Room object that hosts these properties. modifier_set: A honeybee ModifierSet object to specify all default modifiers for the Faces of the Room. If None, the Room will use the honeybee default modifier set, which is only representative of typical indoor conditions in the visible spectrum. Default: None. Properties: * host * modifier_set """ __slots__ = ('_host', '_modifier_set') def __init__(self, host, modifier_set=None): """Initialize Room radiance properties.""" # set the main properties of the Room self._host = host self.modifier_set = modifier_set @property def host(self): """Get the Room object hosting these properties.""" return self._host @property def modifier_set(self): """Get or set the Room ModifierSet object. If not set, it will be the Honeybee default generic ModifierSet. """ if self._modifier_set is not None: # set by the user return self._modifier_set else: return generic_modifier_set_visible @modifier_set.setter def modifier_set(self, value): if value is not None: assert isinstance(value, ModifierSet), \ 'Expected ModifierSet. Got {}'.format(type(value)) value.lock() # lock in case modifier set has multiple references self._modifier_set = value
[docs] def generate_sensor_grid( self, x_dim, y_dim=None, offset=1.0, remove_out=False, wall_offset=0): """Get a radiance SensorGrid generated from this Room's floors. The output grid will have this room referenced in its room_identifier property. It will also include a Mesh3D object with faces that align with the grid positions under the grid's mesh property. Note that the x_dim and y_dim refer to dimensions within the XY coordinate system of the floor faces's planes. So rotating the planes of the floor faces will result in rotated grid cells. Args: x_dim: The x dimension of the grid cells as a number. y_dim: The y dimension of the grid cells as a number. If None, the y dimension will be assumed to be the same as the x dimension. (Default: None). offset: A number for how far to offset the grid from the base face. (Default is 1.0, which will not offset the grid to be 1 unit above the floor). remove_out: Boolean to note whether an extra check should be run to remove sensor points that lie outside the Room volume. Note that this can add significantly to runtime and this check is not necessary in the case that all walls are vertical and all floors are horizontal (Default: False). wall_offset: A number for the distance at which sensors close to walls should be removed. Note that this option has no effect unless the value is more than half of the x_dim or y_dim. (Default: 0). Returns: A honeybee_radiance SensorGrid generated from the floors of the room. Will be None if the Room has no floors or the criteria for wall_offset and/or remove_out results in all sensors being removed. Usage: .. code-block:: python from honeybee.room import Room room = Room.from_box(3.0, 6.0, 3.2, 180) south_face = room[3] south_face.apertures_by_ratio(0.4, 0.01) sensor_grid = room.properties.radiance.generate_grid(0.5, 0.5, 1) """ # generate the mesh grid from the floor Faces floor_grid = self._base_sensor_mesh( x_dim, y_dim, offset, remove_out, wall_offset) if floor_grid is None: # no valid mesh could be generated return None # create the sensor grid from the mesh sensor_grid = SensorGrid.from_mesh3d( clean_rad_string(self.host.display_name), floor_grid) sensor_grid.room_identifier = self.host.identifier sensor_grid.display_name = self.host.display_name sensor_grid.base_geometry = \ tuple(face.geometry.move(face.normal.reverse() * offset) for face in self.host.faces if isinstance(face.type, Floor)) return sensor_grid
[docs] def generate_sensor_grid_radial( self, x_dim, y_dim=None, offset=1.0, remove_out=False, wall_offset=0, dir_count=8, start_vector=Vector3D(0, -1, 0), mesh_radius=None): """Get a SensorGrid of radial directions around positions from the floors. This type of sensor grid is particularly helpful for studies of multiple view directions, such as imageless glare studies. The output grid will have this room referenced in its room_identifier property. It will also include a Mesh3D of radial faces around each position under the grid's mesh property. Note that the x_dim and y_dim refer to dimensions within the XY coordinate system of the floor faces's planes. So rotating the planes of the floor faces will result in rotated grid cells. Args: x_dim: The x dimension of the grid cells as a number. y_dim: The y dimension of the grid cells as a number. If None, the y dimension will be assumed to be the same as the x dimension. (Default: None). offset: A number for how far to offset the grid from the base face. (Default: 1.0, which will not offset the grid to be 1 unit above the floor). remove_out: Boolean to note whether an extra check should be run to remove sensor points that lie outside the Room volume. Note that this can add significantly to runtime and this check is not necessary in the case that all walls are vertical and all floors are horizontal (Default: False). wall_offset: A number for the distance at which sensors close to walls should be removed. Note that this option has no effect unless the value is more than half of the x_dim or y_dim. (Default: 0). dir_count: A positive integer for the number of radial directions to be generated around each position. (Default: 8). start_vector: A Vector3D to set the start direction of the generated directions. This can be used to orient the resulting sensors to specific parts of the scene. It can also change the elevation of the resulting directions since this start vector will always be rotated in the XY plane to generate the resulting directions. (Default: (0, -1, 0)). mesh_radius: An optional number to override the radius of the meshes generated around each sensor. If None, it will be equal to 45% of the x_dim or y_dim (whichever is smaller). Set to zero to ensure no mesh is added to the resulting sensor grids. (Default: None). Returns: A honeybee_radiance SensorGrid generated from the floors of the room. Will be None if the Room has no floors or the criteria for wall_offset and/or remove_out results in all sensors being removed. """ # generate the mesh grid from the floor Faces floor_grid = self._base_sensor_mesh( x_dim, y_dim, offset, remove_out, wall_offset) if floor_grid is None: # no valid mesh could be generated return None # create the sensor grid from the mesh if mesh_radius is None: small_dim = x_dim if y_dim is None else min((x_dim, y_dim)) mesh_radius = small_dim * 0.45 grid_name = '{}_Radial'.format(clean_rad_string(self.host.display_name)) sensor_grid = SensorGrid.from_mesh3d_radial( grid_name, floor_grid, dir_count, start_vector, mesh_radius) sensor_grid.room_identifier = self.host.identifier sensor_grid.display_name = self.host.display_name sensor_grid.base_geometry = \ tuple(face.geometry.move(face.normal.reverse() * offset) for face in self.host.faces if isinstance(face.type, Floor)) return sensor_grid
[docs] def generate_exterior_face_sensor_grid( self, dimension, offset=0.1, face_type='Wall', punched_geometry=False): """Get a radiance SensorGrid generated from the exterior Faces of this room. This will be None if the Room has no exterior Faces. The output grid will have this room referenced in its room_identifier property. It will also include a Mesh3D object with faces that align with the grid positions under the grid's mesh property. Args: dimension: The dimension of the grid cells as a number. offset: A number for how far to offset the grid from the base face. Positive numbers indicate an offset towards the exterior. (Default is 0.1, which will offset the grid to be 0.1 unit from the faces). face_type: Text to specify the type of face that will be used to generate grids. Note that only Faces with Outdoors boundary conditions will be used, meaning that most Floors will typically be excluded unless they represent the underside of a cantilever. Choose from the following. (Default: Wall). * Wall * Roof * Floor * All punched_geometry: Boolean to note whether the punched_geometry of the faces should be used (True) with the areas of sub-faces removed from the grid or the full geometry should be used (False). (Default:False). Returns: A honeybee_radiance SensorGrid generated from the exterior Faces of the room. Will be None if the Room has no exterior Faces. Usage: .. code-block:: python from honeybee.room import Room room = Room.from_box(3.0, 6.0, 3.2, 180) s_grid = room.properties.radiance.generate_exterior_face_sensor_grid(0.5) """ # generate the mesh grid from the exterior Faces face_grid = self.host.generate_exterior_face_grid( dimension, offset, face_type, punched_geometry) if face_grid is None: # no valid mesh could be generated return None # create the sensor grid from the mesh f_nm = 'Faces' if face_type.title() == 'All' else face_type.title() grid_name = '{}_Exterior{}'.format(self.host.display_name, f_nm) sensor_grid = SensorGrid.from_mesh3d(clean_rad_string(grid_name), face_grid) sensor_grid.room_identifier = self.host.identifier sensor_grid.display_name = self.host.display_name return sensor_grid
[docs] def generate_exterior_aperture_sensor_grid( self, dimension, offset=0.1, aperture_type='All'): """Get a radiance SensorGrid generated from the exterior Apertures of this room. Will be None if the Room has no exterior Apertures. The output grid will have this room referenced in its room_identifier property. It will also include a Mesh3D object with faces that align with the grid positions under the grid's mesh property. Args: dimension: The dimension of the grid cells as a number. offset: A number for how far to offset the grid from the base face. Positive numbers indicate an offset towards the exterior while negative numbers indicate an offset towards the interior, essentially modeling the value of sun on the building interior. (Default is 0.1, which will offset the grid to be 0.1 unit from the faces). aperture_type: Text to specify the type of Aperture that will be used to generate grids. Window indicates Apertures in Walls. Skylights are in parent Roof faces. Choose from the following. (Default: All). * Window * Skylight * All Returns: A honeybee_radiance SensorGrid generated from the exterior Apertures of the room. Will be None if the Room has no exterior Apertures or if the grid size is too large for the Apertures. Usage: .. code-block:: python from honeybee.room import Room room = Room.from_box(3.0, 6.0, 3.2, 180) room[3].apertures_by_ratio(0.4) s_grid = room.properties.radiance.generate_exterior_aperture_sensor_grid(0.5) """ # generate the mesh grid from the exterior Apertures ap_grid = self.host.generate_exterior_aperture_grid( dimension, offset, aperture_type) if ap_grid is None: # no valid mesh could be generated return None # create the sensor grid from the mesh f_nm = 'Apertures' if aperture_type.title() == 'All' else aperture_type.title() grid_name = '{}_Exterior{}'.format(self.host.display_name, f_nm) sensor_grid = SensorGrid.from_mesh3d(clean_rad_string(grid_name), ap_grid) sensor_grid.room_identifier = self.host.identifier sensor_grid.display_name = self.host.display_name return sensor_grid
[docs] def generate_view(self, direction, up_vector=(0, 0, 1), type='v', h_size=60, v_size=60, shift=None, lift=None): """Get a single view in the center of the room facing a given direction. Note that the view will be located at the center of the bounding box around the room geometry and not the volume centroid. Also note that the view may not lie inside the room if the room is highly concave. The output view will have this room referenced in their room_identifier property. Args: direction: Set the view direction (-vd) vector to (x, y, z). The length of this vector indicates the focal distance as needed by the pixel depth of field (-pd) in rpict. up_vector: Set the view up (-vu) vector (vertical direction) to (x, y, z) default: (0, 0, 1). type: A single character for the view type (-vt). Choose from the following. * v - Perspective * h - Hemispherical fisheye * l - Parallel * c - Cylindrical panorama * a - Angular fisheye * s - Planisphere [stereographic] projection For more detailed description about view types check rpict manual page: (http://radsite.lbl.gov/radiance/man_html/rpict.1.html) h_size: Set the view horizontal size (-vh). For a perspective projection (including fisheye views), val is the horizontal field of view (in degrees). For a parallel projection, val is the view width in world coordinates. v_size: Set the view vertical size (-vv). For a perspective projection (including fisheye views), val is the horizontal field of view (in degrees). For a parallel projection, val is the view width in world coordinates. shift: Set the view shift (-vs). This is the amount the actual image will be shifted to the right of the specified view. This option is useful for generating skewed perspectives or rendering an image a piece at a time. A value of 1 means that the rendered image starts just to the right of the normal view. A value of -1 would be to the left. Larger or fractional values are permitted as well. lift: Set the view lift (-vl) to a value. This is the amount the actual image will be lifted up from the specified view. Returns: A honeybee_radiance View generated in the center of the Room. Usage: .. code-block:: python from honeybee.room import Room room = Room.from_box(3.0, 6.0, 3.2, 180) south_face = room[3] south_face.apertures_by_ratio(0.4, 0.01) view = room.properties.radiance.generate_view((0, -1, 0)) """ pos = (self.host.center.x, self.host.center.y, self.host.center.z) view = View(self.host.identifier, pos, direction, up_vector, type, h_size, v_size, shift, lift) view.room_identifier = self.host.identifier view.display_name = self.host.display_name return view
[docs] @classmethod def from_dict(cls, data, host): """Create RoomRadianceProperties from a dictionary. Note that the dictionary must be a non-abridged version for this classmethod to work. Args: data: A dictionary representation of RoomRadianceProperties with the format below. host: A Room object that hosts these properties. .. code-block:: python { 'type': 'RoomRadianceProperties', 'modifier_set': {}, # ModifierSet dictionary } """ assert data['type'] == 'RoomRadianceProperties', \ 'Expected RoomRadianceProperties. Got {}.'.format(data['type']) new_prop = cls(host) if 'modifier_set' in data and data['modifier_set'] is not None: new_prop.modifier_set = ModifierSet.from_dict(data['modifier_set']) return new_prop
[docs] def apply_properties_from_dict(self, abridged_data, modifier_sets): """Apply properties from a RoomRadiancePropertiesAbridged dictionary. Args: abridged_data: A RoomRadiancePropertiesAbridged dictionary (typically coming from a Model) with the format below. modifier_sets: A dictionary of ModifierSets with identifiers of the sets as keys, which will be used to re-assign modifier_sets. .. code-block:: python { 'type': 'RoomRadiancePropertiesAbridged', 'modifier_set': str, # ModifierSet identifier } """ if 'modifier_set' in abridged_data and abridged_data['modifier_set'] is not None: self.modifier_set = modifier_sets[abridged_data['modifier_set']]
[docs] def to_dict(self, abridged=False): """Return Room radiance properties as a dictionary. Args: abridged: Boolean for whether the full dictionary of the Room should be written (False) or just the identifier of the the individual properties (True). Default: False. """ base = {'radiance': {}} base['radiance']['type'] = 'RoomRadianceProperties' if not \ abridged else 'RoomRadiancePropertiesAbridged' # write the ModifierSet into the dictionary if self._modifier_set is not None: base['radiance']['modifier_set'] = \ self._modifier_set.identifier if abridged else \ self._modifier_set.to_dict() return base
[docs] def duplicate(self, new_host=None): """Get a copy of this object. Args: new_host: A new Room object that hosts these properties. If None, the properties will be duplicated with the same host. """ _host = new_host or self._host new_room = RoomRadianceProperties(_host, self._modifier_set) return new_room
[docs] def is_equivalent(self, other): """Check to see if these Radiance properties are equivalent to another object. """ if not is_equivalent(self._modifier_set, other._modifier_set): return False return True
def _base_sensor_mesh(self, x_dim, y_dim, offset, remove_out, wall_offset): """Get a base Mesh3D from the Room floors to be used for sensor girds.""" # generate the mesh grid from the floor Faces floor_grid = self.host.generate_grid(x_dim, y_dim, offset) if floor_grid is None: # no floors in the host Room return None # remove any outdoor sensors if this has been requested if remove_out: geo = self.host.geometry pattern = [geo.is_point_inside(pt) for pt in floor_grid.face_centroids] try: floor_grid, vertex_pattern = floor_grid.remove_faces(pattern) except AssertionError: # the grid lies completely outside of the room return None # remove any sensors within a certain distance of the walls, if requested if wall_offset >= x_dim / 2 or (y_dim is not None and wall_offset >= y_dim / 2): wall_geos = [f.geometry for f in self.host.faces if isinstance(f.type, Wall)] pattern = [] for pt in floor_grid.face_centroids: for wg in wall_geos: close_pt = wg.plane.closest_point(pt) p_dist = pt.distance_to_point(close_pt) if p_dist <= wall_offset: close_pt_2d = wg.plane.xyz_to_xy(close_pt) g_dist = wg.polygon2d.distance_to_point(close_pt_2d) f_dist = math.sqrt(p_dist ** 2 + g_dist ** 2) if f_dist <= wall_offset: pattern.append(False) break else: pattern.append(True) try: floor_grid, vertex_pattern = floor_grid.remove_faces(pattern) except AssertionError: # the grid lies completely outside of the room return None return floor_grid
[docs] def ToString(self): return self.__repr__()
def __repr__(self): return 'Room Radiance Properties: [host: {}]'.format(self.host.display_name)