def get_window_group(config: util.MradConfig) -> Tuple[dict, list]: window_groups = {} window_normals: List[radgeom.Vector] = [] for wname, windowpath in config.windows.items(): _window_primitives = radutil.unpack_primitives(windowpath) window_groups[wname] = _window_primitives _normal = radutil.parse_polygon( _window_primitives[0].real_arg).normal() window_normals.append(_normal) return window_groups, window_normals
def get_sender_grid(config: util.MradConfig) -> dict: """.""" sender_grid = {} for name, surface_path in config.grid_surface_paths.items(): surface_polygon = radutil.parse_polygon( radutil.unpack_primitives(surface_path)[0].real_arg) sensor_pts = radutil.gen_grid(surface_polygon, float(config.grid_height), float(config.grid_spacing)) sender_grid[name] = radmtx.Sender.as_pts(pts_list=sensor_pts, ray_cnt=int(config.ray_count)) return sender_grid
def merge_windows(primitive_list: List[Primitive]): """Merge rectangles if coplanar.""" polygons = [radutil.parse_polygon(p.real_arg) for p in primitive_list] normals = [p.normal() for p in polygons] if len(set(normals)) > 1: logger.warning("Windows Oriented Differently") points = [i for p in polygons for i in p.vertices] hull_polygon = rg.Convexhull(points, normals[0]).hull modifier = primitive_list[0].modifier identifier = primitive_list[0].identifier new_prim = radutil.polygon2prim(hull_polygon, modifier, identifier) return new_prim
def prepare_surface(*, prims, basis, left, offset, source, out) -> str: """Prepare the sender or receiver surface, adding appropriate tags. Args: prims(list): list of primitives basis(str): sampling basis left(bool): use instead the left-hand rule offset(float): offset surface in its normal direction source(str): surface light source for receiver out: output path Returns: The receiver as string """ if basis is None: raise ValueError('Sampling basis cannot be None') upvector = str(radutil.up_vector(prims)).replace(' ', ',') upvector = "-" + upvector if left else upvector modifier_set = {p.modifier for p in prims} if len(modifier_set) != 1: logger.warning("Primitives don't share modifier") src_mod = f"rflx{prims[0].modifier}" header = f'#@rfluxmtx h={basis} u={upvector}\n' if out is not None: header += f"#@rfluxmtx o={out}\n\n" if source is not None: source_line = f"void {source} {src_mod}\n0\n0\n4 1 1 1 0\n\n" header += source_line modifiers = [p.modifier for p in prims] content = '' for prim in prims: if prim.identifier in modifiers: _identifier = 'discarded' else: _identifier = prim.identifier _modifier = src_mod if offset is not None: poly = radutil.parse_polygon(prim.real_arg) offset_vec = poly.normal().scale(offset) moved_pts = [pt + offset_vec for pt in poly.vertices] _real_args = radgeom.Polygon(moved_pts).to_real() else: _real_args = prim.real_arg new_prim = radutil.Primitive(_modifier, prim.ptype, _identifier, prim.str_arg, _real_args) content += str(new_prim) + '\n' return header + content
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
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
def check_srf_normal(self, zone): """Check the surface normal in each zone.""" polygons = [ ru.parse_polygon(v.real_arg) for v in zone['Floor'].values() ] polygons.extend( [ru.parse_polygon(v.real_arg) for v in zone['Ceiling'].values()]) polygons.extend( [ru.parse_polygon(v.real_arg) for v in zone['Wall'].values()]) polygons.extend( [ru.parse_polygon(v.real_arg) for v in zone['Window'].values()]) centroid = rg.polygon_center(*polygons) new_floor = {} for key, val in zone['Floor'].items(): fpolygon = ru.parse_polygon(val.real_arg) angle2center = fpolygon.normal().angle_from(centroid - fpolygon.centroid()) if angle2center < PI / 4: new_prim = Primitive(val.modifier, val.ptype, val.identifier, '0', fpolygon.flip().to_real()) new_floor[key] = new_prim else: new_floor[key] = val zone['Floor'] = new_floor new_window = {} for key, val in zone['Window'].items(): wpolygon = ru.parse_polygon(val.real_arg) angle2center = wpolygon.normal().angle_from(centroid - wpolygon.centroid()) if angle2center > PI / 4: new_prim = Primitive(val.modifier, val.ptype, val.identifier, '0', wpolygon.flip().to_real()) new_window[key] = new_prim else: new_window[key] = val zone['Window'] = new_window return zone
def main(): parser = ArgumentParser() genfmtx_parser = genfmtx_args(parser) genfmtx_parser.add_argument('-v', '--verbose', action='count', default=0, help='verbose mode') args = genfmtx_parser.parse_args() logger = logging.getLogger('frads') formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s') console_handler = logging.StreamHandler() _level = args.verbose * 10 logger.setLevel(_level) console_handler.setLevel(_level) console_handler.setFormatter(formatter) logger.addHandler(console_handler) raw_wndw_prims = radutil.unpack_primitives(args.window) ncp_prims = radutil.unpack_primitives(args.ncp) wndw_prims = [p for p in raw_wndw_prims if p.ptype == 'polygon'] port_prims = fcd.genport(wpolys=wndw_prims, npolys=ncp_prims, depth=None, scale=None) wndw_polygon = [ radutil.parse_polygon(p.real_arg) for p in wndw_prims if p.ptype == 'polygon' ] args.env.append(args.ncp) all_prims = [] for env in args.env: all_prims.extend(radutil.unpack_primitives(env)) ncp_mod = [prim.modifier for prim in ncp_prims if prim.ptype == 'polygon'][0] ncp_mat: radutil.Primitive ncp_type: str = '' for prim in all_prims: if prim.identifier == ncp_mod: ncp_mat = prim ncp_type = prim.ptype break if ncp_type == '': raise ValueError("Unknown NCP material") wrap2xml = args.wrap dirname = os.path.dirname(args.o) dirname = '.' if dirname == '' else dirname if args.solar and ncp_type == 'BSDF': logger.info('Computing for solar and visible spectrum...') wrap2xml = False xmlpath = ncp_mat.str_arg.split()[2] td = tf.mkdtemp() with open(xmlpath) as rdr: raw = rdr.read() raw = raw.replace('<Wavelength unit="Integral">Visible</Wavelength>', '<Wavelength unit="Integral">Visible2</Wavelength>') raw = raw.replace('<Wavelength unit="Integral">Solar</Wavelength>', '<Wavelength unit="Integral">Visible</Wavelength>') raw = raw.replace('<Wavelength unit="Integral">Visible2</Wavelength>', '<Wavelength unit="Integral">Solar</Wavelength>') solar_xml_path = os.path.join(td, 'solar.xml') with open(solar_xml_path, 'w') as wtr: wtr.write(raw) _strarg = ncp_mat.str_arg.split() _strarg[2] = solar_xml_path solar_ncp_mat = radutil.Primitive(ncp_mat.modifier, ncp_mat.ptype, ncp_mat.identifier + ".solar", ' '.join(_strarg), '0') _env_path = os.path.join(td, 'env_solar.rad') with open(_env_path, 'w') as wtr: for prim in all_prims: wtr.write(str(prim)) outsolar = os.path.join(dirname, '_solar_' + util.basename(args.o)) process_thread = Thread(target=fcd.Genfmtx, kwargs={ 'win_polygons': wndw_polygon, 'port_prim': port_prims, 'out': outsolar, 'env': [_env_path], 'sbasis': args.ss, 'rbasis': args.rs, 'opt': args.opt, 'refl': args.refl, 'forw': args.forw, 'wrap': wrap2xml }) process_thread.start() #fcd.Genfmtx(win_polygons=wndw_polygon, port_prim=port_prims, out=outsolar, # env=[_env_path], sbasis=args['ss'], rbasis=args['rs'], # opt=args['opt'], refl=args['refl'], forw=args['forw'], wrap=wrap2xml) fcd.Genfmtx(win_polygons=wndw_polygon, port_prim=port_prims, out=args.o, env=args.env, sbasis=args.ss, rbasis=args.rs, opt=args.opt, refl=args.refl, forw=args.forw, wrap=wrap2xml) if args.solar and ncp_type == 'BSDF': process_thread.join() vis_dict = {} sol_dict = {} oname = util.basename(args['o']) mtxs = [ os.path.join(dirname, mtx) for mtx in os.listdir(dirname) if mtx.endswith('.mtx') ] for mtx in mtxs: _direc = util.basename(mtx).split('_')[-1][:2] mtxname = util.basename(mtx) if mtxname.startswith(oname): #vis_dict[_direc] = os.path.join(dirname, f"_vis_{_direc}") vis_dict[_direc] = os.path.join(td, f"vis_{_direc}") out2 = os.path.join(dirname, f"vis_{_direc}") klems_wrap(vis_dict[_direc], out2, mtx, args.ss) if mtxname.startswith('_solar_'): sol_dict[_direc] = os.path.join(td, f"sol_{_direc}") out2 = os.path.join(dirname, f"sol_{_direc}") klems_wrap(sol_dict[_direc], out2, mtx, args.ss) cmd = f"wrapBSDF -a {args.ss} -c -s Visible " cmd += ' '.join([f"-{key} {vis_dict[key]}" for key in vis_dict]) cmd += ' -s Solar ' cmd += ' '.join([f"-{key} {sol_dict[key]}" for key in sol_dict]) cmd += f" > {os.path.join(dirname, oname)}.xml" os.system(cmd) shutil.rmtree(td) [os.remove(mtx) for mtx in mtxs]