"""Radiance file.
Create, modify and generate radiance files from a collection of hbobjects.
"""
from ..futil import write_to_file_by_name, copy_files_to_folder, preparedir
from .geometry.polygon import Polygon
from .material.plastic import BlackMaterial
from .material.glow import WhiteGlow
from .radparser import parse_from_file
import datetime
import os
[docs]class RadFile(object):
"""Radiance file.
Create, modify and generate radiance files from a collection of hbobjects.
You can also use this class for a single HBSurface or HBZone to get to_rad_string.
Attributes:
hb_surfaces: A collection of honeybee surfaces.
additional_materials: Additional radiance material objects that will be added on
top of the file.
"""
__slots__ = ('hb_surfaces', 'additional_materials')
# TODO(Mostapha) add property for inputs to check the input values
def __init__(self, hb_surfaces, additional_materials=None):
"""Initiate a radiance file."""
self.hb_surfaces = hb_surfaces
if additional_materials:
raise NotImplementedError('additional_materials is not implemented!')
[docs] @classmethod
def from_file(cls, file_paths):
"""create a RadFile from Radiance files."""
# parse the file and get the materials and geometries
geometries = []
materials = []
for file_path in file_paths:
for obj in parse_from_file(file_path):
if obj.startswith('#'):
continue
return cls(geometries, materials)
[docs] def find_bsdf_materials(self, mode=1):
"""Return a list fo BSDF materials if any."""
mode = 1 if mode is None else mode
if mode == 0:
# do not include children surface
mt = set(srf.radiance_material for srf in self.hb_surfaces
if hasattr(srf.radiance_material, 'xmlfile'))
elif mode == 1:
# do not include children surface
mt_base = [srf.radiance_material for srf in self.hb_surfaces
if hasattr(srf.radiance_material, 'xmlfile')]
mt_child = [childSrf.radiance_material
for srf in self.hb_surfaces
for childSrf in srf.children_surfaces
if srf.has_child_surfaces and
hasattr(childSrf.radiance_material, 'xmlfile')]
mt = set(mt_base + mt_child)
elif mode == 2:
# only child surfaces
mt = set(childSrf.radiance_material
for srf in self.hb_surfaces
for childSrf in srf.children_surfaces
if srf.has_child_surfaces and
hasattr(childSrf.radiance_material, 'xmlfile'))
return tuple(mt)
[docs] def radiance_material_names(self, mode=1):
"""Get list of material names.
Args:
mode: An integer 0-2 (Default: 1)
0 - Do not include children surfaces.
1 - Include children surfaces.
2 - Only children surfaces.
"""
mode = 1 if mode is None else mode
if mode == 0:
# do not include children surface
mt = set(srf.radiance_material.name for srf in self.hb_surfaces)
elif mode == 1:
# do not include children surface
mt_base = [srf.radiance_material.name for srf in self.hb_surfaces]
mt_child = [childSrf.radiance_material.name
for srf in self.hb_surfaces
for childSrf in srf.children_surfaces
if srf.has_child_surfaces]
mt = set(mt_base + mt_child)
elif mode == 2:
# only child surfaces
mt = set(childSrf.radiance_material.name
for srf in self.hb_surfaces
for childSrf in srf.children_surfaces
if srf.has_child_surfaces)
return tuple(mt)
[docs] @staticmethod
def copy_and_replace_xml_files(material_string, bsdf_materials, target_folder):
"""Find and replace xml files full path and copy XML files under bsdf folder.
The root folder in Radiance is the place that commands are executed
which in honeybee is the root so the relative path is scene/bsdf
this will make this mathod fairly inflexible.
Args:
material_string: A joined string of radiance materials.
bsdf_materials: A collection of BSDF materials.
target_folder: The study folder where the materials will be written.
"""
bsdf_files = tuple(mat.xmlfile for mat in bsdf_materials)
basefolder = os.path.split(os.path.normpath(target_folder))[0]
target_folder = os.path.join(basefolder, 'bsdf')
is_created = preparedir(target_folder, False)
assert is_created, 'Failed to create {}'.format(target_folder)
# copy the xml file locally
copy_files_to_folder(bsdf_files, target_folder)
# replace the full path with relative path
# The root folder in Radiance is the place that commands are executed
# which in honeybee is the root so the relative path is scene/glazing/bsdf
# this will make this mathod fairly inflexible.
for mat in bsdf_materials:
path, name = os.path.split(mat.xmlfile)
material_string = material_string.replace(
os.path.normpath(mat.xmlfile), 'scene/bsdf/%s' % name
)
return material_string
[docs] def materials(self, mode=1, join=False, blacked=False, glowed=False):
"""Get materials as a list of radiance strings.
Args:
mode: An integer 0-2 (Default: 1)
0 - Do not include children surfaces.
1 - Include children surfaces.
2 - Only children surfaces.
join: Set to True to join the output strings (Default: False).
blacked: If True materials will all be set to plastic 0 0 0 0 0.
"""
assert not (blacked and glowed), \
ValueError('You can either use blacked or glowed option.')
mode = 1 if mode is None else mode
if mode == 0:
# do not include children surface
if blacked:
mt = set(BlackMaterial(srf.radiance_material.name).to_rad_string()
for srf in self.hb_surfaces)
elif glowed:
mt = set(WhiteGlow(srf.radiance_material.name).to_rad_string()
for srf in self.hb_surfaces)
else:
mt = set(srf.radiance_material.to_rad_string()
for srf in self.hb_surfaces)
elif mode == 1:
# do not include children surface
if blacked:
mt_base = [BlackMaterial(srf.radiance_material.name).to_rad_string()
for srf in self.hb_surfaces]
mt_child = [BlackMaterial(childSrf.radiance_material.name)
.to_rad_string()
for srf in self.hb_surfaces
for childSrf in srf.children_surfaces
if srf.has_child_surfaces]
elif glowed:
mt_base = [
WhiteGlow(srf.radiance_material.name).to_rad_string()
for srf in self.hb_surfaces]
mt_child = [
WhiteGlow(childSrf.radiance_material.name).to_rad_string()
for srf in self.hb_surfaces
for childSrf in srf.children_surfaces
if srf.has_child_surfaces]
else:
mt_base = [srf.radiance_material.to_rad_string()
for srf in self.hb_surfaces]
mt_child = [childSrf.radiance_material.to_rad_string()
for srf in self.hb_surfaces
for childSrf in srf.children_surfaces
if srf.has_child_surfaces]
mt = set(mt_base + mt_child)
elif mode == 2:
# only child surfaces
if blacked:
mt = set(BlackMaterial(childSrf.radiance_material.name).to_rad_string()
for srf in self.hb_surfaces
for childSrf in srf.children_surfaces
if srf.has_child_surfaces)
elif glowed:
mt = set(WhiteGlow(childSrf.radiance_material.name)
.to_rad_string()
for srf in self.hb_surfaces
for childSrf in srf.children_surfaces
if srf.has_child_surfaces)
else:
mt = set(childSrf.radiance_material.to_rad_string()
for srf in self.hb_surfaces
for childSrf in srf.children_surfaces
if srf.has_child_surfaces)
return '\n'.join(mt) if join else tuple(mt)
[docs] def geometries(self, mode=1, join=False, flipped=False):
"""Get geometry as a list of radiance strings.
Args:
mode: An integer 0-2 (Default: 1)
0 - Do not include children surfaces.
1 - Include children surfaces.
2 - Only children surfaces.
join: Set to True to join the output strings (Default: False).
flipped: Flip the surface geometry.
"""
mode = 1 if mode is None else mode
get_polygon = self.get_surface_rad_string
if mode == 0:
# do not include children surface
geo = (get_polygon(srf, flipped) for srf in self.hb_surfaces)
elif mode == 1:
# do not include children surface
geo_base = [get_polygon(srf, flipped) for srf in self.hb_surfaces]
geo_child = [get_polygon(childSrf, flipped) for srf in self.hb_surfaces
for childSrf in srf.children_surfaces if srf.has_child_surfaces]
geo = geo_base + geo_child
elif mode == 2:
# only child surfaces
geo = (get_polygon(childSrf, flipped) for srf in self.hb_surfaces
for childSrf in srf.children_surfaces if srf.has_child_surfaces)
return '\n'.join(geo) if join else tuple(geo)
[docs] def to_rad_string(self, mode=1, include_materials=True, flipped=False, blacked=False,
glowed=False):
"""Get full radiance file as a string.
Args:
mode: An integer 0-2 (Default: 1)
0 - Do not include children surfaces.
1 - Include children surfaces.
2 - Only children surfaces.
include_materials: Set to False if you only want the geometry definition
(default:True).
flipped: Flip the surface geometry.
blacked: If True materials will all be set to plastic 0 0 0 0 0.
"""
mode = 1 if mode is None else mode
if include_materials:
return '\n'.join((self.materials(mode, True, blacked, glowed),
self.geometries(mode, True, flipped))) + '\n'
else:
return self.geometries(mode, True, flipped) + '\n'
[docs] def write(self, folder, filename, mode=1, include_materials=True,
flipped=False, blacked=False, glowed=False, mkdir=False):
"""write materials and geometries to a file.
Args:
folder: Target folder.
filename: File name and extension as a string.
mode: An integer 0-2 (Default: 1)
0 - Do not include children surfaces.
1 - Include children surfaces.
2 - Only children surfaces.
include_materials: Set to False if you only want the geometry definition
(default:True).
flipped: Flip the surface geometry.
blacked: If True materials will all be set to plastic 0 0 0 0 0.
mkdir: Create the folder if does not exist already.
"""
data = str(self.to_rad_string(mode, include_materials, flipped, blacked, glowed))
if not (glowed or blacked):
data = self.copy_and_replace_xml_files(
data, self.find_bsdf_materials(mode), folder
)
text = self.header() + '\n\n' + data
return write_to_file_by_name(folder, filename, text, mkdir)
[docs] def write_materials(self, folder, filename, mode=1, blacked=False, glowed=False,
mkdir=False):
"""Write materials to a file.
Args:
folder: Target folder.
filename: File name and extension as a string.
mode: An integer 0-2 (Default: 1)
0 - Do not include children surfaces.
1 - Include children surfaces.
2 - Only children surfaces.
include_materials: Set to False if you only want the geometry definition
(default:True).
flipped: Flip the surface geometry.
blacked: If True materials will all be set to plastic 0 0 0 0 0 0 0.
glowed: If True materials will all be set to glow 0 0 1 1 1 0. You can
either use blacked or glowed.
mkdir: Create the folder if does not exist already.
"""
data = self.materials(mode, True, blacked, glowed)
if not (glowed or blacked):
data = self.copy_and_replace_xml_files(
data, self.find_bsdf_materials(mode), folder
)
text = self.header() + '\n\n' + data
return write_to_file_by_name(folder, filename, text, mkdir)
[docs] def write_geometries(self, folder, filename, mode=1, flipped=False, mkdir=False):
"""write geometries to a file.
Args:
folder: Target folder.
filename: File name and extension as a string.
mode: An integer 0-2 (Default: 1)
0 - Do not include children surfaces.
1 - Include children surfaces.
2 - Only children surfaces.
flipped: Flip the surface geometry.
mkdir: Create the folder if does not exist already.
"""
data = self.to_rad_string(mode, False, flipped, False)
text = self.header() + '\n\n' + data
return write_to_file_by_name(folder, filename, text, mkdir)
[docs] def write_black_material(self, folder, filename, mkdir=False):
"""Write black material to a file."""
text = self.header() + '\n\n' + BlackMaterial().to_rad_string()
return write_to_file_by_name(folder, filename, text, mkdir)
# TODO(): fix the bug for multiple surfaces. The material only changes for
# the first one.
[docs] def write_geometries_blacked(self, folder, filename, mode=0, flipped=False,
mkdir=False):
"""Write all the surfaces to a file with BlackMaterial.
Use this method to write objects like window-groups.
"""
geo = self.geometries(mode, join=True, flipped=flipped)
mat_name = BlackMaterial().name
# replace the material in string with BlackMaterial.name
names = self.radiance_material_names(mode)
for name in names:
geo = geo.replace(name, mat_name)
text = self.header() + '\n\n' + geo
return write_to_file_by_name(folder, filename, text, mkdir)
[docs] def write_glow_material(self, folder, filename, mkdir=False):
"""Write white glow material to a file."""
text = self.header() + '\n\n' + WhiteGlow().to_rad_string()
return write_to_file_by_name(folder, filename, text, mkdir)
[docs] def write_geometries_glowed(self, folder, filename, mode=0, flipped=False,
mkdir=False):
"""Write all the surfaces to a file with WhiteGlow.
Use this method to write objects like window-groups.
"""
geo = self.geometries(mode, flipped=flipped)
mat_name = WhiteGlow().name
# replace the material in string with BlackMaterial.name
names = self.radiance_material_names(mode)
for name in names:
geo = geo.replace(name, mat_name)
text = self.header() + '\n\n' + geo
return write_to_file_by_name(folder, filename, text, mkdir)
[docs] @staticmethod
def get_surface_rad_string(surface, flipped=False):
"""Get the polygon definition for a honeybee surface.
This is a static method. For the full string try geometries method.
"""
points = surface.duplicate_vertices(flipped)
name = surface.name
sub_srf_count = len(points)
# create a place holder for each point group (face)
# for a planar surface sub_srf_count is one
place_holder = list(range(sub_srf_count))
for ptCount, pts in enumerate(points):
# modify name for each sub_surface
_name = name if sub_srf_count == 1 else '{}_{}'.format(name, ptCount)
# collect definition for each subsurface
pl = Polygon(_name, pts, modifier=surface.radiance_material)
place_holder[ptCount] = pl.to_rad_string(include_modifier=False)
return '\n'.join(place_holder)
[docs] def ToString(self):
"""Overwrite .NET's ToString."""
return self.__repr__()
def __str__(self):
"""Get the full string of the file."""
return self.to_rad_string()
def __repr__(self):
"""rad file."""
return 'RadFile::#{}'.format(len(self.hb_surfaces))