honeybee-openstudio 2.10.4 → 2.11.0

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.
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