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,458 @@
|
|
|
1
|
+
"""honeybee radiance standards library commands."""
|
|
2
|
+
import click
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
import logging
|
|
6
|
+
import json
|
|
7
|
+
import zipfile
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
|
|
10
|
+
from honeybee_radiance.config import folders
|
|
11
|
+
from honeybee_radiance.lib.modifiers import modifier_by_identifier, MODIFIERS
|
|
12
|
+
from honeybee_radiance.lib.modifiersets import modifier_set_by_identifier, \
|
|
13
|
+
lib_dict_abridged_to_modifier_set, MODIFIER_SETS
|
|
14
|
+
from honeybee_radiance.mutil import dict_to_modifier
|
|
15
|
+
|
|
16
|
+
from honeybee_radiance.lib._loadmodifiers import load_modifiers_from_folder
|
|
17
|
+
from honeybee_radiance.lib._loadmodifiersets import load_modifiersets_from_folder
|
|
18
|
+
|
|
19
|
+
_logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@click.group(help='Commands for retrieving objects from the standards library.')
|
|
23
|
+
def lib():
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@lib.command('modifiers')
|
|
28
|
+
@click.option('--output-file', help='Optional file to output the JSON string of '
|
|
29
|
+
'the object. By default, it will be printed out to stdout',
|
|
30
|
+
type=click.File('w'), default='-', show_default=True)
|
|
31
|
+
def modifiers(output_file):
|
|
32
|
+
"""Get a list of all modifiers in the standards library."""
|
|
33
|
+
try:
|
|
34
|
+
output_file.write(json.dumps(MODIFIERS))
|
|
35
|
+
except Exception as e:
|
|
36
|
+
_logger.exception('Failed to load modifiers.\n{}'.format(e))
|
|
37
|
+
sys.exit(1)
|
|
38
|
+
else:
|
|
39
|
+
sys.exit(0)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@lib.command('modifier-sets')
|
|
43
|
+
@click.option('--output-file', help='Optional file to output the JSON string of '
|
|
44
|
+
'the object. By default, it will be printed out to stdout',
|
|
45
|
+
type=click.File('w'), default='-', show_default=True)
|
|
46
|
+
def modifier_sets(output_file):
|
|
47
|
+
"""Get a list of all modifier sets in the standards library."""
|
|
48
|
+
try:
|
|
49
|
+
output_file.write(json.dumps(MODIFIER_SETS))
|
|
50
|
+
except Exception as e:
|
|
51
|
+
_logger.exception('Failed to load modifier sets.\n{}'.format(e))
|
|
52
|
+
sys.exit(1)
|
|
53
|
+
else:
|
|
54
|
+
sys.exit(0)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@lib.command('modifier-by-id')
|
|
58
|
+
@click.argument('modifier-id', type=str)
|
|
59
|
+
@click.option('--output-file', help='Optional file to output the JSON string of '
|
|
60
|
+
'the object. By default, it will be printed out to stdout',
|
|
61
|
+
type=click.File('w'), default='-', show_default=True)
|
|
62
|
+
def modifier_by_id(modifier_id, output_file):
|
|
63
|
+
"""Get a modifier definition from the standards lib with its identifier.
|
|
64
|
+
\n
|
|
65
|
+
Args:
|
|
66
|
+
modifier_id: The identifier of a modifier in the library.
|
|
67
|
+
"""
|
|
68
|
+
try:
|
|
69
|
+
output_file.write(json.dumps(modifier_by_identifier(modifier_id).to_dict()))
|
|
70
|
+
except Exception as e:
|
|
71
|
+
_logger.exception(
|
|
72
|
+
'Retrieval from modifier library failed.\n{}'.format(e))
|
|
73
|
+
sys.exit(1)
|
|
74
|
+
else:
|
|
75
|
+
sys.exit(0)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@lib.command('modifier-set-by-id')
|
|
79
|
+
@click.argument('modifier-set-id', type=str)
|
|
80
|
+
@click.option('--none-defaults', help='Boolean to note whether default modifiers '
|
|
81
|
+
'in the set should be included in detail (False) or should be None '
|
|
82
|
+
'(True).', type=bool, default=True, show_default=True)
|
|
83
|
+
@click.option('--abridged', help='Optional boolean to note wether an abridged definition'
|
|
84
|
+
' should be returned.', type=bool, default=False, show_default=True)
|
|
85
|
+
@click.option('--output-file', help='Optional file to output the JSON string of '
|
|
86
|
+
'the object. By default, it will be printed out to stdout',
|
|
87
|
+
type=click.File('w'), default='-', show_default=True)
|
|
88
|
+
def modifier_set_by_id(modifier_set_id, none_defaults, abridged, output_file):
|
|
89
|
+
"""Get an modifier set definition from the standards lib with its identifier.
|
|
90
|
+
\n
|
|
91
|
+
Args:
|
|
92
|
+
modifier_set_id: The identifier of a modifier set in the library.
|
|
93
|
+
"""
|
|
94
|
+
try:
|
|
95
|
+
m_set = modifier_set_by_identifier(modifier_set_id)
|
|
96
|
+
output_file.write(json.dumps(m_set.to_dict(
|
|
97
|
+
none_for_defaults=none_defaults, abridged=abridged)))
|
|
98
|
+
except Exception as e:
|
|
99
|
+
_logger.exception(
|
|
100
|
+
'Retrieval from modifier set library failed.\n{}'.format(e))
|
|
101
|
+
sys.exit(1)
|
|
102
|
+
else:
|
|
103
|
+
sys.exit(0)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@lib.command('modifiers-by-id')
|
|
107
|
+
@click.argument('modifier-ids', nargs=-1)
|
|
108
|
+
@click.option('--output-file', help='Optional file to output the JSON strings of '
|
|
109
|
+
'the objects. By default, it will be printed out to stdout',
|
|
110
|
+
type=click.File('w'), default='-', show_default=True)
|
|
111
|
+
def modifiers_by_id(modifier_ids, output_file):
|
|
112
|
+
"""Get several modifier definitions from the standards lib at once.
|
|
113
|
+
\n
|
|
114
|
+
Args:
|
|
115
|
+
modifier_ids: A list of modifier identifiers to be retrieved from the library.
|
|
116
|
+
"""
|
|
117
|
+
try:
|
|
118
|
+
mods = [modifier_by_identifier(mod_id) for mod_id in modifier_ids]
|
|
119
|
+
output_file.write(json.dumps([mod.to_dict() for mod in mods]))
|
|
120
|
+
except Exception as e:
|
|
121
|
+
_logger.exception(
|
|
122
|
+
'Retrieval from modifier library failed.\n{}'.format(e))
|
|
123
|
+
sys.exit(1)
|
|
124
|
+
else:
|
|
125
|
+
sys.exit(0)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@lib.command('modifier-sets-by-id')
|
|
129
|
+
@click.argument('modifier-set-ids', nargs=-1)
|
|
130
|
+
@click.option('--none-defaults', help='Boolean to note whether default modifiers '
|
|
131
|
+
'in the set should be included in detail (False) or should be None '
|
|
132
|
+
'(True).', type=bool, default=True, show_default=True)
|
|
133
|
+
@click.option('--abridged', help='Optional boolean to note wether an abridged definition'
|
|
134
|
+
' should be returned.', type=bool, default=False, show_default=True)
|
|
135
|
+
@click.option('--output-file', help='Optional file to output the JSON string of '
|
|
136
|
+
'the object. By default, it will be printed out to stdout',
|
|
137
|
+
type=click.File('w'), default='-', show_default=True)
|
|
138
|
+
def modifier_sets_by_id(modifier_set_ids, none_defaults, abridged, output_file):
|
|
139
|
+
"""Get several modifier set definitions from the standards lib at once.
|
|
140
|
+
\n
|
|
141
|
+
Args:
|
|
142
|
+
modifier_set_ids: A list of modifier set identifiers to be retrieved
|
|
143
|
+
from the library.
|
|
144
|
+
"""
|
|
145
|
+
try:
|
|
146
|
+
m_sets = [modifier_set_by_identifier(m_id) for m_id in modifier_set_ids]
|
|
147
|
+
output_file.write(json.dumps([ms.to_dict(
|
|
148
|
+
none_for_defaults=none_defaults, abridged=abridged) for ms in m_sets]))
|
|
149
|
+
except Exception as e:
|
|
150
|
+
_logger.exception(
|
|
151
|
+
'Retrieval from modifier set library failed.\n{}'.format(e))
|
|
152
|
+
sys.exit(1)
|
|
153
|
+
else:
|
|
154
|
+
sys.exit(0)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@lib.command('to-model-properties')
|
|
158
|
+
@click.option(
|
|
159
|
+
'--standards-folder', '-s', default=None, help='A directory containing sub-folders '
|
|
160
|
+
'of resource objects (modifiers, modifiersets) to be loaded as '
|
|
161
|
+
'ModelRadianceProperties. Note that this standards folder MUST contain these '
|
|
162
|
+
'sub-folders. Each sub-folder can contain JSON files of objects following '
|
|
163
|
+
'honeybee schema or RAD/MAT files (if appropriate). If None, the honeybee '
|
|
164
|
+
'default standards folder will be used.', type=click.Path(
|
|
165
|
+
exists=True, file_okay=False, dir_okay=True, resolve_path=True)
|
|
166
|
+
)
|
|
167
|
+
@click.option(
|
|
168
|
+
'--exclude-abridged/--include-abridged', ' /-a', help='Flag to note whether '
|
|
169
|
+
'fully abridged objects in the user standards library should be included in '
|
|
170
|
+
'the output file. This is useful when some of the sub-objects contained within '
|
|
171
|
+
'the user standards are referenced in another installed standards package that '
|
|
172
|
+
'is not a part of the user personal standards library (eg. honeybee-standards). '
|
|
173
|
+
'When abridged objects are excluded, only objects that contain all '
|
|
174
|
+
'sub-objects within the user library will be in the output-file.',
|
|
175
|
+
default=True, show_default=True
|
|
176
|
+
)
|
|
177
|
+
@click.option(
|
|
178
|
+
'--output-file', '-f', help='Optional JSON file to output the JSON string of '
|
|
179
|
+
'the translation. By default this will be printed out to stdout',
|
|
180
|
+
type=click.File('w'), default='-', show_default=True
|
|
181
|
+
)
|
|
182
|
+
def to_model_properties(standards_folder, exclude_abridged, output_file):
|
|
183
|
+
"""Translate a lib folder of standards to a JSON of honeybee ModelRadianceProperties.
|
|
184
|
+
|
|
185
|
+
This is useful in workflows where one must import everything within a user's
|
|
186
|
+
standards folder and requires all objects to be in a consistent format.
|
|
187
|
+
All objects in the resulting ModelRadianceProperties will be abridged and
|
|
188
|
+
duplicated objects in the folder will be removed such that there
|
|
189
|
+
is only one of each object.
|
|
190
|
+
"""
|
|
191
|
+
try:
|
|
192
|
+
# set the folder to the default standards_folder if unspecified
|
|
193
|
+
folder = standards_folder if standards_folder is not None else \
|
|
194
|
+
folders.standards_data_folder
|
|
195
|
+
|
|
196
|
+
# load modifiers from the standards folder
|
|
197
|
+
mod_folder = os.path.join(folder, 'modifiers')
|
|
198
|
+
all_m = load_modifiers_from_folder(mod_folder)
|
|
199
|
+
|
|
200
|
+
# load modifier sets from the standards folder
|
|
201
|
+
mod_set_folder = os.path.join(folder, 'modifiersets')
|
|
202
|
+
all_mod_sets, misc_m = load_modifiersets_from_folder(mod_set_folder, all_m)
|
|
203
|
+
all_mods = set(list(all_m.values()) + misc_m)
|
|
204
|
+
|
|
205
|
+
# add all object dictionaries into one object
|
|
206
|
+
base = {'type': 'ModelRadianceProperties'}
|
|
207
|
+
base['modifiers'] = [m.to_dict() for m in all_mods]
|
|
208
|
+
base['modifier_sets'] = \
|
|
209
|
+
[ms.to_dict(abridged=True) for ms in all_mod_sets.values()]
|
|
210
|
+
|
|
211
|
+
# if set to include abridged, add any of such objects to the dictionary
|
|
212
|
+
if not exclude_abridged:
|
|
213
|
+
_add_abridged_objects(base['modifiers'], mod_folder)
|
|
214
|
+
_add_abridged_objects(base['modifier_sets'], mod_set_folder)
|
|
215
|
+
|
|
216
|
+
# write out the JSON file
|
|
217
|
+
output_file.write(json.dumps(base))
|
|
218
|
+
except Exception as e:
|
|
219
|
+
_logger.exception('Loading standards to properties failed.\n{}'.format(e))
|
|
220
|
+
sys.exit(1)
|
|
221
|
+
else:
|
|
222
|
+
sys.exit(0)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _add_abridged_objects(model_prop_array, lib_folder):
|
|
226
|
+
"""Add abridged resource objects to an existing model properties array.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
model_prop_array: An array of resource object dictionaries from a
|
|
230
|
+
ModelRadianceProperties dictionary.
|
|
231
|
+
lib_folder: A folder from which abridged objects will be loaded.
|
|
232
|
+
"""
|
|
233
|
+
obj_ids = set(obj['identifier'] for obj in model_prop_array)
|
|
234
|
+
for f in os.listdir(lib_folder):
|
|
235
|
+
f_path = os.path.join(lib_folder, f)
|
|
236
|
+
if os.path.isfile(f_path) and f_path.endswith('.json'):
|
|
237
|
+
with open(f_path) as json_file:
|
|
238
|
+
data = json.load(json_file)
|
|
239
|
+
if 'type' in data: # single object
|
|
240
|
+
if data['identifier'] not in obj_ids:
|
|
241
|
+
model_prop_array.append(data)
|
|
242
|
+
else: # a collection of several objects
|
|
243
|
+
for obj_identifier in data:
|
|
244
|
+
if obj_identifier not in obj_ids:
|
|
245
|
+
model_prop_array.append(data[obj_identifier])
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@lib.command('purge')
|
|
249
|
+
@click.option(
|
|
250
|
+
'--standards-folder', '-s', default=None, help='A directory containing sub-folders '
|
|
251
|
+
'of resource objects (modifiers, modifiersets) to be purged of files. If '
|
|
252
|
+
'unspecified, the current user honeybee default standards folder will be used.',
|
|
253
|
+
type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True)
|
|
254
|
+
)
|
|
255
|
+
@click.option(
|
|
256
|
+
'--json-only/--all', ' /-a', help='Flag to note whether only JSON files should '
|
|
257
|
+
'be purged from the library or all files should be purged, including RAD files. '
|
|
258
|
+
'Given that all objects added to the library through the `add` command will always '
|
|
259
|
+
'be JSON, only purging the JSONs is useful when one wishes to clear these objects '
|
|
260
|
+
'while preserving objects that originated from other sources.',
|
|
261
|
+
default=True, show_default=True
|
|
262
|
+
)
|
|
263
|
+
@click.option(
|
|
264
|
+
'--backup/--no-backup', ' /-xb', help='Flag to note whether a backup .zip file '
|
|
265
|
+
'of the user standards library should be made before the purging operation. '
|
|
266
|
+
'This is done by default in case the user ever wants to recover their old '
|
|
267
|
+
'standards but can be turned off if a backup is not desired.',
|
|
268
|
+
default=True, show_default=True
|
|
269
|
+
)
|
|
270
|
+
@click.option(
|
|
271
|
+
'--log-file', '-log', help='Optional file to output a log of the purging process. '
|
|
272
|
+
'By default this will be printed out to stdout',
|
|
273
|
+
type=click.File('w'), default='-', show_default=True
|
|
274
|
+
)
|
|
275
|
+
def purge_lib(standards_folder, json_only, backup, log_file):
|
|
276
|
+
"""Purge the library of all user radiance standards that it contains.
|
|
277
|
+
|
|
278
|
+
This is useful when a user's standard library has become filled with duplicated
|
|
279
|
+
objects or the user wishes to start fresh by re-exporting updated objects.
|
|
280
|
+
"""
|
|
281
|
+
try:
|
|
282
|
+
# set the folder to the default standards_folder if unspecified
|
|
283
|
+
folder = standards_folder if standards_folder is not None else \
|
|
284
|
+
folders.standards_data_folder
|
|
285
|
+
resources = ('modifiers', 'modifiersets')
|
|
286
|
+
sub_folders = [os.path.join(folder, std) for std in resources]
|
|
287
|
+
|
|
288
|
+
# make a backup of the folder if requested
|
|
289
|
+
if backup:
|
|
290
|
+
r_names, s_files, s_paths = [], [], []
|
|
291
|
+
for sf, r_name in zip(sub_folders, resources):
|
|
292
|
+
for s_file in os.listdir(sf):
|
|
293
|
+
s_path = os.path.join(sf, s_file)
|
|
294
|
+
if os.path.isfile(s_path):
|
|
295
|
+
r_names.append(r_name)
|
|
296
|
+
s_files.append(s_file)
|
|
297
|
+
s_paths.append(s_path)
|
|
298
|
+
if len(s_paths) != 0: # there are resources to back up
|
|
299
|
+
backup_name = '.standards_backup_{}.zip'.format(
|
|
300
|
+
str(datetime.now()).split('.')[0].replace(':', '-'))
|
|
301
|
+
backup_file = os.path.join(os.path.dirname(folder), backup_name)
|
|
302
|
+
with zipfile.ZipFile(backup_file, 'w') as zf:
|
|
303
|
+
for r_name, s_file, s_path in zip(r_names, s_files, s_paths):
|
|
304
|
+
zf.write(s_path, os.path.join(r_name, s_file))
|
|
305
|
+
|
|
306
|
+
# loop through the sub-folders and delete the files
|
|
307
|
+
rel_files = []
|
|
308
|
+
for sf in sub_folders:
|
|
309
|
+
for s_file in os.listdir(sf):
|
|
310
|
+
s_path = os.path.join(sf, s_file)
|
|
311
|
+
if os.path.isfile(s_path):
|
|
312
|
+
if json_only:
|
|
313
|
+
if s_file.lower().endswith('.json'):
|
|
314
|
+
rel_files.append(s_path)
|
|
315
|
+
else:
|
|
316
|
+
rel_files.append(s_path)
|
|
317
|
+
purged_files, fail_files = [], []
|
|
318
|
+
for rf in rel_files:
|
|
319
|
+
try:
|
|
320
|
+
os.remove(rf)
|
|
321
|
+
purged_files.append(rf)
|
|
322
|
+
except Exception:
|
|
323
|
+
fail_files.append(rf)
|
|
324
|
+
|
|
325
|
+
# report all of the deleted files in the log file
|
|
326
|
+
if len(rel_files) == 0:
|
|
327
|
+
log_file.write('The standards folder is empty so no files were removed.')
|
|
328
|
+
if len(purged_files) != 0:
|
|
329
|
+
msg = 'The following files were removed in the purging ' \
|
|
330
|
+
'operations:\n{}\n'.format(' \n'.join(purged_files))
|
|
331
|
+
log_file.write(msg)
|
|
332
|
+
if len(fail_files) != 0:
|
|
333
|
+
msg = 'The following files could not be removed in the purging ' \
|
|
334
|
+
'operations:\n{}\n'.format(' \n'.join(fail_files))
|
|
335
|
+
log_file.write(msg)
|
|
336
|
+
except Exception as e:
|
|
337
|
+
_logger.exception('Purging user standards library failed.\n{}'.format(e))
|
|
338
|
+
sys.exit(1)
|
|
339
|
+
else:
|
|
340
|
+
sys.exit(0)
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
@lib.command('add')
|
|
344
|
+
@click.argument('properties-file', type=click.Path(
|
|
345
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
346
|
+
@click.option(
|
|
347
|
+
'--standards-folder', '-s', default=None, help='A directory containing sub-folders '
|
|
348
|
+
'of resource objects (modifiers, modifiersets) to which the properties-file objects '
|
|
349
|
+
'will be added. If unspecified, the current user honeybee default standards folder '
|
|
350
|
+
'will be used.', type=click.Path(
|
|
351
|
+
exists=True, file_okay=False, dir_okay=True, resolve_path=True)
|
|
352
|
+
)
|
|
353
|
+
@click.option(
|
|
354
|
+
'--log-file', '-log', help='Optional file to output a log of the purging process. '
|
|
355
|
+
'By default this will be printed out to stdout',
|
|
356
|
+
type=click.File('w'), default='-', show_default=True
|
|
357
|
+
)
|
|
358
|
+
def add_to_lib(properties_file, standards_folder, log_file):
|
|
359
|
+
"""Add an object or set of objects to the user's standard library.
|
|
360
|
+
|
|
361
|
+
\b
|
|
362
|
+
Args:
|
|
363
|
+
properties_file: A JSON file of a ModelRadianceProperties object containing
|
|
364
|
+
the objects to be written into the user standards library. All sub-objects
|
|
365
|
+
within this ModelRadianceProperties object must be Abridged if the sub-object
|
|
366
|
+
has an abridged schema and these abridged schemas are allowed to
|
|
367
|
+
reference either other objects in the ModelRadianceProperties or existing
|
|
368
|
+
objects within the standards library.
|
|
369
|
+
"""
|
|
370
|
+
try:
|
|
371
|
+
# set the folder to the default standards_folder if unspecified
|
|
372
|
+
folder = standards_folder if standards_folder is not None else \
|
|
373
|
+
folders.standards_data_folder
|
|
374
|
+
|
|
375
|
+
# load up the model radiance properties from the JSON
|
|
376
|
+
with open(properties_file) as inf:
|
|
377
|
+
data = json.load(inf)
|
|
378
|
+
assert 'type' in data, 'Properties file lacks required type key.'
|
|
379
|
+
assert data['type'] == 'ModelRadianceProperties', 'Expected ' \
|
|
380
|
+
'ModelRadianceProperties JSON object. Got {}.'.format(data['type'])
|
|
381
|
+
success_objects, dup_id_objects, mis_dep_objects = [], [], []
|
|
382
|
+
|
|
383
|
+
# extract, check, and write the modifiers
|
|
384
|
+
mods = {}
|
|
385
|
+
if 'modifiers' in data and data['modifiers'] is not None and \
|
|
386
|
+
len(data['modifiers']) != 0:
|
|
387
|
+
for mod_obj in data['modifiers']:
|
|
388
|
+
msg = _object_message('Modifier', mod_obj)
|
|
389
|
+
if mod_obj['identifier'] in MODIFIERS:
|
|
390
|
+
dup_id_objects.append(msg)
|
|
391
|
+
else:
|
|
392
|
+
try:
|
|
393
|
+
mods[mod_obj['identifier']] = dict_to_modifier(mod_obj)
|
|
394
|
+
success_objects.append(msg)
|
|
395
|
+
except (ValueError, KeyError, AssertionError):
|
|
396
|
+
mis_dep_objects.append(msg)
|
|
397
|
+
if mods:
|
|
398
|
+
mod_dict = {m.identifier: m.to_dict() for m in mods.values()}
|
|
399
|
+
mod_json = os.path.join(folder, 'modifiers', 'custom_modifiers.json')
|
|
400
|
+
_update_user_json(mod_dict, mod_json)
|
|
401
|
+
|
|
402
|
+
# extract, check, and write the modifier sets
|
|
403
|
+
mod_sets = {}
|
|
404
|
+
if 'modifier_sets' in data and data['modifier_sets'] is not None:
|
|
405
|
+
for ms in data['modifier_sets']:
|
|
406
|
+
msg = _object_message('Modifier Set', ms)
|
|
407
|
+
if ms['identifier'] in MODIFIER_SETS:
|
|
408
|
+
dup_id_objects.append(msg)
|
|
409
|
+
else:
|
|
410
|
+
try:
|
|
411
|
+
mod_sets[ms['identifier']] = \
|
|
412
|
+
lib_dict_abridged_to_modifier_set(ms, mods)
|
|
413
|
+
success_objects.append(msg)
|
|
414
|
+
except (ValueError, KeyError, AssertionError):
|
|
415
|
+
mis_dep_objects.append(msg)
|
|
416
|
+
if mod_sets:
|
|
417
|
+
ms_dict = {m.identifier: m.to_dict(abridged=True) for m in mod_sets.values()}
|
|
418
|
+
ms_json = os.path.join(folder, 'modifiersets', 'custom_sets.json')
|
|
419
|
+
_update_user_json(ms_dict, ms_json)
|
|
420
|
+
|
|
421
|
+
# write a report of the objects that were or were not added
|
|
422
|
+
success_objects, dup_id_objects, mis_dep_objects
|
|
423
|
+
m_start = 'THESE OBJECTS'
|
|
424
|
+
if len(success_objects) != 0:
|
|
425
|
+
msg = '{} WERE SUCCESSFULLY ADDED TO THE STANDARDS LIBRARY:\n{}\n\n'.format(
|
|
426
|
+
m_start, ' \n'.join(success_objects))
|
|
427
|
+
log_file.write(msg)
|
|
428
|
+
if len(dup_id_objects) != 0:
|
|
429
|
+
msg = '{} WERE NOT ADDED SINCE THEY ALREADY EXIST IN THE STANDARDS ' \
|
|
430
|
+
'LIBRARY:\n{}\n\n'.format(m_start, ' \n'.join(dup_id_objects))
|
|
431
|
+
log_file.write(msg)
|
|
432
|
+
if len(mis_dep_objects) != 0:
|
|
433
|
+
msg = '{} WERE NOT ADDED BECAUSE THEY ARE INVALID OR ARE MISSING ' \
|
|
434
|
+
'DEPENDENT OBJECTS:\n{}\n\n'.format(m_start, ' \n'.join(mis_dep_objects))
|
|
435
|
+
log_file.write(msg)
|
|
436
|
+
except Exception as e:
|
|
437
|
+
_logger.exception('Adding to user standards library failed.\n{}'.format(e))
|
|
438
|
+
sys.exit(1)
|
|
439
|
+
else:
|
|
440
|
+
sys.exit(0)
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def _object_message(obj_type, obj_dict):
|
|
444
|
+
"""Get the reporting message of an object to add to the user library."""
|
|
445
|
+
obj_name = obj_dict['display_name'] if 'display_name' in obj_dict and \
|
|
446
|
+
obj_dict['display_name'] is not None else obj_dict['identifier']
|
|
447
|
+
return '{}: {}'.format(obj_type, obj_name)
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def _update_user_json(dict_to_add, user_json):
|
|
451
|
+
"""Update a JSON file within a user standards folder."""
|
|
452
|
+
if os.path.isfile(user_json):
|
|
453
|
+
with open(user_json) as inf:
|
|
454
|
+
exist_data = json.load(inf)
|
|
455
|
+
dict_to_add.update(exist_data)
|
|
456
|
+
with open(user_json, 'w') as outf:
|
|
457
|
+
json.dump(dict_to_add, outf, indent=4)
|
|
458
|
+
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""honeybee radiance modifier commands."""
|
|
2
|
+
import click
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
import logging
|
|
6
|
+
import json
|
|
7
|
+
|
|
8
|
+
from honeybee_radiance.sensorgrid import SensorGrid
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
_logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@click.group(help='Commands for generating and modifying Radiance modifiers.')
|
|
15
|
+
def modifier():
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@modifier.command('split-modifiers')
|
|
20
|
+
@click.argument('modifier-file', type=click.Path(
|
|
21
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
22
|
+
@click.argument(
|
|
23
|
+
'output-folder',
|
|
24
|
+
type=click.Path(file_okay=False, dir_okay=True, resolve_path=True))
|
|
25
|
+
@click.option(
|
|
26
|
+
'--sensor-count', '-sc', help='The number of sensors in the sensor grid '
|
|
27
|
+
'that will be used in rcontrib with the distributed modifiers.',
|
|
28
|
+
type=int, default=5000, show_default=True)
|
|
29
|
+
@click.option(
|
|
30
|
+
'--grid-file', '-gf', help='Full path to a sensor grid file. This file '
|
|
31
|
+
'is used to count the number of sensors and will override the '
|
|
32
|
+
'--sensor-count option.',
|
|
33
|
+
type=click.Path(exists=True, file_okay=True, dir_okay=False, resolve_path=True),
|
|
34
|
+
default=None, show_default=True)
|
|
35
|
+
@click.option(
|
|
36
|
+
'--max-value', '-max', help='An optional integer to define the maximum value '
|
|
37
|
+
'allowed when multiplying the number of sensors with the number of modifiers '
|
|
38
|
+
'in the distributed modifiers. Default: 40000000.',
|
|
39
|
+
type=int, default=40000000, show_default=True)
|
|
40
|
+
@click.option(
|
|
41
|
+
'--sensor-multiplier', '-d', help='An optional integer to be multiplied by '
|
|
42
|
+
'the grid count to yield a final number of the sensor count. This is useful '
|
|
43
|
+
'in workflows where the sensor grids are modified such as when calculating '
|
|
44
|
+
'view factor. Default: 1.', type=int, default=1)
|
|
45
|
+
def split_modifiers(
|
|
46
|
+
modifier_file, output_folder, sensor_count, grid_file, max_value,
|
|
47
|
+
sensor_multiplier
|
|
48
|
+
):
|
|
49
|
+
"""Split a list of modifiers into multiple files.
|
|
50
|
+
|
|
51
|
+
This command splits the modifiers based on the sensor count and the max value.
|
|
52
|
+
The max value is divided by the sensor count to calculate the maximum number
|
|
53
|
+
of modifiers in each distributed file of modifiers.
|
|
54
|
+
|
|
55
|
+
This command creates a new folder with evenly distributed modifiers. The folder
|
|
56
|
+
will include a ``_dist_info.json`` file which has the information to recreate the
|
|
57
|
+
original input files from this folder and the results generated based on the modifiers
|
|
58
|
+
in this folder.
|
|
59
|
+
|
|
60
|
+
``_redist_info.json`` file includes an array of JSON objects. Each object has
|
|
61
|
+
the distribution information, which in comparison to the command to split grids
|
|
62
|
+
is much simpler.
|
|
63
|
+
|
|
64
|
+
\b
|
|
65
|
+
Args:
|
|
66
|
+
modifier_file: Full path to a file with Radiance modifiers. The modifiers
|
|
67
|
+
must be the identifiers of the modifiers and not the actual Radiance
|
|
68
|
+
description of the modifiers.
|
|
69
|
+
output_folder: A new folder to write the newly created files.
|
|
70
|
+
"""
|
|
71
|
+
try:
|
|
72
|
+
if grid_file:
|
|
73
|
+
sg = SensorGrid.from_file(grid_file)
|
|
74
|
+
sensor_count = sg.count
|
|
75
|
+
sensor_count *= sensor_multiplier
|
|
76
|
+
|
|
77
|
+
with open(modifier_file, 'r') as file:
|
|
78
|
+
modifiers = [modifier.strip() for modifier in file.readlines()]
|
|
79
|
+
|
|
80
|
+
modifier_count = len(modifiers)
|
|
81
|
+
|
|
82
|
+
max_lines_per_file = int(max_value / sensor_count)
|
|
83
|
+
lines_per_file = max(1, max_lines_per_file)
|
|
84
|
+
dist_count = int(len(modifiers) / lines_per_file)
|
|
85
|
+
if len(modifiers) % lines_per_file != 0:
|
|
86
|
+
dist_count += 1
|
|
87
|
+
|
|
88
|
+
mod_per_dist = int(round(modifier_count / dist_count)) or 1
|
|
89
|
+
|
|
90
|
+
# create output folder
|
|
91
|
+
if not os.path.isdir(output_folder):
|
|
92
|
+
os.mkdir(output_folder)
|
|
93
|
+
|
|
94
|
+
remainder = modifier_count % dist_count # extra lines to distribute
|
|
95
|
+
|
|
96
|
+
start_index = 0
|
|
97
|
+
dist_info = []
|
|
98
|
+
out_dist_info = []
|
|
99
|
+
for i in range(dist_count):
|
|
100
|
+
end_index = start_index + mod_per_dist + (1 if i <= remainder else 0)
|
|
101
|
+
|
|
102
|
+
lines_to_write = modifiers[start_index:end_index]
|
|
103
|
+
|
|
104
|
+
dist_info.append(
|
|
105
|
+
{
|
|
106
|
+
'dist_info': [{'identifier': i}]
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
out_dist_info.append(
|
|
110
|
+
{
|
|
111
|
+
'identifier': str(i),
|
|
112
|
+
'count': len(lines_to_write)
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
# create a file and write the lines
|
|
116
|
+
with open(os.path.join(output_folder, '%s.mod' % i), 'w') as f:
|
|
117
|
+
f.write('\n'.join(lines_to_write))
|
|
118
|
+
|
|
119
|
+
# update the start index for the next iteration
|
|
120
|
+
start_index = end_index
|
|
121
|
+
|
|
122
|
+
dist_info_file = os.path.join(output_folder, '_redist_info.json')
|
|
123
|
+
with open(dist_info_file, 'w') as dist_out_file:
|
|
124
|
+
json.dump(dist_info, dist_out_file, indent=2)
|
|
125
|
+
|
|
126
|
+
dist_info_file = os.path.join(output_folder, '_info.json')
|
|
127
|
+
with open(dist_info_file, 'w') as dist_out_file:
|
|
128
|
+
json.dump(out_dist_info, dist_out_file, indent=2)
|
|
129
|
+
except Exception:
|
|
130
|
+
_logger.exception('Failed to distribute sensor grids in folder.')
|
|
131
|
+
sys.exit(1)
|
|
132
|
+
else:
|
|
133
|
+
sys.exit(0)
|