Beispiel #1
0
    def gen_cull(self, smx_path=None, window_normals=None):
        """Generate culled sun sources based on window orientation and
        climate based sky matrix. The reduced sun sources will significantly
        speed up the matrix generation.
        Args:
            smx_path: sky matrix path, usually the output of gendaymtx
            window_normals: window normals
        Returns:
            Sun receiver primitives strings
            Corresponding modifier strings
        """

        win_norm = []
        if smx_path is not None:
            cmd = f"rmtxop -ff -c .3 .6 .1 -t {smx_path} "
            cmd += "| getinfo - | total -if5186 -t,"
            dtot = [float(i)
                    for i in sp.check_output(cmd, shell=True).split(b',')]
        else:
            dtot = [1] * self.runlen
        out_lines = []
        mod_str = []
        if window_normals is not None:
            win_norm = window_normals
            for i in range(1, self.runlen):
                dirs = radgeom.Vector(*self.rsrc.dir_calc(i))
                _mod = 'sol'+str(i)
                v = 0
                if dtot[i - 1] > 0:
                    for norm in win_norm:
                        if norm * dirs < 0:
                            v = 1
                            mod_str.append(_mod)
                            break
                line = f"void light sol{i} 0 0 3 {v} {v} {v} sol{i} "
                line += f"source sun 0 0 4 {dirs.z:.6g} {dirs.x:.6g} {dirs.z:.6g} 0.533"
                out_lines.append(line)
        else:
            for i in range(1, self.runlen):
                dirs = radgeom.Vector(*self.rsrc.dir_calc(i))
                _mod = 'sol'+str(i)
                v = 0
                if dtot[i - 1] > 0:
                    v = 1
                    mod_str.append(_mod)
                line = f"void light sol{i} 0 0 3 {v} {v} {v} sol{i} "
                line += f"source sun 0 0 4 {dirs.z:.6g} {dirs.x:.6g} {dirs.z:.6g} 0.533"
                out_lines.append(line)
        logger.debug(out_lines)
        logger.debug(mod_str)
        return LSEP.join(out_lines), LSEP.join(mod_str)
Beispiel #2
0
def remove_wea_zero_entry(data, metadata, window_normal=None):
    """Remove data entries with zero solar luminance.
    If window normal supplied, eliminate entries not seen by window.
    Solar luminance determined using Treganza sky model.
    Window field of view is 176 deg with 2 deg tolerance.
    """

    check_window_normal = True if window_normal is not None else False
    new_dataline = []
    zero_dni = lambda row: row.dni!=0
    data = filter(zero_dni, data)
    for row in data:
        cmd = ['gendaylit', str(row.month), str(row.day), str(row.hours),
               '-a', str(metadata.latitude), '-o', str(metadata.longitude),
               '-m', str(metadata.timezone), '-W', str(row.dni), str(row.dhi)]
        process = sp.run(cmd, stderr=sp.PIPE, stdout=sp.PIPE)
        primitives = radutil.parse_primitive(
            process.stdout.decode().splitlines())
        light = 0
        if process.stderr == b'':
            light = float(primitives[0].real_arg.split()[2])
            dirs = radgeom.Vector(*list(map(float, primitives[1].real_arg.split()[1:4])))
            if light > 0:
                if check_window_normal:
                    for normal in window_normal:
                        if normal * dirs < -0.035: # 2deg tolerance
                            new_dataline.append(row)
                            break
                else:
                    new_dataline.append(row)
    return new_dataline
Beispiel #3
0
def up_vector(primitives: list) -> radgeom.Vector:
    """Define the up vector given primitives.

    Args:
        primitives: list of dictionary (primitives)

    Returns:
        returns a str as x,y,z

    """
    xaxis = radgeom.Vector(1, 0, 0)
    yaxis = radgeom.Vector(0, 1, 0)
    norm_dir = samp_dir(primitives)
    if norm_dir not in (xaxis, xaxis.scale(-1)):
        upvect = norm_dir.cross(xaxis)
    else:
        upvect = norm_dir.cross(yaxis)
    return upvect
Beispiel #4
0
 def __init__(self, width, depth, height, origin=radgeom.Vector()):
     self.width = width
     self.depth = depth
     self.height = height
     self.origin = origin
     flr_pt2 = origin + radgeom.Vector(width, 0, 0)
     flr_pt3 = flr_pt2 + radgeom.Vector(0, depth, 0)
     self.floor = radgeom.Polygon.rectangle3pts(origin, flr_pt2, flr_pt3)
     extrusion = self.floor.extrude(radgeom.Vector(0, 0, height))
     self.clng = extrusion[1]
     self.wall_south = Surface(extrusion[2], 'wall.south')
     self.wall_east = Surface(extrusion[3], 'wall.east')
     self.wall_north = Surface(extrusion[4], 'wall.north')
     self.wall_west = Surface(extrusion[5], 'wall.west')
     self.surfaces = [
         self.clng, self.floor, self.wall_west, self.wall_north,
         self.wall_east, self.wall_south
     ]
Beispiel #5
0
def samp_dir(primlist: list) -> radgeom.Vector:
    """Calculate the primitives' average sampling
    direction weighted by area."""
    primlist = [
        p for p in primlist if p.ptype == 'polygon' or p.ptype == 'ring'
    ]
    normal_area = radgeom.Vector()
    for prim in primlist:
        polygon = parse_polygon(prim.real_arg)
        normal_area += polygon.normal().scale(polygon.area())
    sdir = normal_area.scale(1.0 / len(primlist))
    sdir = sdir.normalize()
    return sdir
Beispiel #6
0
def parse_polygon(real_arg: str) -> radgeom.Polygon:
    """Parse real arguments to polygon.
    Args:
        primitive: a dictionary object containing a primitive

    Returns:
        modified primitive
    """
    real_args = real_arg.split()
    coords = [float(i) for i in real_args[1:]]
    arg_cnt = int(real_args[0])
    vertices = [radgeom.Vector(*coords[i:i + 3]) for i in range(0, arg_cnt, 3)]
    return radgeom.Polygon(vertices)
Beispiel #7
0
def assemble_model(config: util.MradConfig) -> Model:
    """Assemble all the pieces together."""
    material_primitives: List[radutil.Primitive]
    material_primitives = sum(
        [radutil.unpack_primitives(path) for path in config.material_paths],
        [])
    window_groups, _window_normals = get_window_group(config)
    window_normals = [
        item for idx, item in enumerate(_window_normals)
        if item not in _window_normals[:idx]
    ]
    sender_grid = get_sender_grid(config)
    sender_view, view_dicts = get_sender_view(config)
    rcvr_sky = radmtx.Receiver.as_sky(basis=config.smx_basis)
    # Sun CFS
    _cfs_path = []
    for wname, path in config.sun_cfs.items():
        ident = util.basename(path)
        if path.endswith('.xml'):
            window_primitives = window_groups[wname]
            upvec = radgeom.Vector(0, 0, 1)
            bsdf_prim = radutil.bsdf_prim('void', ident, path, upvec, pe=True)
            if bsdf_prim not in material_primitives:
                material_primitives.append(bsdf_prim)
            _tmp_cfs_path = f'tmpcfs{wname}.rad'
            with open(_tmp_cfs_path, 'w') as wtr:
                for primitive in window_primitives:
                    new_primitive = radutil.Primitive(ident, primitive.ptype,
                                                      primitive.identifier,
                                                      primitive.str_arg,
                                                      primitive.real_arg)
                    wtr.write(str(new_primitive) + '\n')
            _cfs_path.append(_tmp_cfs_path)
        elif path.endswith('.rad'):
            _cfs_path.append(path)
    black_mat = radutil.Primitive('void', 'plastic', 'black', '0',
                                  '5 0 0 0 0 0')
    glow_mat = radutil.Primitive('void', 'glow', 'glowing', '0', '4 1 1 1 0')
    if black_mat not in material_primitives:
        material_primitives.append(black_mat)
    if glow_mat not in material_primitives:
        material_primitives.append(glow_mat)
    material_path = os.path.join(config.objdir, "all_material.mat")
    with open(material_path, 'w') as wtr:
        [wtr.write(str(primitive) + '\n') for primitive in material_primitives]
    _blackenvpath = os.path.join(config.objdir, 'blackened.rad')
    with open(_blackenvpath, 'w') as wtr:
        for path in config.scene_paths:
            wtr.write(f'\n!xform -m black {path}')
    return Model(material_path, window_groups, window_normals, sender_grid,
                 sender_view, view_dicts, rcvr_sky, _cfs_path, _blackenvpath)
Beispiel #8
0
 def make_window(self, dist_left, dist_bot, width, height, wwr=None):
     if wwr is not None:
         assert type(wwr) == float, 'WWR must be float'
         win_polygon = self.polygon.scale(radgeom.Vector(*[wwr] * 3),
                                          self.centroid)
     else:
         win_pt1 = self.vertices[0]\
                 + self.vect1.scale(dist_bot)\
                 + self.vect2.scale(dist_left)
         win_pt2 = win_pt1 + self.vect1.scale(height)
         win_pt3 = win_pt1 + self.vect2.scale(width)
         win_polygon = radgeom.Polygon.rectangle3pts(
             win_pt3, win_pt1, win_pt2)
     return win_polygon
Beispiel #9
0
def gen_grid(polygon: radgeom.Polygon, height: float, spacing: float) -> list:
    """Generate a grid of points for orthogonal planar surfaces.

    Args:
        polygon: a polygon object
        height: points' distance from the surface in its normal direction
        spacing: distance between the grid points
    Returns:
        List of the points as list
    """
    vertices = polygon.vertices
    plane_height = sum([i.z for i in vertices]) / len(vertices)
    imin, imax, jmin, jmax, _, _ = polygon.extreme()
    xlen_spc = ((imax - imin) / spacing)
    ylen_spc = ((jmax - jmin) / spacing)
    xstart = ((xlen_spc - int(xlen_spc) + 1)) * spacing / 2
    ystart = ((ylen_spc - int(ylen_spc) + 1)) * spacing / 2
    x0 = [x + xstart for x in util.frange_inc(imin, imax, spacing)]
    y0 = [x + ystart for x in util.frange_inc(jmin, jmax, spacing)]
    grid_dir = polygon.normal().reverse()
    grid_hgt = radgeom.Vector(0, 0, plane_height) + grid_dir.scale(height)
    raw_pts = [
        radgeom.Vector(round(i, 3), round(j, 3), round(grid_hgt.z, 3))
        for i in x0 for j in y0
    ]
    scale_factor = 1 - 0.3 / (imax - imin)  # scale boundary down .3 meter
    _polygon = polygon.scale(radgeom.Vector(scale_factor, scale_factor, 0),
                             polygon.centroid())
    _vertices = _polygon.vertices
    if polygon.normal() == radgeom.Vector(0, 0, 1):
        pt_incls = pt_inclusion(_vertices)
    else:
        pt_incls = pt_inclusion(_vertices[::-1])
    _grid = [p for p in raw_pts if pt_incls.test_inside(p) > 0]
    grid = [p.to_list() + grid_dir.to_list() for p in _grid]
    return grid
Beispiel #10
0
def get_port(win_polygon, win_norm, ncs_prims):
    """
    Generate ports polygons that encapsulate the window and NCP geometries.

    window and NCP geometries are rotated around +Z axis until
    the area projected onto XY plane is the smallest, thus the systems are facing
    orthogonal direction. A boundary box is then generated with a slight
    outward offset. This boundary box is then rotated back the same amount
    to encapsulate the original window and NCP geomteries.
    """
    ncs_polygon = [radutil.parse_polygon(p.real_arg)
                   for p in ncs_prims if p.ptype=='polygon']
    if 1 in [int(abs(i)) for i in win_norm.to_list()]:
        ncs_polygon.append(win_polygon)
        bbox = rg.getbbox(ncs_polygon, offset=0.00)
        bbox.remove([b for b in bbox if b.normal().reverse()==win_norm][0])
        return [b.move(win_norm.scale(-.1)) for b in bbox]
    xax = [1, 0, 0]
    _xax = [-1, 0, 0]
    yax = [0, 1, 0]
    _yax = [0, -1, 0]
    zaxis = rg.Vector(0, 0, 1)
    rm_pg = [xax, _yax, _xax, yax]
    area_list = []
    win_normals = []
    # Find axiel aligned rotation angle
    bboxes = []
    for deg in range(90):
        rad = math.radians(deg)
        win_polygon_r = win_polygon.rotate(zaxis, rad)
        win_normals.append(win_polygon_r.normal())
        ncs_polygon_r = [p.rotate(zaxis, rad) for p in ncs_polygon]
        ncs_polygon_r.append(win_polygon_r)
        _bbox = rg.getbbox(ncs_polygon_r, offset=0.001)
        bboxes.append(_bbox)
        area_list.append(_bbox[0].area())
    # Rotate to position
    deg = area_list.index(min(area_list))
    rrad = math.radians(deg)
    bbox = bboxes[deg]
    _win_normal = [round(i, 1) for i in win_normals[deg].to_list()]
    del bbox[rm_pg.index(_win_normal) + 2]
    rotate_back = [pg.rotate(zaxis, rrad * -1) for pg in bbox]
    return rotate_back
Beispiel #11
0
def genport(*, wpolys, npolys, depth, scale):
    """Generate the appropriate aperture for F matrix generation."""
    if len(wpolys) > 1:
        wpoly = merge_windows(wpolys)
    else:
        wpoly = wpolys[0]
    wpoly = radutil.parse_polygon(wpoly.real_arg)
    wnorm = wpoly.normal()
    if npolys is not None:
        all_ports = get_port(wpoly, wnorm, npolys)
    elif depth is None:
        raise ValueError('Need to specify (depth and scale) or ncp path')
    else:  # user direct input
        extrude_vector = wpoly.normal().reverse().scale(depth)
        scale_vector = rg.Vector(scale, scale, scale)
        scaled_window = wpoly.scale(scale_vector, wpoly.centroid())
        all_ports = scaled_window.extrude(extrude_vector)[1:]
    port_prims = []
    for pi in range(len(all_ports)):
        new_prim = radutil.polygon2prim(all_ports[pi], 'port',
                                     'portf%s' % str(pi + 1))
        logger.debug(str(new_prim))
        port_prims.append(new_prim)
    return port_prims
Beispiel #12
0
def analyze_window(window_prim, movedown):
    """Parse window primitive and prepare for genBSDF."""
    window_polygon = window_prim['polygon']
    vertices = window_polygon.vertices
    assert len(vertices) == 4, "4-sided polygon only"
    window_center = window_polygon.centroid()
    window_normal = window_polygon.normal()
    window_normal
    dim1 = vertices[0] - vertices[1]
    dim2 = vertices[1] - vertices[2]
    if dim1.normalize() in (radgeom.Vector(0, 0, 1), radgeom.Vector(0, 0, -1)):
        height = dim1.length
        width = dim2.length
    else:
        height = dim2.length
        width = dim1.length
    _south = radgeom.Vector(0, -1, 0)
    angle2negY = window_normal.angle_from(_south)
    rotate_window = window_center.rotate_3d(radgeom.Vector(
        0, 0, 1), angle2negY).rotate_3d(radgeom.Vector(1, 0, 0), math.pi / 2)
    translate = radgeom.Vector(0, 0, -movedown) - rotate_window
    return height, width, angle2negY, translate
Beispiel #13
0
def main():
    """Generate a BSDF for macroscopic systems."""
    parser = argparse.ArgumentParser()
    parser.add_argument('-blinds',
                        nargs=3,
                        type=float,
                        metavar=('depth', 'spacing', 'angle'))
    parser.add_argument('-curve', default='')
    parser.add_argument('-custom', nargs=3)
    parser.add_argument('-section', nargs=2)
    parser.add_argument('-gap', type=float, default=0.0)
    parser.add_argument('-ext', action='store_true')
    parser.add_argument('-m',
                        nargs=3,
                        type=float,
                        required=True,
                        metavar=('refl', 'spec', 'rough'))
    parser.add_argument('-g', type=float)
    parser.add_argument('-window')
    parser.add_argument('-env')
    parser.add_argument('-o', default='default_blinds.rad')
    args = parser.parse_args()
    mat_prim = radutil.neutral_plastic_prim('void', 'blindmaterial', *args.m)
    mat_prim_str = radutil.put_primitive(mat_prim)
    if args.custom:
        # Custom periodic geometry
        pass
    elif args.section:
        # Custom section drawing
        pass
    elif args.window:
        primitves = radutil.unpack_primitives(args.window)
        env_primitives = radutil.unpack_primitives(args.env)
        env_identifier = [prim['identifier'] for prim in env_primitives]
        windows = [
            p for p in primitves if p['identifier'].startswith('window')
        ]
        window = windows[0]  # only take the first window primitive
        depth, spacing, angle = args.blinds
        movedown = depth * math.cos(math.radians(float(angle)))
        window_movedown = 0 if args.ext else movedown + args.gap
        # window_polygon = radutil.parse_polygon(window.real_args)
        height, width, angle2negY, translate = radutil.analyze_window(
            window, window_movedown)
        xform_cmd = f'!xform -rz {math.degrees(angle2negY)} -rx -90 -t {translate.x} {translate.y} {translate.z} {args.env}\n'
        xform_cmd += f'!xform -rz {math.degrees(angle2negY)} -rx -90 -t {translate.x} {translate.y} {translate.z} {args.window}\n'
        print(xform_cmd)
        slat_cmd = radutil.gen_blinds(depth, width, height, spacing, angle,
                                      args.curve, movedown)
        print(slat_cmd)
        lower_bound = max(movedown, window_movedown)
        with open("tmp_blinds.rad", 'w') as wtr:
            wtr.write(mat_prim_str)
            wtr.write(xform_cmd)
            wtr.write(slat_cmd)
        cmd = f"genBSDF -n 4 -f +b -c 500 +geom meter -dim {-width/2} {width/2} {-height/2} {height/2} -{lower_bound} 0 tmp_blinds.rad"
        print(cmd)
        _stdout = sp.run(cmd.split(), check=True,
                         stdout=sp.PIPE).stdout.decode()
        xml_name = "{}_blinds_{}_{}_{}.xml".format(window['identifier'], depth,
                                                   spacing, angle)
        with open(xml_name, 'w') as wtr:
            wtr.write(_stdout)
        #move back
        pkgbsdf_cmd = ["pkgBSDF", "-s", xml_name]
        xform_back_cmd = [
            "xform", "-t",
            str(-translate.x),
            str(-translate.y),
            str(-translate.z)
        ]
        xform_back_cmd += ["-rx", "90", "-rz", str(-math.degrees(angle2negY))]
        ps1 = sp.Popen(pkgbsdf_cmd, stdout=sp.PIPE)
        ps2 = sp.Popen(xform_back_cmd, stdin=ps1.stdout, stdout=sp.PIPE)
        ps1.stdout.close()
        _stdout, _ = ps2.communicate()
        result_primitives = radutil.parse_primitive(
            _stdout.decode().splitlines())
        result_primitives = [
            prim for prim in result_primitives
            if prim['identifier'] not in env_identifier
        ]
        with open(args.o, 'w') as wtr:
            [
                wtr.write(radutil.put_primitive(prim))
                for prim in result_primitives
            ]
    else:
        width = 10  # default blinds width
        height = 0.096  # default blinds height
        depth, spacing, angle = args.blinds
        if args.ext:
            glass_z = 0
            movedown = args.gap + depth * math.cos(math.radians(angle))
        else:
            glass_z = args.gap + depth * math.cos(math.radians(angle))
            movedown = depth * math.cos(math.radians(angle))
        lower_bound = min(-glass_z, -movedown)
        genblinds_cmd = radutil.gen_blinds(depth, width, height, spacing,
                                           angle, args.curve, movedown)
        pt1 = radgeom.Vector(-width / 2, height / 2, -glass_z)
        pt2 = radgeom.Vector(-width / 2, -height / 2, -glass_z)
        pt3 = radgeom.Vector(width / 2, -height / 2, -glass_z)
        tmis = util.tmit2tmis(.38)
        glass_prim = radutil.glass_prim('void', 'glass1', tmis, tmis, tmis)
        glazing_polygon = radgeom.Polygon.rectangle3pts(pt1, pt2, pt3)
        glazing_prim_str = radutil.put_primitive(glass_prim)
        glazing_prim_str += radutil.put_primitive(
            radutil.polygon2prim(glazing_polygon, 'glass1', 'window'))
        with open("tmp_blinds.rad", 'w') as wtr:
            wtr.write(mat_prim_str)
            wtr.write(glazing_prim_str)
            wtr.write(genblinds_cmd)
        cmd = f"genBSDF -n 4 +f +b -c 500 -geom meter -dim -0.025 0.025 -0.012 0.012 {lower_bound} 0 tmp_blinds.rad"
        _stdout = sp.run(cmd.split(), check=True,
                         stdout=sp.PIPE).stdout.decode()
        xml_name = "blinds_{}_{}_{}.xml".format(depth, spacing, angle)
        with open(xml_name, 'w') as wtr:
            wtr.write(_stdout)
    os.remove("tmp_blinds.rad")
Beispiel #14
0
 def get_zones(self, epjs):
     """Looping through zones."""
     opaque_srfs = epjs['BuildingSurface:Detailed']
     construction = self.get_construction(epjs)
     ext_zones = {}
     for zone_name in epjs['Zone']:
         zone_srfs = {
             k: v
             for k, v in opaque_srfs.items() if v['zone_name'] == zone_name
         }
         if self.check_ext_window(zone_srfs):
             zone = {
                 'Wall': {},
                 'Floor': {},
                 'Ceiling': {},
                 'Window': {},
                 'Roof': {}
             }
             for sn in zone_srfs:
                 surface_name = sn.replace(" ", "_")
                 surface = zone_srfs[sn]
                 srf_type = surface['surface_type']
                 cnstrct = construction[surface['construction_name']]
                 inner_layer, outer_layer, cthick = self.parse_cnstrct(
                     cnstrct)
                 if self.mat_prims[inner_layer].primitive.identifier == '':
                     inner_layer = 'void'
                 else:
                     inner_layer = inner_layer.replace(' ', '_')
                 srf_polygon = rg.Polygon(
                     [rg.Vector(*v.values()) for v in surface['vertices']])
                 srf_windows = []
                 if sn in self.fene_hosts:
                     zone_fsrfs = {
                         n: val
                         for n, val in self.fene_srfs.items()
                         if val['building_surface_name'] == sn
                     }
                     for fn in zone_fsrfs:
                         fsurface = zone_fsrfs[fn]
                         nfvert = int(fsurface['number_of_vertices'])
                         fverts = [[
                             fsurface[k] for k in fsurface
                             if k.startswith(f'vertex_{n+1}')
                         ] for n in range(nfvert)]
                         wndw_polygon = rg.Polygon(
                             [rg.Vector(*vert) for vert in fverts])
                         srf_windows.append(wndw_polygon)
                         window_construct = construction[
                             fsurface['construction_name']]
                         window_material = self.parse_wndw_cnstrct(
                             window_construct)
                         window_name = fn.replace(" ", "_")
                         zone['Window'][window_name] = ru.polygon2prim(
                             wndw_polygon, window_material, window_name)
                         srf_polygon -= wndw_polygon
                     facade = self.thicken(srf_polygon, srf_windows, cthick)
                     zone[srf_type][
                         f'ext_{surface_name}'] = ru.polygon2prim(
                             facade[1], outer_layer, f"ext_{surface_name}")
                     for idx in range(2, len(facade)):
                         zone[srf_type][
                             f'sill_{surface_name}.{idx}'] = ru.polygon2prim(
                                 facade[idx], inner_layer,
                                 f"sill_{surface_name}.{idx}")
                 zone[srf_type][surface_name] = ru.polygon2prim(
                     srf_polygon, inner_layer, surface_name)
             ext_zones[zone_name] = self.check_srf_normal(zone)
     return ext_zones