Source code for honeybee.boundarycondition

"""Boundary Condition for Face, Aperture, Door."""
import re

from .typing import float_in_range, tuple_with_length
from .altnumber import autocalculate


class _BoundaryCondition(object):
    """Base boundary condition class."""

    __slots__ = ()

    def __init__(self):
        """Initialize Boundary condition."""

    @property
    def name(self):
        """Get the name of the boundary condition (ie. 'Outdoors', 'Ground')."""
        return self.__class__.__name__

    @property
    def view_factor(self):
        """Get the view factor to the ground."""
        return 'autocalculate'

    @property
    def sun_exposure_idf(self):
        """Get a text string for sun exposure, which is write-able into an IDF."""
        return 'NoSun'

    @property
    def wind_exposure_idf(self):
        """ Get a text string for wind exposure, which is write-able into an IDF."""
        return 'NoWind'

    def to_dict(self):
        """Get the boundary condition as a dictionary."""
        return {'type': self.name}

    def ToString(self):
        """Overwrite .NET ToString."""
        return self.__repr__()

    def __repr__(self):
        return self.name


[docs]class Outdoors(_BoundaryCondition): """Outdoor boundary condition. Args: sun_exposure: A boolean noting whether the boundary is exposed to sun. Default: True. wind_exposure: A boolean noting whether the boundary is exposed to wind. Default: True. view_factor: A number between 0 and 1 for the view factor to the ground. This input can also be an Autocalculate object to signify that the view factor automatically calculated. Default: autocalculate. """ __slots__ = ('_sun_exposure', '_wind_exposure', '_view_factor') def __init__(self, sun_exposure=True, wind_exposure=True, view_factor=autocalculate): """Initialize Outdoors boundary condition.""" assert isinstance(sun_exposure, bool), \ 'Input sun_exposure must be a Boolean. Got {}.'.format(type(sun_exposure)) self._sun_exposure = sun_exposure assert isinstance(wind_exposure, bool), \ 'Input wind_exposure must be a Boolean. Got {}.'.format(type(wind_exposure)) self._wind_exposure = wind_exposure if view_factor == autocalculate: self._view_factor = autocalculate else: self._view_factor = float_in_range( view_factor, 0.0, 1.0, 'view factor to ground')
[docs] @classmethod def from_dict(cls, data): """Initialize Outdoors BoundaryCondition from a dictionary. Args: data: A dictionary representation of the boundary condition. """ assert data['type'] == 'Outdoors', 'Expected dictionary for Outdoors boundary ' \ 'condition. Got {}.'.format(data['type']) sun_exposure = True if 'sun_exposure' not in data else data['sun_exposure'] wind_exposure = True if 'wind_exposure' not in data else data['wind_exposure'] view_factor = autocalculate if 'view_factor' not in data or \ data['view_factor'] == autocalculate.to_dict() else data['view_factor'] return cls(sun_exposure, wind_exposure, view_factor)
@property def sun_exposure(self): """Get a boolean noting whether the boundary is exposed to sun.""" return self._sun_exposure @property def wind_exposure(self): """Get a boolean noting whether the boundary is exposed to wind.""" return self._wind_exposure @property def view_factor(self): """Get the view factor to the ground as a number or 'autocalculate'.""" return self._view_factor @property def sun_exposure_idf(self): """Get a text string for sun exposure, which is write-able into an IDF.""" return 'NoSun' if not self.sun_exposure else 'SunExposed' @property def wind_exposure_idf(self): """Get a text string for wind exposure, which is write-able into an IDF.""" return 'NoWind' if not self.wind_exposure else 'WindExposed'
[docs] def to_dict(self, full=False): """Get the boundary condition as a dictionary. Args: full: Set to True to get the full dictionary which includes energy simulation specific keys such as sun_exposure, wind_exposure and view_factor. (Default: False). """ bc_dict = {'type': self.name} if full: bc_dict['sun_exposure'] = self.sun_exposure bc_dict['wind_exposure'] = self.wind_exposure bc_dict['view_factor'] = autocalculate.to_dict() if \ self.view_factor == autocalculate else self.view_factor return bc_dict
def __key(self): """A tuple based on the object properties, useful for hashing.""" return (self.sun_exposure, self.wind_exposure, self.view_factor) def __hash__(self): return hash(self.__key()) def __eq__(self, other): return isinstance(other, Outdoors) and self.__key() == other.__key()
[docs]class Surface(_BoundaryCondition): """Boundary condition when an object is adjacent to another object.""" __slots__ = ('_boundary_condition_objects',) def __init__(self, boundary_condition_objects, sub_face=False): """Initialize Surface boundary condition. Args: boundary_condition_objects: A list of up to 3 object identifiers that are adjacent to this one. The first object is always immediately adjacent and is of the same object type (Face, Aperture, Door). When this boundary condition is applied to a Face, the second object in the tuple will be the parent Room of the adjacent object. When the boundary condition is applied to a sub-face (Door or Aperture), the second object will be the parent Face of the adjacent sub-face and the third object will be the parent Room of the adjacent sub-face. sub_face: Boolean to note whether this boundary condition is applied to a sub-face (an Aperture or a Door) instead of a Face. (Default: False). """ if sub_face: self._boundary_condition_objects = tuple_with_length( boundary_condition_objects, 3, str, 'boundary_condition_objects for Apertures or Doors') else: self._boundary_condition_objects = tuple_with_length( boundary_condition_objects, 2, str, 'boundary_condition_objects for Faces')
[docs] @classmethod def from_dict(cls, data, sub_face=False): """Initialize Surface BoundaryCondition from a dictionary. Args: data: A dictionary representation of the boundary condition. sub_face: Boolean to note whether this boundary condition is applied to a sub-face (an Aperture or a Door) instead of a Face. Default: False. """ assert data['type'] == 'Surface', 'Expected dictionary for Surface boundary ' \ 'condition. Got {}.'.format(data['type']) return cls(data['boundary_condition_objects'], sub_face)
[docs] @classmethod def from_other_object(cls, other_object, sub_face=False): """Initialize Surface boundary condition from an adjacent other object. Args: other_object: Another object (Face, Aperture, Door) of the same type that this boundary condition is assigned. This other_object will be set as the adjacent object in this boundary condition. sub_face: Boolean to note whether this boundary condition is applied to a sub-face (an Aperture or a Door) instead of a Face. Default: False. """ error_msg = 'Surface boundary conditions can only be assigned to objects' \ ' with parent Rooms.' bc_objects = [other_object.identifier] if other_object.has_parent: bc_objects.append(other_object.parent.identifier) if sub_face: if other_object.parent.has_parent: bc_objects.append(other_object.parent.parent.identifier) else: raise AttributeError(error_msg) else: raise AttributeError(error_msg) return cls(bc_objects, sub_face)
@property def boundary_condition_objects(self): """Get a tuple of up to 3 object identifiers that are adjacent to this one. The first object is always the one that is immediately adjacent and is of the same object type (Face, Aperture, Door). When this boundary condition is applied to a Face, the second object in the tuple will be the parent Room of the adjacent object. When the boundary condition is applied to a sub-face (Door or Aperture), the second object will be the parent Face of the sub-face and the third object will be the parent Room of the adjacent sub-face. """ return self._boundary_condition_objects @property def boundary_condition_object(self): """Get the identifier of the object adjacent to this one.""" return self._boundary_condition_objects[0]
[docs] def to_dict(self): """Get the boundary condition as a dictionary. Args: full: Set to True to get the full dictionary which includes energy simulation specific keys such as sun_exposure, wind_exposure and view_factor. Default: False. """ return {'type': self.name, 'boundary_condition_objects': self.boundary_condition_objects}
def __key(self): """A tuple based on the object properties, useful for hashing.""" return self.boundary_condition_objects def __hash__(self): return hash(self.__key()) def __eq__(self, other): return isinstance(other, Surface) and self.__key() == other.__key()
[docs]class Ground(_BoundaryCondition): """Ground boundary condition. Args: data: A dictionary representation of the boundary condition. """ __slots__ = ()
[docs] @classmethod def from_dict(cls, data): """Initialize Ground BoundaryCondition from a dictionary.""" assert data['type'] == 'Ground', 'Expected dictionary for Ground boundary ' \ 'condition. Got {}.'.format(data['type']) return cls()
def __eq__(self, other): return isinstance(other, Ground)
class _BoundaryConditions(object): """Boundary conditions.""" def __init__(self): self._outdoors = Outdoors() self._ground = Ground() self._bc_name_dict = None @property def outdoors(self): """Default outdoor boundary condition.""" return self._outdoors @property def ground(self): """Default ground boundary condition.""" return self._ground def surface(self, other_object, sub_face=False): """Get a Surface boundary condition. Args: other_object: The other object that is adjacent to the one that will bear this Surface boundary condition. sub_face: Boolean to note whether the boundary condition is for a sub-face (Aperture or Door) instead of a Face. (Default: False). """ return Surface.from_other_object(other_object, sub_face) def by_name(self, bc_name): """Get a boundary condition object instance by its name. This method will correct for capitalization as well as the presence of spaces and underscores. Note that this method only works for boundary conditions with all of their inputs defaulted. Args: bc_name: A boundary condition name. """ if self._bc_name_dict is None: self._build_bc_name_dict() try: return self._bc_name_dict[re.sub(r'[\s_]', '', bc_name.lower())] except KeyError: raise ValueError( '"{}" is not a valid boundary condition name.\nChoose from the ' 'following: {}'.format(bc_name, list(self._bc_name_dict.keys()))) def _build_bc_name_dict(self): """Build a dictionary that can be used to lookup boundary conditions by name.""" attr = [atr for atr in dir(self) if not atr.startswith('_')] clean_attr = [re.sub(r'[\s_]', '', atr.lower()) for atr in attr] self._bc_name_dict = {} for atr_name, atr in zip(clean_attr, attr): try: full_attr = getattr(self, '_' + atr) self._bc_name_dict[atr_name] = full_attr except AttributeError: pass # callable method that has no static default object def __contains__(self, value): return isinstance(value, _BoundaryCondition) boundary_conditions = _BoundaryConditions()
[docs]def get_bc_from_position(positions, ground_depth=0): """Return a boundary condition based on the relationship to a ground plane. Positions that are entirely at or below the ground_depth will get a Ground boundary condition. If there are any positions above the ground_depth, an Outdoors boundary condition will be returned. args: positions: A list of ladybug_geometry Point3D objects representing the vertices of an object. ground_depth: The Z value above which positions are considered Outdoors instead of Ground. Returns: Face type instance. """ for position in positions: if position.z > ground_depth: return boundary_conditions.outdoors return boundary_conditions.ground