Source code for honeybee_plus.radiance.recipe.directreflection.gridbased

from ..solaraccess.gridbased import SolarAccessGridBased
from ...material.mirror import Mirror
from ..recipeutil import write_rad_files, write_extra_files
from ..recipedcutil import rgb_matrix_file_to_ill

from ...command.oconv import Oconv
from ...command.rcontrib import Rcontrib
from ...analysisgrid import AnalysisGrid
from ...sky.analemma import Analemma
from ....futil import write_to_file
from ....hbsurface import HBSurface

from ladybug.sunpath import Sunpath
from ladybug.location import Location

import os


[docs]class DirectReflectionGridBased(SolarAccessGridBased): """Direct reflection recipe. This recipe calculates the direct and first reflection from reflective surfaces. """ def __init__(self, sun_vectors, hoys, analysis_grids, timestep=1, reflective_surfaces=None, context_surfaces=None, sub_folder='directreflection'): """ Args: sun_vectors: A list of ladybug sun vectors as (x, y, z) values. Z value for sun vectors should be negative (coming from sun toward earth) hoys: A list of hours of the year for each sun vector. analysis_grids: List of analysis grids. timestep: The number of timesteps per hour for sun vectors. This number should be smaller than 60 and divisible by 60. The default is set to 1 such that one sun vector is generated for each hour (Default: 1). reflective_surfaces: A list of Honeybee surfaces to be modeled as reflective materials (Default: None). context_surfaces: A list of non-reflective Honeybee surfaces (default: None). sub_folder: Analysis subfolder for this recipe. (Default: "directreflection") """ # update materials for reflective objects and context reflective_surfaces = reflective_surfaces or [] context_surfaces = context_surfaces or [] hb_objects = self.update_reflective_surfaces(reflective_surfaces) + \ context_surfaces # create honeybee objects SolarAccessGridBased.__init__(self, sun_vectors, hoys, analysis_grids, timestep, hb_objects, sub_folder) # update radiance paramters # set -dr to 1 to capture first reflection from mirror like surfaces. self._radiance_parameters.direct_sec_relays = 1
[docs] @classmethod def from_json(cls, rec_json): """Create the solar access recipe from json. { "id": "solar_access", "type": "gridbased", "location": null, // a honeybee location - see below "hoys": [], // list of hours of the year "surfaces": [], // list of honeybee surfaces "analysis_grids": [] // list of analysis grids "sun_vectors": [] // list of sun vectors if location is not provided } """ raise NotImplementedError() hoys = rec_json["hoys"] if 'sun_vectors' not in rec_json or not rec_json['sun_vectors']: # create sun vectors from location inputs loc = Location.from_json(rec_json['location']) sp = Sunpath.from_location(loc) suns = (sp.calculate_sun_from_hoy(hoy) for hoy in hoys) sun_vectors = tuple(s.sun_vector for s in suns if s.is_during_day) else: sun_vectors = rec_json['sun_vectors'] 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"]) return cls(sun_vectors, hoys, analysis_grids, 1, hb_objects)
[docs] @classmethod def from_suns(cls, suns, point_groups, vector_groups=[], timestep=1, reflective_surfaces=None, context_surfaces=None, sub_folder='directreflection'): """Create direct reflection recipe from LB sun objects. Attributes: suns: A list of ladybug suns. 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. timestep: The number of timesteps per hour for sun vectors. This number should be smaller than 60 and divisible by 60. The default is set to 1 such that one sun vector is generated for each hour (Default: 1). reflective_surfaces: A list of Honeybee surfaces to be modeled as reflective materials (Default: None). context_surfaces: A list of non-reflective Honeybee surfaces (default: None). sub_folder: Analysis subfolder for this recipe. (Default: "directreflection") """ # update materials for reflective objects and context reflective_surfaces = reflective_surfaces or [] context_surfaces = context_surfaces or [] hb_objects = cls.update_reflective_surfaces(reflective_surfaces) + \ context_surfaces return cls.from_suns(suns, point_groups, vector_groups, timestep, hb_objects, sub_folder)
[docs] @classmethod def from_location_and_hoys(cls, location, hoys, point_groups, vector_groups=[], timestep=1, reflective_surfaces=None, context_surfaces=None, sub_folder='directreflection'): """Create direct reflection recipe from Location and hours of year.""" # update materials for reflective objects and context reflective_surfaces = reflective_surfaces or [] context_surfaces = context_surfaces or [] hb_objects = cls.update_reflective_surfaces(reflective_surfaces) + \ context_surfaces return cls.from_location_and_hoys( location, hoys, point_groups, vector_groups, timestep, hb_objects, sub_folder )
[docs] @classmethod def from_location_and_analysis_period( cls, location, analysis_period, point_groups, vector_groups=None, reflective_surfaces=None, context_surfaces=None, sub_folder='directreflection'): """Create direct reflection recipe from Location and analysis period.""" # update materials for reflective objects and context reflective_surfaces = reflective_surfaces or [] context_surfaces = context_surfaces or [] hb_objects = cls.update_reflective_surfaces(reflective_surfaces) + \ context_surfaces return cls.from_location_and_analysis_period( location, analysis_period, point_groups, vector_groups, hb_objects, sub_folder)
[docs] @staticmethod def update_reflective_surfaces(surfaces): """Update surface materials to reflective mirror.""" reflective_material = Mirror('reflective_material', 1, 1, 1) duplicate_surfaces = [surface.duplicate() for surface in surfaces] for surface in duplicate_surfaces: # changing base material is for test and will be removed surface.radiance_properties.material = reflective_material surface.radiance_properties.black_material = reflective_material return duplicate_surfaces
[docs] def write(self, target_folder, project_name='untitled', header=True, transpose=False): """Write analysis files to target folder. Files for sunlight hours analysis are: test points <project_name.pts>: List of analysis points. suns file <*.sun>: list of sun sources . suns material file <*_suns.mat>: Radiance materials for sun sources. suns geometry file <*_suns.rad>: Radiance geometries for sun sources. 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. batch file <*.bat>: An executable batch file which has the list of commands. oconv [material file] [geometry file] [sun materials file] [sun geometries file] > [octree file] rcontrib -ab 0 -ad 10000 -I -M [sunlist.txt] -dc 1 [octree file]< [pts file] > [rcontrib results file] 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. transpose: Set to True to transpose the results matrix. By defalut each row include the results for each point and each column is the results for a different timestep (default: False). Returns: True in case of success. """ # 0.prepare target folder self._commands = [] # create main folder target_folder/project_name project_folder = \ super(SolarAccessGridBased, 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', True) # 1.write points points_file = self.write_analysis_grids(project_folder, project_name) # 2.write sun files ann = Analemma(self.sun_vectors, self.hoys) ann.execute(project_folder + '/sky') sun_modifiers = os.path.join('.', 'sky', ann.sunlist_file) suns_geo = os.path.join(project_folder + '/sky', ann.analemma_file) # 2.1.add sun list to modifiers self._radiance_parameters.mod_file = sun_modifiers self._radiance_parameters.y_dimension = self.total_point_count # 3.write batch file if header: self._commands.append(self.header(project_folder)) # TODO(Mostapha): add window_groups here if any! # # 4.1.prepare oconv oct_scene_files = opqfiles + glzfiles + wgsfiles + [suns_geo] + \ 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 Rcontrib rct = Rcontrib('result/total..' + project_name, rcontrib_parameters=self._radiance_parameters) rct.octree_file = str(oc.output_file) rct.points_file = self.relpath(points_file, project_folder) rmtx = rgb_matrix_file_to_ill((str(rct.output_file),), 'result/total..{}.ill'.format(project_name), transpose) # # 4.3 write batch file self._commands.append(oc.to_rad_string()) self._commands.append(rct.to_rad_string()) self._commands.append(rmtx.to_rad_string()) # add rcontrib for single reflection self._radiance_parameters.direct_sec_relays = 0 rct2 = Rcontrib('result/sun..' + project_name, rcontrib_parameters=self._radiance_parameters) rct2.octree_file = str(oc.output_file) rct2.points_file = self.relpath(points_file, project_folder) rmtx2 = rgb_matrix_file_to_ill((str(rct2.output_file),), 'result/sun..{}.ill'.format(project_name), transpose) # add the last two lines to batch file. self._commands.append(rct2.to_rad_string()) self._commands.append(rmtx2.to_rad_string()) self._result_files = os.path.join(project_folder, str(rmtx.output_file)) batch_file = os.path.join(project_folder, "commands.bat") return write_to_file(batch_file, '\n'.join(self.commands))
[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() hours = self.hoys rf = self._result_files df = rf.replace('total..', 'sun..') start_line = 0 for count, analysisGrid in enumerate(self.analysis_grids): if count: start_line += len(self.analysis_grids[count - 1]) # TODO(): Add timestep analysisGrid.set_coupled_values_from_file( rf, df, hours, start_line=start_line, header=True, check_point_count=False, mode=1 ) return self.analysis_grids
[docs] def to_json(self): """Create the solar access recipe from json. { "id": "solar_access", "type": "gridbased", "location": null, // a honeybee location - see below "hoys": [], // list of hours of the year "surfaces": [], // list of honeybee surfaces "analysis_grids": [], // list of analysis grids "sun_vectors": [] } """ raise NotImplementedError() return { "id": "direct_reflection", "type": "gridbased", "location": None, "hoys": self.hoys, "surfaces": [srf.to_json() for srf in self.hb_objects], "analysis_grids": [ag.to_json() for ag in self.analysis_grids], "sun_vectors": [tuple(-1 * c for c in v) for v in self.sun_vectors] }