honeybee-radiance-postprocess 0.4.555__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.
Files changed (50) hide show
  1. honeybee_radiance_postprocess/__init__.py +1 -0
  2. honeybee_radiance_postprocess/__main__.py +4 -0
  3. honeybee_radiance_postprocess/annual.py +73 -0
  4. honeybee_radiance_postprocess/annualdaylight.py +289 -0
  5. honeybee_radiance_postprocess/annualirradiance.py +35 -0
  6. honeybee_radiance_postprocess/breeam/__init__.py +1 -0
  7. honeybee_radiance_postprocess/breeam/breeam.py +552 -0
  8. honeybee_radiance_postprocess/cli/__init__.py +33 -0
  9. honeybee_radiance_postprocess/cli/abnt.py +392 -0
  10. honeybee_radiance_postprocess/cli/breeam.py +96 -0
  11. honeybee_radiance_postprocess/cli/datacollection.py +133 -0
  12. honeybee_radiance_postprocess/cli/grid.py +295 -0
  13. honeybee_radiance_postprocess/cli/leed.py +143 -0
  14. honeybee_radiance_postprocess/cli/merge.py +161 -0
  15. honeybee_radiance_postprocess/cli/mtxop.py +161 -0
  16. honeybee_radiance_postprocess/cli/postprocess.py +1092 -0
  17. honeybee_radiance_postprocess/cli/schedule.py +103 -0
  18. honeybee_radiance_postprocess/cli/translate.py +216 -0
  19. honeybee_radiance_postprocess/cli/two_phase.py +252 -0
  20. honeybee_radiance_postprocess/cli/util.py +121 -0
  21. honeybee_radiance_postprocess/cli/viewfactor.py +157 -0
  22. honeybee_radiance_postprocess/cli/well.py +110 -0
  23. honeybee_radiance_postprocess/data_type.py +102 -0
  24. honeybee_radiance_postprocess/dynamic.py +273 -0
  25. honeybee_radiance_postprocess/electriclight.py +24 -0
  26. honeybee_radiance_postprocess/en17037.py +304 -0
  27. honeybee_radiance_postprocess/helper.py +266 -0
  28. honeybee_radiance_postprocess/ies/__init__.py +1 -0
  29. honeybee_radiance_postprocess/ies/lm.py +224 -0
  30. honeybee_radiance_postprocess/ies/lm_schedule.py +248 -0
  31. honeybee_radiance_postprocess/leed/__init__.py +1 -0
  32. honeybee_radiance_postprocess/leed/leed.py +801 -0
  33. honeybee_radiance_postprocess/leed/leed_schedule.py +256 -0
  34. honeybee_radiance_postprocess/metrics.py +439 -0
  35. honeybee_radiance_postprocess/reader.py +80 -0
  36. honeybee_radiance_postprocess/results/__init__.py +4 -0
  37. honeybee_radiance_postprocess/results/annual_daylight.py +752 -0
  38. honeybee_radiance_postprocess/results/annual_irradiance.py +196 -0
  39. honeybee_radiance_postprocess/results/results.py +1416 -0
  40. honeybee_radiance_postprocess/type_hints.py +38 -0
  41. honeybee_radiance_postprocess/util.py +211 -0
  42. honeybee_radiance_postprocess/vis_metadata.py +49 -0
  43. honeybee_radiance_postprocess/well/__init__.py +1 -0
  44. honeybee_radiance_postprocess/well/well.py +509 -0
  45. honeybee_radiance_postprocess-0.4.555.dist-info/METADATA +79 -0
  46. honeybee_radiance_postprocess-0.4.555.dist-info/RECORD +50 -0
  47. honeybee_radiance_postprocess-0.4.555.dist-info/WHEEL +5 -0
  48. honeybee_radiance_postprocess-0.4.555.dist-info/entry_points.txt +2 -0
  49. honeybee_radiance_postprocess-0.4.555.dist-info/licenses/LICENSE +661 -0
  50. honeybee_radiance_postprocess-0.4.555.dist-info/top_level.txt +1 -0
@@ -0,0 +1,103 @@
1
+ """honeybee radiance postprocess schedule commands."""
2
+ import click
3
+ import sys
4
+ import logging
5
+
6
+ from ..results.annual_daylight import AnnualDaylight
7
+ from ..dynamic import DynamicSchedule
8
+
9
+ _logger = logging.getLogger(__name__)
10
+
11
+
12
+ @click.group(help='Commands to create schedules for Radiance results.')
13
+ def schedule():
14
+ pass
15
+
16
+
17
+ @schedule.command('control-schedules')
18
+ @click.argument(
19
+ 'folder',
20
+ type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True)
21
+ )
22
+ @click.option(
23
+ '--base-schedule-file', '-bs', help='Path to a schedule file. A list of '
24
+ '8760 fractional values for the lighting schedule representing the usage '
25
+ 'of lights without any daylight controls. The values of this schedule '
26
+ 'will be multiplied by the hourly dimming fraction to yield the output '
27
+ 'lighting schedules. If None, a schedule from 9AM to 5PM on weekdays will '
28
+ 'be used.',
29
+ type=click.Path(exists=False, file_okay=True, dir_okay=False, resolve_path=True)
30
+ )
31
+ @click.option(
32
+ '--states', '-st', help='A JSON file with a dictionary of states. If states '
33
+ 'are not provided the default states will be used for any aperture groups.',
34
+ default=None, show_default=True,
35
+ type=click.Path(exists=False, file_okay=True, dir_okay=False, resolve_path=True)
36
+ )
37
+ @click.option(
38
+ '--grids-filter', '-gf', help='A pattern to filter the grids.', default='*',
39
+ show_default=True
40
+ )
41
+ @click.option(
42
+ '--ill-setpoint',
43
+ help='A number for the illuminance setpoint in lux beyond which electric '
44
+ 'lights are dimmed if there is sufficient daylight.', default=300,
45
+ type=float, show_default=True
46
+ )
47
+ @click.option(
48
+ '--min-power-in',
49
+ help='A number between 0 and 1 for the the lowest power the lighting '
50
+ 'system can dim down to, expressed as a fraction of maximum input power.',
51
+ default=0.3, type=float, show_default=True
52
+ )
53
+ @click.option(
54
+ '--min-light-out',
55
+ help='A number between 0 and 1 the lowest lighting output the lighting '
56
+ 'system can dim down to, expressed as a fraction of maximum light output. '
57
+ 'Note that setting this to 1 means lights are not dimmed at all until the '
58
+ 'illuminance setpoint is reached.', default=0.2, type=float,
59
+ show_default=True
60
+ )
61
+ @click.option(
62
+ '--off-at-min', is_flag=True, default=False, help='Boolean to '
63
+ 'note whether lights should switch off completely when they get to the '
64
+ 'minimum power input.'
65
+ )
66
+ @click.option(
67
+ '--sub-folder', '-sf', help='Optional relative path for subfolder to '
68
+ 'write output schedule files.', default='schedules'
69
+ )
70
+ def control_schedules(
71
+ folder, states, grids_filter, base_schedule_file, ill_setpoint,
72
+ min_power_in, min_light_out, off_at_min, sub_folder
73
+ ):
74
+ """Generate electric lighting schedules from annual daylight results.
75
+
76
+ \b
77
+ Args:
78
+ folder: Results folder. This folder is an output folder of annual daylight
79
+ recipe. Folder should include grids_info.json and sun-up-hours.txt. The
80
+ command uses the list in grids_info.json to find the result files for each
81
+ sensor grid.
82
+ """
83
+ try:
84
+ if base_schedule_file:
85
+ with open(base_schedule_file) as base_schedule:
86
+ base_schedule = [float(h) for h in base_schedule.readlines()]
87
+ else:
88
+ base_schedule = None
89
+
90
+ if states:
91
+ states = DynamicSchedule.from_json(states)
92
+
93
+ results = AnnualDaylight(folder)
94
+ results.daylight_control_schedules_to_folder(
95
+ sub_folder, states=states, grids_filter=grids_filter,
96
+ base_schedule=base_schedule, ill_setpoint=ill_setpoint,
97
+ min_power_in=min_power_in, min_light_out=min_light_out,
98
+ off_at_min=off_at_min)
99
+ except Exception:
100
+ _logger.exception('Failed to generate control schedules.')
101
+ sys.exit(1)
102
+ else:
103
+ sys.exit(0)
@@ -0,0 +1,216 @@
1
+ """Commands to translate objects."""
2
+ import sys
3
+ import logging
4
+ from pathlib import Path
5
+ import shutil
6
+ import click
7
+ import json
8
+ try:
9
+ import cupy as np
10
+ is_gpu = True
11
+ except ImportError:
12
+ is_gpu = False
13
+ import numpy as np
14
+
15
+ from ..reader import binary_to_array
16
+ from ..util import get_delimiter
17
+
18
+ _logger = logging.getLogger(__name__)
19
+
20
+
21
+ @click.group(help='Commands to translate objects.')
22
+ def translate():
23
+ pass
24
+
25
+
26
+ @translate.command('npy-to-txt')
27
+ @click.argument(
28
+ 'npy-file', type=click.Path(exists=True, dir_okay=False, resolve_path=True)
29
+ )
30
+ @click.option(
31
+ '--name', '-n', help='Output file name.', default='output', show_default=True
32
+ )
33
+ @click.option(
34
+ '--output-folder', '-of', help='Output folder.', default='.',
35
+ type=click.Path(exists=False, file_okay=False, dir_okay=True, resolve_path=True)
36
+ )
37
+ @click.option(
38
+ '--extension', '-ext', help='Output file extension', default='txt', show_default=True
39
+ )
40
+ @click.option(
41
+ '--output-format', '-fmt', help='Output format for each element in the array',
42
+ default='%.7e', show_default=True
43
+ )
44
+ @click.option(
45
+ '--delimiter', '-d', help='Delimiter in the text file.', default='\t',
46
+ type=click.Choice(['\t', ' ', ',', ';', 'tab', 'space', 'comma', 'semicolon'])
47
+ )
48
+ def npy_to_txt(npy_file, name, output_folder, extension, output_format, delimiter):
49
+ """Convert a npy file to text file.
50
+
51
+ This command reads a NumPy array from a npy file and saves it as readable file. The
52
+ converted file is tab separated.
53
+
54
+ \b
55
+ Args:
56
+ npy-file: Path to npy file.
57
+ """
58
+ try:
59
+ delimiter = get_delimiter(delimiter)
60
+ array = np.load(npy_file)
61
+ output = Path(output_folder, f'{name}.{extension}')
62
+ output.parent.mkdir(parents=True, exist_ok=True)
63
+ np.savetxt(output, array, fmt=output_format, delimiter=delimiter)
64
+
65
+ except Exception:
66
+ _logger.exception('Converting npy file to text file failed.')
67
+ sys.exit(1)
68
+ else:
69
+ sys.exit(0)
70
+
71
+
72
+ @translate.command('txt-to-npy')
73
+ @click.argument(
74
+ 'txt-file', type=click.Path(exists=True, dir_okay=False, resolve_path=True)
75
+ )
76
+ @click.option(
77
+ '--name', '-n', help='Output file name.', default='output', show_default=True
78
+ )
79
+ @click.option(
80
+ '--output-folder', '-of', help='Output folder.', default='.',
81
+ type=click.Path(exists=False, file_okay=False, dir_okay=True, resolve_path=True)
82
+ )
83
+ @click.option(
84
+ '--delimiter', '-d', help='Delimiter in the text file.', default='\t',
85
+ type=click.Choice(['\t', ' ', ',', ';', 'tab', 'space', 'comma', 'semicolon'])
86
+ )
87
+ def txt_to_npy(txt_file, name, output_folder, delimiter):
88
+ """Convert a text file to npy file.
89
+
90
+ This command reads a separated text file and saves it as a NumPy file. As
91
+ an example the input file could be the annual illuminance values.
92
+
93
+ \b
94
+ Args:
95
+ txt-file: Path to text file.
96
+ """
97
+ try:
98
+ delimiter = get_delimiter(delimiter)
99
+ array = np.genfromtxt(txt_file, dtype=np.float32, delimiter=delimiter)
100
+ output = Path(output_folder, name)
101
+ output.parent.mkdir(parents=True, exist_ok=True)
102
+ np.save(output, array)
103
+
104
+ except Exception:
105
+ _logger.exception('Converting text file to npy file failed.')
106
+ sys.exit(1)
107
+ else:
108
+ sys.exit(0)
109
+
110
+
111
+ @translate.command('binary-to-npy')
112
+ @click.argument(
113
+ 'mtx-file', type=click.Path(exists=True, dir_okay=False, resolve_path=True)
114
+ )
115
+ @click.option(
116
+ '--conversion', help='Conversion as a string. This option is useful to post-process '
117
+ 'the results from 3 RGB components into one as part of this command.'
118
+ )
119
+ @click.option(
120
+ '--name', '-n', help='Output file name.', default='output', show_default=True
121
+ )
122
+ @click.option(
123
+ '--output-folder', '-of', help='Output folder.', default='.',
124
+ type=click.Path(exists=False, file_okay=False, dir_okay=True, resolve_path=True)
125
+ )
126
+ def binary_to_npy(mtx_file, conversion, name, output_folder):
127
+ """Convert a binary Radiance file to a npy file.
128
+
129
+ This command reads a binary Radiance matrix file and saves it as a NumPy file.
130
+
131
+ \b
132
+ Args:
133
+ mtx-file: Path to binary Radiance file.
134
+ """
135
+ try:
136
+ array = binary_to_array(mtx_file)
137
+ if conversion:
138
+ conversion = list(map(float, conversion.split(' ')))
139
+ conversion = np.array(conversion, dtype=np.float32)
140
+ array = np.dot(array, conversion)
141
+ output = Path(output_folder, name)
142
+ output.parent.mkdir(parents=True, exist_ok=True)
143
+ np.save(output, array)
144
+
145
+ except Exception:
146
+ _logger.exception('Converting binary Radiance file to npy file failed.')
147
+ sys.exit(1)
148
+ else:
149
+ sys.exit(0)
150
+
151
+
152
+ @translate.command('annual-daylight-npy-to-ill')
153
+ @click.argument(
154
+ 'folder',
155
+ type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True)
156
+ )
157
+ @click.option(
158
+ '--output-folder', '-of', help='Output folder. If not provided the output '
159
+ 'folder will be created in the same directory as the results folder. The '
160
+ 'new folder will be called results_ill.', default=None,
161
+ type=click.Path(exists=False, file_okay=False, dir_okay=True, resolve_path=True)
162
+ )
163
+ def annual_daylight_npy_to_ill(folder, output_folder):
164
+ """Convert an annual daylight results folder to older version.
165
+
166
+ This command reads an annual daylight results folder with results saved as
167
+ npy files (NumPy), and converts the npy files to text files in the old
168
+ results folder format.
169
+
170
+ \b
171
+ Args:
172
+ folder: Results folder. This folder is an output folder of annual
173
+ daylight recipe. Folder should include grids_info.json and
174
+ sun-up-hours.txt.
175
+ """
176
+ try:
177
+ folder = Path(folder)
178
+ static_ill_folder = folder.joinpath('__static_apertures__/default/total')
179
+ if not static_ill_folder.exists():
180
+ raise FileNotFoundError(
181
+ 'No results were found for static apertures in the results '
182
+ 'folder.')
183
+ grids_info_file = folder.joinpath('grids_info.json')
184
+ if not grids_info_file.exists():
185
+ raise FileNotFoundError(
186
+ 'The file grids_info.json was not found in the results folder.')
187
+ sun_up_hours_file = folder.joinpath('sun-up-hours.txt')
188
+ if not sun_up_hours_file.exists():
189
+ raise FileNotFoundError(
190
+ 'The file sun-up-hours.txt was not found in the results folder.')
191
+
192
+ if output_folder is None:
193
+ output_folder = folder.parent.joinpath('results_ill')
194
+ else:
195
+ output_folder = Path(output_folder)
196
+ output_folder.mkdir(parents=True, exist_ok=True)
197
+
198
+ with open(grids_info_file) as json_file:
199
+ grids_info = json.load(json_file)
200
+
201
+ for grid_info in grids_info:
202
+ full_id = grid_info['full_id']
203
+ npy_file = static_ill_folder.joinpath(f'{full_id}.npy')
204
+
205
+ array = np.load(npy_file)
206
+ output = Path(output_folder, full_id + '.ill')
207
+ np.savetxt(output, array, fmt='%.7e', delimiter='\t')
208
+
209
+ # copy grids_info and sun-up-hours
210
+ shutil.copy(grids_info_file, output_folder.joinpath('grids_info.json'))
211
+ shutil.copy(sun_up_hours_file, output_folder.joinpath('sun-up-hours.txt'))
212
+ except Exception:
213
+ _logger.exception('Converting annual daylight results folder failed.')
214
+ sys.exit(1)
215
+ else:
216
+ sys.exit(0)
@@ -0,0 +1,252 @@
1
+ """Commands to work with two phase Radiance matrices using NumPy."""
2
+ import sys
3
+ import logging
4
+ from pathlib import Path
5
+ import click
6
+ try:
7
+ import cupy as np
8
+ is_gpu = True
9
+ except ImportError:
10
+ is_gpu = False
11
+ import numpy as np
12
+
13
+ from ..reader import binary_to_array, ascii_to_array
14
+
15
+ _logger = logging.getLogger(__name__)
16
+
17
+
18
+ @click.group(help='Commands to work with two phase Radiance matrices using NumPy.')
19
+ def two_phase():
20
+ pass
21
+
22
+
23
+ @two_phase.command('rgb-to-illuminance')
24
+ @click.argument(
25
+ 'total-mtx', type=click.Path(exists=True, dir_okay=False, resolve_path=True)
26
+ )
27
+ @click.argument(
28
+ 'direct-mtx', type=click.Path(exists=True, dir_okay=False, resolve_path=True)
29
+ )
30
+ @click.argument(
31
+ 'direct-sunlight-mtx', type=click.Path(exists=True, dir_okay=False, resolve_path=True)
32
+ )
33
+ @click.option(
34
+ '--binary/--ascii', is_flag=True, default=True, help='Switch between binary '
35
+ 'and ascii input matrices. Default is binary.'
36
+ )
37
+ @click.option(
38
+ '--float32/--float16', is_flag=True, default=True, help='Switch between float32 '
39
+ 'and float16 output matrices. Default is float32.'
40
+ )
41
+ @click.option(
42
+ '--total-name', '-n', help='Total output file name.', default='total',
43
+ show_default=True
44
+ )
45
+ @click.option(
46
+ '--direct-name', '-n', help='Direct output file name.', default='direct',
47
+ show_default=True
48
+ )
49
+ @click.option(
50
+ '--output-folder', '-of', help='Output folder.', default='.',
51
+ type=click.Path(exists=False, file_okay=False, dir_okay=True, resolve_path=True)
52
+ )
53
+ def rgb_to_illuminance(
54
+ total_mtx, direct_mtx, direct_sunlight_mtx, binary, float32, total_name,
55
+ direct_name, output_folder
56
+ ):
57
+ """Process results of two phase simulations (total, direct, direct sunlight).
58
+
59
+ The function will replace the direct illuminance with the direct sunlight
60
+ illuminance: total - direct + direct_sunlight.
61
+
62
+ The function has two output files. One for the illuminance of the above
63
+ calculation, and one for the direct sunlight illuminance. In both cases
64
+ the conversion from RGB to illuminance is executed.
65
+
66
+ \b
67
+ Args:
68
+ total-mtx: Path to total matrix.
69
+ direct-mtx: Path to direct matrix.
70
+ direct-sunlight-mtx: Path to direct sunlight matrix.
71
+ """
72
+ try:
73
+ if binary:
74
+ total = binary_to_array(total_mtx)
75
+ direct = binary_to_array(direct_mtx)
76
+ direct_sunlight = binary_to_array(direct_sunlight_mtx)
77
+ else:
78
+ total = ascii_to_array(total_mtx)
79
+ direct = ascii_to_array(direct_mtx)
80
+ direct_sunlight = ascii_to_array(direct_sunlight_mtx)
81
+
82
+ data = total - direct + direct_sunlight
83
+
84
+ conversion = np.array([47.4, 119.9, 11.6], dtype=np.float32)
85
+ total_illuminance = np.dot(data, conversion)
86
+ direct_sunlight_illuminance = np.dot(direct_sunlight, conversion)
87
+
88
+ if not float32:
89
+ total_illuminance = np.minimum(
90
+ total_illuminance, np.finfo(np.float16).max).astype(
91
+ np.float16)
92
+ direct_sunlight_illuminance = np.minimum(
93
+ direct_sunlight_illuminance, np.finfo(np.float16).max).astype(
94
+ np.float16)
95
+
96
+ # save total illuminance
97
+ total_output = Path(output_folder, total_name)
98
+ total_output.parent.mkdir(parents=True, exist_ok=True)
99
+ np.save(total_output, total_illuminance)
100
+
101
+ # save direct sunlight illuminance
102
+ direct_output = Path(output_folder, direct_name)
103
+ direct_output.parent.mkdir(parents=True, exist_ok=True)
104
+
105
+ np.save(direct_output, direct_sunlight_illuminance)
106
+
107
+ except Exception:
108
+ _logger.exception('Processing annual results failed.')
109
+ sys.exit(1)
110
+ else:
111
+ sys.exit(0)
112
+
113
+
114
+ @two_phase.command('rgb-to-illuminance-file')
115
+ @click.argument(
116
+ 'mtx-file', type=click.Path(exists=True, dir_okay=False, resolve_path=True)
117
+ )
118
+ @click.option(
119
+ '--binary/--ascii', is_flag=True, default=True, help='Switch between binary '
120
+ 'and ascii input matrices. Default is binary.'
121
+ )
122
+ @click.option(
123
+ '--float32/--float16', is_flag=True, default=True, help='Switch between float32 '
124
+ 'and float16 output matrices. Default is float32.'
125
+ )
126
+ @click.option(
127
+ '--name', '-n', help='Name of output file.', default='illuminance',
128
+ show_default=True
129
+ )
130
+ @click.option(
131
+ '--output-folder', '-of', help='Output folder.', default='.',
132
+ type=click.Path(exists=False, file_okay=False, dir_okay=True, resolve_path=True)
133
+ )
134
+ def rgb_to_illuminance_file(
135
+ mtx_file, binary, float32, name, output_folder
136
+ ):
137
+ """Convert a RGB Radiance matrix to illuminance and save the array as a
138
+ NumPy file.
139
+
140
+ \b
141
+ Args:
142
+ mtx-file: Path to matrix file to convert.
143
+ """
144
+ try:
145
+ if binary:
146
+ mtx = binary_to_array(mtx_file)
147
+ else:
148
+ mtx = ascii_to_array(mtx_file)
149
+
150
+ conversion = np.array([47.4, 119.9, 11.6], dtype=np.float32)
151
+ total_illuminance = np.dot(mtx, conversion)
152
+
153
+ if not float32:
154
+ total_illuminance = np.minimum(
155
+ total_illuminance, np.finfo(np.float16).max).astype(
156
+ np.float16)
157
+
158
+ # save total illuminance
159
+ total_output = Path(output_folder, name)
160
+ total_output.parent.mkdir(parents=True, exist_ok=True)
161
+ np.save(total_output, total_illuminance)
162
+
163
+ except Exception:
164
+ _logger.exception('Processing annual results failed.')
165
+ sys.exit(1)
166
+ else:
167
+ sys.exit(0)
168
+
169
+
170
+ @two_phase.command('add-remove-sky-matrix')
171
+ @click.argument(
172
+ 'total-mtx', type=click.Path(exists=True, dir_okay=False, resolve_path=True)
173
+ )
174
+ @click.argument(
175
+ 'direct-mtx', type=click.Path(exists=True, dir_okay=False, resolve_path=True)
176
+ )
177
+ @click.argument(
178
+ 'direct-sunlight-mtx', type=click.Path(exists=True, dir_okay=False, resolve_path=True)
179
+ )
180
+ @click.option(
181
+ '--binary/--ascii', is_flag=True, default=True, help='Switch between binary '
182
+ 'and ascii input matrices. Default is binary.'
183
+ )
184
+ @click.option(
185
+ '--float32/--float16', is_flag=True, default=True, help='Switch between float32 '
186
+ 'and float16 output matrices. Default is float32.'
187
+ )
188
+ @click.option(
189
+ '--total-name', '-n', help='Total output file name.', default='total',
190
+ show_default=True
191
+ )
192
+ @click.option(
193
+ '--direct-name', '-n', help='Direct output file name.', default='direct',
194
+ show_default=True
195
+ )
196
+ @click.option(
197
+ '--output-folder', '-of', help='Output folder.', default='.',
198
+ type=click.Path(exists=False, file_okay=False, dir_okay=True, resolve_path=True)
199
+ )
200
+ def add_remove_sky_matrix(
201
+ total_mtx, direct_mtx, direct_sunlight_mtx, binary, float32, total_name,
202
+ direct_name, output_folder
203
+ ):
204
+ """Process results of two phase simulations (total, direct, direct sunlight).
205
+
206
+ The function will replace the direct values with the direct sunlight
207
+ values: total - direct + direct_sunlight.
208
+
209
+ The function has two output files. One for the results of the above
210
+ calculation, and one for the direct sunlight values.
211
+
212
+ \b
213
+ Args:
214
+ total-mtx: Path to total matrix.
215
+ direct-mtx: Path to direct matrix.
216
+ direct-sunlight-mtx: Path to direct sunlight matrix.
217
+ """
218
+ try:
219
+ if binary:
220
+ total = binary_to_array(total_mtx)
221
+ direct = binary_to_array(direct_mtx)
222
+ direct_sunlight = binary_to_array(direct_sunlight_mtx)
223
+ else:
224
+ total = ascii_to_array(total_mtx)
225
+ direct = ascii_to_array(direct_mtx)
226
+ direct_sunlight = ascii_to_array(direct_sunlight_mtx)
227
+
228
+ data = total - direct + direct_sunlight
229
+
230
+ if not float32:
231
+ data = np.minimum(
232
+ data, np.finfo(np.float16).max).astype(
233
+ np.float16)
234
+ direct_sunlight = np.minimum(
235
+ direct_sunlight, np.finfo(np.float16).max).astype(
236
+ np.float16)
237
+
238
+ # save total values
239
+ total_output = Path(output_folder, total_name)
240
+ total_output.parent.mkdir(parents=True, exist_ok=True)
241
+ np.save(total_output, data)
242
+
243
+ # save direct values
244
+ direct_output = Path(output_folder, direct_name)
245
+ direct_output.parent.mkdir(parents=True, exist_ok=True)
246
+ np.save(direct_output, direct_sunlight)
247
+
248
+ except Exception:
249
+ _logger.exception('Processing annual results failed.')
250
+ sys.exit(1)
251
+ else:
252
+ sys.exit(0)
@@ -0,0 +1,121 @@
1
+ from ladybug.analysisperiod import AnalysisPeriod
2
+ from datetime import datetime
3
+ import copy
4
+
5
+
6
+ def get_hoys(start_date, start_time, end_date, end_time, timestep, leap_year):
7
+ """Return list of hours from start date, star hour, end date and end hour.
8
+
9
+ Date should be formatted as MMM-DD (e.g JUL-21) and hours must be formatted
10
+ as HH:MM (e.g 18:30).
11
+ """
12
+ # convert datetimes
13
+ try:
14
+ start_date = datetime.strptime(start_date, '%b-%d')
15
+ except ValueError as e:
16
+ raise ValueError('Wrong input for start date:\n\t{}'.format(e))
17
+ try:
18
+ start_time = datetime.strptime(start_time, '%H:%M')
19
+ except ValueError as e:
20
+ raise ValueError('Wrong input for start time:\n\t{}'.format(e))
21
+ try:
22
+ end_date = datetime.strptime(end_date, '%b-%d')
23
+ except ValueError as e:
24
+ raise ValueError('Wrong input for end date:\n\t{}'.format(e))
25
+ try:
26
+ end_time = datetime.strptime(end_time, '%H:%M')
27
+ except ValueError as e:
28
+ raise ValueError('Wrong input for end time:\n\t{}'.format(e))
29
+
30
+ org_end_time = copy.copy(end_time)
31
+ if end_time.minute != 0:
32
+ if end_time.hour != 23:
33
+ end_time = datetime(
34
+ end_time.year, end_time.month, end_time.day, end_time.hour + 1, 0
35
+ )
36
+ else:
37
+ end_time = datetime(
38
+ end_time.year, end_time.month, end_time.day + 1, 0, 0
39
+ )
40
+ ap = AnalysisPeriod(
41
+ start_date.month, start_date.day, start_time.hour,
42
+ end_date.month, end_date.day, end_time.hour,
43
+ timestep, leap_year
44
+ )
45
+
46
+ hoys = ap.hoys
47
+
48
+ # filter start and end hours if needed
49
+ start_index = 0
50
+ end_index = None
51
+ if start_time.minute != 0:
52
+ # remove the hours that are smaller than this hour
53
+ for start_index, h in enumerate(hoys):
54
+ if round(60 * h) % 60 >= start_time.minute:
55
+ break
56
+
57
+ if org_end_time.minute != 0:
58
+ for end_index, h in enumerate(reversed(hoys)):
59
+ if (60 * h) % 60 <= org_end_time.minute:
60
+ break
61
+
62
+ if start_index == 0 and end_index is None:
63
+ return hoys
64
+ elif end_index is None:
65
+ return hoys[start_index:]
66
+ else:
67
+ return hoys[start_index: -1 * (end_index + 1)]
68
+
69
+
70
+ def handle_operator(operator):
71
+ """Handle operator for rmtxopt command."""
72
+ if operator == '+':
73
+ return '+'
74
+ elif operator == '-':
75
+ return '+ -s 1.0'
76
+ elif operator == '/':
77
+ return '/'
78
+ elif operator == '*':
79
+ return '"*"'
80
+ else:
81
+ raise ValueError('Invalid operator: %s' % operator)
82
+
83
+
84
+ def remove_header(input_file):
85
+ """Remove the header text from a Radiance matrix file."""
86
+ inf = open(input_file)
87
+ first_line = next(inf)
88
+ if first_line[:10] == '#?RADIANCE':
89
+ for line in inf:
90
+ if line[:7] == 'FORMAT=':
91
+ # pass next empty line
92
+ next(inf)
93
+ first_line = next(inf)
94
+ break
95
+ continue
96
+ return first_line, inf
97
+
98
+
99
+ def get_compare_func(include_min, include_max, comply):
100
+ if include_max and include_min:
101
+ if comply:
102
+ compare = lambda value, minimum, maximum: minimum <= value <= maximum
103
+ else:
104
+ compare = lambda value, minimum, maximum: not (minimum <= value <= maximum)
105
+ elif not include_max and not include_min:
106
+ if comply:
107
+ compare = lambda value, minimum, maximum: minimum < value < maximum
108
+ else:
109
+ compare = lambda value, minimum, maximum: not (minimum < value < maximum)
110
+ elif include_max and not include_min:
111
+ if comply:
112
+ compare = lambda value, minimum, maximum: minimum < value <= maximum
113
+ else:
114
+ compare = lambda value, minimum, maximum: not (minimum < value <= maximum)
115
+ elif not include_max and include_min:
116
+ if comply:
117
+ compare = lambda value, minimum, maximum: minimum <= value < maximum
118
+ else:
119
+ compare = lambda value, minimum, maximum: not (minimum <= value < maximum)
120
+
121
+ return compare