honeybee-radiance 1.66.190__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of honeybee-radiance might be problematic. Click here for more details.
- honeybee_radiance/__init__.py +11 -0
- honeybee_radiance/__main__.py +4 -0
- honeybee_radiance/_extend_honeybee.py +93 -0
- honeybee_radiance/cli/__init__.py +88 -0
- honeybee_radiance/cli/dc.py +400 -0
- honeybee_radiance/cli/edit.py +529 -0
- honeybee_radiance/cli/glare.py +118 -0
- honeybee_radiance/cli/grid.py +859 -0
- honeybee_radiance/cli/lib.py +458 -0
- honeybee_radiance/cli/modifier.py +133 -0
- honeybee_radiance/cli/mtx.py +226 -0
- honeybee_radiance/cli/multiphase.py +1034 -0
- honeybee_radiance/cli/octree.py +640 -0
- honeybee_radiance/cli/postprocess.py +1186 -0
- honeybee_radiance/cli/raytrace.py +219 -0
- honeybee_radiance/cli/rpict.py +125 -0
- honeybee_radiance/cli/schedule.py +56 -0
- honeybee_radiance/cli/setconfig.py +63 -0
- honeybee_radiance/cli/sky.py +545 -0
- honeybee_radiance/cli/study.py +66 -0
- honeybee_radiance/cli/sunpath.py +331 -0
- honeybee_radiance/cli/threephase.py +255 -0
- honeybee_radiance/cli/translate.py +400 -0
- honeybee_radiance/cli/util.py +121 -0
- honeybee_radiance/cli/view.py +261 -0
- honeybee_radiance/cli/viewfactor.py +347 -0
- honeybee_radiance/config.json +6 -0
- honeybee_radiance/config.py +427 -0
- honeybee_radiance/dictutil.py +50 -0
- honeybee_radiance/dynamic/__init__.py +5 -0
- honeybee_radiance/dynamic/group.py +479 -0
- honeybee_radiance/dynamic/multiphase.py +557 -0
- honeybee_radiance/dynamic/state.py +718 -0
- honeybee_radiance/dynamic/stategeo.py +352 -0
- honeybee_radiance/geometry/__init__.py +13 -0
- honeybee_radiance/geometry/bubble.py +42 -0
- honeybee_radiance/geometry/cone.py +215 -0
- honeybee_radiance/geometry/cup.py +54 -0
- honeybee_radiance/geometry/cylinder.py +197 -0
- honeybee_radiance/geometry/geometrybase.py +37 -0
- honeybee_radiance/geometry/instance.py +40 -0
- honeybee_radiance/geometry/mesh.py +38 -0
- honeybee_radiance/geometry/polygon.py +174 -0
- honeybee_radiance/geometry/ring.py +214 -0
- honeybee_radiance/geometry/source.py +182 -0
- honeybee_radiance/geometry/sphere.py +178 -0
- honeybee_radiance/geometry/tube.py +46 -0
- honeybee_radiance/lib/__init__.py +1 -0
- honeybee_radiance/lib/_loadmodifiers.py +72 -0
- honeybee_radiance/lib/_loadmodifiersets.py +69 -0
- honeybee_radiance/lib/modifiers.py +58 -0
- honeybee_radiance/lib/modifiersets.py +63 -0
- honeybee_radiance/lightpath.py +204 -0
- honeybee_radiance/lightsource/__init__.py +1 -0
- honeybee_radiance/lightsource/_gendaylit.py +479 -0
- honeybee_radiance/lightsource/dictutil.py +49 -0
- honeybee_radiance/lightsource/ground.py +160 -0
- honeybee_radiance/lightsource/sky/__init__.py +7 -0
- honeybee_radiance/lightsource/sky/_skybase.py +177 -0
- honeybee_radiance/lightsource/sky/certainirradiance.py +232 -0
- honeybee_radiance/lightsource/sky/cie.py +378 -0
- honeybee_radiance/lightsource/sky/climatebased.py +501 -0
- honeybee_radiance/lightsource/sky/hemisphere.py +160 -0
- honeybee_radiance/lightsource/sky/skydome.py +113 -0
- honeybee_radiance/lightsource/sky/skymatrix.py +163 -0
- honeybee_radiance/lightsource/sky/strutil.py +34 -0
- honeybee_radiance/lightsource/sky/sunmatrix.py +212 -0
- honeybee_radiance/lightsource/sunpath.py +247 -0
- honeybee_radiance/modifier/__init__.py +3 -0
- honeybee_radiance/modifier/material/__init__.py +30 -0
- honeybee_radiance/modifier/material/absdf.py +477 -0
- honeybee_radiance/modifier/material/antimatter.py +54 -0
- honeybee_radiance/modifier/material/ashik2.py +51 -0
- honeybee_radiance/modifier/material/brtdfunc.py +81 -0
- honeybee_radiance/modifier/material/bsdf.py +292 -0
- honeybee_radiance/modifier/material/dielectric.py +53 -0
- honeybee_radiance/modifier/material/glass.py +431 -0
- honeybee_radiance/modifier/material/glow.py +246 -0
- honeybee_radiance/modifier/material/illum.py +51 -0
- honeybee_radiance/modifier/material/interface.py +49 -0
- honeybee_radiance/modifier/material/light.py +206 -0
- honeybee_radiance/modifier/material/materialbase.py +36 -0
- honeybee_radiance/modifier/material/metal.py +167 -0
- honeybee_radiance/modifier/material/metal2.py +41 -0
- honeybee_radiance/modifier/material/metdata.py +41 -0
- honeybee_radiance/modifier/material/metfunc.py +41 -0
- honeybee_radiance/modifier/material/mirror.py +340 -0
- honeybee_radiance/modifier/material/mist.py +86 -0
- honeybee_radiance/modifier/material/plasdata.py +58 -0
- honeybee_radiance/modifier/material/plasfunc.py +59 -0
- honeybee_radiance/modifier/material/plastic.py +354 -0
- honeybee_radiance/modifier/material/plastic2.py +58 -0
- honeybee_radiance/modifier/material/prism1.py +57 -0
- honeybee_radiance/modifier/material/prism2.py +48 -0
- honeybee_radiance/modifier/material/spotlight.py +50 -0
- honeybee_radiance/modifier/material/trans.py +518 -0
- honeybee_radiance/modifier/material/trans2.py +49 -0
- honeybee_radiance/modifier/material/transdata.py +50 -0
- honeybee_radiance/modifier/material/transfunc.py +53 -0
- honeybee_radiance/modifier/mixture/__init__.py +6 -0
- honeybee_radiance/modifier/mixture/mixdata.py +49 -0
- honeybee_radiance/modifier/mixture/mixfunc.py +54 -0
- honeybee_radiance/modifier/mixture/mixpict.py +52 -0
- honeybee_radiance/modifier/mixture/mixtext.py +66 -0
- honeybee_radiance/modifier/mixture/mixturebase.py +28 -0
- honeybee_radiance/modifier/modifierbase.py +40 -0
- honeybee_radiance/modifier/pattern/__init__.py +9 -0
- honeybee_radiance/modifier/pattern/brightdata.py +49 -0
- honeybee_radiance/modifier/pattern/brightfunc.py +47 -0
- honeybee_radiance/modifier/pattern/brighttext.py +81 -0
- honeybee_radiance/modifier/pattern/colordata.py +56 -0
- honeybee_radiance/modifier/pattern/colorfunc.py +47 -0
- honeybee_radiance/modifier/pattern/colorpict.py +54 -0
- honeybee_radiance/modifier/pattern/colortext.py +73 -0
- honeybee_radiance/modifier/pattern/patternbase.py +34 -0
- honeybee_radiance/modifier/texture/__init__.py +4 -0
- honeybee_radiance/modifier/texture/texdata.py +29 -0
- honeybee_radiance/modifier/texture/texfunc.py +26 -0
- honeybee_radiance/modifier/texture/texturebase.py +27 -0
- honeybee_radiance/modifierset.py +1091 -0
- honeybee_radiance/mutil.py +60 -0
- honeybee_radiance/postprocess/__init__.py +1 -0
- honeybee_radiance/postprocess/annual.py +108 -0
- honeybee_radiance/postprocess/annualdaylight.py +425 -0
- honeybee_radiance/postprocess/annualglare.py +201 -0
- honeybee_radiance/postprocess/annualirradiance.py +187 -0
- honeybee_radiance/postprocess/electriclight.py +119 -0
- honeybee_radiance/postprocess/en17037.py +261 -0
- honeybee_radiance/postprocess/leed.py +304 -0
- honeybee_radiance/postprocess/solartracking.py +90 -0
- honeybee_radiance/primitive.py +554 -0
- honeybee_radiance/properties/__init__.py +1 -0
- honeybee_radiance/properties/_base.py +390 -0
- honeybee_radiance/properties/aperture.py +197 -0
- honeybee_radiance/properties/door.py +198 -0
- honeybee_radiance/properties/face.py +123 -0
- honeybee_radiance/properties/model.py +1291 -0
- honeybee_radiance/properties/room.py +490 -0
- honeybee_radiance/properties/shade.py +186 -0
- honeybee_radiance/properties/shademesh.py +116 -0
- honeybee_radiance/putil.py +44 -0
- honeybee_radiance/reader.py +214 -0
- honeybee_radiance/sensor.py +166 -0
- honeybee_radiance/sensorgrid.py +1008 -0
- honeybee_radiance/view.py +1101 -0
- honeybee_radiance/writer.py +951 -0
- honeybee_radiance-1.66.190.dist-info/METADATA +89 -0
- honeybee_radiance-1.66.190.dist-info/RECORD +152 -0
- honeybee_radiance-1.66.190.dist-info/WHEEL +5 -0
- honeybee_radiance-1.66.190.dist-info/entry_points.txt +2 -0
- honeybee_radiance-1.66.190.dist-info/licenses/LICENSE +661 -0
- honeybee_radiance-1.66.190.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,859 @@
|
|
|
1
|
+
"""honeybee radiance grid commands."""
|
|
2
|
+
import click
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
import logging
|
|
6
|
+
import re
|
|
7
|
+
import json
|
|
8
|
+
import shutil
|
|
9
|
+
|
|
10
|
+
from ladybug_geometry.geometry3d import Vector3D, Face3D
|
|
11
|
+
from ladybug.futil import preparedir
|
|
12
|
+
from honeybee.model import Model
|
|
13
|
+
from honeybee.units import parse_distance_string
|
|
14
|
+
from honeybee.typing import clean_rad_string, clean_and_id_rad_string
|
|
15
|
+
|
|
16
|
+
from honeybee_radiance.sensorgrid import SensorGrid
|
|
17
|
+
from honeybee_radiance_folder.gridutil import redistribute_sensors, \
|
|
18
|
+
restore_original_distribution
|
|
19
|
+
|
|
20
|
+
_logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@click.group(help='Commands for generating and modifying sensor grids.')
|
|
24
|
+
def grid():
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@grid.command('split')
|
|
29
|
+
@click.argument('grid-file', type=click.Path(
|
|
30
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
31
|
+
@click.argument('count', type=int)
|
|
32
|
+
@click.option('--folder', help='Output folder.', default='.', show_default=True,
|
|
33
|
+
type=click.Path(file_okay=False, dir_okay=True, resolve_path=True))
|
|
34
|
+
@click.option('--log-file', help='Optional log file to output the name of the newly'
|
|
35
|
+
' created grids. By default the list will be printed out to stdout',
|
|
36
|
+
type=click.File('w'), default='-')
|
|
37
|
+
def split_grid(grid_file, count, folder, log_file):
|
|
38
|
+
"""Split a radiance grid file into smaller grids based on maximum sensor count.
|
|
39
|
+
|
|
40
|
+
\b
|
|
41
|
+
Args:
|
|
42
|
+
grid-file: Full path to input sensor grid file.
|
|
43
|
+
count: Maximum number of sensors in new files. The number will be rounded to
|
|
44
|
+
closest round number for each file. For example if the input file has 21
|
|
45
|
+
sensors and input count is set to 5 this command will generate 4 files where
|
|
46
|
+
the first three files will have 5 sensors and the last file will have 6.
|
|
47
|
+
"""
|
|
48
|
+
try:
|
|
49
|
+
grid = SensorGrid.from_file(grid_file)
|
|
50
|
+
file_count = max(1, int(round(grid.count / count)))
|
|
51
|
+
files = grid.to_files(folder, file_count, mkdir=True)
|
|
52
|
+
|
|
53
|
+
log_file.write(json.dumps(files))
|
|
54
|
+
except Exception:
|
|
55
|
+
_logger.exception('Failed to split grid file.')
|
|
56
|
+
sys.exit(1)
|
|
57
|
+
else:
|
|
58
|
+
sys.exit(0)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@grid.command('merge')
|
|
62
|
+
@click.argument('input-folder', type=click.Path(
|
|
63
|
+
file_okay=False, dir_okay=True, resolve_path=True))
|
|
64
|
+
@click.argument('base-name', type=str)
|
|
65
|
+
@click.argument('extension', default='.pts', type=str)
|
|
66
|
+
@click.option('--folder', help='Optional output folder.', default='.', show_default=True)
|
|
67
|
+
@click.option('--name', help='Optional output filename. Default is base-name.')
|
|
68
|
+
def merge_grid(input_folder, base_name, extension, folder, name):
|
|
69
|
+
"""Merge several radiance files into a single file.
|
|
70
|
+
|
|
71
|
+
This command removes headers from file if it exist.
|
|
72
|
+
|
|
73
|
+
\b
|
|
74
|
+
Args:
|
|
75
|
+
input_folder: Input folder.
|
|
76
|
+
base_name: File base name. All of the files must start with base name and
|
|
77
|
+
continue with _ and an integer values.
|
|
78
|
+
extension: File extension. [Default: .pts]
|
|
79
|
+
"""
|
|
80
|
+
try:
|
|
81
|
+
pattern = r'{}_\d+{}'.format(base_name, extension)
|
|
82
|
+
grids = sorted(f for f in os.listdir(input_folder) if re.match(pattern, f))
|
|
83
|
+
if len(grids) == 0:
|
|
84
|
+
raise ValueError('Found no file to merge.')
|
|
85
|
+
name = name or base_name
|
|
86
|
+
output_file = os.path.normpath(os.path.join(folder, name + extension))
|
|
87
|
+
# get the new dir name as grid name might be group/name
|
|
88
|
+
dirname = os.path.dirname(output_file)
|
|
89
|
+
if dirname and not os.path.exists(dirname):
|
|
90
|
+
os.makedirs(dirname)
|
|
91
|
+
|
|
92
|
+
with open(output_file, 'w') as outf:
|
|
93
|
+
for f in grids:
|
|
94
|
+
with open(os.path.join(input_folder, f)) as inf:
|
|
95
|
+
first_line = next(inf)
|
|
96
|
+
if first_line[:10] == '#?RADIANCE':
|
|
97
|
+
for line in inf:
|
|
98
|
+
if line[:7] == 'FORMAT=':
|
|
99
|
+
# pass next empty line
|
|
100
|
+
next(inf)
|
|
101
|
+
break
|
|
102
|
+
continue
|
|
103
|
+
else:
|
|
104
|
+
outf.write(first_line)
|
|
105
|
+
# add rest of the file to outfile
|
|
106
|
+
for line in inf:
|
|
107
|
+
outf.write(line)
|
|
108
|
+
except Exception:
|
|
109
|
+
_logger.exception('Failed to merge grid files.')
|
|
110
|
+
sys.exit(1)
|
|
111
|
+
else:
|
|
112
|
+
sys.exit(0)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@grid.command('split-folder')
|
|
116
|
+
@click.argument(
|
|
117
|
+
'input-folder',
|
|
118
|
+
type=click.Path(file_okay=False, dir_okay=True, resolve_path=True))
|
|
119
|
+
@click.argument(
|
|
120
|
+
'output-folder',
|
|
121
|
+
type=click.Path(file_okay=False, dir_okay=True, resolve_path=True))
|
|
122
|
+
@click.argument('grid-count', type=int)
|
|
123
|
+
@click.argument('extension', default='.pts', type=str)
|
|
124
|
+
@click.option(
|
|
125
|
+
'--grid-divisor', '-d', help='An optional integer to be divided by the grid-count '
|
|
126
|
+
'to yield a final number of grids to generate. This is useful in workflows where '
|
|
127
|
+
'the grid-count is being interpreted as a cpu-count but there are multiple '
|
|
128
|
+
'processors acting on a single grid. To ignore this limitation set the value '
|
|
129
|
+
'to 1. Default: 1.', type=int, default=1)
|
|
130
|
+
@click.option(
|
|
131
|
+
'--min-sensor-count', '-msc', help='Minimum number of sensors in each output grid. '
|
|
132
|
+
'Use this number to ensure the number of sensors in output grids never gets very '
|
|
133
|
+
'small. This input will override the input grid-count when specified. '
|
|
134
|
+
'To ignore this limitation, set the value to 1. Default: 1.', type=int,
|
|
135
|
+
default=1)
|
|
136
|
+
@click.option(
|
|
137
|
+
'--grid-info-file', help='Optional input JSON file containing information about '
|
|
138
|
+
'the sensor grids to be split. If unspecified, it will be assumed that this '
|
|
139
|
+
'JSON already exists in the input-folder with the name _info.json', default=None,
|
|
140
|
+
type=click.Path(file_okay=True, dir_okay=False, resolve_path=True))
|
|
141
|
+
def split_grid_folder(
|
|
142
|
+
input_folder, output_folder, grid_count, extension,
|
|
143
|
+
grid_divisor, min_sensor_count, grid_info_file
|
|
144
|
+
):
|
|
145
|
+
"""Create new sensor grids folder with evenly distribute sensors.
|
|
146
|
+
|
|
147
|
+
This function creates a new folder with evenly distributed sensor grids. The folder
|
|
148
|
+
will include a ``_dist_info.json`` file which has the information to recreate the
|
|
149
|
+
original input files from this folder and the results generated based on the grids
|
|
150
|
+
in this folder.
|
|
151
|
+
|
|
152
|
+
``_dist_info.json`` file includes an array of JSON objects. Each object has the
|
|
153
|
+
``id`` or the original file and the distribution information. The distribution
|
|
154
|
+
information includes the id of the new files that the sensors has been distributed
|
|
155
|
+
to and the start and end line in the target file.
|
|
156
|
+
|
|
157
|
+
This file is being used to restructure the data that is generated based on the newly
|
|
158
|
+
created sensor grids.
|
|
159
|
+
|
|
160
|
+
.. code-block:: python
|
|
161
|
+
|
|
162
|
+
[
|
|
163
|
+
{
|
|
164
|
+
"id": "room_1",
|
|
165
|
+
"dist_info": [
|
|
166
|
+
{"id": 0, "st_ln": 0, "end_ln": 175},
|
|
167
|
+
{"id": 1, "st_ln": 0, "end_ln": 21}
|
|
168
|
+
]
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"id": "room_2",
|
|
172
|
+
"dist_info": [
|
|
173
|
+
{"id": 1, "st_ln": 22, "end_ln": 135}
|
|
174
|
+
]
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
\b
|
|
179
|
+
Args:
|
|
180
|
+
input_folder: Input sensor grids folder.
|
|
181
|
+
output_folder: A new folder to write the newly created files.
|
|
182
|
+
grid_count: Number of output sensor grids to be created. This number
|
|
183
|
+
is usually equivalent to the number of processes that will be used to run
|
|
184
|
+
the simulations in parallel.
|
|
185
|
+
extension: Extension of the files to split. The default is ``pts`` for
|
|
186
|
+
sensor files. Another common extension is ``csv`` for data aligned with
|
|
187
|
+
the sensor grids.
|
|
188
|
+
"""
|
|
189
|
+
try:
|
|
190
|
+
if os.path.isdir(input_folder):
|
|
191
|
+
if grid_info_file is not None and os.path.isfile(grid_info_file):
|
|
192
|
+
info_file = os.path.join(input_folder, '_info.json')
|
|
193
|
+
shutil.copyfile(grid_info_file, info_file)
|
|
194
|
+
grid_count = int(grid_count / grid_divisor)
|
|
195
|
+
grid_count = 1 if grid_count < 1 else grid_count
|
|
196
|
+
redistribute_sensors(
|
|
197
|
+
input_folder, output_folder, grid_count, min_sensor_count,
|
|
198
|
+
extension=extension.replace('.', '')
|
|
199
|
+
)
|
|
200
|
+
except Exception:
|
|
201
|
+
_logger.exception('Failed to distribute sensor grids in folder.')
|
|
202
|
+
sys.exit(1)
|
|
203
|
+
else:
|
|
204
|
+
sys.exit(0)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@grid.command('merge-folder')
|
|
208
|
+
@click.argument(
|
|
209
|
+
'input-folder',
|
|
210
|
+
type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True))
|
|
211
|
+
@click.argument(
|
|
212
|
+
'output-folder',
|
|
213
|
+
type=click.Path(file_okay=False, dir_okay=True, resolve_path=True))
|
|
214
|
+
@click.argument('extension', type=str)
|
|
215
|
+
@click.option(
|
|
216
|
+
'--dist-info', '-di',
|
|
217
|
+
help='An optional input for distribution information to put the grids back together '
|
|
218
|
+
'. Alternatively, the command will look for a _redist_info.json file inside the '
|
|
219
|
+
'folder.', type=click.Path(file_okay=True, dir_okay=False, resolve_path=True)
|
|
220
|
+
)
|
|
221
|
+
def merge_grid_folder(input_folder, output_folder, extension, dist_info):
|
|
222
|
+
"""Restructure files in a distributed folder.
|
|
223
|
+
|
|
224
|
+
\b
|
|
225
|
+
Args:
|
|
226
|
+
input_folder: Path to input folder.
|
|
227
|
+
output_folder: Path to the new restructured folder
|
|
228
|
+
extension: Extension of the files to collect data from. It will be ``pts`` for
|
|
229
|
+
sensor files. Another common extension is ``ill`` for the results of daylight
|
|
230
|
+
studies.
|
|
231
|
+
"""
|
|
232
|
+
try:
|
|
233
|
+
# handle optional case for Functions input
|
|
234
|
+
if dist_info and not os.path.isfile(dist_info):
|
|
235
|
+
dist_info = None
|
|
236
|
+
restore_original_distribution(input_folder, output_folder, extension, dist_info)
|
|
237
|
+
except Exception:
|
|
238
|
+
_logger.exception('Failed to restructure data from folder.')
|
|
239
|
+
sys.exit(1)
|
|
240
|
+
else:
|
|
241
|
+
sys.exit(0)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@grid.command('mirror')
|
|
245
|
+
@click.argument('grid-file', type=click.Path(
|
|
246
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
247
|
+
@click.option(
|
|
248
|
+
'--vector', '-v', default=None, help='An optional list of three values '
|
|
249
|
+
'(separated by spaces) to standardize the direction of all rays in the output '
|
|
250
|
+
'files. For example, inputting "0 0 1" will ensure that the output sensor files '
|
|
251
|
+
'all have vectors pointing up in the base file and down in the mirrored file. If '
|
|
252
|
+
'unspecified, the direction of sensors in the input file will be used.'
|
|
253
|
+
)
|
|
254
|
+
@click.option(
|
|
255
|
+
'--name', '-n', default='grid', help='File name, which will be incorporated into '
|
|
256
|
+
'both the base grid and the mirrored grid.'
|
|
257
|
+
)
|
|
258
|
+
@click.option(
|
|
259
|
+
'--suffix', '-s', default='ref', show_default=True,
|
|
260
|
+
help='Text for the suffix to be applied to the mirrored grid file.',
|
|
261
|
+
)
|
|
262
|
+
@click.option(
|
|
263
|
+
'--folder', '-f', default='.', help='Output folder into which the base grid '
|
|
264
|
+
'and mirrored grid files will be written.'
|
|
265
|
+
)
|
|
266
|
+
@click.option(
|
|
267
|
+
'--log-file', '-log', help='Optional log file to output the list of generated '
|
|
268
|
+
'radiant enclosure JSONs. By default this will be printed to stdout.',
|
|
269
|
+
type=click.File('w'), default='-'
|
|
270
|
+
)
|
|
271
|
+
def mirror_grid(grid_file, vector, name, suffix, folder, log_file):
|
|
272
|
+
"""Mirror a honeybee Model's SensorGrids and format them for thermal mapping.
|
|
273
|
+
|
|
274
|
+
This involves setting the direction of every sensor to point up (0, 0, 1) and
|
|
275
|
+
then adding a mirrored sensor grid with the same sensor positions that all
|
|
276
|
+
point downward. In thermal mapping workflows, the upward-pointing grids can
|
|
277
|
+
be used to account for direct and diffuse shortwave irradiance while the
|
|
278
|
+
downward pointing grids account for ground-reflected shortwave irradiance.
|
|
279
|
+
|
|
280
|
+
\b
|
|
281
|
+
Args:
|
|
282
|
+
model_json: Full path to a Model JSON file.
|
|
283
|
+
"""
|
|
284
|
+
try:
|
|
285
|
+
# create the directory if it's not there and set up output paths
|
|
286
|
+
if not os.path.isdir(folder):
|
|
287
|
+
preparedir(folder)
|
|
288
|
+
base_file = os.path.join(folder, '{}.pts'.format(name))
|
|
289
|
+
rev_file = os.path.join(folder, '{}_{}.pts'.format(name, suffix))
|
|
290
|
+
|
|
291
|
+
# loop through the lines of the grid_file and mirror the sensors
|
|
292
|
+
if vector is not None and vector != '':
|
|
293
|
+
# process the vector if it exists
|
|
294
|
+
vec = [float(v) for v in vector.split()]
|
|
295
|
+
assert len(vec) == 3, \
|
|
296
|
+
'Vector "{}" must have 3 values. Got {}.'.format(vector, len(vec))
|
|
297
|
+
vec_str = ' {} {} {}\n'.format(*vec)
|
|
298
|
+
rev_vec = [-v for v in vec]
|
|
299
|
+
rev_vec_str = ' {} {} {}\n'.format(*rev_vec)
|
|
300
|
+
# get the lines from the grid file
|
|
301
|
+
with open(grid_file) as sg_file:
|
|
302
|
+
with open(base_file, 'w') as b_file, open(rev_file, 'w') as r_file:
|
|
303
|
+
for line in sg_file:
|
|
304
|
+
origin_str = ' '.join(line.split()[:3])
|
|
305
|
+
b_file.write(origin_str + vec_str)
|
|
306
|
+
r_file.write(origin_str + rev_vec_str)
|
|
307
|
+
else:
|
|
308
|
+
# loop through each sensor and reverse the vector
|
|
309
|
+
with open(grid_file) as sg_file:
|
|
310
|
+
with open(rev_file, 'w') as r_file:
|
|
311
|
+
for line in sg_file:
|
|
312
|
+
ray_vals = line.strip().split()
|
|
313
|
+
origin_str = ' '.join(ray_vals[:3])
|
|
314
|
+
vec_vals = (-float(v) for v in ray_vals[3:])
|
|
315
|
+
rev_vec_str = ' {} {} {}\n'.format(*vec_vals)
|
|
316
|
+
r_file.write(origin_str + rev_vec_str)
|
|
317
|
+
# copy the input grid file to the base file location
|
|
318
|
+
shutil.copyfile(grid_file, base_file)
|
|
319
|
+
|
|
320
|
+
# write the resulting file paths to the log file
|
|
321
|
+
log_file.write(json.dumps([base_file, rev_file], indent=4))
|
|
322
|
+
except Exception as e:
|
|
323
|
+
_logger.exception('Sensor grid mirroring failed.\n{}'.format(e))
|
|
324
|
+
sys.exit(1)
|
|
325
|
+
else:
|
|
326
|
+
sys.exit(0)
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
@grid.command('from-rooms')
|
|
330
|
+
@click.argument('model-file', type=click.Path(
|
|
331
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
332
|
+
@click.option('--grid-size', '-s', help='A number for the dimension of the mesh grid '
|
|
333
|
+
'cells. This can include the units of the distance (eg. 1ft) '
|
|
334
|
+
'or, if no units are provided, the value will be interpreted in the '
|
|
335
|
+
'honeybee model units.', type=str, default='0.5m', show_default=True)
|
|
336
|
+
@click.option('--offset', '-o', help='A number for the distance at which the '
|
|
337
|
+
'the sensor grid should be offset from the floor. This can include the '
|
|
338
|
+
'units of the distance (eg. 3ft) or, if no units are provided, the '
|
|
339
|
+
'value will be interpreted in the honeybee model units.',
|
|
340
|
+
type=str, default='0.8m', show_default=True)
|
|
341
|
+
@click.option('--include-mesh/--exclude-mesh', ' /-xm', help='Flag to note whether to '
|
|
342
|
+
'include a Mesh3D object that aligns with the grid positions under the '
|
|
343
|
+
'"mesh" property of each grid. Excluding the mesh can reduce size but '
|
|
344
|
+
'will mean Radiance results cannot be visualized as colored meshes.',
|
|
345
|
+
default=True, show_default=True)
|
|
346
|
+
@click.option('--keep-out/--remove-out', ' /-out', help='Flag to note whether an extra '
|
|
347
|
+
'check should be run to remove sensor points that lie outside the Room '
|
|
348
|
+
'volume. Note that this can add significantly to the runtime and this '
|
|
349
|
+
'check is not necessary in the case that all walls are vertical '
|
|
350
|
+
'and all floors are horizontal.', default=True, show_default=True)
|
|
351
|
+
@click.option('--wall-offset', '-w', help='A number for the distance at which sensors '
|
|
352
|
+
'close to walls should be removed. This can include the units of the '
|
|
353
|
+
'distance (eg. 3ft) or, if no units are provided, the value will be '
|
|
354
|
+
'interpreted in the honeybee model units. Note that this option has '
|
|
355
|
+
'no effect unless the value is more than half of the grid-size.',
|
|
356
|
+
type=str, default='0m', show_default=True)
|
|
357
|
+
@click.option('--room', '-r', multiple=True, help='Room identifier to specify the '
|
|
358
|
+
'room for which sensor grids should be generated. You can pass multiple '
|
|
359
|
+
'rooms (each preceded by -r). By default, all rooms get sensor grids.')
|
|
360
|
+
@click.option('--write-json/--write-pts', ' /-pts', help='Flag to note whether output '
|
|
361
|
+
'data collection should be in JSON format or the typical CSV-style format '
|
|
362
|
+
'of the Radiance .pts files.', default=True, show_default=True)
|
|
363
|
+
@click.option('--folder', help='Optional output folder. If specified, the --output-file '
|
|
364
|
+
'will be ignored and each sensor grid will be written into its own '
|
|
365
|
+
'.json or .pts file within the folder.', default=None,
|
|
366
|
+
type=click.Path(exists=True, file_okay=False,
|
|
367
|
+
dir_okay=True, resolve_path=True))
|
|
368
|
+
@click.option('--output-file', '-f', help='Optional file to output the JSON or CSV '
|
|
369
|
+
'string of the sensor grids. By default this will be printed '
|
|
370
|
+
'to stdout', type=click.File('w'), default='-', show_default=True)
|
|
371
|
+
def from_rooms(model_file, grid_size, offset, include_mesh, keep_out, wall_offset,
|
|
372
|
+
room, write_json, folder, output_file):
|
|
373
|
+
"""Generate SensorGrids from the Room floors of a honeybee model.
|
|
374
|
+
|
|
375
|
+
\b
|
|
376
|
+
Args:
|
|
377
|
+
model_file: Full path to a HBJSON or HBPkl Model file.
|
|
378
|
+
"""
|
|
379
|
+
try:
|
|
380
|
+
# re-serialize the Model and extract rooms and units
|
|
381
|
+
model = Model.from_file(model_file)
|
|
382
|
+
rooms = model.rooms if room is None or len(room) == 0 else \
|
|
383
|
+
[r for r in model.rooms if r.identifier in room]
|
|
384
|
+
grid_size = parse_distance_string(grid_size, model.units)
|
|
385
|
+
offset = parse_distance_string(offset, model.units)
|
|
386
|
+
wall_offset = parse_distance_string(wall_offset, model.units)
|
|
387
|
+
|
|
388
|
+
# loop through the rooms and generate sensor grids
|
|
389
|
+
sensor_grids = []
|
|
390
|
+
remove_out = not keep_out
|
|
391
|
+
for room in rooms:
|
|
392
|
+
sg = room.properties.radiance.generate_sensor_grid(
|
|
393
|
+
grid_size, offset=offset, remove_out=remove_out, wall_offset=wall_offset)
|
|
394
|
+
if sg is not None:
|
|
395
|
+
sensor_grids.append(sg)
|
|
396
|
+
if not include_mesh:
|
|
397
|
+
for sg in sensor_grids:
|
|
398
|
+
sg.mesh = None
|
|
399
|
+
|
|
400
|
+
# write the sensor grids to the output file or folder
|
|
401
|
+
if folder is None:
|
|
402
|
+
if write_json:
|
|
403
|
+
output_file.write(json.dumps([sg.to_dict() for sg in sensor_grids]))
|
|
404
|
+
else:
|
|
405
|
+
output_file.write('\n'.join([sg.to_radiance() for sg in sensor_grids]))
|
|
406
|
+
else:
|
|
407
|
+
if write_json:
|
|
408
|
+
for sg in sensor_grids:
|
|
409
|
+
sg.to_json(folder)
|
|
410
|
+
else:
|
|
411
|
+
for sg in sensor_grids:
|
|
412
|
+
sg.to_file(folder)
|
|
413
|
+
except Exception as e:
|
|
414
|
+
_logger.exception('Grid generation failed.\n{}'.format(e))
|
|
415
|
+
sys.exit(1)
|
|
416
|
+
else:
|
|
417
|
+
sys.exit(0)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
@grid.command('from-rooms-radial')
|
|
421
|
+
@click.argument('model-file', type=click.Path(
|
|
422
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
423
|
+
@click.option('--grid-size', '-s', help='A number for the dimension of the '
|
|
424
|
+
'radial grid. This can include the units of the distance (eg. 1ft) '
|
|
425
|
+
'or, if no units are provided, the value will be interpreted in the '
|
|
426
|
+
'honeybee model units.', type=str, default='0.5m', show_default=True)
|
|
427
|
+
@click.option('--offset', '-o', help='A number for the distance at which the '
|
|
428
|
+
'the sensor grid should be offset from the floor. This can include the '
|
|
429
|
+
'units of the distance (eg. 3ft) or, if no units are provided, the '
|
|
430
|
+
'value will be interpreted in the honeybee model units.',
|
|
431
|
+
type=str, default='1.2m', show_default=True)
|
|
432
|
+
@click.option('--include-mesh/--exclude-mesh', ' /-xm', help='Flag to note whether to '
|
|
433
|
+
'include a Mesh3D object that aligns with the grid positions under the '
|
|
434
|
+
'"mesh" property of each grid. Excluding the mesh can reduce size but '
|
|
435
|
+
'will mean Radiance results cannot be visualized as colored meshes.',
|
|
436
|
+
default=True, show_default=True)
|
|
437
|
+
@click.option('--keep-out/--remove-out', ' /-out', help='Flag to note whether an extra '
|
|
438
|
+
'check should be run to remove sensor points that lie outside the Room '
|
|
439
|
+
'volume. Note that this can add significantly to the runtime and this '
|
|
440
|
+
'check is not necessary in the case that all walls are vertical '
|
|
441
|
+
'and all floors are horizontal.', default=True, show_default=True)
|
|
442
|
+
@click.option('--wall-offset', '-w', help='A number for the distance at which sensors '
|
|
443
|
+
'close to walls should be removed. This can include the units of the '
|
|
444
|
+
'distance (eg. 3ft) or, if no units are provided, the value will be '
|
|
445
|
+
'interpreted in the honeybee model units. Note that this option has '
|
|
446
|
+
'no effect unless the value is more than half of the grid-size.',
|
|
447
|
+
type=str, default='0m', show_default=True)
|
|
448
|
+
@click.option('--dir-count', '-d', help='A positive integer for the number of '
|
|
449
|
+
'radial directions to be generated around each position.',
|
|
450
|
+
type=click.INT, default=8, show_default=True)
|
|
451
|
+
@click.option('--start-vector', '-v', help='An optional list of three values '
|
|
452
|
+
'(separated by spaces) set the start direction of the generated '
|
|
453
|
+
'directions. This can be used to orient the resulting sensors to '
|
|
454
|
+
'specific parts of the scene. It can also change the elevation of the '
|
|
455
|
+
'resulting directions since this start vector will always be rotated in '
|
|
456
|
+
'the XY plane to generate the resulting directions.',
|
|
457
|
+
type=str, default='0 -1 0', show_default=True)
|
|
458
|
+
@click.option('--mesh-radius', '-m', help='An optional number to override the radius '
|
|
459
|
+
'of the meshes generated around each sensor. If unspecified, it will be '
|
|
460
|
+
'equal to 45 percent of the grid-size. Set to zero to ensure no mesh is '
|
|
461
|
+
'added to the resulting sensor grids.', type=float, default=None)
|
|
462
|
+
@click.option('--room', '-r', multiple=True, help='Room identifier to specify the '
|
|
463
|
+
'room for which sensor grids should be generated. You can pass multiple '
|
|
464
|
+
'rooms (each preceded by -r). By default, all rooms get sensor grids.')
|
|
465
|
+
@click.option('--write-json/--write-pts', ' /-pts', help='Flag to note whether output '
|
|
466
|
+
'data collection should be in JSON format or the typical CSV-style format '
|
|
467
|
+
'of the Radiance .pts files.', default=True, show_default=True)
|
|
468
|
+
@click.option('--folder', help='Optional output folder. If specified, the --output-file '
|
|
469
|
+
'will be ignored and each sensor grid will be written into its own '
|
|
470
|
+
'.json or .pts file within the folder.', default=None,
|
|
471
|
+
type=click.Path(exists=True, file_okay=False,
|
|
472
|
+
dir_okay=True, resolve_path=True))
|
|
473
|
+
@click.option('--output-file', '-f', help='Optional file to output the JSON or CSV '
|
|
474
|
+
'string of the sensor grids. By default this will be printed '
|
|
475
|
+
'to stdout', type=click.File('w'), default='-', show_default=True)
|
|
476
|
+
def from_rooms_radial(
|
|
477
|
+
model_file, grid_size, offset, include_mesh, keep_out, wall_offset,
|
|
478
|
+
dir_count, start_vector, mesh_radius, room, write_json, folder, output_file):
|
|
479
|
+
"""Generate SensorGrids of radial directions around positions from room floors.
|
|
480
|
+
|
|
481
|
+
\b
|
|
482
|
+
Args:
|
|
483
|
+
model_file: Full path to a HBJSON or HBPkl Model file.
|
|
484
|
+
"""
|
|
485
|
+
try:
|
|
486
|
+
# re-serialize the Model and extract rooms and units
|
|
487
|
+
model = Model.from_file(model_file)
|
|
488
|
+
rooms = model.rooms if room is None or len(room) == 0 else \
|
|
489
|
+
[r for r in model.rooms if r.identifier in room]
|
|
490
|
+
grid_size = parse_distance_string(grid_size, model.units)
|
|
491
|
+
offset = parse_distance_string(offset, model.units)
|
|
492
|
+
wall_offset = parse_distance_string(wall_offset, model.units)
|
|
493
|
+
vec = [float(v) for v in start_vector.split()]
|
|
494
|
+
st_vec = Vector3D(*vec)
|
|
495
|
+
|
|
496
|
+
# loop through the rooms and generate sensor grids
|
|
497
|
+
sensor_grids = []
|
|
498
|
+
remove_out = not keep_out
|
|
499
|
+
for room in rooms:
|
|
500
|
+
sg = room.properties.radiance.generate_sensor_grid_radial(
|
|
501
|
+
grid_size, offset=offset, remove_out=remove_out, wall_offset=wall_offset,
|
|
502
|
+
dir_count=dir_count, start_vector=st_vec, mesh_radius=mesh_radius)
|
|
503
|
+
if sg is not None:
|
|
504
|
+
sensor_grids.append(sg)
|
|
505
|
+
if not include_mesh:
|
|
506
|
+
for sg in sensor_grids:
|
|
507
|
+
sg.mesh = None
|
|
508
|
+
|
|
509
|
+
# write the sensor grids to the output file or folder
|
|
510
|
+
if folder is None:
|
|
511
|
+
if write_json:
|
|
512
|
+
output_file.write(json.dumps([sg.to_dict() for sg in sensor_grids]))
|
|
513
|
+
else:
|
|
514
|
+
output_file.write('\n'.join([sg.to_radiance() for sg in sensor_grids]))
|
|
515
|
+
else:
|
|
516
|
+
if write_json:
|
|
517
|
+
for sg in sensor_grids:
|
|
518
|
+
sg.to_json(folder)
|
|
519
|
+
else:
|
|
520
|
+
for sg in sensor_grids:
|
|
521
|
+
sg.to_file(folder)
|
|
522
|
+
except Exception as e:
|
|
523
|
+
_logger.exception('Grid generation failed.\n{}'.format(e))
|
|
524
|
+
sys.exit(1)
|
|
525
|
+
else:
|
|
526
|
+
sys.exit(0)
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
@grid.command('from-exterior-faces')
|
|
530
|
+
@click.argument('model-file', type=click.Path(
|
|
531
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
532
|
+
@click.option('--grid-size', '-s', help='A number for the dimension of the mesh grid '
|
|
533
|
+
'cells. This can include the units of the distance (eg. 1ft) '
|
|
534
|
+
'or, if no units are provided, the value will be interpreted in the '
|
|
535
|
+
'honeybee model units.', type=str, default='0.5m', show_default=True)
|
|
536
|
+
@click.option('--offset', '-o', help='A number for the distance at which the '
|
|
537
|
+
'the sensor grid should be offset from the faces. This can include the '
|
|
538
|
+
'units of the distance (eg. 3ft) or, if no units are provided, the '
|
|
539
|
+
'value will be interpreted in the honeybee model units.',
|
|
540
|
+
type=str, default='0.1m', show_default=True)
|
|
541
|
+
@click.option('--face-type', '-t', help='Text to specify the type of face that will be '
|
|
542
|
+
'used to generate grids. Note that only Faces with Outdoors boundary '
|
|
543
|
+
'conditions will be used, meaning that most Floors will typically '
|
|
544
|
+
'be excluded unless they represent the underside of a cantilever. '
|
|
545
|
+
'Choose from Wall, Roof, Floor, All.',
|
|
546
|
+
type=str, default='Wall', show_default=True)
|
|
547
|
+
@click.option('--full-geometry/--punched-geometry', ' /-p', help='Flag to note whether '
|
|
548
|
+
'the punched_geometry of the faces should be used with the areas '
|
|
549
|
+
'of sub-faces removed from the grid or the full geometry should be used.',
|
|
550
|
+
default=True)
|
|
551
|
+
@click.option('--include-mesh/--exclude-mesh', ' /-xm', help='Flag to note whether to '
|
|
552
|
+
'include a Mesh3D object that aligns with the grid positions under the '
|
|
553
|
+
'"mesh" property of each grid. Excluding the mesh can reduce size but '
|
|
554
|
+
'will mean Radiance results cannot be visualized as colored meshes.',
|
|
555
|
+
default=True, show_default=True)
|
|
556
|
+
@click.option('--room', '-r', multiple=True, help='Room identifier to specify the '
|
|
557
|
+
'room for which sensor grids should be generated. You can pass multiple '
|
|
558
|
+
'rooms (each preceded by -r). By default, all rooms get sensor grids '
|
|
559
|
+
'joined into a single grid.')
|
|
560
|
+
@click.option('--write-json/--write-pts', ' /-pts', help='Flag to note whether output '
|
|
561
|
+
'data collection should be in JSON format or the typical CSV-style format '
|
|
562
|
+
'of the Radiance .pts files.', default=True, show_default=True)
|
|
563
|
+
@click.option('--folder', help='Optional output folder. If specified, the --output-file '
|
|
564
|
+
'will be ignored and each sensor grid will be written into its own '
|
|
565
|
+
'.json or .pts file within the folder.', default=None,
|
|
566
|
+
type=click.Path(exists=True, file_okay=False,
|
|
567
|
+
dir_okay=True, resolve_path=True))
|
|
568
|
+
@click.option('--output-file', '-f', help='Optional file to output the JSON or CSV '
|
|
569
|
+
'string of the sensor grids. By default this will be printed '
|
|
570
|
+
'to stdout', type=click.File('w'), default='-', show_default=True)
|
|
571
|
+
def from_exterior_faces(
|
|
572
|
+
model_file, grid_size, offset, face_type, full_geometry, include_mesh,
|
|
573
|
+
room, write_json, folder, output_file):
|
|
574
|
+
"""Generate SensorGrids from the exterior Faces of a honeybee model.
|
|
575
|
+
|
|
576
|
+
\b
|
|
577
|
+
Args:
|
|
578
|
+
model_file: Full path to a HBJSON or HBPkl Model file.
|
|
579
|
+
"""
|
|
580
|
+
try:
|
|
581
|
+
# re-serialize the Model and extract rooms and units
|
|
582
|
+
model = Model.from_file(model_file)
|
|
583
|
+
rooms = None if room is None or len(room) == 0 else \
|
|
584
|
+
[r for r in model.rooms if r.identifier in room]
|
|
585
|
+
grid_size = parse_distance_string(grid_size, model.units)
|
|
586
|
+
offset = parse_distance_string(offset, model.units)
|
|
587
|
+
punched_geometry = not full_geometry
|
|
588
|
+
|
|
589
|
+
# loop through the rooms and generate sensor grids
|
|
590
|
+
sensor_grids = []
|
|
591
|
+
if rooms is None:
|
|
592
|
+
sg = model.properties.radiance.generate_exterior_face_sensor_grid(
|
|
593
|
+
grid_size, offset=offset, face_type=face_type,
|
|
594
|
+
punched_geometry=punched_geometry)
|
|
595
|
+
sensor_grids.append(sg)
|
|
596
|
+
else:
|
|
597
|
+
for room in rooms:
|
|
598
|
+
sg = room.properties.radiance.generate_exterior_face_sensor_grid(
|
|
599
|
+
grid_size, offset=offset, face_type=face_type,
|
|
600
|
+
punched_geometry=punched_geometry)
|
|
601
|
+
if sg is not None:
|
|
602
|
+
sensor_grids.append(sg)
|
|
603
|
+
if not include_mesh:
|
|
604
|
+
for sg in sensor_grids:
|
|
605
|
+
sg.mesh = None
|
|
606
|
+
|
|
607
|
+
# write the sensor grids to the output file or folder
|
|
608
|
+
if folder is None:
|
|
609
|
+
if write_json:
|
|
610
|
+
output_file.write(json.dumps([sg.to_dict() for sg in sensor_grids]))
|
|
611
|
+
else:
|
|
612
|
+
output_file.write('\n'.join([sg.to_radiance() for sg in sensor_grids]))
|
|
613
|
+
else:
|
|
614
|
+
if write_json:
|
|
615
|
+
for sg in sensor_grids:
|
|
616
|
+
sg.to_json(folder)
|
|
617
|
+
else:
|
|
618
|
+
for sg in sensor_grids:
|
|
619
|
+
sg.to_file(folder)
|
|
620
|
+
except Exception as e:
|
|
621
|
+
_logger.exception('Grid generation failed.\n{}'.format(e))
|
|
622
|
+
sys.exit(1)
|
|
623
|
+
else:
|
|
624
|
+
sys.exit(0)
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
@grid.command('from-exterior-apertures')
|
|
628
|
+
@click.argument('model-file', type=click.Path(
|
|
629
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
630
|
+
@click.option('--grid-size', '-s', help='A number for the dimension of the mesh grid '
|
|
631
|
+
'cells. This can include the units of the distance (eg. 1ft) '
|
|
632
|
+
'or, if no units are provided, the value will be interpreted in the '
|
|
633
|
+
'honeybee model units.', type=str, default='0.5m', show_default=True)
|
|
634
|
+
@click.option('--offset', '-o', help='A number for the distance at which the '
|
|
635
|
+
'the sensor grid should be offset from the apertures. This can include the'
|
|
636
|
+
' units of the distance (eg. 3ft) or, if no units are provided, the '
|
|
637
|
+
'value will be interpreted in the honeybee model units.',
|
|
638
|
+
type=str, default='0.1m', show_default=True)
|
|
639
|
+
@click.option('--aperture-type', '-t', help='Text to specify the type of aperture that '
|
|
640
|
+
'will be used to generate grids. Note that only Faces with Outdoors '
|
|
641
|
+
'boundary conditions will be used, meaning that most Floors will typically'
|
|
642
|
+
' be excluded unless they represent the underside of a cantilever. '
|
|
643
|
+
'Choose from Window, Skylight, All.',
|
|
644
|
+
type=str, default='All', show_default=True)
|
|
645
|
+
@click.option('--include-mesh/--exclude-mesh', ' /-xm', help='Flag to note whether to '
|
|
646
|
+
'include a Mesh3D object that aligns with the grid positions under the '
|
|
647
|
+
'"mesh" property of each grid. Excluding the mesh can reduce size but '
|
|
648
|
+
'will mean Radiance results cannot be visualized as colored meshes.',
|
|
649
|
+
default=True, show_default=True)
|
|
650
|
+
@click.option('--room', '-r', multiple=True, help='Room identifier to specify the '
|
|
651
|
+
'room for which sensor grids should be generated. You can pass multiple '
|
|
652
|
+
'rooms (each preceded by -r). By default, all rooms get sensor grids '
|
|
653
|
+
'joined into a single grid.')
|
|
654
|
+
@click.option('--write-json/--write-pts', ' /-pts', help='Flag to note whether output '
|
|
655
|
+
'data collection should be in JSON format or the typical CSV-style format '
|
|
656
|
+
'of the Radiance .pts files.', default=True, show_default=True)
|
|
657
|
+
@click.option('--folder', help='Optional output folder. If specified, the --output-file '
|
|
658
|
+
'will be ignored and each sensor grid will be written into its own '
|
|
659
|
+
'.json or .pts file within the folder.', default=None,
|
|
660
|
+
type=click.Path(exists=True, file_okay=False,
|
|
661
|
+
dir_okay=True, resolve_path=True))
|
|
662
|
+
@click.option('--output-file', '-f', help='Optional file to output the JSON or CSV '
|
|
663
|
+
'string of the sensor grids. By default this will be printed '
|
|
664
|
+
'to stdout', type=click.File('w'), default='-', show_default=True)
|
|
665
|
+
def from_exterior_apertures(
|
|
666
|
+
model_file, grid_size, offset, aperture_type, include_mesh,
|
|
667
|
+
room, write_json, folder, output_file):
|
|
668
|
+
"""Generate SensorGrids from the exterior Faces of a honeybee model.
|
|
669
|
+
|
|
670
|
+
\b
|
|
671
|
+
Args:
|
|
672
|
+
model_file: Full path to a HBJSON or HBPkl Model file.
|
|
673
|
+
"""
|
|
674
|
+
try:
|
|
675
|
+
# re-serialize the Model and extract rooms and units
|
|
676
|
+
model = Model.from_file(model_file)
|
|
677
|
+
rooms = None if room is None or len(room) == 0 else \
|
|
678
|
+
[r for r in model.rooms if r.identifier in room]
|
|
679
|
+
grid_size = parse_distance_string(grid_size, model.units)
|
|
680
|
+
offset = parse_distance_string(offset, model.units)
|
|
681
|
+
|
|
682
|
+
# loop through the rooms and generate sensor grids
|
|
683
|
+
sensor_grids = []
|
|
684
|
+
if rooms is None:
|
|
685
|
+
sg = model.properties.radiance.generate_exterior_aperture_sensor_grid(
|
|
686
|
+
grid_size, offset=offset, aperture_type=aperture_type)
|
|
687
|
+
sensor_grids.append(sg)
|
|
688
|
+
else:
|
|
689
|
+
for room in rooms:
|
|
690
|
+
sg = room.properties.radiance.generate_exterior_aperture_sensor_grid(
|
|
691
|
+
grid_size, offset=offset, aperture_type=aperture_type)
|
|
692
|
+
if sg is not None:
|
|
693
|
+
sensor_grids.append(sg)
|
|
694
|
+
if not include_mesh:
|
|
695
|
+
for sg in sensor_grids:
|
|
696
|
+
sg.mesh = None
|
|
697
|
+
|
|
698
|
+
# write the sensor grids to the output file or folder
|
|
699
|
+
if folder is None:
|
|
700
|
+
if write_json:
|
|
701
|
+
output_file.write(json.dumps([sg.to_dict() for sg in sensor_grids]))
|
|
702
|
+
else:
|
|
703
|
+
output_file.write('\n'.join([sg.to_radiance() for sg in sensor_grids]))
|
|
704
|
+
else:
|
|
705
|
+
if write_json:
|
|
706
|
+
for sg in sensor_grids:
|
|
707
|
+
sg.to_json(folder)
|
|
708
|
+
else:
|
|
709
|
+
for sg in sensor_grids:
|
|
710
|
+
sg.to_file(folder)
|
|
711
|
+
except Exception as e:
|
|
712
|
+
_logger.exception('Grid generation failed.\n{}'.format(e))
|
|
713
|
+
sys.exit(1)
|
|
714
|
+
else:
|
|
715
|
+
sys.exit(0)
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
@grid.command('from-face3ds')
|
|
719
|
+
@click.argument('face3d-file', type=click.Path(
|
|
720
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
721
|
+
@click.option('--grid-name', '-n', help='Text string for the name of the SensorGrid, '
|
|
722
|
+
'which will also be used to assign SensorGrid ID. This will be used to '
|
|
723
|
+
'identify the object across a model and in the exported Radiance files '
|
|
724
|
+
'so it is recommended that it be relatively unique. If unspecified, '
|
|
725
|
+
'a random name will be generated.', type=str, default=None)
|
|
726
|
+
@click.option('--grid-size', '-s', help='A number for the dimension of the mesh grid '
|
|
727
|
+
'cells. This can include the units of the distance (eg. 1ft) '
|
|
728
|
+
'or, if no units are provided, the value will be interpreted in the '
|
|
729
|
+
'--units.', type=str, default='0.5m', show_default=True)
|
|
730
|
+
@click.option('--offset', '-o', help='A number for the distance at which the the sensor '
|
|
731
|
+
'grid should be offset from the base geometry. This can include the'
|
|
732
|
+
' units of the distance (eg. 3ft) or, if no units are provided, the '
|
|
733
|
+
'value will be interpreted in the --units.',
|
|
734
|
+
type=str, default='0', show_default=True)
|
|
735
|
+
@click.option('--units', '-u', help=' Text for the units system in which the Face3D '
|
|
736
|
+
'geometry exists, which will be used to interpret the --grid-size and '
|
|
737
|
+
'--offset inputs. Must be (Meters, Millimeters, Feet, Inches, '
|
|
738
|
+
'Centimeters).', type=str, default='Meters', show_default=True)
|
|
739
|
+
@click.option('--no-flip/--flip', ' /-fl', help='Flag to note whether the mesh '
|
|
740
|
+
'normals should be reversed from the direction of the face geometries'
|
|
741
|
+
'and the --offset move the sensors in the opposite direction from the '
|
|
742
|
+
'face normals.', default=True, show_default=True)
|
|
743
|
+
@click.option('--include-mesh/--exclude-mesh', ' /-xm', help='Flag to note whether to '
|
|
744
|
+
'include a Mesh3D object that aligns with the grid positions under the '
|
|
745
|
+
'"mesh" property of each grid. Excluding the mesh can reduce size but '
|
|
746
|
+
'will mean Radiance results cannot be visualized as colored meshes.',
|
|
747
|
+
default=True, show_default=True)
|
|
748
|
+
@click.option('--write-json/--write-pts', ' /-pts', help='Flag to note whether output '
|
|
749
|
+
'data collection should be in JSON format or the typical CSV-style format '
|
|
750
|
+
'of the Radiance .pts files.', default=True, show_default=True)
|
|
751
|
+
@click.option('--folder', help='Optional output folder. If specified, the --output-file '
|
|
752
|
+
'will be ignored and each sensor grid will be written into its own '
|
|
753
|
+
'.json or .pts file within the folder.', default=None,
|
|
754
|
+
type=click.Path(exists=True, file_okay=False,
|
|
755
|
+
dir_okay=True, resolve_path=True))
|
|
756
|
+
@click.option('--output-file', '-f', help='Optional file to output the JSON or CSV '
|
|
757
|
+
'string of the sensor grids. By default this will be printed '
|
|
758
|
+
'to stdout', type=click.File('w'), default='-', show_default=True)
|
|
759
|
+
def from_face3ds(
|
|
760
|
+
face3d_file, grid_name, grid_size, offset, units, no_flip,
|
|
761
|
+
include_mesh, write_json, folder, output_file):
|
|
762
|
+
"""Generate a SensorGrid from a JSON array of Face3D objects.
|
|
763
|
+
|
|
764
|
+
\b
|
|
765
|
+
Args:
|
|
766
|
+
face3d_file: Full path to a JSON file containing an array of Face3D objects
|
|
767
|
+
that will be used to generate the sensor grid. This could also be a
|
|
768
|
+
nested array (list of lists of Face3Ds), in which case a separate
|
|
769
|
+
SensorGrid will be computed for each sub-list.
|
|
770
|
+
"""
|
|
771
|
+
try:
|
|
772
|
+
# re-serialize the Face3Ds
|
|
773
|
+
with open(face3d_file) as inf:
|
|
774
|
+
data = json.load(inf)
|
|
775
|
+
face_arrays = []
|
|
776
|
+
for obj in data:
|
|
777
|
+
if isinstance(obj, list):
|
|
778
|
+
face_arrays.append([Face3D.from_dict(f) for f in obj])
|
|
779
|
+
else: # assume that it is a single Face3D
|
|
780
|
+
face_arrays.append([Face3D.from_dict(obj)])
|
|
781
|
+
|
|
782
|
+
# process all of the other inputs
|
|
783
|
+
grid_size = parse_distance_string(grid_size, units)
|
|
784
|
+
offset = parse_distance_string(offset, units)
|
|
785
|
+
base_id = clean_rad_string(grid_name) if grid_name is not None \
|
|
786
|
+
else clean_and_id_rad_string('SensorGrid')
|
|
787
|
+
flip = not no_flip
|
|
788
|
+
|
|
789
|
+
# loop through the face3ds and generate sensor grids
|
|
790
|
+
sensor_grids = []
|
|
791
|
+
for i, faces in enumerate(face_arrays):
|
|
792
|
+
grid_id = base_id if len(face_arrays) == 1 else '{}_{}'.format(base_id, i)
|
|
793
|
+
try:
|
|
794
|
+
sensor_grids.append(
|
|
795
|
+
SensorGrid.from_face3d(grid_id, faces, grid_size, None, offset, flip)
|
|
796
|
+
)
|
|
797
|
+
except AssertionError: # none of the Face3Ds make a valid grid
|
|
798
|
+
continue
|
|
799
|
+
if not include_mesh:
|
|
800
|
+
for sg in sensor_grids:
|
|
801
|
+
sg.mesh = None
|
|
802
|
+
|
|
803
|
+
# write the sensor grids to the output file or folder
|
|
804
|
+
if folder is None:
|
|
805
|
+
if write_json:
|
|
806
|
+
output_file.write(json.dumps([sg.to_dict() for sg in sensor_grids]))
|
|
807
|
+
else:
|
|
808
|
+
output_file.write('\n'.join([sg.to_radiance() for sg in sensor_grids]))
|
|
809
|
+
else:
|
|
810
|
+
if write_json:
|
|
811
|
+
for sg in sensor_grids:
|
|
812
|
+
sg.to_json(folder)
|
|
813
|
+
else:
|
|
814
|
+
for sg in sensor_grids:
|
|
815
|
+
sg.to_file(folder)
|
|
816
|
+
except Exception as e:
|
|
817
|
+
_logger.exception('Grid generation failed.\n{}'.format(e))
|
|
818
|
+
sys.exit(1)
|
|
819
|
+
else:
|
|
820
|
+
sys.exit(0)
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
@grid.command('enclosure-info')
|
|
824
|
+
@click.argument('model-file', type=click.Path(
|
|
825
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
826
|
+
@click.argument('grid-file', type=click.Path(
|
|
827
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
828
|
+
@click.option(
|
|
829
|
+
'--air-boundary-distance', '-d', help='A number to set the distance from air '
|
|
830
|
+
'boundaries over which values should be interpolated. Using 0 will assume a '
|
|
831
|
+
'hard edge between Rooms of the same radiant enclosures. This can include the '
|
|
832
|
+
'units of the distance (eg. 3ft) or, if no units are provided the value will '
|
|
833
|
+
'be interpreted in the honeybee model units.',
|
|
834
|
+
type=str, default='2m', show_default=True)
|
|
835
|
+
@click.option(
|
|
836
|
+
'--output-file', '-f', help='Optional output file for the generated radiant '
|
|
837
|
+
'enclosure JSON. By default this will be printed to stdout',
|
|
838
|
+
type=click.File('w'), default='-'
|
|
839
|
+
)
|
|
840
|
+
def enclosure_info_grid(model_file, grid_file, air_boundary_distance, output_file):
|
|
841
|
+
"""Get a JSON of radiant enclosure information from a .pts file of a sensor grid.
|
|
842
|
+
|
|
843
|
+
\b
|
|
844
|
+
Args:
|
|
845
|
+
model_file: Full path to a Model JSON file (HBJSON) or a Model pkl (HBpkl) file.
|
|
846
|
+
grid-file: Full path to a sensor grid file (.pts).
|
|
847
|
+
"""
|
|
848
|
+
try:
|
|
849
|
+
# re-serialize the Model
|
|
850
|
+
model = Model.from_file(model_file)
|
|
851
|
+
grid = SensorGrid.from_file(grid_file)
|
|
852
|
+
ab_distance = parse_distance_string(air_boundary_distance, model.units)
|
|
853
|
+
# write out the list of radiant enclosure JSON info
|
|
854
|
+
output_file.write(json.dumps(grid.enclosure_info_dict(model, ab_distance)))
|
|
855
|
+
except Exception as e:
|
|
856
|
+
_logger.exception('Creation of radiant enclosure info failed.\n{}'.format(e))
|
|
857
|
+
sys.exit(1)
|
|
858
|
+
else:
|
|
859
|
+
sys.exit(0)
|