"""Radiance Grid-based Analysis Recipe."""
from .._gridbasedbase import GenericGridBased
from ..recipeutil import write_rad_files, write_extra_files
from ...parameters.rtrace import LowQuality
from ...command.oconv import Oconv
from ...command.rtrace import Rtrace
from ...command.rcalc import Rcalc
from ....futil import write_to_file
from ...analysisgrid import AnalysisGrid
from ....hbsurface import HBSurface
from ...sky.cie import CIE
from ...parameters.rtrace import RtraceParameters
from ladybug.dt import DateTime
import os
[docs]class GridBased(GenericGridBased):
"""Grid base analysis base class.
Attributes:
sky: A honeybee sky for the analysis
analysis_grids: List of analysis grids.
simulation_type: 0: Illuminance(lux), 1: Radiation (kWh), 2: Luminance (Candela)
(Default: 0)
rad_parameters: Radiance parameters for grid based analysis (rtrace).
(Default: gridbased.LowQuality)
hb_objects: An optional list of Honeybee surfaces or zones (Default: None).
sub_folder: Analysis subfolder for this recipe. (Default: "gridbased")
Usage:
# create the sky
sky = SkyWithCertainIlluminanceLevel(2000)
# initiate analysis_recipe
analysis_recipe = GridBased(
sky, testPoints, ptsVectors, simType
)
# add honeybee object
analysis_recipe.hb_objects = HBObjs
# write analysis files to local drive
analysis_recipe.write(_folder_, _name_)
# run the analysis
analysis_recipe.run(debaug=False)
# get the results
print(analysis_recipe.results())
"""
# TODO: implemnt isChanged at AnalysisRecipe level to reload the results
# if there has been no changes in inputs.
def __init__(self, sky, analysis_grids, simulation_type=0, rad_parameters=None,
hb_objects=None, sub_folder="gridbased"):
"""Create grid-based recipe."""
GenericGridBased.__init__(
self, analysis_grids, hb_objects, sub_folder)
self.sky = sky
"""A honeybee sky for the analysis."""
self.radiance_parameters = rad_parameters
"""Radiance parameters for grid based analysis (rtrace).
(Default: gridbased.LowQuality)"""
self.simulation_type = simulation_type
"""Simulation type: 0: Illuminance(lux), 1: Radiation (wh),
2: Luminance (Candela) (Default: 0)
"""
[docs] @classmethod
def from_json(cls, rec_json):
"""Create the solar access recipe from json.
{
"id": "point_in_time",
"type": "gridbased",
"sky": null, // a honeybee sky
"surfaces": [], // list of honeybee surfaces
"analysis_grids": [] // list of analysis grids
// [0] illuminance(lux), [1] radiation (kwh), [2] luminance (Candela).
"analysis_type": 0
}
"""
sky = CIE.from_json(rec_json['sky'])
analysis_grids = \
tuple(AnalysisGrid.from_json(ag) for ag in rec_json['analysis_grids'])
hb_objects = tuple(HBSurface.from_json(srf) for srf in rec_json['surfaces'])
rad_parameters = RtraceParameters.from_json(rec_json["rad_parameters"])
return cls(sky, analysis_grids, rec_json['analysis_type'], rad_parameters,
hb_objects)
[docs] @classmethod
def from_points_and_vectors(cls, sky, point_groups, vector_groups=None,
simulation_type=0, rad_parameters=None,
hb_objects=None, sub_folder="gridbased"):
"""Create grid based recipe from points and vectors.
Args:
sky: A honeybee sky for the analysis
point_groups: A list of (x, y, z) test points or lists of (x, y, z)
test points. Each list of test points will be converted to a
TestPointGroup. If testPts is a single flattened list only one
TestPointGroup will be created.
vector_groups: An optional list of (x, y, z) vectors. Each vector
represents direction of corresponding point in testPts. If the
vector is not provided (0, 0, 1) will be assigned.
simulation_type: 0: Illuminance(lux), 1: Radiation (kWh), 2: Luminance
(Candela) (Default: 0).
rad_parameters: Radiance parameters for grid based analysis (rtrace).
(Default: gridbased.LowQuality)
hb_objects: An optional list of Honeybee surfaces or zones (Default: None).
sub_folder: Analysis subfolder for this recipe. (Default: "gridbased")
"""
analysis_grids = cls.analysis_grids_from_points_and_vectors(point_groups,
vector_groups)
return cls(sky, analysis_grids, simulation_type, rad_parameters, hb_objects,
sub_folder)
@property
def simulation_type(self):
"""Get/set simulation Type.
0: Illuminance(lux), 1: Radiation (kWh), 2: Luminance (Candela) (Default: 0)
"""
return self._simType
@simulation_type.setter
def simulation_type(self, value):
try:
value = int(value)
except TypeError:
value = 0
assert 0 <= value <= 2, \
"Simulation type should be between 0-2. Current value: {}".format(value)
# If this is a radiation analysis make sure the sky is climate-based
if value == 1:
assert self.sky.is_climate_based, \
"The sky for radition analysis should be climate-based."
self._simType = value
if self.sky.is_climate_based:
self.sky.sky_type = value
@property
def sky(self):
"""Get and set sky definition."""
return self._sky
@sky.setter
def sky(self, new_sky):
assert hasattr(new_sky, 'isRadianceSky'), \
'%s is not a valid Honeybee sky.' % type(new_sky)
assert new_sky.is_point_in_time, \
TypeError('Sky must be one of the point-in-time skies.')
self._sky = new_sky.duplicate()
@property
def radiance_parameters(self):
"""Get and set Radiance parameters."""
return self._radiance_parameters
@radiance_parameters.setter
def radiance_parameters(self, rad_parameters):
if not rad_parameters:
rad_parameters = LowQuality()
assert hasattr(rad_parameters, "isRadianceParameters"), \
"%s is not a radiance parameters." % type(rad_parameters)
self._radiance_parameters = rad_parameters
[docs] def write(self, target_folder, project_name='untitled', header=True):
"""Write analysis files to target folder.
Files for a grid based analysis are:
test points <project_name.pts>: List of analysis points.
sky file <*.sky>: Radiance sky for this analysis.
material file <*.mat>: Radiance materials. Will be empty if hb_objects
is None.
geometry file <*.rad>: Radiance geometries. Will be empty if hb_objects
is None.
sky file <*.sky>: Radiance sky for this analysis.
batch file <*.bat>: An executable batch file which has the list of commands.
oconve <*.sky> <project_name.mat> <project_name.rad>
<additional rad_files> > <project_name.oct>
rtrace <radiance_parameters> <project_name.oct> > <project_name.res>
results file <*.res>: Results file once the analysis is over.
Args:
target_folder: Path to parent folder. Files will be created under
target_folder/gridbased. use self.sub_folder to change subfolder name.
project_name: Name of this project as a string.
Returns:
Full path to command.bat
"""
# 0.prepare target folder
self._commands = []
# create main folder target_folder/project_name
project_folder = \
super(GenericGridBased, self).write_content(target_folder, project_name)
# write geometry and material files
opqfiles, glzfiles, wgsfiles = write_rad_files(
project_folder + '/scene', project_name, self.opaque_rad_file,
self.glazing_rad_file, self.window_groups_rad_files
)
# additional radiance files added to the recipe as scene
extrafiles = write_extra_files(self.scene, project_folder + '/scene')
# 1.write points
points_file = self.write_analysis_grids(project_folder, project_name)
# 2.write batch file
if header:
self.commands.append(self.header(project_folder))
# 3.write sky file
self._commands.append(self.sky.to_rad_string(folder='sky'))
# 3.1. write ground and sky materials
skyground = self.sky.write_sky_ground(os.path.join(project_folder, 'sky'))
# TODO(Mostapha): add window_groups here if any!
# # 4.1.prepare oconv
oct_scene_files = \
[os.path.join(project_folder, str(self.sky.command('sky').output_file)),
skyground] + opqfiles + glzfiles + wgsfiles + extrafiles.fp
oct_scene_files_items = []
for f in oct_scene_files:
if isinstance(f, (list, tuple)):
print('Point-in-time recipes cannot currently handle dynamic window'
' groups. The first state will be used for simulation.')
oct_scene_files_items.append(f[0])
else:
oct_scene_files_items.append(f)
oc = Oconv(project_name)
oc.scene_files = tuple(self.relpath(f, project_folder)
for f in oct_scene_files_items)
# # 4.2.prepare rtrace
rt = Rtrace('result/' + project_name,
simulation_type=self.simulation_type,
radiance_parameters=self.radiance_parameters)
rt.radiance_parameters.h = True
rt.octree_file = str(oc.output_file)
rt.points_file = self.relpath(points_file, project_folder)
# # 4.3. add rcalc to convert rgb values to irradiance
rc = Rcalc('result/{}.ill'.format(project_name), str(rt.output_file))
if os.name == 'nt':
rc.rcalc_parameters.expression = '"$1=(0.265*$1+0.67*$2+0.065*$3)*179"'
else:
rc.rcalc_parameters.expression = "'$1=(0.265*$1+0.67*$2+0.065*$3)*179'"
# # 4.4 write batch file
self._commands.append(oc.to_rad_string())
self._commands.append(rt.to_rad_string())
self._commands.append(rc.to_rad_string())
batch_file = os.path.join(project_folder, "commands.bat")
write_to_file(batch_file, "\n".join(self.commands))
self._result_files = os.path.join(project_folder, str(rc.output_file))
return batch_file
[docs] def results(self):
"""Return results for this analysis."""
assert self._isCalculated, \
"You haven't run the Recipe yet. Use self.run " + \
"to run the analysis before loading the results."
print('Unloading the current values from the analysis grids.')
for ag in self.analysis_grids:
ag.unload()
sky = self.sky
dt = DateTime(sky.month, sky.day, int(sky.hour),
int(60 * (sky.hour - int(sky.hour))))
rf = self._result_files
start_line = 0
mode = 179 if self.simulation_type == 1 else 0
for count, analysisGrid in enumerate(self.analysis_grids):
if count:
start_line += len(self.analysis_grids[count - 1])
analysisGrid.set_values_from_file(
rf, (int(dt.hoy),), start_line=start_line, header=False, mode=mode
)
return self.analysis_grids
[docs] def ToString(self):
"""Overwrite .NET ToString method."""
return self.__repr__()
[docs] def to_json(self):
"""Create point-in-time recipe from json.
{
"id": "point_in_time",
"type": "gridbased",
"sky": null, // a honeybee sky
"surfaces": [], // list of honeybee surfaces
"analysis_grids": [] // list of analysis grids
// [0] illuminance(lux), [1] radiation (kwh), [2] luminance (Candela).
"analysis_type": 0
}
"""
return {
"id": "point_in_time",
"type": "gridbased",
"sky": self.sky.to_json(),
"surfaces": [srf.to_json() for srf in self.hb_objects],
"analysis_grids": [ag.to_json() for ag in self.analysis_grids],
"analysis_type": self.simulation_type,
"rad_parameters": self.radiance_parameters.to_json()
}
def __repr__(self):
"""Represent grid based recipe."""
_analysisType = {
0: "Illuminance", 1: "Radiation", 2: "Luminance"
}
return "%s: %s\n#PointGroups: %d #Points: %d" % \
(self.__class__.__name__,
_analysisType[self.simulation_type],
self.analysis_grid_count,
self.total_point_count)