diff --git a/ladybug_grasshopper/icon/LB Benefit Sky Matrix.png b/ladybug_grasshopper/icon/LB Benefit Sky Matrix.png new file mode 100644 index 00000000..fad8941b Binary files /dev/null and b/ladybug_grasshopper/icon/LB Benefit Sky Matrix.png differ diff --git a/ladybug_grasshopper/icon/LB Cumulative Sky Matrix.png b/ladybug_grasshopper/icon/LB Cumulative Sky Matrix.png index bc6812f3..56315c7a 100644 Binary files a/ladybug_grasshopper/icon/LB Cumulative Sky Matrix.png and b/ladybug_grasshopper/icon/LB Cumulative Sky Matrix.png differ diff --git a/ladybug_grasshopper/icon/LB Radiation Dome.png b/ladybug_grasshopper/icon/LB Radiation Dome.png new file mode 100644 index 00000000..5adb40db Binary files /dev/null and b/ladybug_grasshopper/icon/LB Radiation Dome.png differ diff --git a/ladybug_grasshopper/icon/LB Radiation Rose.png b/ladybug_grasshopper/icon/LB Radiation Rose.png new file mode 100644 index 00000000..fb82b945 Binary files /dev/null and b/ladybug_grasshopper/icon/LB Radiation Rose.png differ diff --git a/ladybug_grasshopper/icon/LB Sky Dome.png b/ladybug_grasshopper/icon/LB Sky Dome.png index 0fd081dc..74ff4db7 100644 Binary files a/ladybug_grasshopper/icon/LB Sky Dome.png and b/ladybug_grasshopper/icon/LB Sky Dome.png differ diff --git a/ladybug_grasshopper/icon/LB Wind Profile.png b/ladybug_grasshopper/icon/LB Wind Profile.png index e1d7672d..d8e75db7 100644 Binary files a/ladybug_grasshopper/icon/LB Wind Profile.png and b/ladybug_grasshopper/icon/LB Wind Profile.png differ diff --git a/ladybug_grasshopper/icon/LB Wind Rose.png b/ladybug_grasshopper/icon/LB Wind Rose.png index 6700509f..8a3c265e 100644 Binary files a/ladybug_grasshopper/icon/LB Wind Rose.png and b/ladybug_grasshopper/icon/LB Wind Rose.png differ diff --git a/ladybug_grasshopper/json/LB_Benefit_Sky_Matrix.json b/ladybug_grasshopper/json/LB_Benefit_Sky_Matrix.json new file mode 100644 index 00000000..d6d01a92 --- /dev/null +++ b/ladybug_grasshopper/json/LB_Benefit_Sky_Matrix.json @@ -0,0 +1,99 @@ +{ + "version": "1.5.0", + "nickname": "BenefitMatrix", + "outputs": [ + [ + { + "access": "None", + "name": "sky_mtx", + "description": "A sky matrix object containing the radiation benefit/harm coming from each\npatch of the sky. This can be used for a radiation study, a radition rose,\nor a sky dome visualization. It can also be deconstructed into its\nindividual values with the \"LB Deconstruct Matrix\" component.", + "type": null, + "default": null + } + ] + ], + "inputs": [ + { + "access": "item", + "name": "north_", + "description": "A number between -360 and 360 for the counterclockwise\ndifference between the North and the positive Y-axis in degrees.\n90 is West and 270 is East. This can also be Vector for the\ndirection to North. (Default: 0)", + "type": "System.Object", + "default": null + }, + { + "access": "item", + "name": "_location", + "description": "A ladybug Location that has been output from the \"LB Import EPW\"\ncomponent or the \"LB Construct Location\" component.", + "type": "System.Object", + "default": null + }, + { + "access": "item", + "name": "_temperature", + "description": "An annual hourly DataCollection of temperature, which will be used\nto establish whether radiation is desired or not for each time step.", + "type": "System.Object", + "default": null + }, + { + "access": "item", + "name": "_bal_temp_", + "description": "The temperature in Celsius between which radiation switches from being a\nbenefit to a harm. Typical residential buildings have balance temperatures\nas high as 18C and commercial buildings tend to have lower values\naround 12C. (Default 15C).", + "type": "double", + "default": null + }, + { + "access": "item", + "name": "_bal_offset_", + "description": "The temperature offset from the balance temperature in Celsius where\nradiation is neither harmful nor helpful. (Default: 2).", + "type": "double", + "default": null + }, + { + "access": "item", + "name": "_direct_rad", + "description": "An annual hourly DataCollection of Direct Normal Radiation such\nas that which is output from the \"LB Import EPW\" component or the\n\"LB Import STAT\" component.", + "type": "System.Object", + "default": null + }, + { + "access": "item", + "name": "_diffuse_rad", + "description": "An annual hourly DataCollection of Diffuse Horizontal Radiation\nsuch as that which is output from the \"LB Import EPW\" component or\nthe \"LB Import STAT\" component.", + "type": "System.Object", + "default": null + }, + { + "access": "list", + "name": "_hoys_", + "description": "A number or list of numbers between 0 and 8760 that respresent\nthe hour(s) of the year for which to generate the sky matrix. The\n\"LB Calculate HOY\" component can output this number given a month,\nday and hour. The \"LB Analysis Period\" component can output a\nlist of HOYs within a certain hour or date range. By default,\nthe matrix will be for the entire year.", + "type": "double", + "default": null + }, + { + "access": "item", + "name": "high_density_", + "description": "A Boolean to indicate whether the higher-density Reinhart sky\nmatrix should be generated (True), which has roughly 4 times the sky\npatches as the (default) original Tregenza sky (False). Note that,\nwhile the Reinhart sky has a higher resolution and is more accurate,\nit will result in considerably longer calculation time for incident\nradiation studies. The difference in sky resolution can be observed\nwith the \"LB Sky Dome\" component. (Default: False).", + "type": "bool", + "default": null + }, + { + "access": "item", + "name": "_ground_ref_", + "description": "A number between 0 and 1 to note the average ground reflectance\nthat is associated with the sky matrix. (Default: 0.2).", + "type": "double", + "default": null + }, + { + "access": "item", + "name": "_folder_", + "description": "The folder in which the Radiance commands are executed to\nproduce the sky matrix. If None, it will be written to Ladybug's\ndefault EPW folder.", + "type": "string", + "default": null + } + ], + "subcategory": "2 :: Visualize Data", + "code": "\nimport math\n\ntry:\n from ladybug_geometry.geometry2d.pointvector import Vector2D\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_geometry:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_radiance.skymatrix import SkyMatrix\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.togeometry import to_vector2d\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\ntry:\n from lbt_recipes.version import check_radiance_date\nexcept ImportError as e:\n raise ImportError('\\nFailed to import honeybee_radiance:\\n\\t{}'.format(e))\n\n# check the istalled Radiance date and get the path to the gemdaymtx executable\ncheck_radiance_date()\n\n\nif all_required_inputs(ghenv.Component):\n # process and set defaults for all of the global inputs\n _bal_temp_ = 15 if _bal_temp_ is None else _bal_temp_\n _bal_offset_ = 2 if _bal_offset_ is None else _bal_offset_\n if north_ is not None: # process the north_\n try:\n north_ = math.degrees(\n to_vector2d(north_).angle_clockwise(Vector2D(0, 1)))\n except AttributeError: # north angle instead of vector\n north_ = float(north_)\n else:\n north_ = 0\n ground_r = 0.2 if _ground_ref_ is None else _ground_ref_\n\n # create the sky matrix object\n sky_mtx = SkyMatrix.from_components_benefit(\n _location, _direct_rad, _diffuse_rad, _temperature, _bal_temp_, _bal_offset_,\n _hoys_, north_, high_density_, ground_r)\n if _folder_:\n sky_mtx.folder = _folder_\n", + "category": "Ladybug", + "name": "LB Benefit Sky Matrix", + "description": "Get a matrix representing the benefit/harm of radiation based on temperature data.\n_\nRadiation benefit matrices are helpful for evaluating building massing and facade\ndesigns in terms of passive solar heat gain vs. cooling energy increase.\n_\nThis component uses Radiance's gendaymtx function to calculate the radiation\nfor each patch of the sky. Gendaymtx is written by Ian Ashdown and Greg Ward.\nMorere information can be found in Radiance manual at:\nhttp://www.radiance-online.org/learning/documentation/manual-pages/pdfs/gendaymtx.pdf\n-" +} \ No newline at end of file diff --git a/ladybug_grasshopper/json/LB_Cumulative_Sky_Matrix.json b/ladybug_grasshopper/json/LB_Cumulative_Sky_Matrix.json index 7a0de564..df45c785 100644 --- a/ladybug_grasshopper/json/LB_Cumulative_Sky_Matrix.json +++ b/ladybug_grasshopper/json/LB_Cumulative_Sky_Matrix.json @@ -1,5 +1,5 @@ { - "version": "1.5.0", + "version": "1.5.1", "nickname": "SkyMatrix", "outputs": [ [ @@ -51,7 +51,7 @@ { "access": "item", "name": "high_density_", - "description": "A Boolean to indicate whether the higher-density Reinhart\nsky matrix should be generated (True), which has roughly 4 times\nthe sky patches as the (default) original Tregenza sky (False).\nNote that, while the Reinhart sky has a higher resolution and is\nmore accurate, it will result in considerably longer calculation\ntime for incident radiation studies. The difference in sky\nresolution can be observed with the (Default: False).", + "description": "A Boolean to indicate whether the higher-density Reinhart sky\nmatrix should be generated (True), which has roughly 4 times the sky\npatches as the (default) original Tregenza sky (False). Note that,\nwhile the Reinhart sky has a higher resolution and is more accurate,\nit will result in considerably longer calculation time for incident\nradiation studies. The difference in sky resolution can be observed\nwith the \"LB Sky Dome\" component. (Default: False).", "type": "bool", "default": null }, @@ -71,7 +71,7 @@ } ], "subcategory": "2 :: Visualize Data", - "code": "\nimport os\nimport subprocess\nimport math\n\ntry:\n from ladybug_geometry.geometry2d.pointvector import Vector2D\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_geometry:\\n\\t{}'.format(e))\n\ntry:\n from ladybug.wea import Wea\n from ladybug.viewsphere import view_sphere\n from ladybug.dt import DateTime\n from ladybug.config import folders as lb_folders\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.togeometry import to_vector2d\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, objectify_output\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\n\n# TODO: Remove dependency on honeybee + Radiance after genskymtx is in its own LB extension\ntry:\n from honeybee_radiance.config import folders as hb_folders\n from lbt_recipes.version import check_radiance_date\nexcept ImportError as e:\n raise ImportError('\\nFailed to import honeybee_radiance:\\n\\t{}'.format(e))\n\n# check the istalled Radiance date and get the path to the gemdaymtx executable\ncheck_radiance_date()\ngendaymtx_exe = os.path.join(hb_folders.radbin_path, 'gendaymtx.exe') if \\\n os.name == 'nt' else os.path.join(hb_folders.radbin_path, 'gendaymtx')\n\n\n# constants for converting RGB values output by gendaymtx to broadband radiation\nPATCHES_PER_ROW = {\n 1: view_sphere.TREGENZA_PATCHES_PER_ROW + (1,),\n 2: view_sphere.REINHART_PATCHES_PER_ROW + (1,)\n}\nPATCH_ROW_COEFF = {\n 1 : view_sphere.TREGENZA_COEFFICIENTS,\n 2: view_sphere.REINHART_COEFFICIENTS\n}\n\n\ndef broadband_radiation(patch_row_str, row_number, wea_duration, sky_density=1):\n \"\"\"Parse a row of gendaymtx RGB patch data in W/sr/m2 to radiation in kWh/m2.\n\n This includes aplying broadband weighting to the RGB bands, multiplication\n by the steradians of each patch, and multiplying by the duration of time that\n they sky matrix represents in hours.\n\n Args:\n patch_row_str: Text string for a single row of RGB patch data.\n row_number: Interger for the row number that the patch corresponds to.\n sky_density: Integer (either 1 or 2) for the density.\n wea_duration: Number for the duration of the Wea in hours. This is used\n to convert between the average value output by the command and the\n cumulative value that is needed for all ladybug analyses.\n \"\"\"\n R, G, B = patch_row_str.split(' ')\n weight_val = 0.265074126 * float(R) + 0.670114631 * float(G) + 0.064811243 * float(B)\n return weight_val * PATCH_ROW_COEFF[sky_density][row_number] * wea_duration / 1000\n\n\ndef parse_mtx_data(data_str, wea_duration, sky_density=1):\n \"\"\"Parse a string of Radiance gendaymtx data to a list of radiation-per-patch.\n\n This function handles the removing of the header and the conversion of the\n RGB irradianc-=per-steraidian values to broadband radiation. It also removes\n the first patch, which is the ground and is not used by Ladybug.\n\n Args:\n data_str: The string that has been output by gendaymtx to stdout.\n wea_duration: Number for the duration of the Wea in hours. This is used\n to convert between the average value output by the command and the\n cumulative value that is needed for all ladybug analyses.\n sky_density: Integer (either 1 or 2) for the density.\n \"\"\"\n # split lines and remove the header, ground patch and last line break\n data_lines = data_str.split('\\n')\n patch_lines = data_lines[9:-1]\n\n # loop through the rows and convert the radiation RGB values\n broadband_irr = []\n patch_counter = 0\n for i, row_patch_count in enumerate(PATCHES_PER_ROW[sky_density]):\n row_slice = patch_lines[patch_counter:patch_counter + row_patch_count]\n irr_vals = (broadband_radiation(row, i, wea_duration, sky_density)\n for row in row_slice)\n broadband_irr.extend(irr_vals)\n patch_counter += row_patch_count\n return broadband_irr\n\n\nif all_required_inputs(ghenv.Component):\n # process and set defaults for all of the global inputs\n if north_ is not None: # process the north_\n try:\n north_ = math.degrees(\n to_vector2d(north_).angle_clockwise(Vector2D(0, 1)))\n except AttributeError: # north angle instead of vector\n north_ = float(north_)\n else:\n north_ = 0\n density = 2 if high_density_ else 1\n ground_r = 0.2 if _ground_ref_ is None else _ground_ref_\n\n # filter the radiation by _hoys if they are input\n if len(_hoys_) != 0:\n _direct_rad = _direct_rad.filter_by_hoys(_hoys_)\n _diffuse_rad = _diffuse_rad.filter_by_hoys(_hoys_)\n\n # create the wea and write it to the default_epw_folder\n wea = Wea(_location, _direct_rad, _diffuse_rad)\n wea_duration = len(wea) / wea.timestep\n wea_folder = _folder_ if _folder_ is not None else \\\n os.path.join(lb_folders.default_epw_folder, 'sky_matrices')\n metd = _direct_rad.header.metadata\n wea_basename = metd['city'].replace(' ', '_') if 'city' in metd else 'unnamed'\n wea_path = os.path.join(wea_folder, wea_basename)\n wea_file = wea.write(wea_path)\n\n # execute the Radiance gendaymtx command\n use_shell = True if os.name == 'nt' else False\n # command for direct patches\n cmds = [gendaymtx_exe, '-m', str(density), '-d', '-O1', '-A', wea_file]\n process = subprocess.Popen(cmds, stdout=subprocess.PIPE, shell=use_shell)\n stdout = process.communicate()\n dir_data_str = stdout[0]\n # command for diffuse patches\n cmds = [gendaymtx_exe, '-m', str(density), '-s', '-O1', '-A', wea_file]\n process = subprocess.Popen(cmds, stdout=subprocess.PIPE, shell=use_shell)\n stdout = process.communicate()\n diff_data_str = stdout[0]\n\n # parse the data into a single matrix\n dir_vals = parse_mtx_data(dir_data_str, wea_duration, density)\n diff_vals = parse_mtx_data(diff_data_str, wea_duration, density)\n\n # collect sky metadata like the north, which will be used by other components\n metadata = [north_, ground_r]\n if _hoys_:\n metadata.extend([DateTime.from_hoy(h) for h in (_hoys_[0], _hoys_[-1])])\n else:\n metadata.extend([wea.analysis_period.st_time, wea.analysis_period.end_time])\n for key, val in _direct_rad.header.metadata.items():\n metadata.append('{} : {}'.format(key, val))\n\n # wrap everything together into an object to output from the component\n mtx_data = (metadata, dir_vals, diff_vals)\n sky_mtx = objectify_output('Cumulative Sky Matrix', mtx_data)\n", + "code": "\nimport math\n\ntry:\n from ladybug_geometry.geometry2d.pointvector import Vector2D\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_geometry:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_radiance.skymatrix import SkyMatrix\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.togeometry import to_vector2d\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\ntry:\n from lbt_recipes.version import check_radiance_date\nexcept ImportError as e:\n raise ImportError('\\nFailed to import honeybee_radiance:\\n\\t{}'.format(e))\n\n# check the istalled Radiance date and get the path to the gemdaymtx executable\ncheck_radiance_date()\n\n\nif all_required_inputs(ghenv.Component):\n # process and set defaults for all of the global inputs\n if north_ is not None: # process the north_\n try:\n north_ = math.degrees(\n to_vector2d(north_).angle_clockwise(Vector2D(0, 1)))\n except AttributeError: # north angle instead of vector\n north_ = float(north_)\n else:\n north_ = 0\n ground_r = 0.2 if _ground_ref_ is None else _ground_ref_\n\n # create the sky matrix object\n sky_mtx = SkyMatrix.from_components(\n _location, _direct_rad, _diffuse_rad, _hoys_, north_, high_density_, ground_r)\n if _folder_:\n sky_mtx.folder = _folder_\n", "category": "Ladybug", "name": "LB Cumulative Sky Matrix", "description": "Get a matrix containing radiation values from each patch of a sky dome.\n_\nCreating this matrix is a necessary pre-step before doing incident radiation\nanalysis with Rhino geometry or generating a radiation rose.\n_\nThis component uses Radiance's gendaymtx function to calculate the radiation\nfor each patch of the sky. Gendaymtx is written by Ian Ashdown and Greg Ward.\nMorere information can be found in Radiance manual at:\nhttp://www.radiance-online.org/learning/documentation/manual-pages/pdfs/gendaymtx.pdf\n-" diff --git a/ladybug_grasshopper/json/LB_Incident_Radiation.json b/ladybug_grasshopper/json/LB_Incident_Radiation.json index 66294718..08290ff6 100644 --- a/ladybug_grasshopper/json/LB_Incident_Radiation.json +++ b/ladybug_grasshopper/json/LB_Incident_Radiation.json @@ -1,5 +1,5 @@ { - "version": "1.5.0", + "version": "1.5.1", "nickname": "IncidentRadiation", "outputs": [ [ @@ -58,7 +58,7 @@ { "access": "item", "name": "_sky_mtx", - "description": "A Sky Matrix from the \"LB Cumulative Sky Matrix\" component, which\ndescribes the radiation coming from the various patches of the sky.\nThe \"LB Sky Dome\" component can be used to visualize any sky matrix\nto understand its relationship to the test geometry.", + "description": "A Sky Matrix from the \"LB Cumulative Sky Matrix\" component or the\n\"LB Benefit Sky Matrix\" component, which describes the radiation\ncoming from the various patches of the sky. The \"LB Sky Dome\"\ncomponent can be used to visualize any sky matrix to understand\nits relationship to the test geometry.", "type": "System.Object", "default": null }, @@ -90,6 +90,13 @@ "type": "double", "default": null }, + { + "access": "item", + "name": "irradiance_", + "description": "Boolean to note whether the study should output units of cumulative\nRadiation (kWh/m2) [False] or units of average Irradiance (W/m2)\n[True]. (Default: False).", + "type": "bool", + "default": null + }, { "access": "item", "name": "legend_par_", @@ -113,7 +120,7 @@ } ], "subcategory": "3 :: Analyze Geometry", - "code": "\nimport math\ntry: # python 2\n from itertools import izip as zip\nexcept ImportError: # python 3\n pass\n\ntry:\n from ladybug.viewsphere import view_sphere\n from ladybug.graphic import GraphicContainer\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.config import conversion_to_meters\n from ladybug_{{cad}}.togeometry import to_joined_gridded_mesh3d\n from ladybug_{{cad}}.fromgeometry import from_mesh3d, from_point3d, from_vector3d\n from ladybug_{{cad}}.fromobjects import legend_objects\n from ladybug_{{cad}}.text import text_objects\n from ladybug_{{cad}}.intersect import join_geometry_to_mesh, intersect_mesh_rays\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, hide_output, \\\n show_output, objectify_output, de_objectify_output, recommended_processor_count\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\n\nif all_required_inputs(ghenv.Component) and _run:\n # set the default offset distance and _cpu_count\n _offset_dist_ = _offset_dist_ if _offset_dist_ is not None \\\n else 0.1 / conversion_to_meters()\n workers = _cpu_count_ if _cpu_count_ is not None else recommended_processor_count()\n\n # create the gridded mesh from the geometry\n study_mesh = to_joined_gridded_mesh3d(_geometry, _grid_size)\n points = [from_point3d(pt.move(vec * _offset_dist_)) for pt, vec in\n zip(study_mesh.face_centroids, study_mesh.face_normals)]\n hide_output(ghenv.Component, 1)\n\n # mesh the geometry and context\n shade_mesh = join_geometry_to_mesh(_geometry + context_)\n\n # deconstruct the matrix and get the sky dome vectors\n mtx = de_objectify_output(_sky_mtx)\n total_sky_rad = [dir_rad + dif_rad for dir_rad, dif_rad in zip(mtx[1], mtx[2])]\n ground_rad = [(sum(total_sky_rad) / len(total_sky_rad)) * mtx[0][1]] * len(total_sky_rad)\n all_rad = total_sky_rad + ground_rad \n lb_vecs = view_sphere.tregenza_dome_vectors if len(total_sky_rad) == 145 \\\n else view_sphere.reinhart_dome_vectors\n if mtx[0][0] != 0: # there is a north input for sky; rotate vectors\n north_angle = math.radians(mtx[0][0])\n lb_vecs = tuple(vec.rotate_xy(north_angle) for vec in lb_vecs)\n lb_grnd_vecs = tuple(vec.reverse() for vec in lb_vecs)\n all_vecs = [from_vector3d(vec) for vec in lb_vecs + lb_grnd_vecs]\n\n # intersect the rays with the mesh\n normals = [from_vector3d(vec) for vec in study_mesh.face_normals]\n int_matrix_init, angles = intersect_mesh_rays(\n shade_mesh, points, all_vecs, normals, cpu_count=workers)\n\n # compute the results\n results = []\n int_matrix = []\n for int_vals, angles in zip(int_matrix_init, angles):\n pt_rel = [ival * math.cos(ang) for ival, ang in zip(int_vals, angles)]\n int_matrix.append(pt_rel)\n rad_result = sum(r * w for r, w in zip(pt_rel, all_rad))\n results.append(rad_result)\n\n # output the intersection matrix and compute total radiation\n int_mtx = objectify_output('Geometry/Sky Intersection Matrix', int_matrix)\n unit_conv = conversion_to_meters() ** 2\n total = 0\n for rad, area in zip(results, study_mesh.face_areas):\n total += rad * area * unit_conv\n\n # create the mesh and legend outputs\n graphic = GraphicContainer(results, study_mesh.min, study_mesh.max, legend_par_)\n graphic.legend_parameters.title = 'kWh/m2'\n title = text_objects(\n 'Incident Radiation', graphic.lower_title_location,\n graphic.legend_parameters.text_height * 1.5,\n graphic.legend_parameters.font)\n\n # create all of the visual outputs\n study_mesh.colors = graphic.value_colors\n mesh = from_mesh3d(study_mesh)\n legend = legend_objects(graphic.legend)\n", + "code": "\nimport math\ntry: # python 2\n from itertools import izip as zip\nexcept ImportError: # python 3\n pass\n\ntry:\n from ladybug.viewsphere import view_sphere\n from ladybug.graphic import GraphicContainer\n from ladybug.legend import LegendParameters\n from ladybug.color import Colorset\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.config import conversion_to_meters\n from ladybug_{{cad}}.togeometry import to_joined_gridded_mesh3d\n from ladybug_{{cad}}.fromgeometry import from_mesh3d, from_point3d, from_vector3d\n from ladybug_{{cad}}.fromobjects import legend_objects\n from ladybug_{{cad}}.text import text_objects\n from ladybug_{{cad}}.intersect import join_geometry_to_mesh, intersect_mesh_rays\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, hide_output, \\\n show_output, objectify_output, de_objectify_output, recommended_processor_count\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\n\nif all_required_inputs(ghenv.Component) and _run:\n # set the default offset distance and _cpu_count\n _offset_dist_ = _offset_dist_ if _offset_dist_ is not None \\\n else 0.1 / conversion_to_meters()\n workers = _cpu_count_ if _cpu_count_ is not None else recommended_processor_count()\n\n # create the gridded mesh from the geometry\n study_mesh = to_joined_gridded_mesh3d(_geometry, _grid_size)\n points = [from_point3d(pt.move(vec * _offset_dist_)) for pt, vec in\n zip(study_mesh.face_centroids, study_mesh.face_normals)]\n hide_output(ghenv.Component, 1)\n\n # mesh the geometry and context\n shade_mesh = join_geometry_to_mesh(_geometry + context_)\n\n # deconstruct the matrix and get the sky dome vectors\n mtx = de_objectify_output(_sky_mtx)\n total_sky_rad = [dir_rad + dif_rad for dir_rad, dif_rad in zip(mtx[1], mtx[2])]\n ground_rad = [(sum(total_sky_rad) / len(total_sky_rad)) * mtx[0][1]] * len(total_sky_rad)\n all_rad = total_sky_rad + ground_rad \n lb_vecs = view_sphere.tregenza_dome_vectors if len(total_sky_rad) == 145 \\\n else view_sphere.reinhart_dome_vectors\n if mtx[0][0] != 0: # there is a north input for sky; rotate vectors\n north_angle = math.radians(mtx[0][0])\n lb_vecs = tuple(vec.rotate_xy(north_angle) for vec in lb_vecs)\n lb_grnd_vecs = tuple(vec.reverse() for vec in lb_vecs)\n all_vecs = [from_vector3d(vec) for vec in lb_vecs + lb_grnd_vecs]\n\n # intersect the rays with the mesh\n normals = [from_vector3d(vec) for vec in study_mesh.face_normals]\n int_matrix_init, angles = intersect_mesh_rays(\n shade_mesh, points, all_vecs, normals, cpu_count=workers)\n\n # compute the results\n results = []\n int_matrix = []\n for int_vals, angs in zip(int_matrix_init, angles):\n pt_rel = [ival * math.cos(ang) for ival, ang in zip(int_vals, angs)]\n int_matrix.append(pt_rel)\n rad_result = sum(r * w for r, w in zip(pt_rel, all_rad))\n results.append(rad_result)\n\n # convert to irradiance if requested\n study_name = 'Incident Radiation'\n if irradiance_:\n study_name = 'Incident Irradiance'\n factor = 1000 / _sky_mtx.wea_duration if hasattr(_sky_mtx, 'wea_duration') \\\n else 1000 / (((mtx[0][3] - mtx[0][2]).total_seconds() / 3600) + 1)\n results = [r * factor for r in results]\n\n # output the intersection matrix and compute total radiation\n int_mtx = objectify_output('Geometry/Sky Intersection Matrix', int_matrix)\n unit_conv = conversion_to_meters() ** 2\n total = 0\n for rad, area in zip(results, study_mesh.face_areas):\n total += rad * area * unit_conv\n\n # create the mesh and legend outputs\n l_par = legend_par_ if legend_par_ is not None else LegendParameters()\n if hasattr(_sky_mtx, 'benefit_matrix') and _sky_mtx.benefit_matrix is not None:\n study_name = '{} Benefit/Harm'.format(study_name)\n if l_par.are_colors_default:\n l_par.colors = reversed(Colorset.benefit_harm())\n if l_par.min is None:\n l_par.min = min((min(results), -max(results)))\n if l_par.max is None:\n l_par.max = max((-min(results), max(results)))\n graphic = GraphicContainer(results, study_mesh.min, study_mesh.max, l_par)\n graphic.legend_parameters.title = 'kWh/m2' if not irradiance_ else 'W/m2'\n title = text_objects(\n study_name, graphic.lower_title_location,\n graphic.legend_parameters.text_height * 1.5,\n graphic.legend_parameters.font)\n\n # create all of the visual outputs\n study_mesh.colors = graphic.value_colors\n mesh = from_mesh3d(study_mesh)\n legend = legend_objects(graphic.legend)\n", "category": "Ladybug", "name": "LB Incident Radiation", "description": "Calculate the incident radiation on geometry using a sky matrix from the \"Cumulative\nSky Matrix\" component.\n_\nSuch studies of incident radiation can be used to apprxomiate the energy that can\nbe collected from photovoltaic or solar thermal systems. They are also useful\nfor evaluating the impact of a building's orientation on both energy use and the\nsize/cost of cooling systems. For studies of photovoltaic potential or building\nenergy use impact, a sky matrix from EPW radiation should be used. For studies\nof cooling system size/cost, a sky matrix derived from the STAT file's clear sky\nradiation should be used.\n_\nNOTE THAT NO REFLECTIONS OF SOLAR ENERGY ARE INCLUDED\nIN THE ANALYSIS PERFORMED BY THIS COMPONENT.\n_\nGround reflected irradiance is crudely acounted for by means of an emissive\n\"ground hemisphere,\" which is like the sky dome hemisphere and is derived from\nthe ground reflectance that is associated with the connected _sky_mtx. This\nmeans that including geometry that represents the ground surface will effectively\nblock such crude ground reflection.\n_\nAlso note that this component uses the CAD environment's ray intersection methods,\nwhich can be fast for geometries with low complexity but does not scale well for\ncomplex geometries or many test points. For such complex cases and situations\nwhere relfection of solar energy are important, honeybee-radiance should be used.\n-" diff --git a/ladybug_grasshopper/json/LB_Radiation_Dome.json b/ladybug_grasshopper/json/LB_Radiation_Dome.json new file mode 100644 index 00000000..c8b2fe88 --- /dev/null +++ b/ladybug_grasshopper/json/LB_Radiation_Dome.json @@ -0,0 +1,141 @@ +{ + "version": "1.5.0", + "nickname": "RadiationDome", + "outputs": [ + [ + { + "access": "None", + "name": "mesh", + "description": "A colored mesh of a dome, representing the intensity of radiation/irradiance\nfrom different cardinal directions.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "compass", + "description": "A set of circles, lines and text objects that mark the cardinal\ndirections in relation to the radiation dome.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "legend", + "description": "A legend showing the kWh/m2 or W/m2 values that correspond to the colors\nof the mesh.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "title", + "description": "A text object for the title of the radiation dome.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "dir_vecs", + "description": "A list of vectors for each of the directions the dome is facing.\nAll vectors are unit vectors.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "dir_values", + "description": "Radiation values for each of the dome directions in kWh/m2 or W/m2.\nThis will be one list if show_comp_ is \"False\" and a list of 3\nlists (aka. a Data Tree) for total, direct, diffuse if show_comp_\nis \"True\".", + "type": null, + "default": null + }, + { + "access": "None", + "name": "max_pt", + "description": "A point on the radiation dome with the greatest amount of solar\nradiation/irradiance. For a radiation benefit sky, this is the\norientation with the greatest benefit. This can be used to\nunderstand the optimalorientation of solar panels or the best\ndirection to face for passive solar heating.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "max_info", + "description": "Information about the direction with the greates amount of radiation.\nThis includes the altitude, azimuth, and radiation/irradiance value.", + "type": null, + "default": null + } + ] + ], + "inputs": [ + { + "access": "item", + "name": "_sky_mtx", + "description": "A Sky Matrix from the \"LB Cumulative Sky Matrix\" component or the\n\"LB Benefit Sky Matrix\" component, which describes the radiation\ncoming from the various patches of the sky.", + "type": "System.Object", + "default": null + }, + { + "access": "list", + "name": "context_", + "description": "Rhino Breps and/or Rhino Meshes representing context geometry\nthat can block solar radiation to the center of the radiation dome.", + "type": "GeometryBase", + "default": null + }, + { + "access": "item", + "name": "_az_count_", + "description": "An integer greater than or equal to 3, which notes the number of\nhorizontal orientations to be evaluated on the dome. (Default: 72).", + "type": "int", + "default": null + }, + { + "access": "item", + "name": "_alt_count_", + "description": "An integer greater than or equal to 3, which notes the number of\nvertical orientations to be evaluated on the dome. (Default: 18).", + "type": "int", + "default": null + }, + { + "access": "item", + "name": "_center_pt_", + "description": "A point for the center of the radiation dome. (Default: (0, 0, 0))", + "type": "Point3d", + "default": null + }, + { + "access": "item", + "name": "_scale_", + "description": "A number to set the scale of the Radiation Dome. The default is 1,\nwhich corresponds to a radius of 100 meters in the current Rhino\nmodel's unit system.", + "type": "double", + "default": 1 + }, + { + "access": "item", + "name": "projection_", + "description": "Optional text for the name of a projection to use from the sky\ndome hemisphere to the 2D plane. If None, a 3D sky dome will be drawn\ninstead of a 2D one. (Default: None) Choose from the following:\n* Orthographic\n* Stereographic", + "type": "string", + "default": null + }, + { + "access": "item", + "name": "irradiance_", + "description": "Boolean to note whether the radiation dome should be plotted with units\nof cumulative Radiation (kWh/m2) [False] or with units of average\nIrradiance (W/m2) [True]. (Default: False).", + "type": "bool", + "default": null + }, + { + "access": "item", + "name": "show_comp_", + "description": "Boolean to indicate whether only one dome with total radiation\nshould be displayed (False) or three domes with the solar radiation\ncomponents (total, direct, and diffuse) should be shown. (Default: False).", + "type": "System.Object", + "default": null + }, + { + "access": "item", + "name": "legend_par_", + "description": "An optional LegendParameter object to change the display of the\nradiation dome (Default: None).", + "type": "System.Object", + "default": null + } + ], + "subcategory": "2 :: Visualize Data", + "code": "\ntry:\n from ladybug_geometry.geometry3d.pointvector import Point3D\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_geometry:\\n\\t{}'.format(e))\n\ntry:\n from ladybug.viewsphere import view_sphere\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_radiance.visualize.raddome import RadiationDome\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_radiance:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.config import conversion_to_meters\n from ladybug_{{cad}}.togeometry import to_point3d\n from ladybug_{{cad}}.fromgeometry import from_point3d, \\\n from_vector3d, from_mesh3d\n from ladybug_{{cad}}.intersect import join_geometry_to_mesh, intersect_mesh_rays\n from ladybug_{{cad}}.fromobjects import legend_objects, compass_objects\n from ladybug_{{cad}}.text import text_objects\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, \\\n de_objectify_output, list_to_data_tree\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\n\ndef translate_dome(lb_mesh, lb_compass, graphic, title_txt):\n \"\"\"Translate radiation dome geometry into a format suitable for {{Cad}}.\n\n Args:\n lb_mesh: A ladybug Mesh3D for the dome.\n lb_compass: A ladybug Compass object.\n graphic: A GraphicContainer for the dome.\n title_txt: Text for title of the dome.\n\n Returns:\n dome_mesh: A {{Cad}} colored mesh for the dome.\n dome_compass: {{Cad}} objects for the dome compass.\n dome_legend: {{Cad}} objects for the dome legend.\n dome_title: A bake-able title for the dome.\n \"\"\"\n # translate the dome visualization, including legend and compass\n dome_mesh = from_mesh3d(lb_mesh)\n dome_legend = legend_objects(graphic.legend)\n dome_angles = list(range(0, 360, int(360 / _az_count_)))\n start, stop, step, dome_angles = 0, 360, 360 / _az_count_, []\n while start < stop:\n dome_angles.append(start)\n start += step\n dome_angles = [int(n) for n in dome_angles]\n if len(dome_angles) > 36:\n dome_angles = dome_angles[::2]\n dome_compass = compass_objects(\n lb_compass, z, dome_angles, projection_, graphic.legend_parameters.font)\n\n # construct a title from the metadata\n dome_title = text_objects(title_txt, graphic.lower_title_location,\n graphic.legend_parameters.text_height,\n graphic.legend_parameters.font)\n\n return dome_mesh, dome_compass, dome_legend, dome_title\n\n\nif all_required_inputs(ghenv.Component):\n # set defaults for global variables\n _az_count_ = 72 if _az_count_ is None else _az_count_\n _alt_count_ = 18 if _alt_count_ is None else _alt_count_\n _scale_ = 1 if _scale_ is None else _scale_\n radius = (100 * _scale_) / conversion_to_meters()\n if _center_pt_ is not None: # process the center point\n center_pt3d = to_point3d(_center_pt_)\n z = center_pt3d.z\n else:\n center_pt3d, z = Point3D(), 0\n\n # compute the intersection matrix if context is specified\n n_vecs = RadiationDome.dome_vectors(_az_count_, _alt_count_)\n dir_vecs = [from_vector3d(vec) for vec in n_vecs]\n int_mtx = None\n if len(context_) != 0 and context_[0] is not None:\n shade_mesh = join_geometry_to_mesh(context_)\n p_vecs = view_sphere.tregenza_sphere_vectors if len(_sky_mtx.data[1]) == 145 \\\n else view_sphere.reinhart_sphere_vectors\n patch_dirs = [from_vector3d(vec) for vec in p_vecs]\n int_mtx, angles = intersect_mesh_rays(\n shade_mesh, [from_point3d(center_pt3d)] * len(dir_vecs),\n patch_dirs, dir_vecs)\n\n # create the RadiationRose object\n rad_dome = RadiationDome(\n _sky_mtx, int_mtx, _az_count_, _alt_count_, legend_par_,\n irradiance_, center_pt3d, radius, projection_)\n\n # create the dome visualization\n if not show_comp_: # only create the total dome mesh\n mesh, compass, legend, title = translate_dome(*rad_dome.draw())\n dir_values = rad_dome.total_values\n else: # create domes for total, direct and diffuse\n # loop through the 3 radiation types and produce a dome\n mesh, compass, legend, title = [], [], [], []\n rad_types = ('total', 'direct', 'diffuse')\n for dome_i in range(3):\n cent_pt = Point3D(center_pt3d.x + radius * 3 * dome_i,\n center_pt3d.y, center_pt3d.z)\n dome_mesh, dome_compass, dome_legend, dome_title = \\\n translate_dome(*rad_dome.draw(rad_types[dome_i], cent_pt))\n mesh.append(dome_mesh)\n compass.extend(dome_compass)\n legend.extend(dome_legend)\n title.append(dome_title)\n dir_values = list_to_data_tree(\n (rad_dome.total_values, rad_dome.direct_values, rad_dome.diffuse_values))\n\n # output infomration about the maximum direction\n max_pt = from_point3d(rad_dome.max_point)\n max_info = rad_dome.max_info\n", + "category": "Ladybug", + "name": "LB Radiation Dome", + "description": "Visualize the radiation falling on an object from different directions over a dome.\n_\nThe Radiation Dome depicts the amount of solar energy received by all directions\nover a dome. This is useful for understanding the optimal orientation of solar\npanels and how the performance of the panel might change if it's orientation\nis off from the optimal position. It can also be used to identify the optimal\nwall orientation for passive solar heating when used with skies of\nradiation harm/benefit. When used with clear sky matrices, it can identify\nthe orientations that result in the highest and lowest peak cooling load.\n_\nThe Radiation Dome can be understood in different ways:\n1) It's a 3D representation of the \"LB Radiation Rose,\" depicting all tilt angles\n for that rose at once.\n2) It's the reciprocal of the \"LB Sky Dome,\" since it shows how the radiation from\n that sky falls onto a hemispherical object.\n3) It's an radiation study of a hemisphere. The results here are effectively the\n same as running a hemisphere through the \"LB Incident Radiation\" component.\n-" +} \ No newline at end of file diff --git a/ladybug_grasshopper/json/LB_Radiation_Rose.json b/ladybug_grasshopper/json/LB_Radiation_Rose.json new file mode 100644 index 00000000..c6292aff --- /dev/null +++ b/ladybug_grasshopper/json/LB_Radiation_Rose.json @@ -0,0 +1,141 @@ +{ + "version": "1.5.0", + "nickname": "RadRose", + "outputs": [ + [ + { + "access": "None", + "name": "mesh", + "description": "A colored mesh of arrows, representing the intensity of radiation\nfrom different cardinal directions.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "compass", + "description": "A set of circles, lines and text objects that mark the cardinal\ndirections in relation to the radiation rose.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "orient_lines", + "description": "A list of line segments marking the orientation of each arrow.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "legend", + "description": "A legend showing the kWh/m2 or W/m2 values that correspond to the colors\nof the mesh.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "title", + "description": "A text object for the title of the radiation rose.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "dir_vecs", + "description": "A list of vectors for each of the directions the rose is facing.\nAll vectors are unit vectors.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "dir_values", + "description": "Radiation values for each of the rose directions in kWh/m2 or W/m2.\nThis will be one list if show_comp_ is \"False\" and a list of 3\nlists (aka. a Data Tree) for total, direct, diffuse if show_comp_\nis \"True\".", + "type": null, + "default": null + } + ] + ], + "inputs": [ + { + "access": "item", + "name": "_sky_mtx", + "description": "A Sky Matrix from the \"LB Cumulative Sky Matrix\" component or the\n\"LB Benefit Sky Matrix\" component, which describes the radiation\ncoming from the various patches of the sky.", + "type": "System.Object", + "default": null + }, + { + "access": "list", + "name": "context_", + "description": "Rhino Breps and/or Rhino Meshes representing context geometry\nthat can block solar radiation to the center of the radiation rose.", + "type": "GeometryBase", + "default": null + }, + { + "access": "item", + "name": "_dir_count_", + "description": "An integer greater than or equal to 3, which notes the number of\narrows to be generated for the radiation rose. (Default: 36).", + "type": "int", + "default": null + }, + { + "access": "item", + "name": "_tilt_angle_", + "description": "A number between 0 and 90 that sets the vertical tilt angle (aka.\nthe altitude) for all of the directions. By default, the Radiation\nRose depicts the amount of solar energy received by a vertical\nwall (tilt_angle=0). The tilt_angle can be changed to a specific\nvalue to assess the solar energy falling on geometries that are not\nperfectly vertical, such as a tilted photovoltaic panel. (Default: 0).", + "type": "double", + "default": null + }, + { + "access": "item", + "name": "_center_pt_", + "description": "A point for the center of the radiation rose. (Default: (0, 0, 0))", + "type": "Point3d", + "default": null + }, + { + "access": "item", + "name": "_scale_", + "description": "A number to set the scale of the Radiation Rose. The default is 1,\nwhich corresponds to a radius of 100 meters in the current Rhino\nmodel's unit system.", + "type": "double", + "default": null + }, + { + "access": "item", + "name": "_arrow_scale_", + "description": "A fractional number to note the scale of the radiation rose arrows\nin relation to the entire graphic. (Default: 1).", + "type": "double", + "default": 1 + }, + { + "access": "item", + "name": "max_rad_", + "description": "An optional number to set the level of radiation or irradiance associated\nwith the full radius of the rose. If unspecified, this is determined\nby the maximum level of radiation in the input data but a number\ncan be specified here to fix this at a specific value. This is\nparticularly useful when comparing different roses to one another.", + "type": "double", + "default": null + }, + { + "access": "item", + "name": "irradiance_", + "description": "Boolean to note whether the radiation rose should be plotted with units\nof cumulative Radiation (kWh/m2) [False] or with units of average\nIrradiance (W/m2) [True]. (Default: False).", + "type": "bool", + "default": null + }, + { + "access": "item", + "name": "show_comp_", + "description": "Boolean to indicate whether only one rose with total radiation\nshould be displayed (False) or three roses with the solar radiation\ncomponents (total, direct, and diffuse) should be shown. (Default: False).", + "type": "bool", + "default": null + }, + { + "access": "item", + "name": "legend_par_", + "description": "An optional LegendParameter object to change the display of the\nRadiation Rose.", + "type": "System.Object", + "default": null + } + ], + "subcategory": "2 :: Visualize Data", + "code": "\ntry:\n from ladybug_geometry.geometry3d.pointvector import Point3D\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_geometry:\\n\\t{}'.format(e))\n\ntry:\n from ladybug.viewsphere import view_sphere\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_radiance.visualize.radrose import RadiationRose\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_radiance:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.config import conversion_to_meters\n from ladybug_{{cad}}.togeometry import to_point3d\n from ladybug_{{cad}}.fromgeometry import from_point3d, from_vector3d, \\\n from_linesegment3d, from_mesh3d\n from ladybug_{{cad}}.intersect import join_geometry_to_mesh, intersect_mesh_rays\n from ladybug_{{cad}}.fromobjects import legend_objects, compass_objects\n from ladybug_{{cad}}.text import text_objects\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, \\\n de_objectify_output, list_to_data_tree\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\n\ndef translate_rose(lb_mesh, lb_orient_lines, lb_compass, graphic, title_txt):\n \"\"\"Translate radiation rose geometry into a format suitable for {{Cad}}.\n\n Args:\n lb_mesh: A ladybug Mesh3D for the rose.\n lb_compass: A ladybug Compass object.\n graphic: A GraphicContainer for the rose.\n title_txt: Text for title of the rose.\n\n Returns:\n rose_mesh: A {{Cad}} colored mesh for the rose.\n rose_compass: {{Cad}} objects for the rose compass.\n rose_legend: {{Cad}} objects for the rose legend.\n rose_title: A bake-able title for the rose.\n \"\"\"\n # translate the rose visualization, including legend and compass\n rose_mesh = from_mesh3d(lb_mesh)\n rose_legend = legend_objects(graphic.legend)\n rose_angles = list(range(0, 360, int(360 / _dir_count_)))\n start, stop, step, rose_angles = 0, 360, 360 / _dir_count_, []\n while start < stop:\n rose_angles.append(start)\n start += step\n rose_angles = [int(n) for n in rose_angles]\n if len(rose_angles) > 36:\n rose_angles = rose_angles[::2]\n rose_compass = compass_objects(\n lb_compass, z, rose_angles, None, graphic.legend_parameters.font)\n orient_lines = [from_linesegment3d(l) for l in lb_orient_lines]\n\n # construct a title from the metadata\n rose_title = text_objects(title_txt, graphic.lower_title_location,\n graphic.legend_parameters.text_height,\n graphic.legend_parameters.font)\n\n return rose_mesh, orient_lines, rose_compass, rose_legend, rose_title\n\n\nif all_required_inputs(ghenv.Component):\n # set defaults for global variables\n _dir_count_ = 36 if _dir_count_ is None else _dir_count_\n _tilt_angle_ = 0 if _tilt_angle_ is None else _tilt_angle_\n _arrow_scale_ = 1 if _arrow_scale_ is None else _arrow_scale_\n _scale_ = 1 if _scale_ is None else _scale_\n radius = (100 * _scale_) / conversion_to_meters()\n if _center_pt_ is not None: # process the center point\n center_pt3d = to_point3d(_center_pt_)\n z = center_pt3d.z\n else:\n center_pt3d, z = Point3D(), 0\n\n # compute the intersection matrix if context is specified\n n_vecs = RadiationRose.radial_vectors(_dir_count_, _tilt_angle_)\n dir_vecs = [from_vector3d(vec) for vec in n_vecs]\n int_mtx = None\n if len(context_) != 0 and context_[0] is not None:\n shade_mesh = join_geometry_to_mesh(context_)\n p_vecs = view_sphere.tregenza_sphere_vectors if len(_sky_mtx.data[1]) == 145 \\\n else view_sphere.reinhart_sphere_vectors\n patch_dirs = [from_vector3d(vec) for vec in p_vecs]\n int_mtx, angles = intersect_mesh_rays(\n shade_mesh, [from_point3d(center_pt3d)] * _dir_count_,\n patch_dirs, dir_vecs, 1)\n\n # create the RadiationRose object\n rad_rose = RadiationRose(\n _sky_mtx, int_mtx, _dir_count_, _tilt_angle_, legend_par_,\n irradiance_, center_pt3d, radius, _arrow_scale_)\n\n # create the rose visualization\n if not show_comp_: # only create the total rose mesh\n mesh, orient_lines, compass, legend, title = \\\n translate_rose(*rad_rose.draw(max_rad=max_rad_))\n dir_values = rad_rose.total_values\n else: # create roses for total, direct and diffuse\n # loop through the 3 radiation types and produce a rose\n mesh, orient_lines, compass, legend, title = [], [], [], [], []\n rad_types = ('total', 'direct', 'diffuse')\n for rose_i in range(3):\n cent_pt = Point3D(center_pt3d.x + radius * 3 * rose_i,\n center_pt3d.y, center_pt3d.z)\n rose_mesh, rose_lines, rose_compass, rose_legend, rose_title = \\\n translate_rose(*rad_rose.draw(rad_types[rose_i], cent_pt, max_rad_))\n mesh.append(rose_mesh)\n compass.extend(rose_compass)\n orient_lines.extend(rose_lines)\n legend.extend(rose_legend)\n title.append(rose_title)\n dir_values = list_to_data_tree(\n (rad_rose.total_values, rad_rose.direct_values, rad_rose.diffuse_values))\n", + "category": "Ladybug", + "name": "LB Radiation Rose", + "description": "Visualize the solar energy falling on different direction as a rose.\n_\nBy default, the Radiation Rose depicts the amount of solar energy received\nby a vertical wall facing each of the directions of the compass rose.\n_\nThis is useful for understanding the radiation harm/benefit experienced by\ndifferent building orientations or the orientations with the highest peak cooling\nload (for sky matrices of clear skies). The tilt_angle can be used to assess the\nsolar energy falling on geometries that are not perfectly vertical, such\nas tilted photovoltaic panels.\n-" +} \ No newline at end of file diff --git a/ladybug_grasshopper/json/LB_Sky_Dome.json b/ladybug_grasshopper/json/LB_Sky_Dome.json index 361e6a20..7282b5f2 100644 --- a/ladybug_grasshopper/json/LB_Sky_Dome.json +++ b/ladybug_grasshopper/json/LB_Sky_Dome.json @@ -1,5 +1,5 @@ { - "version": "1.5.0", + "version": "1.5.1", "nickname": "SkyDome", "outputs": [ [ @@ -20,14 +20,14 @@ { "access": "None", "name": "legend", - "description": "A legend showing the kWh/m2 values that correspond to the colors\nof the mesh.", + "description": "A legend showing the kWh/m2 or W/m2 values that correspond to the colors\nof the mesh.", "type": null, "default": null }, { "access": "None", "name": "title", - "description": "A text object for the title of the sunpath.", + "description": "A text object for the title of the sky dome.", "type": null, "default": null }, @@ -41,7 +41,7 @@ { "access": "None", "name": "patch_values", - "description": "Radiation values for each of the sky patches in kWh/m2. This\nwill be one list if show_comp_ is \"False\" and a list of 3 lists (aka.\na Data Tree) for total, direct, diffuse if show_comp_ is \"True\".", + "description": "Radiation values for each of the sky patches in kWh/m2 or W/m2.\nThis will be one list if show_comp_ is \"False\" and a list of 3\nlists (aka. a Data Tree) for total, direct, diffuse if show_comp_\nis \"True\".", "type": null, "default": null }, @@ -58,7 +58,7 @@ { "access": "item", "name": "_sky_mtx", - "description": "A Sky Matrix from the \"LB Cumulative Sky Matrix\" component, which\ndescribes the radiation coming from the various patches of the sky.", + "description": "A Sky Matrix from the \"LB Cumulative Sky Matrix\" component or the\n\"LB Benefit Sky Matrix\" component, which describes the radiation\ncoming from the various patches of the sky.", "type": "System.Object", "default": null }, @@ -83,6 +83,13 @@ "type": "string", "default": null }, + { + "access": "item", + "name": "irradiance_", + "description": "Boolean to note whether the sky dome should be plotted with units of\ncumulative Radiation (kWh/m2) [False] or with units of average\nIrradiance (W/m2) [True]. (Default: False).", + "type": "bool", + "default": null + }, { "access": "item", "name": "show_comp_", @@ -99,7 +106,7 @@ } ], "subcategory": "2 :: Visualize Data", - "code": "\nimport math\n\ntry:\n from ladybug_geometry.geometry2d.pointvector import Point2D\n from ladybug_geometry.geometry3d.pointvector import Point3D, Vector3D\n from ladybug_geometry.geometry3d.mesh import Mesh3D\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_geometry:\\n\\t{}'.format(e))\n\ntry:\n from ladybug.viewsphere import view_sphere\n from ladybug.compass import Compass\n from ladybug.graphic import GraphicContainer\n from ladybug.legend import LegendParameters\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.config import conversion_to_meters\n from ladybug_{{cad}}.togeometry import to_point3d\n from ladybug_{{cad}}.fromgeometry import from_mesh3d, from_vector3d\n from ladybug_{{cad}}.fromobjects import legend_objects, compass_objects\n from ladybug_{{cad}}.text import text_objects\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, \\\n de_objectify_output, list_to_data_tree\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\n\ndef draw_dome(dome_data, center, dome_name, legend_par):\n \"\"\"Draw the dome mesh, compass, legend, and title for a sky dome.\n\n Args:\n dome_data: List of radiation values for the dome data\n center: Point3D for the center of the sun path.\n dome_name: Text for the dome name, which will appear in the title.\n legend_par: Legend parameters to be used for the dome\n\n Returns:\n dome_mesh: A colored mesh for the dome based on dome_data.\n dome_compass: A compass for the dome.\n dome_legend: A leend for the colored dome mesh.\n dome_title: A title for the dome.\n values: A list of radiation values that align with the dome_mesh faces.\n \"\"\"\n # create the dome mesh and ensure patch values align with mesh faces\n if len(dome_data) == 145: # tregenza sky\n lb_mesh = view_sphere.tregenza_dome_mesh_high_res.scale(radius)\n values = [] # high res dome has 3 x 3 faces per patch; we must convert\n tot_i = 0 # track the total number of patches converted\n for patch_i in view_sphere.TREGENZA_PATCHES_PER_ROW:\n row_vals = []\n for val in dome_data[tot_i:tot_i + patch_i]:\n row_vals.extend([val] * 3)\n for i in range(3):\n values.extend(row_vals)\n tot_i += patch_i\n values = values + [dome_data[-1]] * 18 # last patch has triangular faces\n else: #reinhart sky\n lb_mesh = view_sphere.reinhart_dome_mesh.scale(radius)\n values = dome_data + [dome_data[-1]] * 11 # last patch has triangular faces\n\n # move and/or rotate the mesh as needed\n if north != 0:\n lb_mesh = lb_mesh.rotate_xy(math.radians(north), Point3D())\n if center != Point3D():\n lb_mesh = lb_mesh.move(Vector3D(center.x, center.y, center.z))\n\n # project the mesh if requested\n if projection_ is not None:\n if projection_.title() == 'Orthographic':\n pts = (Compass.point3d_to_orthographic(pt) for pt in lb_mesh.vertices)\n elif projection_.title() == 'Stereographic':\n pts = (Compass.point3d_to_stereographic(pt, radius, center)\n for pt in lb_mesh.vertices)\n else:\n raise ValueError(\n 'Projection type \"{}\" is not recognized.'.format(projection_))\n pts3d = tuple(Point3D(pt.x, pt.y, z) for pt in pts)\n lb_mesh = Mesh3D(pts3d, lb_mesh.faces)\n\n # output the dome visualization, including legend and compass\n move_fac = radius * 0.15\n min_pt = lb_mesh.min.move(Vector3D(-move_fac, -move_fac, 0))\n max_pt = lb_mesh.max.move(Vector3D(move_fac, move_fac, 0))\n graphic = GraphicContainer(values, min_pt, max_pt, legend_par)\n graphic.legend_parameters.title = 'kWh/m2'\n lb_mesh.colors = graphic.value_colors\n dome_mesh = from_mesh3d(lb_mesh)\n dome_legend = legend_objects(graphic.legend)\n dome_compass = compass_objects(\n Compass(radius, Point2D(center.x, center.y), north), z, None, projection_,\n graphic.legend_parameters.font)\n\n # construct a title from the metadata\n st, end = metadata[2], metadata[3]\n time_str = '{} - {}'.format(st, end) if st != end else st\n title_txt = '{} Radiation\\n{}\\n{}'.format(\n dome_name, time_str, '\\n'.join([dat for dat in metadata[4:]]))\n dome_title = text_objects(title_txt, graphic.lower_title_location,\n graphic.legend_parameters.text_height,\n graphic.legend_parameters.font)\n\n return dome_mesh, dome_compass, dome_legend, dome_title, values\n\n\nif all_required_inputs(ghenv.Component):\n # set defaults for global variables\n _scale_ = 1 if _scale_ is None else _scale_\n radius = (100 * _scale_) / conversion_to_meters()\n if _center_pt_ is not None: # process the center point into a Point2D\n center_pt3d = to_point3d(_center_pt_)\n z = center_pt3d.z\n else:\n center_pt3d, z = Point3D(), 0\n\n # deconstruct the sky matrix and derive key data from it\n metadata, direct, diffuse = de_objectify_output(_sky_mtx)\n north = metadata[0] # first item is the north angle\n sky_type = 1 if len(direct) == 145 else 2 # i for tregenza; 2 for reinhart\n total = [dirr + difr for dirr, difr in zip(direct, diffuse)] # total radiation\n\n # override the legend default min and max to make sense for domes\n l_par = legend_par_.duplicate() if legend_par_ is not None else LegendParameters()\n if l_par.min is None:\n l_par.min = 0\n if l_par.max is None:\n l_par.max = max(total)\n\n # output patch patch vectors\n patch_vecs_lb = view_sphere.tregenza_dome_vectors if len(total) == 145 \\\n else view_sphere.reinhart_dome_vectors\n patch_vecs = [from_vector3d(vec) for vec in patch_vecs_lb]\n\n # create the dome meshes\n if not show_comp_: # only create the total dome mesh\n mesh, compass, legend, title, mesh_values = \\\n draw_dome(total, center_pt3d, 'Total', l_par)\n patch_values = total\n else: # create domes for total, direct and diffuse\n # loop through the 3 radiation types and produce a dome\n mesh, compass, legend, title, mesh_values = [], [], [], [], []\n rad_types = ('Total', 'Direct', 'Diffuse')\n rad_data = (total, direct, diffuse)\n for dome_i in range(3):\n cent_pt = Point3D(center_pt3d.x + radius * 3 * dome_i,\n center_pt3d.y, center_pt3d.z)\n dome_mesh, dome_compass, dome_legend, dome_title, dome_values = \\\n draw_dome(rad_data[dome_i], cent_pt, rad_types[dome_i], l_par)\n mesh.append(dome_mesh)\n compass.extend(dome_compass)\n legend.extend(dome_legend)\n title.append(dome_title)\n mesh_values.append(dome_values)\n patch_values = list_to_data_tree(rad_data)\n mesh_values = list_to_data_tree(mesh_values)\n", + "code": "\ntry:\n from ladybug_geometry.geometry3d.pointvector import Point3D\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_geometry:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_radiance.visualize.skydome import SkyDome\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_radiance:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.config import conversion_to_meters\n from ladybug_{{cad}}.togeometry import to_point3d\n from ladybug_{{cad}}.fromgeometry import from_mesh3d, from_vector3d\n from ladybug_{{cad}}.fromobjects import legend_objects, compass_objects\n from ladybug_{{cad}}.text import text_objects\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, \\\n de_objectify_output, list_to_data_tree\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\n\ndef translate_dome(lb_mesh, lb_compass, graphic, title_txt, values):\n \"\"\"Translate sky dome geometry into a format suitable for {{Cad}}.\n\n Args:\n lb_mesh: A ladybug Mesh3D for the dome.\n lb_compass: A ladybug Compass object.\n graphic: A GraphicContainer for the dome.\n title_txt: Text for title of the dome.\n values: A list of radiation values that align with the dome_mesh faces.\n\n Returns:\n dome_mesh: A {{Cad}} colored mesh for the dome.\n dome_compass: {{Cad}} objects for the dome compass.\n dome_legend: {{Cad}} objects for the dome legend.\n dome_title: A bake-able title for the dome.\n values: A list of radiation values that align with the dome_mesh faces.\n \"\"\"\n # translate the dome visualization, including legend and compass\n dome_mesh = from_mesh3d(lb_mesh)\n dome_legend = legend_objects(graphic.legend)\n dome_compass = compass_objects(\n lb_compass, z, None, sky_dome.projection, graphic.legend_parameters.font)\n\n # construct a title from the metadata\n dome_title = text_objects(title_txt, graphic.lower_title_location,\n graphic.legend_parameters.text_height,\n graphic.legend_parameters.font)\n\n return dome_mesh, dome_compass, dome_legend, dome_title, values\n\n\nif all_required_inputs(ghenv.Component):\n # set defaults for global variables\n _scale_ = 1 if _scale_ is None else _scale_\n radius = (100 * _scale_) / conversion_to_meters()\n if _center_pt_ is not None: # process the center point\n center_pt3d = to_point3d(_center_pt_)\n z = center_pt3d.z\n else:\n center_pt3d, z = Point3D(), 0\n\n # create the SkyDome object\n sky_dome = SkyDome(_sky_mtx, legend_par_, irradiance_,\n center_pt3d, radius, projection_)\n\n # output patch patch vectors\n patch_vecs = [from_vector3d(vec) for vec in sky_dome.patch_vectors]\n\n # create the dome visualization\n if not show_comp_: # only create the total dome mesh\n mesh, compass, legend, title, mesh_values = translate_dome(*sky_dome.draw())\n patch_values = sky_dome.total_values\n else: # create domes for total, direct and diffuse\n # loop through the 3 radiation types and produce a dome\n mesh, compass, legend, title, mesh_values = [], [], [], [], []\n rad_types = ('total', 'direct', 'diffuse')\n for dome_i in range(3):\n cent_pt = Point3D(center_pt3d.x + radius * 3 * dome_i,\n center_pt3d.y, center_pt3d.z)\n dome_mesh, dome_compass, dome_legend, dome_title, dome_values = \\\n translate_dome(*sky_dome.draw(rad_types[dome_i], cent_pt))\n mesh.append(dome_mesh)\n compass.extend(dome_compass)\n legend.extend(dome_legend)\n title.append(dome_title)\n mesh_values.append(dome_values)\n rad_data = (sky_dome.total_values, sky_dome.direct_values, sky_dome.diffuse_values)\n patch_values = list_to_data_tree(rad_data)\n mesh_values = list_to_data_tree(mesh_values)\n", "category": "Ladybug", "name": "LB Sky Dome", "description": "Visualize a sky matrix from the \"LB Cumulative Sky Matrix\" component as a colored\ndome, subdivided into patches with a radiation value for each patch.\n-" diff --git a/ladybug_grasshopper/json/LB_SunPath.json b/ladybug_grasshopper/json/LB_SunPath.json index c6b521cd..b508d2d5 100644 --- a/ladybug_grasshopper/json/LB_SunPath.json +++ b/ladybug_grasshopper/json/LB_SunPath.json @@ -1,5 +1,5 @@ { - "version": "1.5.1", + "version": "1.5.2", "nickname": "Sunpath", "outputs": [ [ diff --git a/ladybug_grasshopper/json/LB_Wind_Profile.json b/ladybug_grasshopper/json/LB_Wind_Profile.json index ef4513ea..86a9f309 100644 --- a/ladybug_grasshopper/json/LB_Wind_Profile.json +++ b/ladybug_grasshopper/json/LB_Wind_Profile.json @@ -1,5 +1,5 @@ { - "version": "1.5.1", + "version": "1.5.2", "nickname": "WindProfile", "outputs": [ [ diff --git a/ladybug_grasshopper/json/LB_Wind_Rose.json b/ladybug_grasshopper/json/LB_Wind_Rose.json index b3bd8bed..3b1131a9 100644 --- a/ladybug_grasshopper/json/LB_Wind_Rose.json +++ b/ladybug_grasshopper/json/LB_Wind_Rose.json @@ -1,5 +1,5 @@ { - "version": "1.5.2", + "version": "1.5.3", "nickname": "WindRose", "outputs": [ [ diff --git a/ladybug_grasshopper/src/LB Benefit Sky Matrix.py b/ladybug_grasshopper/src/LB Benefit Sky Matrix.py new file mode 100644 index 00000000..78fcec40 --- /dev/null +++ b/ladybug_grasshopper/src/LB Benefit Sky Matrix.py @@ -0,0 +1,123 @@ +# Ladybug: A Plugin for Environmental Analysis (GPL) +# This file is part of Ladybug. +# +# Copyright (c) 2022, Ladybug Tools. +# You should have received a copy of the GNU Affero General Public License +# along with Ladybug; If not, see . +# +# @license AGPL-3.0-or-later + +""" +Get a matrix representing the benefit/harm of radiation based on temperature data. +_ +Radiation benefit matrices are helpful for evaluating building massing and facade +designs in terms of passive solar heat gain vs. cooling energy increase. +_ +This component uses Radiance's gendaymtx function to calculate the radiation +for each patch of the sky. Gendaymtx is written by Ian Ashdown and Greg Ward. +Morere information can be found in Radiance manual at: +http://www.radiance-online.org/learning/documentation/manual-pages/pdfs/gendaymtx.pdf +- + + Args: + north_: A number between -360 and 360 for the counterclockwise + difference between the North and the positive Y-axis in degrees. + 90 is West and 270 is East. This can also be Vector for the + direction to North. (Default: 0) + _location: A ladybug Location that has been output from the "LB Import EPW" + component or the "LB Construct Location" component. + _temperature: An annual hourly DataCollection of temperature, which will be used + to establish whether radiation is desired or not for each time step. + _bal_temp_: The temperature in Celsius between which radiation switches from being a + benefit to a harm. Typical residential buildings have balance temperatures + as high as 18C and commercial buildings tend to have lower values + around 12C. (Default 15C). + _bal_offset_: The temperature offset from the balance temperature in Celsius where + radiation is neither harmful nor helpful. (Default: 2). + _direct_rad: An annual hourly DataCollection of Direct Normal Radiation such + as that which is output from the "LB Import EPW" component or the + "LB Import STAT" component. + _diffuse_rad: An annual hourly DataCollection of Diffuse Horizontal Radiation + such as that which is output from the "LB Import EPW" component or + the "LB Import STAT" component. + _hoys_: A number or list of numbers between 0 and 8760 that respresent + the hour(s) of the year for which to generate the sky matrix. The + "LB Calculate HOY" component can output this number given a month, + day and hour. The "LB Analysis Period" component can output a + list of HOYs within a certain hour or date range. By default, + the matrix will be for the entire year. + high_density_: A Boolean to indicate whether the higher-density Reinhart sky + matrix should be generated (True), which has roughly 4 times the sky + patches as the (default) original Tregenza sky (False). Note that, + while the Reinhart sky has a higher resolution and is more accurate, + it will result in considerably longer calculation time for incident + radiation studies. The difference in sky resolution can be observed + with the "LB Sky Dome" component. (Default: False). + _ground_ref_: A number between 0 and 1 to note the average ground reflectance + that is associated with the sky matrix. (Default: 0.2). + _folder_: The folder in which the Radiance commands are executed to + produce the sky matrix. If None, it will be written to Ladybug's + default EPW folder. + + Returns: + report: ... + sky_mtx: A sky matrix object containing the radiation benefit/harm coming from each + patch of the sky. This can be used for a radiation study, a radition rose, + or a sky dome visualization. It can also be deconstructed into its + individual values with the "LB Deconstruct Matrix" component. +""" + +ghenv.Component.Name = 'LB Benefit Sky Matrix' +ghenv.Component.NickName = 'BenefitMatrix' +ghenv.Component.Message = '1.5.0' +ghenv.Component.Category = 'Ladybug' +ghenv.Component.SubCategory = '2 :: Visualize Data' +ghenv.Component.AdditionalHelpFromDocStrings = '3' + +import math + +try: + from ladybug_geometry.geometry2d.pointvector import Vector2D +except ImportError as e: + raise ImportError('\nFailed to import ladybug_geometry:\n\t{}'.format(e)) + +try: + from ladybug_radiance.skymatrix import SkyMatrix +except ImportError as e: + raise ImportError('\nFailed to import ladybug:\n\t{}'.format(e)) + +try: + from ladybug_rhino.togeometry import to_vector2d + from ladybug_rhino.grasshopper import all_required_inputs +except ImportError as e: + raise ImportError('\nFailed to import ladybug_rhino:\n\t{}'.format(e)) + +try: + from lbt_recipes.version import check_radiance_date +except ImportError as e: + raise ImportError('\nFailed to import honeybee_radiance:\n\t{}'.format(e)) + +# check the istalled Radiance date and get the path to the gemdaymtx executable +check_radiance_date() + + +if all_required_inputs(ghenv.Component): + # process and set defaults for all of the global inputs + _bal_temp_ = 15 if _bal_temp_ is None else _bal_temp_ + _bal_offset_ = 2 if _bal_offset_ is None else _bal_offset_ + if north_ is not None: # process the north_ + try: + north_ = math.degrees( + to_vector2d(north_).angle_clockwise(Vector2D(0, 1))) + except AttributeError: # north angle instead of vector + north_ = float(north_) + else: + north_ = 0 + ground_r = 0.2 if _ground_ref_ is None else _ground_ref_ + + # create the sky matrix object + sky_mtx = SkyMatrix.from_components_benefit( + _location, _direct_rad, _diffuse_rad, _temperature, _bal_temp_, _bal_offset_, + _hoys_, north_, high_density_, ground_r) + if _folder_: + sky_mtx.folder = _folder_ diff --git a/ladybug_grasshopper/src/LB Cumulative Sky Matrix.py b/ladybug_grasshopper/src/LB Cumulative Sky Matrix.py index f6dee262..8303ccdc 100644 --- a/ladybug_grasshopper/src/LB Cumulative Sky Matrix.py +++ b/ladybug_grasshopper/src/LB Cumulative Sky Matrix.py @@ -38,13 +38,13 @@ day and hour. The "LB Analysis Period" component can output a list of HOYs within a certain hour or date range. By default, the matrix will be for the entire year. - high_density_: A Boolean to indicate whether the higher-density Reinhart - sky matrix should be generated (True), which has roughly 4 times - the sky patches as the (default) original Tregenza sky (False). - Note that, while the Reinhart sky has a higher resolution and is - more accurate, it will result in considerably longer calculation - time for incident radiation studies. The difference in sky - resolution can be observed with the (Default: False). + high_density_: A Boolean to indicate whether the higher-density Reinhart sky + matrix should be generated (True), which has roughly 4 times the sky + patches as the (default) original Tregenza sky (False). Note that, + while the Reinhart sky has a higher resolution and is more accurate, + it will result in considerably longer calculation time for incident + radiation studies. The difference in sky resolution can be observed + with the "LB Sky Dome" component. (Default: False). _ground_ref_: A number between 0 and 1 to note the average ground reflectance that is associated with the sky matrix. (Default: 0.2). _folder_: The folder in which the Radiance commands are executed to @@ -61,13 +61,11 @@ ghenv.Component.Name = 'LB Cumulative Sky Matrix' ghenv.Component.NickName = 'SkyMatrix' -ghenv.Component.Message = '1.5.0' +ghenv.Component.Message = '1.5.1' ghenv.Component.Category = 'Ladybug' ghenv.Component.SubCategory = '2 :: Visualize Data' ghenv.Component.AdditionalHelpFromDocStrings = '3' -import os -import subprocess import math try: @@ -76,92 +74,23 @@ raise ImportError('\nFailed to import ladybug_geometry:\n\t{}'.format(e)) try: - from ladybug.wea import Wea - from ladybug.viewsphere import view_sphere - from ladybug.dt import DateTime - from ladybug.config import folders as lb_folders + from ladybug_radiance.skymatrix import SkyMatrix except ImportError as e: raise ImportError('\nFailed to import ladybug:\n\t{}'.format(e)) try: from ladybug_rhino.togeometry import to_vector2d - from ladybug_rhino.grasshopper import all_required_inputs, objectify_output + from ladybug_rhino.grasshopper import all_required_inputs except ImportError as e: raise ImportError('\nFailed to import ladybug_rhino:\n\t{}'.format(e)) - -# TODO: Remove dependency on honeybee + Radiance after genskymtx is in its own LB extension try: - from honeybee_radiance.config import folders as hb_folders from lbt_recipes.version import check_radiance_date except ImportError as e: raise ImportError('\nFailed to import honeybee_radiance:\n\t{}'.format(e)) # check the istalled Radiance date and get the path to the gemdaymtx executable check_radiance_date() -gendaymtx_exe = os.path.join(hb_folders.radbin_path, 'gendaymtx.exe') if \ - os.name == 'nt' else os.path.join(hb_folders.radbin_path, 'gendaymtx') - - -# constants for converting RGB values output by gendaymtx to broadband radiation -PATCHES_PER_ROW = { - 1: view_sphere.TREGENZA_PATCHES_PER_ROW + (1,), - 2: view_sphere.REINHART_PATCHES_PER_ROW + (1,) -} -PATCH_ROW_COEFF = { - 1 : view_sphere.TREGENZA_COEFFICIENTS, - 2: view_sphere.REINHART_COEFFICIENTS -} - - -def broadband_radiation(patch_row_str, row_number, wea_duration, sky_density=1): - """Parse a row of gendaymtx RGB patch data in W/sr/m2 to radiation in kWh/m2. - - This includes aplying broadband weighting to the RGB bands, multiplication - by the steradians of each patch, and multiplying by the duration of time that - they sky matrix represents in hours. - - Args: - patch_row_str: Text string for a single row of RGB patch data. - row_number: Interger for the row number that the patch corresponds to. - sky_density: Integer (either 1 or 2) for the density. - wea_duration: Number for the duration of the Wea in hours. This is used - to convert between the average value output by the command and the - cumulative value that is needed for all ladybug analyses. - """ - R, G, B = patch_row_str.split(' ') - weight_val = 0.265074126 * float(R) + 0.670114631 * float(G) + 0.064811243 * float(B) - return weight_val * PATCH_ROW_COEFF[sky_density][row_number] * wea_duration / 1000 - - -def parse_mtx_data(data_str, wea_duration, sky_density=1): - """Parse a string of Radiance gendaymtx data to a list of radiation-per-patch. - - This function handles the removing of the header and the conversion of the - RGB irradianc-=per-steraidian values to broadband radiation. It also removes - the first patch, which is the ground and is not used by Ladybug. - - Args: - data_str: The string that has been output by gendaymtx to stdout. - wea_duration: Number for the duration of the Wea in hours. This is used - to convert between the average value output by the command and the - cumulative value that is needed for all ladybug analyses. - sky_density: Integer (either 1 or 2) for the density. - """ - # split lines and remove the header, ground patch and last line break - data_lines = data_str.split('\n') - patch_lines = data_lines[9:-1] - - # loop through the rows and convert the radiation RGB values - broadband_irr = [] - patch_counter = 0 - for i, row_patch_count in enumerate(PATCHES_PER_ROW[sky_density]): - row_slice = patch_lines[patch_counter:patch_counter + row_patch_count] - irr_vals = (broadband_radiation(row, i, wea_duration, sky_density) - for row in row_slice) - broadband_irr.extend(irr_vals) - patch_counter += row_patch_count - return broadband_irr if all_required_inputs(ghenv.Component): @@ -174,50 +103,10 @@ def parse_mtx_data(data_str, wea_duration, sky_density=1): north_ = float(north_) else: north_ = 0 - density = 2 if high_density_ else 1 ground_r = 0.2 if _ground_ref_ is None else _ground_ref_ - # filter the radiation by _hoys if they are input - if len(_hoys_) != 0: - _direct_rad = _direct_rad.filter_by_hoys(_hoys_) - _diffuse_rad = _diffuse_rad.filter_by_hoys(_hoys_) - - # create the wea and write it to the default_epw_folder - wea = Wea(_location, _direct_rad, _diffuse_rad) - wea_duration = len(wea) / wea.timestep - wea_folder = _folder_ if _folder_ is not None else \ - os.path.join(lb_folders.default_epw_folder, 'sky_matrices') - metd = _direct_rad.header.metadata - wea_basename = metd['city'].replace(' ', '_') if 'city' in metd else 'unnamed' - wea_path = os.path.join(wea_folder, wea_basename) - wea_file = wea.write(wea_path) - - # execute the Radiance gendaymtx command - use_shell = True if os.name == 'nt' else False - # command for direct patches - cmds = [gendaymtx_exe, '-m', str(density), '-d', '-O1', '-A', wea_file] - process = subprocess.Popen(cmds, stdout=subprocess.PIPE, shell=use_shell) - stdout = process.communicate() - dir_data_str = stdout[0] - # command for diffuse patches - cmds = [gendaymtx_exe, '-m', str(density), '-s', '-O1', '-A', wea_file] - process = subprocess.Popen(cmds, stdout=subprocess.PIPE, shell=use_shell) - stdout = process.communicate() - diff_data_str = stdout[0] - - # parse the data into a single matrix - dir_vals = parse_mtx_data(dir_data_str, wea_duration, density) - diff_vals = parse_mtx_data(diff_data_str, wea_duration, density) - - # collect sky metadata like the north, which will be used by other components - metadata = [north_, ground_r] - if _hoys_: - metadata.extend([DateTime.from_hoy(h) for h in (_hoys_[0], _hoys_[-1])]) - else: - metadata.extend([wea.analysis_period.st_time, wea.analysis_period.end_time]) - for key, val in _direct_rad.header.metadata.items(): - metadata.append('{} : {}'.format(key, val)) - - # wrap everything together into an object to output from the component - mtx_data = (metadata, dir_vals, diff_vals) - sky_mtx = objectify_output('Cumulative Sky Matrix', mtx_data) + # create the sky matrix object + sky_mtx = SkyMatrix.from_components( + _location, _direct_rad, _diffuse_rad, _hoys_, north_, high_density_, ground_r) + if _folder_: + sky_mtx.folder = _folder_ diff --git a/ladybug_grasshopper/src/LB Incident Radiation.py b/ladybug_grasshopper/src/LB Incident Radiation.py index 6dcf7dbd..e2fcac41 100644 --- a/ladybug_grasshopper/src/LB Incident Radiation.py +++ b/ladybug_grasshopper/src/LB Incident Radiation.py @@ -35,10 +35,11 @@ - Args: - _sky_mtx: A Sky Matrix from the "LB Cumulative Sky Matrix" component, which - describes the radiation coming from the various patches of the sky. - The "LB Sky Dome" component can be used to visualize any sky matrix - to understand its relationship to the test geometry. + _sky_mtx: A Sky Matrix from the "LB Cumulative Sky Matrix" component or the + "LB Benefit Sky Matrix" component, which describes the radiation + coming from the various patches of the sky. The "LB Sky Dome" + component can be used to visualize any sky matrix to understand + its relationship to the test geometry. _geometry: Rhino Breps and/or Rhino Meshes for which incident radiation analysis will be conducted. If Breps are input, they will be subdivided using the _grid_size to yeild individual points at which analysis will @@ -58,6 +59,9 @@ of the input _geometry. Typically, this should be a small positive number to ensure points are not blocked by the mesh. (Default: 10 cm in the equivalent Rhino Model units). + irradiance_: Boolean to note whether the study should output units of cumulative + Radiation (kWh/m2) [False] or units of average Irradiance (W/m2) + [True]. (Default: False). legend_par_: Optional legend parameters from the "LB Legend Parameters" that will be used to customize the display of the results. _cpu_count_: An integer to set the number of CPUs used in the execution of the @@ -98,7 +102,7 @@ ghenv.Component.Name = "LB Incident Radiation" ghenv.Component.NickName = 'IncidentRadiation' -ghenv.Component.Message = '1.5.0' +ghenv.Component.Message = '1.5.1' ghenv.Component.Category = 'Ladybug' ghenv.Component.SubCategory = '3 :: Analyze Geometry' ghenv.Component.AdditionalHelpFromDocStrings = '1' @@ -112,6 +116,8 @@ try: from ladybug.viewsphere import view_sphere from ladybug.graphic import GraphicContainer + from ladybug.legend import LegendParameters + from ladybug.color import Colorset except ImportError as e: raise ImportError('\nFailed to import ladybug:\n\t{}'.format(e)) @@ -164,12 +170,20 @@ # compute the results results = [] int_matrix = [] - for int_vals, angles in zip(int_matrix_init, angles): - pt_rel = [ival * math.cos(ang) for ival, ang in zip(int_vals, angles)] + for int_vals, angs in zip(int_matrix_init, angles): + pt_rel = [ival * math.cos(ang) for ival, ang in zip(int_vals, angs)] int_matrix.append(pt_rel) rad_result = sum(r * w for r, w in zip(pt_rel, all_rad)) results.append(rad_result) + # convert to irradiance if requested + study_name = 'Incident Radiation' + if irradiance_: + study_name = 'Incident Irradiance' + factor = 1000 / _sky_mtx.wea_duration if hasattr(_sky_mtx, 'wea_duration') \ + else 1000 / (((mtx[0][3] - mtx[0][2]).total_seconds() / 3600) + 1) + results = [r * factor for r in results] + # output the intersection matrix and compute total radiation int_mtx = objectify_output('Geometry/Sky Intersection Matrix', int_matrix) unit_conv = conversion_to_meters() ** 2 @@ -178,10 +192,19 @@ total += rad * area * unit_conv # create the mesh and legend outputs - graphic = GraphicContainer(results, study_mesh.min, study_mesh.max, legend_par_) - graphic.legend_parameters.title = 'kWh/m2' + l_par = legend_par_ if legend_par_ is not None else LegendParameters() + if hasattr(_sky_mtx, 'benefit_matrix') and _sky_mtx.benefit_matrix is not None: + study_name = '{} Benefit/Harm'.format(study_name) + if l_par.are_colors_default: + l_par.colors = reversed(Colorset.benefit_harm()) + if l_par.min is None: + l_par.min = min((min(results), -max(results))) + if l_par.max is None: + l_par.max = max((-min(results), max(results))) + graphic = GraphicContainer(results, study_mesh.min, study_mesh.max, l_par) + graphic.legend_parameters.title = 'kWh/m2' if not irradiance_ else 'W/m2' title = text_objects( - 'Incident Radiation', graphic.lower_title_location, + study_name, graphic.lower_title_location, graphic.legend_parameters.text_height * 1.5, graphic.legend_parameters.font) diff --git a/ladybug_grasshopper/src/LB Radiation Dome.py b/ladybug_grasshopper/src/LB Radiation Dome.py new file mode 100644 index 00000000..07bec957 --- /dev/null +++ b/ladybug_grasshopper/src/LB Radiation Dome.py @@ -0,0 +1,207 @@ +# Ladybug: A Plugin for Environmental Analysis (GPL) +# This file is part of Ladybug. +# +# Copyright (c) 2022, Ladybug Tools. +# You should have received a copy of the GNU Affero General Public License +# along with Ladybug; If not, see . +# +# @license AGPL-3.0-or-later + +""" +Visualize the radiation falling on an object from different directions over a dome. +_ +The Radiation Dome depicts the amount of solar energy received by all directions +over a dome. This is useful for understanding the optimal orientation of solar +panels and how the performance of the panel might change if it's orientation +is off from the optimal position. It can also be used to identify the optimal +wall orientation for passive solar heating when used with skies of +radiation harm/benefit. When used with clear sky matrices, it can identify +the orientations that result in the highest and lowest peak cooling load. +_ +The Radiation Dome can be understood in different ways: +1) It's a 3D representation of the "LB Radiation Rose," depicting all tilt angles + for that rose at once. +2) It's the reciprocal of the "LB Sky Dome," since it shows how the radiation from + that sky falls onto a hemispherical object. +3) It's an radiation study of a hemisphere. The results here are effectively the + same as running a hemisphere through the "LB Incident Radiation" component. +- + + Args: + _sky_mtx: A Sky Matrix from the "LB Cumulative Sky Matrix" component or the + "LB Benefit Sky Matrix" component, which describes the radiation + coming from the various patches of the sky. + context_: Rhino Breps and/or Rhino Meshes representing context geometry + that can block solar radiation to the center of the radiation dome. + _az_count_: An integer greater than or equal to 3, which notes the number of + horizontal orientations to be evaluated on the dome. (Default: 72). + _alt_count_: An integer greater than or equal to 3, which notes the number of + vertical orientations to be evaluated on the dome. (Default: 18). + _center_pt_: A point for the center of the radiation dome. (Default: (0, 0, 0)) + _scale_: A number to set the scale of the Radiation Dome. The default is 1, + which corresponds to a radius of 100 meters in the current Rhino + model's unit system. + projection_: Optional text for the name of a projection to use from the sky + dome hemisphere to the 2D plane. If None, a 3D sky dome will be drawn + instead of a 2D one. (Default: None) Choose from the following: + * Orthographic + * Stereographic + irradiance_: Boolean to note whether the radiation dome should be plotted with units + of cumulative Radiation (kWh/m2) [False] or with units of average + Irradiance (W/m2) [True]. (Default: False). + show_comp_: Boolean to indicate whether only one dome with total radiation + should be displayed (False) or three domes with the solar radiation + components (total, direct, and diffuse) should be shown. (Default: False). + legend_par_: An optional LegendParameter object to change the display of the + radiation dome (Default: None). + + Returns: + report: ... + mesh: A colored mesh of a dome, representing the intensity of radiation/irradiance + from different cardinal directions. + compass: A set of circles, lines and text objects that mark the cardinal + directions in relation to the radiation dome. + legend: A legend showing the kWh/m2 or W/m2 values that correspond to the colors + of the mesh. + title: A text object for the title of the radiation dome. + dir_vecs: A list of vectors for each of the directions the dome is facing. + All vectors are unit vectors. + dir_values: Radiation values for each of the dome directions in kWh/m2 or W/m2. + This will be one list if show_comp_ is "False" and a list of 3 + lists (aka. a Data Tree) for total, direct, diffuse if show_comp_ + is "True". + max_pt: A point on the radiation dome with the greatest amount of solar + radiation/irradiance. For a radiation benefit sky, this is the + orientation with the greatest benefit. This can be used to + understand the optimalorientation of solar panels or the best + direction to face for passive solar heating. + max_info: Information about the direction with the greates amount of radiation. + This includes the altitude, azimuth, and radiation/irradiance value. +""" + +ghenv.Component.Name = 'LB Radiation Dome' +ghenv.Component.NickName = 'RadiationDome' +ghenv.Component.Message = '1.5.0' +ghenv.Component.Category = 'Ladybug' +ghenv.Component.SubCategory = '2 :: Visualize Data' +ghenv.Component.AdditionalHelpFromDocStrings = '4' + +try: + from ladybug_geometry.geometry3d.pointvector import Point3D +except ImportError as e: + raise ImportError('\nFailed to import ladybug_geometry:\n\t{}'.format(e)) + +try: + from ladybug.viewsphere import view_sphere +except ImportError as e: + raise ImportError('\nFailed to import ladybug:\n\t{}'.format(e)) + +try: + from ladybug_radiance.visualize.raddome import RadiationDome +except ImportError as e: + raise ImportError('\nFailed to import ladybug_radiance:\n\t{}'.format(e)) + +try: + from ladybug_rhino.config import conversion_to_meters + from ladybug_rhino.togeometry import to_point3d + from ladybug_rhino.fromgeometry import from_point3d, \ + from_vector3d, from_mesh3d + from ladybug_rhino.intersect import join_geometry_to_mesh, intersect_mesh_rays + from ladybug_rhino.fromobjects import legend_objects, compass_objects + from ladybug_rhino.text import text_objects + from ladybug_rhino.grasshopper import all_required_inputs, \ + de_objectify_output, list_to_data_tree +except ImportError as e: + raise ImportError('\nFailed to import ladybug_rhino:\n\t{}'.format(e)) + + +def translate_dome(lb_mesh, lb_compass, graphic, title_txt): + """Translate radiation dome geometry into a format suitable for Rhino. + + Args: + lb_mesh: A ladybug Mesh3D for the dome. + lb_compass: A ladybug Compass object. + graphic: A GraphicContainer for the dome. + title_txt: Text for title of the dome. + + Returns: + dome_mesh: A Rhino colored mesh for the dome. + dome_compass: Rhino objects for the dome compass. + dome_legend: Rhino objects for the dome legend. + dome_title: A bake-able title for the dome. + """ + # translate the dome visualization, including legend and compass + dome_mesh = from_mesh3d(lb_mesh) + dome_legend = legend_objects(graphic.legend) + dome_angles = list(range(0, 360, int(360 / _az_count_))) + start, stop, step, dome_angles = 0, 360, 360 / _az_count_, [] + while start < stop: + dome_angles.append(start) + start += step + dome_angles = [int(n) for n in dome_angles] + if len(dome_angles) > 36: + dome_angles = dome_angles[::2] + dome_compass = compass_objects( + lb_compass, z, dome_angles, projection_, graphic.legend_parameters.font) + + # construct a title from the metadata + dome_title = text_objects(title_txt, graphic.lower_title_location, + graphic.legend_parameters.text_height, + graphic.legend_parameters.font) + + return dome_mesh, dome_compass, dome_legend, dome_title + + +if all_required_inputs(ghenv.Component): + # set defaults for global variables + _az_count_ = 72 if _az_count_ is None else _az_count_ + _alt_count_ = 18 if _alt_count_ is None else _alt_count_ + _scale_ = 1 if _scale_ is None else _scale_ + radius = (100 * _scale_) / conversion_to_meters() + if _center_pt_ is not None: # process the center point + center_pt3d = to_point3d(_center_pt_) + z = center_pt3d.z + else: + center_pt3d, z = Point3D(), 0 + + # compute the intersection matrix if context is specified + n_vecs = RadiationDome.dome_vectors(_az_count_, _alt_count_) + dir_vecs = [from_vector3d(vec) for vec in n_vecs] + int_mtx = None + if len(context_) != 0 and context_[0] is not None: + shade_mesh = join_geometry_to_mesh(context_) + p_vecs = view_sphere.tregenza_sphere_vectors if len(_sky_mtx.data[1]) == 145 \ + else view_sphere.reinhart_sphere_vectors + patch_dirs = [from_vector3d(vec) for vec in p_vecs] + int_mtx, angles = intersect_mesh_rays( + shade_mesh, [from_point3d(center_pt3d)] * len(dir_vecs), + patch_dirs, dir_vecs) + + # create the RadiationRose object + rad_dome = RadiationDome( + _sky_mtx, int_mtx, _az_count_, _alt_count_, legend_par_, + irradiance_, center_pt3d, radius, projection_) + + # create the dome visualization + if not show_comp_: # only create the total dome mesh + mesh, compass, legend, title = translate_dome(*rad_dome.draw()) + dir_values = rad_dome.total_values + else: # create domes for total, direct and diffuse + # loop through the 3 radiation types and produce a dome + mesh, compass, legend, title = [], [], [], [] + rad_types = ('total', 'direct', 'diffuse') + for dome_i in range(3): + cent_pt = Point3D(center_pt3d.x + radius * 3 * dome_i, + center_pt3d.y, center_pt3d.z) + dome_mesh, dome_compass, dome_legend, dome_title = \ + translate_dome(*rad_dome.draw(rad_types[dome_i], cent_pt)) + mesh.append(dome_mesh) + compass.extend(dome_compass) + legend.extend(dome_legend) + title.append(dome_title) + dir_values = list_to_data_tree( + (rad_dome.total_values, rad_dome.direct_values, rad_dome.diffuse_values)) + + # output infomration about the maximum direction + max_pt = from_point3d(rad_dome.max_point) + max_info = rad_dome.max_info diff --git a/ladybug_grasshopper/src/LB Radiation Rose.py b/ladybug_grasshopper/src/LB Radiation Rose.py new file mode 100644 index 00000000..98b61625 --- /dev/null +++ b/ladybug_grasshopper/src/LB Radiation Rose.py @@ -0,0 +1,200 @@ +# Ladybug: A Plugin for Environmental Analysis (GPL) +# This file is part of Ladybug. +# +# Copyright (c) 2022, Ladybug Tools. +# You should have received a copy of the GNU Affero General Public License +# along with Ladybug; If not, see . +# +# @license AGPL-3.0-or-later + +""" +Visualize the solar energy falling on different direction as a rose. +_ +By default, the Radiation Rose depicts the amount of solar energy received +by a vertical wall facing each of the directions of the compass rose. +_ +This is useful for understanding the radiation harm/benefit experienced by +different building orientations or the orientations with the highest peak cooling +load (for sky matrices of clear skies). The tilt_angle can be used to assess the +solar energy falling on geometries that are not perfectly vertical, such +as tilted photovoltaic panels. +- + + Args: + _sky_mtx: A Sky Matrix from the "LB Cumulative Sky Matrix" component or the + "LB Benefit Sky Matrix" component, which describes the radiation + coming from the various patches of the sky. + context_: Rhino Breps and/or Rhino Meshes representing context geometry + that can block solar radiation to the center of the radiation rose. + _dir_count_: An integer greater than or equal to 3, which notes the number of + arrows to be generated for the radiation rose. (Default: 36). + _tilt_angle_: A number between 0 and 90 that sets the vertical tilt angle (aka. + the altitude) for all of the directions. By default, the Radiation + Rose depicts the amount of solar energy received by a vertical + wall (tilt_angle=0). The tilt_angle can be changed to a specific + value to assess the solar energy falling on geometries that are not + perfectly vertical, such as a tilted photovoltaic panel. (Default: 0). + _center_pt_: A point for the center of the radiation rose. (Default: (0, 0, 0)) + _scale_: A number to set the scale of the Radiation Rose. The default is 1, + which corresponds to a radius of 100 meters in the current Rhino + model's unit system. + _arrow_scale_: A fractional number to note the scale of the radiation rose arrows + in relation to the entire graphic. (Default: 1). + max_rad_: An optional number to set the level of radiation or irradiance associated + with the full radius of the rose. If unspecified, this is determined + by the maximum level of radiation in the input data but a number + can be specified here to fix this at a specific value. This is + particularly useful when comparing different roses to one another. + irradiance_: Boolean to note whether the radiation rose should be plotted with units + of cumulative Radiation (kWh/m2) [False] or with units of average + Irradiance (W/m2) [True]. (Default: False). + show_comp_: Boolean to indicate whether only one rose with total radiation + should be displayed (False) or three roses with the solar radiation + components (total, direct, and diffuse) should be shown. (Default: False). + legend_par_: An optional LegendParameter object to change the display of the + Radiation Rose. + + Returns: + report: ... + mesh: A colored mesh of arrows, representing the intensity of radiation + from different cardinal directions. + compass: A set of circles, lines and text objects that mark the cardinal + directions in relation to the radiation rose. + orient_lines: A list of line segments marking the orientation of each arrow. + legend: A legend showing the kWh/m2 or W/m2 values that correspond to the colors + of the mesh. + title: A text object for the title of the radiation rose. + dir_vecs: A list of vectors for each of the directions the rose is facing. + All vectors are unit vectors. + dir_values: Radiation values for each of the rose directions in kWh/m2 or W/m2. + This will be one list if show_comp_ is "False" and a list of 3 + lists (aka. a Data Tree) for total, direct, diffuse if show_comp_ + is "True". +""" + +ghenv.Component.Name = 'LB Radiation Rose' +ghenv.Component.NickName = 'RadRose' +ghenv.Component.Message = '1.5.0' +ghenv.Component.Category = 'Ladybug' +ghenv.Component.SubCategory = '2 :: Visualize Data' +ghenv.Component.AdditionalHelpFromDocStrings = '4' + +try: + from ladybug_geometry.geometry3d.pointvector import Point3D +except ImportError as e: + raise ImportError('\nFailed to import ladybug_geometry:\n\t{}'.format(e)) + +try: + from ladybug.viewsphere import view_sphere +except ImportError as e: + raise ImportError('\nFailed to import ladybug:\n\t{}'.format(e)) + +try: + from ladybug_radiance.visualize.radrose import RadiationRose +except ImportError as e: + raise ImportError('\nFailed to import ladybug_radiance:\n\t{}'.format(e)) + +try: + from ladybug_rhino.config import conversion_to_meters + from ladybug_rhino.togeometry import to_point3d + from ladybug_rhino.fromgeometry import from_point3d, from_vector3d, \ + from_linesegment3d, from_mesh3d + from ladybug_rhino.intersect import join_geometry_to_mesh, intersect_mesh_rays + from ladybug_rhino.fromobjects import legend_objects, compass_objects + from ladybug_rhino.text import text_objects + from ladybug_rhino.grasshopper import all_required_inputs, \ + de_objectify_output, list_to_data_tree +except ImportError as e: + raise ImportError('\nFailed to import ladybug_rhino:\n\t{}'.format(e)) + + +def translate_rose(lb_mesh, lb_orient_lines, lb_compass, graphic, title_txt): + """Translate radiation rose geometry into a format suitable for Rhino. + + Args: + lb_mesh: A ladybug Mesh3D for the rose. + lb_compass: A ladybug Compass object. + graphic: A GraphicContainer for the rose. + title_txt: Text for title of the rose. + + Returns: + rose_mesh: A Rhino colored mesh for the rose. + rose_compass: Rhino objects for the rose compass. + rose_legend: Rhino objects for the rose legend. + rose_title: A bake-able title for the rose. + """ + # translate the rose visualization, including legend and compass + rose_mesh = from_mesh3d(lb_mesh) + rose_legend = legend_objects(graphic.legend) + rose_angles = list(range(0, 360, int(360 / _dir_count_))) + start, stop, step, rose_angles = 0, 360, 360 / _dir_count_, [] + while start < stop: + rose_angles.append(start) + start += step + rose_angles = [int(n) for n in rose_angles] + if len(rose_angles) > 36: + rose_angles = rose_angles[::2] + rose_compass = compass_objects( + lb_compass, z, rose_angles, None, graphic.legend_parameters.font) + orient_lines = [from_linesegment3d(l) for l in lb_orient_lines] + + # construct a title from the metadata + rose_title = text_objects(title_txt, graphic.lower_title_location, + graphic.legend_parameters.text_height, + graphic.legend_parameters.font) + + return rose_mesh, orient_lines, rose_compass, rose_legend, rose_title + + +if all_required_inputs(ghenv.Component): + # set defaults for global variables + _dir_count_ = 36 if _dir_count_ is None else _dir_count_ + _tilt_angle_ = 0 if _tilt_angle_ is None else _tilt_angle_ + _arrow_scale_ = 1 if _arrow_scale_ is None else _arrow_scale_ + _scale_ = 1 if _scale_ is None else _scale_ + radius = (100 * _scale_) / conversion_to_meters() + if _center_pt_ is not None: # process the center point + center_pt3d = to_point3d(_center_pt_) + z = center_pt3d.z + else: + center_pt3d, z = Point3D(), 0 + + # compute the intersection matrix if context is specified + n_vecs = RadiationRose.radial_vectors(_dir_count_, _tilt_angle_) + dir_vecs = [from_vector3d(vec) for vec in n_vecs] + int_mtx = None + if len(context_) != 0 and context_[0] is not None: + shade_mesh = join_geometry_to_mesh(context_) + p_vecs = view_sphere.tregenza_sphere_vectors if len(_sky_mtx.data[1]) == 145 \ + else view_sphere.reinhart_sphere_vectors + patch_dirs = [from_vector3d(vec) for vec in p_vecs] + int_mtx, angles = intersect_mesh_rays( + shade_mesh, [from_point3d(center_pt3d)] * _dir_count_, + patch_dirs, dir_vecs, 1) + + # create the RadiationRose object + rad_rose = RadiationRose( + _sky_mtx, int_mtx, _dir_count_, _tilt_angle_, legend_par_, + irradiance_, center_pt3d, radius, _arrow_scale_) + + # create the rose visualization + if not show_comp_: # only create the total rose mesh + mesh, orient_lines, compass, legend, title = \ + translate_rose(*rad_rose.draw(max_rad=max_rad_)) + dir_values = rad_rose.total_values + else: # create roses for total, direct and diffuse + # loop through the 3 radiation types and produce a rose + mesh, orient_lines, compass, legend, title = [], [], [], [], [] + rad_types = ('total', 'direct', 'diffuse') + for rose_i in range(3): + cent_pt = Point3D(center_pt3d.x + radius * 3 * rose_i, + center_pt3d.y, center_pt3d.z) + rose_mesh, rose_lines, rose_compass, rose_legend, rose_title = \ + translate_rose(*rad_rose.draw(rad_types[rose_i], cent_pt, max_rad_)) + mesh.append(rose_mesh) + compass.extend(rose_compass) + orient_lines.extend(rose_lines) + legend.extend(rose_legend) + title.append(rose_title) + dir_values = list_to_data_tree( + (rad_rose.total_values, rad_rose.direct_values, rad_rose.diffuse_values)) diff --git a/ladybug_grasshopper/src/LB Sky Dome.py b/ladybug_grasshopper/src/LB Sky Dome.py index 7a36b8ba..0b243671 100644 --- a/ladybug_grasshopper/src/LB Sky Dome.py +++ b/ladybug_grasshopper/src/LB Sky Dome.py @@ -13,8 +13,9 @@ - Args: - _sky_mtx: A Sky Matrix from the "LB Cumulative Sky Matrix" component, which - describes the radiation coming from the various patches of the sky. + _sky_mtx: A Sky Matrix from the "LB Cumulative Sky Matrix" component or the + "LB Benefit Sky Matrix" component, which describes the radiation + coming from the various patches of the sky. _center_pt_: A point for the center of the dome. (Default: (0, 0, 0)) _scale_: A number to set the scale of the sky dome. The default is 1, which corresponds to a radius of 100 meters in the current Rhino @@ -24,6 +25,9 @@ instead of a 2D one. (Default: None) Choose from the following: * Orthographic * Stereographic + irradiance_: Boolean to note whether the sky dome should be plotted with units of + cumulative Radiation (kWh/m2) [False] or with units of average + Irradiance (W/m2) [True]. (Default: False). show_comp_: Boolean to indicate whether only one dome with total radiation should be displayed (False) or three domes with the solar radiation components (total, direct, and diffuse) should be shown. (Default: False). @@ -36,16 +40,17 @@ the sky patches within the sky dome. compass: A set of circles, lines and text objects that mark the cardinal directions in relation to the sky dome. - legend: A legend showing the kWh/m2 values that correspond to the colors + legend: A legend showing the kWh/m2 or W/m2 values that correspond to the colors of the mesh. - title: A text object for the title of the sunpath. + title: A text object for the title of the sky dome. patch_vecs: A list of vectors for each of the patches of the sky dome. All vectors are unit vectors and point from the center towards each of the patches. They can be used to construct visualizations of the rays used to perform radiation analysis. - patch_values: Radiation values for each of the sky patches in kWh/m2. This - will be one list if show_comp_ is "False" and a list of 3 lists (aka. - a Data Tree) for total, direct, diffuse if show_comp_ is "True". + patch_values: Radiation values for each of the sky patches in kWh/m2 or W/m2. + This will be one list if show_comp_ is "False" and a list of 3 + lists (aka. a Data Tree) for total, direct, diffuse if show_comp_ + is "True". mesh_values: Radiation values for each face of the dome mesh in kWh/m2. This can be used to post-process the radiation data and then regenerate the dome visualization using the mesh output from this component @@ -60,27 +65,20 @@ ghenv.Component.Name = 'LB Sky Dome' ghenv.Component.NickName = 'SkyDome' -ghenv.Component.Message = '1.5.0' +ghenv.Component.Message = '1.5.1' ghenv.Component.Category = 'Ladybug' ghenv.Component.SubCategory = '2 :: Visualize Data' ghenv.Component.AdditionalHelpFromDocStrings = '3' -import math - try: - from ladybug_geometry.geometry2d.pointvector import Point2D - from ladybug_geometry.geometry3d.pointvector import Point3D, Vector3D - from ladybug_geometry.geometry3d.mesh import Mesh3D + from ladybug_geometry.geometry3d.pointvector import Point3D except ImportError as e: raise ImportError('\nFailed to import ladybug_geometry:\n\t{}'.format(e)) try: - from ladybug.viewsphere import view_sphere - from ladybug.compass import Compass - from ladybug.graphic import GraphicContainer - from ladybug.legend import LegendParameters + from ladybug_radiance.visualize.skydome import SkyDome except ImportError as e: - raise ImportError('\nFailed to import ladybug:\n\t{}'.format(e)) + raise ImportError('\nFailed to import ladybug_radiance:\n\t{}'.format(e)) try: from ladybug_rhino.config import conversion_to_meters @@ -94,76 +92,30 @@ raise ImportError('\nFailed to import ladybug_rhino:\n\t{}'.format(e)) -def draw_dome(dome_data, center, dome_name, legend_par): - """Draw the dome mesh, compass, legend, and title for a sky dome. +def translate_dome(lb_mesh, lb_compass, graphic, title_txt, values): + """Translate sky dome geometry into a format suitable for Rhino. Args: - dome_data: List of radiation values for the dome data - center: Point3D for the center of the sun path. - dome_name: Text for the dome name, which will appear in the title. - legend_par: Legend parameters to be used for the dome + lb_mesh: A ladybug Mesh3D for the dome. + lb_compass: A ladybug Compass object. + graphic: A GraphicContainer for the dome. + title_txt: Text for title of the dome. + values: A list of radiation values that align with the dome_mesh faces. Returns: - dome_mesh: A colored mesh for the dome based on dome_data. - dome_compass: A compass for the dome. - dome_legend: A leend for the colored dome mesh. - dome_title: A title for the dome. + dome_mesh: A Rhino colored mesh for the dome. + dome_compass: Rhino objects for the dome compass. + dome_legend: Rhino objects for the dome legend. + dome_title: A bake-able title for the dome. values: A list of radiation values that align with the dome_mesh faces. """ - # create the dome mesh and ensure patch values align with mesh faces - if len(dome_data) == 145: # tregenza sky - lb_mesh = view_sphere.tregenza_dome_mesh_high_res.scale(radius) - values = [] # high res dome has 3 x 3 faces per patch; we must convert - tot_i = 0 # track the total number of patches converted - for patch_i in view_sphere.TREGENZA_PATCHES_PER_ROW: - row_vals = [] - for val in dome_data[tot_i:tot_i + patch_i]: - row_vals.extend([val] * 3) - for i in range(3): - values.extend(row_vals) - tot_i += patch_i - values = values + [dome_data[-1]] * 18 # last patch has triangular faces - else: #reinhart sky - lb_mesh = view_sphere.reinhart_dome_mesh.scale(radius) - values = dome_data + [dome_data[-1]] * 11 # last patch has triangular faces - - # move and/or rotate the mesh as needed - if north != 0: - lb_mesh = lb_mesh.rotate_xy(math.radians(north), Point3D()) - if center != Point3D(): - lb_mesh = lb_mesh.move(Vector3D(center.x, center.y, center.z)) - - # project the mesh if requested - if projection_ is not None: - if projection_.title() == 'Orthographic': - pts = (Compass.point3d_to_orthographic(pt) for pt in lb_mesh.vertices) - elif projection_.title() == 'Stereographic': - pts = (Compass.point3d_to_stereographic(pt, radius, center) - for pt in lb_mesh.vertices) - else: - raise ValueError( - 'Projection type "{}" is not recognized.'.format(projection_)) - pts3d = tuple(Point3D(pt.x, pt.y, z) for pt in pts) - lb_mesh = Mesh3D(pts3d, lb_mesh.faces) - - # output the dome visualization, including legend and compass - move_fac = radius * 0.15 - min_pt = lb_mesh.min.move(Vector3D(-move_fac, -move_fac, 0)) - max_pt = lb_mesh.max.move(Vector3D(move_fac, move_fac, 0)) - graphic = GraphicContainer(values, min_pt, max_pt, legend_par) - graphic.legend_parameters.title = 'kWh/m2' - lb_mesh.colors = graphic.value_colors + # translate the dome visualization, including legend and compass dome_mesh = from_mesh3d(lb_mesh) dome_legend = legend_objects(graphic.legend) dome_compass = compass_objects( - Compass(radius, Point2D(center.x, center.y), north), z, None, projection_, - graphic.legend_parameters.font) + lb_compass, z, None, sky_dome.projection, graphic.legend_parameters.font) # construct a title from the metadata - st, end = metadata[2], metadata[3] - time_str = '{} - {}'.format(st, end) if st != end else st - title_txt = '{} Radiation\n{}\n{}'.format( - dome_name, time_str, '\n'.join([dat for dat in metadata[4:]])) dome_title = text_objects(title_txt, graphic.lower_title_location, graphic.legend_parameters.text_height, graphic.legend_parameters.font) @@ -175,49 +127,37 @@ def draw_dome(dome_data, center, dome_name, legend_par): # set defaults for global variables _scale_ = 1 if _scale_ is None else _scale_ radius = (100 * _scale_) / conversion_to_meters() - if _center_pt_ is not None: # process the center point into a Point2D + if _center_pt_ is not None: # process the center point center_pt3d = to_point3d(_center_pt_) z = center_pt3d.z else: center_pt3d, z = Point3D(), 0 - # deconstruct the sky matrix and derive key data from it - metadata, direct, diffuse = de_objectify_output(_sky_mtx) - north = metadata[0] # first item is the north angle - sky_type = 1 if len(direct) == 145 else 2 # i for tregenza; 2 for reinhart - total = [dirr + difr for dirr, difr in zip(direct, diffuse)] # total radiation - - # override the legend default min and max to make sense for domes - l_par = legend_par_.duplicate() if legend_par_ is not None else LegendParameters() - if l_par.min is None: - l_par.min = 0 - if l_par.max is None: - l_par.max = max(total) + # create the SkyDome object + sky_dome = SkyDome(_sky_mtx, legend_par_, irradiance_, + center_pt3d, radius, projection_) # output patch patch vectors - patch_vecs_lb = view_sphere.tregenza_dome_vectors if len(total) == 145 \ - else view_sphere.reinhart_dome_vectors - patch_vecs = [from_vector3d(vec) for vec in patch_vecs_lb] + patch_vecs = [from_vector3d(vec) for vec in sky_dome.patch_vectors] - # create the dome meshes + # create the dome visualization if not show_comp_: # only create the total dome mesh - mesh, compass, legend, title, mesh_values = \ - draw_dome(total, center_pt3d, 'Total', l_par) - patch_values = total + mesh, compass, legend, title, mesh_values = translate_dome(*sky_dome.draw()) + patch_values = sky_dome.total_values else: # create domes for total, direct and diffuse # loop through the 3 radiation types and produce a dome mesh, compass, legend, title, mesh_values = [], [], [], [], [] - rad_types = ('Total', 'Direct', 'Diffuse') - rad_data = (total, direct, diffuse) + rad_types = ('total', 'direct', 'diffuse') for dome_i in range(3): cent_pt = Point3D(center_pt3d.x + radius * 3 * dome_i, center_pt3d.y, center_pt3d.z) dome_mesh, dome_compass, dome_legend, dome_title, dome_values = \ - draw_dome(rad_data[dome_i], cent_pt, rad_types[dome_i], l_par) + translate_dome(*sky_dome.draw(rad_types[dome_i], cent_pt)) mesh.append(dome_mesh) compass.extend(dome_compass) legend.extend(dome_legend) title.append(dome_title) mesh_values.append(dome_values) + rad_data = (sky_dome.total_values, sky_dome.direct_values, sky_dome.diffuse_values) patch_values = list_to_data_tree(rad_data) mesh_values = list_to_data_tree(mesh_values) diff --git a/ladybug_grasshopper/src/LB SunPath.py b/ladybug_grasshopper/src/LB SunPath.py index 7fd2f3c6..ec33bcb8 100644 --- a/ladybug_grasshopper/src/LB SunPath.py +++ b/ladybug_grasshopper/src/LB SunPath.py @@ -98,10 +98,10 @@ ghenv.Component.Name = 'LB SunPath' ghenv.Component.NickName = 'Sunpath' -ghenv.Component.Message = '1.5.1' +ghenv.Component.Message = '1.5.2' ghenv.Component.Category = 'Ladybug' ghenv.Component.SubCategory = '2 :: Visualize Data' -ghenv.Component.AdditionalHelpFromDocStrings = '2' +ghenv.Component.AdditionalHelpFromDocStrings = '3' try: from ladybug_geometry.geometry2d.pointvector import Vector2D, Point2D diff --git a/ladybug_grasshopper/src/LB Wind Profile.py b/ladybug_grasshopper/src/LB Wind Profile.py index 0d4f04f6..133a7d3b 100644 --- a/ladybug_grasshopper/src/LB Wind Profile.py +++ b/ladybug_grasshopper/src/LB Wind Profile.py @@ -118,10 +118,10 @@ ghenv.Component.Name = 'LB Wind Profile' ghenv.Component.NickName = 'WindProfile' -ghenv.Component.Message = '1.5.1' +ghenv.Component.Message = '1.5.2' ghenv.Component.Category = 'Ladybug' ghenv.Component.SubCategory = '2 :: Visualize Data' -ghenv.Component.AdditionalHelpFromDocStrings = '4' +ghenv.Component.AdditionalHelpFromDocStrings = '5' import math diff --git a/ladybug_grasshopper/src/LB Wind Rose.py b/ladybug_grasshopper/src/LB Wind Rose.py index b773705f..9c216991 100644 --- a/ladybug_grasshopper/src/LB Wind Rose.py +++ b/ladybug_grasshopper/src/LB Wind Rose.py @@ -105,10 +105,10 @@ ghenv.Component.Name = 'LB Wind Rose' ghenv.Component.NickName = 'WindRose' -ghenv.Component.Message = '1.5.2' +ghenv.Component.Message = '1.5.3' ghenv.Component.Category = 'Ladybug' ghenv.Component.SubCategory = '2 :: Visualize Data' -ghenv.Component.AdditionalHelpFromDocStrings = '4' +ghenv.Component.AdditionalHelpFromDocStrings = '5' import math diff --git a/ladybug_grasshopper/user_objects/LB Benefit Sky Matrix.ghuser b/ladybug_grasshopper/user_objects/LB Benefit Sky Matrix.ghuser new file mode 100644 index 00000000..98d1874e Binary files /dev/null and b/ladybug_grasshopper/user_objects/LB Benefit Sky Matrix.ghuser differ diff --git a/ladybug_grasshopper/user_objects/LB Cumulative Sky Matrix.ghuser b/ladybug_grasshopper/user_objects/LB Cumulative Sky Matrix.ghuser index a9d16ee1..133d656f 100644 Binary files a/ladybug_grasshopper/user_objects/LB Cumulative Sky Matrix.ghuser and b/ladybug_grasshopper/user_objects/LB Cumulative Sky Matrix.ghuser differ diff --git a/ladybug_grasshopper/user_objects/LB Incident Radiation.ghuser b/ladybug_grasshopper/user_objects/LB Incident Radiation.ghuser index 139de710..9c76794c 100644 Binary files a/ladybug_grasshopper/user_objects/LB Incident Radiation.ghuser and b/ladybug_grasshopper/user_objects/LB Incident Radiation.ghuser differ diff --git a/ladybug_grasshopper/user_objects/LB Radiation Dome.ghuser b/ladybug_grasshopper/user_objects/LB Radiation Dome.ghuser new file mode 100644 index 00000000..479fe116 Binary files /dev/null and b/ladybug_grasshopper/user_objects/LB Radiation Dome.ghuser differ diff --git a/ladybug_grasshopper/user_objects/LB Radiation Rose.ghuser b/ladybug_grasshopper/user_objects/LB Radiation Rose.ghuser new file mode 100644 index 00000000..17e3c83e Binary files /dev/null and b/ladybug_grasshopper/user_objects/LB Radiation Rose.ghuser differ diff --git a/ladybug_grasshopper/user_objects/LB Sky Dome.ghuser b/ladybug_grasshopper/user_objects/LB Sky Dome.ghuser index 3057a2d1..e2f4f69f 100644 Binary files a/ladybug_grasshopper/user_objects/LB Sky Dome.ghuser and b/ladybug_grasshopper/user_objects/LB Sky Dome.ghuser differ diff --git a/ladybug_grasshopper/user_objects/LB SunPath.ghuser b/ladybug_grasshopper/user_objects/LB SunPath.ghuser index 12b78e2c..2b553cd7 100644 Binary files a/ladybug_grasshopper/user_objects/LB SunPath.ghuser and b/ladybug_grasshopper/user_objects/LB SunPath.ghuser differ diff --git a/ladybug_grasshopper/user_objects/LB Wind Profile.ghuser b/ladybug_grasshopper/user_objects/LB Wind Profile.ghuser index 315f89de..525dac64 100644 Binary files a/ladybug_grasshopper/user_objects/LB Wind Profile.ghuser and b/ladybug_grasshopper/user_objects/LB Wind Profile.ghuser differ diff --git a/ladybug_grasshopper/user_objects/LB Wind Rose.ghuser b/ladybug_grasshopper/user_objects/LB Wind Rose.ghuser index 98762c4c..74f1f05c 100644 Binary files a/ladybug_grasshopper/user_objects/LB Wind Rose.ghuser and b/ladybug_grasshopper/user_objects/LB Wind Rose.ghuser differ diff --git a/samples/direct_sun_study.gh b/samples/direct_sun_study.gh index 5db3fc55..892331c8 100644 Binary files a/samples/direct_sun_study.gh and b/samples/direct_sun_study.gh differ diff --git a/samples/psychrometric_chart.gh b/samples/psychrometric_chart.gh index 99963001..f90cb1d3 100644 Binary files a/samples/psychrometric_chart.gh and b/samples/psychrometric_chart.gh differ diff --git a/samples/radiation_benefit_on_sunpath.gh b/samples/radiation_benefit_on_sunpath.gh index 5aac85e8..f43809d3 100644 Binary files a/samples/radiation_benefit_on_sunpath.gh and b/samples/radiation_benefit_on_sunpath.gh differ diff --git a/samples/radiation_study.gh b/samples/radiation_study.gh index 5bb505c5..c422b3c9 100644 Binary files a/samples/radiation_study.gh and b/samples/radiation_study.gh differ diff --git a/samples/visualization_set.gh b/samples/visualization_set.gh index edb14333..735c4191 100644 Binary files a/samples/visualization_set.gh and b/samples/visualization_set.gh differ