honeybee-openstudio 2.10.4 → 2.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/honeybee-openstudio.gemspec +1 -1
- data/lib/measures/from_honeybee_model/measure.rb +23 -0
- data/lib/to_openstudio/model.rb +64 -4
- data/lib/to_openstudio/schedule/fixed_interval.rb +146 -30
- data/lib/to_openstudio/schedule/ruleset.rb +2 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f86f836af76719222d4ce8393755cd79a6243e52e700c40f54808f72b6d9f2bf
|
4
|
+
data.tar.gz: 98176f5bb8055c871951f56b21717f9f6b3efc35e2b87ae5dbf98520cb2eb954
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d2ddb1e445513e442caf0e7ec9846ce9653a25291cead36e00d7b2ca41e3f8b928d5c5a8ffa79880c11d557293c2af22e211b7be525eef32db3ee315ce15209
|
7
|
+
data.tar.gz: 3f69624f318a6d3dc5dc8db9ddafd850dbe487bf9d135996cfa9e1cd91fb286cbb1f49e63fb825f6e19dba79bda98ee396a604e4c8fe2027e2e0249e91a323a9
|
data/.gitignore
CHANGED
data/honeybee-openstudio.gemspec
CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = 'honeybee-openstudio'
|
7
|
-
spec.version = '2.
|
7
|
+
spec.version = '2.11.0'
|
8
8
|
spec.authors = ['Tanushree Charan', 'Dan Macumber', 'Chris Mackey', 'Mostapha Sadeghipour Roudsari']
|
9
9
|
spec.email = ['tanushree.charan@nrel.gov', 'chris@ladybug.tools']
|
10
10
|
|
@@ -60,6 +60,19 @@ class FromHoneybeeModel < OpenStudio::Measure::ModelMeasure
|
|
60
60
|
model_json.setDisplayName('Path to the Honeybee Model JSON file')
|
61
61
|
args << model_json
|
62
62
|
|
63
|
+
# Make an argument for schedule csv dir
|
64
|
+
schedule_csv_dir = OpenStudio::Measure::OSArgument.makeStringArgument('schedule_csv_dir', false)
|
65
|
+
schedule_csv_dir.setDisplayName('Directory for exported CSV Schedules')
|
66
|
+
schedule_csv_dir.setDescription('If set, Fixed Interval Schedules will be translated to CSV Schedules in this directory')
|
67
|
+
schedule_csv_dir.setDefaultValue('')
|
68
|
+
args << schedule_csv_dir
|
69
|
+
|
70
|
+
# Make an argument for include datetimes
|
71
|
+
include_datetimes = OpenStudio::Measure::OSArgument.makeBoolArgument('include_datetimes', false)
|
72
|
+
include_datetimes.setDisplayName('Include date time column in exported CSV Schedules')
|
73
|
+
include_datetimes.setDefaultValue(false)
|
74
|
+
args << include_datetimes
|
75
|
+
|
63
76
|
return args
|
64
77
|
end
|
65
78
|
|
@@ -72,6 +85,8 @@ class FromHoneybeeModel < OpenStudio::Measure::ModelMeasure
|
|
72
85
|
end
|
73
86
|
|
74
87
|
model_json = runner.getStringArgumentValue('model_json', user_arguments)
|
88
|
+
schedule_csv_dir = runner.getStringArgumentValue('schedule_csv_dir', user_arguments)
|
89
|
+
include_datetimes = runner.getBoolArgumentValue('include_datetimes', user_arguments)
|
75
90
|
|
76
91
|
if !File.exist?(model_json)
|
77
92
|
runner.registerError("Cannot find file '#{model_json}'")
|
@@ -80,6 +95,14 @@ class FromHoneybeeModel < OpenStudio::Measure::ModelMeasure
|
|
80
95
|
|
81
96
|
honeybee_model = Honeybee::Model.read_from_disk(model_json)
|
82
97
|
|
98
|
+
if schedule_csv_dir && !schedule_csv_dir.empty?
|
99
|
+
if !Dir.exist?(schedule_csv_dir)
|
100
|
+
runner.registerError("Directory for exported CSV Schedules does not exist '#{schedule_csv_dir}'")
|
101
|
+
return false
|
102
|
+
end
|
103
|
+
honeybee_model.set_schedule_csv_dir(schedule_csv_dir, include_datetimes)
|
104
|
+
end
|
105
|
+
|
83
106
|
STDOUT.flush
|
84
107
|
honeybee_model.to_openstudio_model(model)
|
85
108
|
STDOUT.flush
|
data/lib/to_openstudio/model.rb
CHANGED
@@ -34,9 +34,38 @@ require 'honeybee/model'
|
|
34
34
|
require 'openstudio'
|
35
35
|
|
36
36
|
module Honeybee
|
37
|
+
|
38
|
+
def self.write_schedule_csv(schedule_csv_dir, schedule_csv)
|
39
|
+
filename = schedule_csv[:filename]
|
40
|
+
columns = schedule_csv[:columns]
|
41
|
+
if !columns.empty?
|
42
|
+
n = columns[0].size
|
43
|
+
path = File.join(schedule_csv_dir, filename)
|
44
|
+
File.open(path, 'w') do |file|
|
45
|
+
(0...n).each do |i|
|
46
|
+
row = []
|
47
|
+
columns.each do |column|
|
48
|
+
row << column[i]
|
49
|
+
end
|
50
|
+
file.puts row.join(',')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
37
56
|
class Model
|
38
57
|
|
39
58
|
attr_reader :openstudio_model
|
59
|
+
attr_reader :schedule_csv_dir, :include_datetimes, :schedule_csvs
|
60
|
+
|
61
|
+
# if a schedule csv dir is specified then ScheduleFixedIntervalAbridged objects
|
62
|
+
# will be translated to ScheduleFile objects instead of ScheduleFixedInterval
|
63
|
+
# the optional schedule_csv_include_datetimes argument controls whether schedule csv
|
64
|
+
# files include a first column of date times for verification
|
65
|
+
def set_schedule_csv_dir(schedule_csv_dir, include_datetimes = false)
|
66
|
+
@schedule_csv_dir = schedule_csv_dir
|
67
|
+
@include_datetimes = include_datetimes
|
68
|
+
end
|
40
69
|
|
41
70
|
# convert to openstudio model, clears errors and warnings
|
42
71
|
def to_openstudio_model(openstudio_model=nil, log_report=true)
|
@@ -99,7 +128,7 @@ module Honeybee
|
|
99
128
|
create_schedule_type_limits(@hash[:properties][:energy][:schedule_type_limits])
|
100
129
|
end
|
101
130
|
if @hash[:properties][:energy][:schedules]
|
102
|
-
create_schedules(@hash[:properties][:energy][:schedules])
|
131
|
+
create_schedules(@hash[:properties][:energy][:schedules], false, true)
|
103
132
|
end
|
104
133
|
|
105
134
|
if log_report
|
@@ -283,7 +312,32 @@ module Honeybee
|
|
283
312
|
end
|
284
313
|
end
|
285
314
|
|
286
|
-
def create_schedules(schedule_dicts, check_existing=false)
|
315
|
+
def create_schedules(schedule_dicts, check_existing=false, check_leap_year=true)
|
316
|
+
|
317
|
+
# clear out schedule_csvs
|
318
|
+
@schedule_csvs = {}
|
319
|
+
|
320
|
+
if check_leap_year
|
321
|
+
is_leap_year = :unknown
|
322
|
+
schedule_dicts.each do |schedule|
|
323
|
+
# set is leap year = true in case start date has 3 integers
|
324
|
+
this_leap_year = false
|
325
|
+
if schedule[:start_date] && schedule[:start_date][2]
|
326
|
+
this_leap_year = true
|
327
|
+
end
|
328
|
+
if is_leap_year == :unknown
|
329
|
+
is_leap_year = this_leap_year
|
330
|
+
elsif is_leap_year != this_leap_year
|
331
|
+
raise("Mixed leap year information.")
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
if is_leap_year != :unknown
|
336
|
+
year_description = openstudio_model.getYearDescription
|
337
|
+
year_description.setIsLeapYear(is_leap_year)
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
287
341
|
schedule_dicts.each do |schedule|
|
288
342
|
# check if there's already a schedule in the model with the identifier
|
289
343
|
add_obj = true
|
@@ -305,9 +359,15 @@ module Honeybee
|
|
305
359
|
else
|
306
360
|
raise("Unknown schedule type #{schedule_type}.")
|
307
361
|
end
|
308
|
-
schedule_object.to_openstudio(@openstudio_model)
|
362
|
+
schedule_object.to_openstudio(@openstudio_model, @schedule_csv_dir, @include_datetimes, @schedule_csvs)
|
309
363
|
end
|
310
364
|
end
|
365
|
+
|
366
|
+
# write schedule csvs
|
367
|
+
@schedule_csvs.each_value do |schedule_csv|
|
368
|
+
Honeybee.write_schedule_csv(@schedule_csv_dir, schedule_csv)
|
369
|
+
end
|
370
|
+
|
311
371
|
end
|
312
372
|
|
313
373
|
def create_program_types(program_dicts, check_existing=false)
|
@@ -343,7 +403,7 @@ module Honeybee
|
|
343
403
|
end
|
344
404
|
@@standards[:schedules].each do |schedule|
|
345
405
|
if schedule[:identifier] == 'Always On'
|
346
|
-
create_schedules([schedule], true)
|
406
|
+
create_schedules([schedule], true, false)
|
347
407
|
end
|
348
408
|
end
|
349
409
|
|
@@ -42,26 +42,60 @@ module Honeybee
|
|
42
42
|
nil
|
43
43
|
end
|
44
44
|
|
45
|
-
def to_openstudio(openstudio_model)
|
45
|
+
def to_openstudio(openstudio_model, schedule_csv_dir = nil, include_datetimes = nil, schedule_csvs = nil)
|
46
|
+
if schedule_csv_dir
|
47
|
+
to_schedule_file(openstudio_model, schedule_csv_dir, include_datetimes, schedule_csvs)
|
48
|
+
else
|
49
|
+
to_schedule_fixed_interval(openstudio_model)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def start_month
|
54
|
+
if @hash[:start_date]
|
55
|
+
return @hash[:start_date][0]
|
56
|
+
end
|
57
|
+
defaults[:start_date][:default][0]
|
58
|
+
end
|
59
|
+
|
60
|
+
def start_day
|
61
|
+
if @hash[:start_date]
|
62
|
+
return @hash[:start_date][1]
|
63
|
+
end
|
64
|
+
defaults[:start_date][:default][1]
|
65
|
+
end
|
66
|
+
|
67
|
+
def interpolate
|
68
|
+
if @hash[:interpolate]
|
69
|
+
return @hash[:interpolate]
|
70
|
+
end
|
71
|
+
defaults[:interpolate][:default]
|
72
|
+
end
|
73
|
+
|
74
|
+
def timestep
|
75
|
+
if @hash[:timestep]
|
76
|
+
return @hash[:timestep]
|
77
|
+
end
|
78
|
+
defaults[:timestep][:default]
|
79
|
+
end
|
80
|
+
|
81
|
+
def placeholder_value
|
82
|
+
if @hash[:placeholder_value]
|
83
|
+
return @hash[:placeholder_value]
|
84
|
+
end
|
85
|
+
defaults[:placeholder_value][:default]
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_schedule_fixed_interval(openstudio_model)
|
46
89
|
# create the new schedule
|
47
90
|
os_fi_schedule = OpenStudio::Model::ScheduleFixedInterval.new(openstudio_model)
|
48
91
|
os_fi_schedule.setName(@hash[:identifier])
|
49
92
|
|
50
93
|
# assign start date
|
51
|
-
|
52
|
-
|
53
|
-
os_fi_schedule.setStartDay(@hash[:start_date][1])
|
54
|
-
else
|
55
|
-
os_fi_schedule.setStartMonth(defaults[:start_date][:default][0])
|
56
|
-
os_fi_schedule.setStartDay(defaults[:start_date][:default][1])
|
57
|
-
end
|
94
|
+
os_fi_schedule.setStartMonth(start_month)
|
95
|
+
os_fi_schedule.setStartDay(start_day)
|
58
96
|
|
59
97
|
# assign the interpolate value
|
60
|
-
|
61
|
-
os_fi_schedule.setInterpolatetoTimestep(@hash[:interpolate])
|
62
|
-
else
|
63
|
-
os_fi_schedule.setInterpolatetoTimestep(defaults[:interpolate][:default])
|
64
|
-
end
|
98
|
+
os_fi_schedule.setInterpolatetoTimestep(interpolate)
|
65
99
|
|
66
100
|
# assign the schedule type limit
|
67
101
|
if @hash[:schedule_type_limit]
|
@@ -73,32 +107,114 @@ module Honeybee
|
|
73
107
|
end
|
74
108
|
|
75
109
|
# assign the timestep
|
76
|
-
|
77
|
-
|
78
|
-
interval_length = 60 / timestep
|
79
|
-
os_fi_schedule.setIntervalLength(interval_length)
|
80
|
-
else
|
81
|
-
timestep = defaults[:timestep][:default]
|
82
|
-
interval_length = 60 / timestep
|
83
|
-
os_fi_schedule.setIntervalLength(interval_length)
|
84
|
-
end
|
110
|
+
interval_length = 60 / timestep
|
111
|
+
os_fi_schedule.setIntervalLength(interval_length)
|
85
112
|
openstudio_interval_length = OpenStudio::Time.new(0, 0, interval_length)
|
86
113
|
|
87
114
|
# assign the values as a timeseries
|
88
115
|
year_description = openstudio_model.getYearDescription
|
116
|
+
start_date = year_description.makeDate(start_month, start_day)
|
117
|
+
timeseries = OpenStudio::TimeSeries.new(start_date, openstudio_interval_length, OpenStudio.createVector(@hash[:values]), '')
|
118
|
+
os_fi_schedule.setTimeSeries(timeseries)
|
89
119
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
120
|
+
os_fi_schedule
|
121
|
+
end
|
122
|
+
|
123
|
+
def to_schedule_file(openstudio_model, schedule_csv_dir, include_datetimes, schedule_csvs)
|
124
|
+
|
125
|
+
# in order to combine schedules in the same csv file they must have the same key
|
126
|
+
schedule_key = "#{@hash[:identifier]}_#{start_month}_#{start_day}_#{timestep}"
|
94
127
|
|
95
|
-
|
128
|
+
# get start and end date times
|
129
|
+
yd = openstudio_model.getYearDescription
|
130
|
+
date_time = OpenStudio::DateTime.new(yd.makeDate(1, 1), OpenStudio::Time.new(0,0,0))
|
131
|
+
start_date_time = OpenStudio::DateTime.new(yd.makeDate(start_month, start_day), OpenStudio::Time.new(0,0,0))
|
132
|
+
end_date_time = OpenStudio::DateTime.new(yd.makeDate(12, 31), OpenStudio::Time.new(1,0,0))
|
96
133
|
|
134
|
+
# get timestep
|
135
|
+
interval_length = 60 / timestep
|
136
|
+
dt = OpenStudio::Time.new(0, 0, interval_length, 0)
|
137
|
+
|
138
|
+
# get values and date times
|
97
139
|
values = @hash[:values]
|
98
|
-
|
99
|
-
|
140
|
+
num_values = values.size
|
141
|
+
i_values = 0
|
142
|
+
padded_values = []
|
143
|
+
date_times = []
|
144
|
+
pv = placeholder_value
|
100
145
|
|
101
|
-
|
146
|
+
while date_time < end_date_time
|
147
|
+
date = date_time.date
|
148
|
+
time = date_time.time
|
149
|
+
date_times << "#{date.dayOfMonth} #{date.monthOfYear.valueName} #{time.hours.to_s.rjust(2,'0')}:#{time.minutes.to_s.rjust(2,'0')}"
|
150
|
+
|
151
|
+
if date_time < start_date_time
|
152
|
+
padded_values << pv
|
153
|
+
elsif i_values < num_values
|
154
|
+
padded_values << values[i_values]
|
155
|
+
i_values += 1
|
156
|
+
else
|
157
|
+
padded_values << pv
|
158
|
+
end
|
159
|
+
|
160
|
+
date_time += dt
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
# find or create the schedule csv object which will hold the filename and columns
|
165
|
+
filename = nil
|
166
|
+
columns = nil
|
167
|
+
os_external_file = nil
|
168
|
+
schedule_csv = schedule_csvs[schedule_key]
|
169
|
+
if schedule_csv.nil?
|
170
|
+
# file name to write
|
171
|
+
filename = "#{@hash[:identifier]}.csv".gsub(' ', '_')
|
172
|
+
|
173
|
+
# columns of data
|
174
|
+
columns = []
|
175
|
+
if include_datetimes
|
176
|
+
columns << ['Date Times'].concat(date_times)
|
177
|
+
end
|
178
|
+
|
179
|
+
# schedule csv file must exist even though it has no content yet
|
180
|
+
path = File.join(schedule_csv_dir, filename)
|
181
|
+
if !File.exist?(path)
|
182
|
+
File.open(path, 'w') {|f| f.puts ''}
|
183
|
+
end
|
184
|
+
|
185
|
+
# get the external file which points to the schedule csv file
|
186
|
+
os_external_file = OpenStudio::Model::ExternalFile.getExternalFile(openstudio_model, filename)
|
187
|
+
os_external_file = os_external_file.get
|
188
|
+
|
189
|
+
schedule_csv = {filename: filename, columns: columns, os_external_file: os_external_file}
|
190
|
+
schedule_csvs[schedule_key] = schedule_csv
|
191
|
+
else
|
192
|
+
filename = schedule_csv[:filename]
|
193
|
+
columns = schedule_csv[:columns]
|
194
|
+
os_external_file = schedule_csv[:os_external_file]
|
195
|
+
end
|
196
|
+
|
197
|
+
# insert the padded_values to write later
|
198
|
+
columns << [@hash[:identifier]].concat(padded_values)
|
199
|
+
|
200
|
+
# create the schedule file
|
201
|
+
column = columns.size # 1 based index
|
202
|
+
rowsToSkip = 1
|
203
|
+
os_schedule_file = OpenStudio::Model::ScheduleFile.new(os_external_file, column, rowsToSkip)
|
204
|
+
os_schedule_file.setName(@hash[:identifier])
|
205
|
+
os_schedule_file.setInterpolatetoTimestep(interpolate)
|
206
|
+
os_schedule_file.setMinutesperItem(interval_length.to_s)
|
207
|
+
|
208
|
+
# assign the schedule type limit
|
209
|
+
if @hash[:schedule_type_limit]
|
210
|
+
schedule_type_limit = openstudio_model.getScheduleTypeLimitsByName(@hash[:schedule_type_limit])
|
211
|
+
unless schedule_type_limit.empty?
|
212
|
+
schedule_type_limit_object = schedule_type_limit.get
|
213
|
+
os_schedule_file.setScheduleTypeLimits(schedule_type_limit_object)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
os_schedule_file
|
102
218
|
end
|
103
219
|
|
104
220
|
end #ScheduleFixedIntervalAbridged
|
@@ -42,7 +42,8 @@ module Honeybee
|
|
42
42
|
nil
|
43
43
|
end
|
44
44
|
|
45
|
-
def to_openstudio(openstudio_model)
|
45
|
+
def to_openstudio(openstudio_model, schedule_csv_dir = nil, include_datetimes = nil, schedule_csvs = nil)
|
46
|
+
|
46
47
|
# create openstudio schedule ruleset object
|
47
48
|
os_sch_ruleset = OpenStudio::Model::ScheduleRuleset.new(openstudio_model)
|
48
49
|
os_sch_ruleset.setName(@hash[:identifier])
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: honeybee-openstudio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tanushree Charan
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: exe
|
13
13
|
cert_chain: []
|
14
|
-
date: 2021-01-
|
14
|
+
date: 2021-01-06 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|