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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f6244a8a9d855497fd78c192ffbc9ede6cd337a9ed4ed1cbabdf654bb361056
4
- data.tar.gz: 5572eb94ca3afea26ea15449e58c762e57973fa763f65671b2855a291b79706f
3
+ metadata.gz: f86f836af76719222d4ce8393755cd79a6243e52e700c40f54808f72b6d9f2bf
4
+ data.tar.gz: 98176f5bb8055c871951f56b21717f9f6b3efc35e2b87ae5dbf98520cb2eb954
5
5
  SHA512:
6
- metadata.gz: 99de2b8728e7529d8046e5d5730c4edb66e159f93ab2776e60cab777d0de22fcc669034340a0d898fc8804f8007532f7df3caa5aaff845b08dce1fdb10f1a677
7
- data.tar.gz: d5ab7db18ff0b5f53431d898361314dc1650329da2471d63040590a7c119c4cd43b9b3e63de5bcb8ba7ffa2d83b6a255a9f44dfcf3b7cc41e8fed4f5ee3d217e
6
+ metadata.gz: 2d2ddb1e445513e442caf0e7ec9846ce9653a25291cead36e00d7b2ca41e3f8b928d5c5a8ffa79880c11d557293c2af22e211b7be525eef32db3ee315ce15209
7
+ data.tar.gz: 3f69624f318a6d3dc5dc8db9ddafd850dbe487bf9d135996cfa9e1cd91fb286cbb1f49e63fb825f6e19dba79bda98ee396a604e4c8fe2027e2e0249e91a323a9
data/.gitignore CHANGED
@@ -7,6 +7,7 @@
7
7
  /coverage/
8
8
  /doc/
9
9
  /pkg/
10
+ /spec/coverage/
10
11
  /spec/output/
11
12
  /spec/reports/
12
13
  /spec/test/
@@ -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.10.4'
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
@@ -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
- if @hash[:start_date]
52
- os_fi_schedule.setStartMonth(@hash[:start_date][0])
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
- unless @hash[:interpolate].nil?
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
- if @hash[:timestep]
77
- timestep = @hash[:timestep]
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
- # set is leap year = true in case start date has 3 integers
91
- if @hash[:start_date][2]
92
- year_description.setIsLeapYear(true)
93
- end
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
- start_date = year_description.makeDate(@hash[:start_date][0], @hash[:start_date][1])
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
- timeseries = OpenStudio::TimeSeries.new(start_date, openstudio_interval_length, OpenStudio.createVector(values), '')
99
- os_fi_schedule.setTimeSeries(timeseries)
140
+ num_values = values.size
141
+ i_values = 0
142
+ padded_values = []
143
+ date_times = []
144
+ pv = placeholder_value
100
145
 
101
- os_fi_schedule
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.10.4
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-02 00:00:00.000000000 Z
14
+ date: 2021-01-06 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler