Source code for dragonfly_doe2.doe.shades

from dataclasses import dataclass
from typing import List
from dragonfly.model import Model
from dragonfly.context import ContextShade
from dragonfly import shadingparameter as shade_params
from ladybug_geometry.geometry3d.face import Face3D
from ladybug_geometry.geometry3d.pointvector import Vector3D
from ladybug_geometry.geometry3d.line import LineSegment3D
from .utils import short_name
from . import blocks as fb
from math import degrees, isclose
import math


[docs]@dataclass class Doe2Shade: # TODO: will need to change things up to support rm2d.shade_params """ DOE2 shade object. Can be either: - 'FIXED-SHADE': azimuth is independent from the building, i.e context shade such as buildings and terrain. Objects that are independent from the orientation of the building during ASHRAE 90.1 baseline orientation averages. - 'BUILDING-SHADE': azimuth is connected to the buildign azimuth, will rotate withh the building on change of azimuth, i.e fin shades, awnings, and other types of "on building" shading devices. """ name: str shade_type: str height: float width: float x_ref: float y_ref: float z_ref: float azimuth: float tilt: float transmittance: float = 0.0
[docs] @classmethod def from_face3d(cls, face: Face3D, indx_identifier, shade_type=None): """Create DOE2 shade object from Face3D object""" face_len_one = LineSegment3D.from_end_points( face.vertices[0], face.vertices[2]).length face_len_two = LineSegment3D.from_end_points( face.vertices[1], face.vertices[3]).length face = face if isclose(face_len_one, face_len_two) else \ Face3D.from_regular_polygon(face.boundary_polygon2d) face = face if face.normal.z <= 0 else face.flip() shade_type = "FIXED-SHADE" if shade_type == None else shade_type orig_point = face.lower_left_corner x_ref, y_ref, z_ref = orig_point[0], orig_point[1], orig_point[2] height = abs(round(LineSegment3D.from_end_points( face.upper_right_corner, face.lower_right_corner).length, 4)) # ? I don't like the lack of uniformity between the height and width methods; but it DOES work.. width = face.plane.xyz_to_xy(face.max) - face.plane.xyz_to_xy(face.min) width = abs(round(width[0], 4)) tilt = round(degrees(face.normal.angle(Vector3D(0, 0, 1))), 0) y_axis = Vector3D(0, 1, 0) projected_normal = Vector3D(face.normal[0], face.normal[1], 0) try: azimuth = math.degrees(projected_normal.angle(y_axis)) cross = face.normal.cross(y_axis) if cross.z < 0: azimuth = 360 - azimuth except ZeroDivisionError: # horizontal azimuth = 180 if face.normal.z > 0 else -180 return cls( name=indx_identifier, shade_type=shade_type, height=height, width=width, x_ref=x_ref, y_ref=y_ref, z_ref=z_ref, azimuth=azimuth, tilt=tilt)
[docs] def to_inp(self): """Returns *.inp shade object string""" return f'{self.name} = {self.shade_type}\n ' \ f'HEIGHT = {self.height}\n ' \ f'WIDTH = {self.width}\n ' \ f'TRANSMITTANCE = {self.transmittance}\n ' \ f'X-REF = {self.x_ref}\n ' \ f'Y-REF = {self.y_ref}\n ' \ f'Z-REF = {self.z_ref}\n ' \ f'AZIMUTH = {self.azimuth}\n ' \ f'TILT = {self.tilt}\n ..'
def __repr__(self): return self.to_inp()
[docs]@dataclass class Doe2ShadeCollection: doe_shades: List[Doe2Shade]
[docs] @classmethod def from_df_context_shades(cls, df_shades: [ContextShade]): """Generate doe2 fixed shades from dragonfly context shades""" shade_faces = [] for shade_i, shade in enumerate(df_shades): for i, geom in enumerate(shade.geometry): shade_geom_name = f"shade_{shade_i}_geom{i}" shade_faces.append((geom, shade_geom_name)) doe_shades = [Doe2Shade.from_face3d(obj[0], obj[1]) for obj in shade_faces] return cls(doe_shades=doe_shades)
[docs] def to_inp(self): block = [fb.fix_bldg_shade] shades = [shade.to_inp() for shade in self.doe_shades] block.append('\n\n'.join(shades)) return '\n'.join(block)
def __repr__(self): return self.to_inp()