honeybee-openstudio 2.10.4 → 2.12.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -35,6 +35,14 @@ require 'to_openstudio/model_object'
35
35
 
36
36
  module Honeybee
37
37
  class TemplateHVAC
38
+ @@vintage_mapper = {
39
+ DOE_Ref_Pre_1980: 'DOE Ref Pre-1980',
40
+ DOE_Ref_1980_2004: 'DOE Ref 1980-2004',
41
+ ASHRAE_2004: '90.1-2004',
42
+ ASHRAE_2007: '90.1-2007',
43
+ ASHRAE_2010: '90.1-2010',
44
+ ASHRAE_2013: '90.1-2013'
45
+ }
38
46
 
39
47
  def to_openstudio(openstudio_model, room_ids)
40
48
 
@@ -47,9 +55,9 @@ module Honeybee
47
55
 
48
56
  # make the standard applier
49
57
  if @hash[:vintage]
50
- standard = Standard.build(@hash[:vintage])
58
+ standard = Standard.build(@@vintage_mapper[@hash[:vintage].to_sym])
51
59
  else
52
- standard = Standard.build(hvac_defaults[:vintage][:default])
60
+ standard = Standard.build(@@vintage_mapper[hvac_defaults[:vintage][:default].to_sym])
53
61
  end
54
62
 
55
63
  # get the default equipment type
@@ -0,0 +1,100 @@
1
+ # *******************************************************************************
2
+ # Honeybee OpenStudio Gem, Copyright (c) 2020, Alliance for Sustainable
3
+ # Energy, LLC, Ladybug Tools LLC and other contributors. All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # (1) Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ #
11
+ # (2) Redistributions in binary form must reproduce the above copyright notice,
12
+ # this list of conditions and the following disclaimer in the documentation
13
+ # and/or other materials provided with the distribution.
14
+ #
15
+ # (3) Neither the name of the copyright holder nor the names of any contributors
16
+ # may be used to endorse or promote products derived from this software without
17
+ # specific prior written permission from the respective party.
18
+ #
19
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
20
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
23
+ # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
24
+ # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ # *******************************************************************************
31
+
32
+ require 'honeybee/load/daylight'
33
+
34
+ require 'to_openstudio/model_object'
35
+
36
+ module Honeybee
37
+ class DaylightingControl
38
+
39
+ def find_existing_openstudio_object(openstudio_model, parent_space_name)
40
+ dl_cntrl_name = parent_space_name + '_Daylighting'
41
+ model_dl_cntrl = openstudio_model.getDaylightingControlByName(dl_cntrl_name)
42
+ return model_dl_cntrl.get unless model_dl_cntrl.empty?
43
+ nil
44
+ end
45
+
46
+ def to_openstudio(openstudio_model, parent_zone, parent_space)
47
+ # create daylighting control openstudio object and set identifier
48
+ os_dl_control = OpenStudio::Model::DaylightingControl.new(openstudio_model)
49
+ space_name = parent_space.name
50
+ unless space_name.empty?
51
+ os_dl_control.setName(parent_space.name.get + '_Daylighting')
52
+ end
53
+ os_dl_control.setSpace(parent_space)
54
+ parent_zone.setPrimaryDaylightingControl(os_dl_control)
55
+
56
+ # assign the position of the sensor point
57
+ os_dl_control.setPositionXCoordinate(@hash[:sensor_position][0])
58
+ os_dl_control.setPositionYCoordinate(@hash[:sensor_position][1])
59
+ os_dl_control.setPositionZCoordinate(@hash[:sensor_position][2])
60
+
61
+ # assign the illuminance setpoint if it exists
62
+ if @hash[:illuminance_setpoint]
63
+ os_dl_control.setIlluminanceSetpoint(@hash[:illuminance_setpoint])
64
+ else
65
+ os_dl_control.setIlluminanceSetpoint(defaults[:illuminance_setpoint][:default])
66
+ end
67
+
68
+ # assign power fraction if it exists
69
+ if @hash[:min_power_input]
70
+ os_dl_control.setMinimumInputPowerFractionforContinuousDimmingControl(@hash[:min_power_input])
71
+ else
72
+ os_dl_control.setMinimumInputPowerFractionforContinuousDimmingControl(defaults[:min_power_input][:default])
73
+ end
74
+
75
+ # assign light output fraction if it exists
76
+ if @hash[:min_power_input]
77
+ os_dl_control.setMinimumLightOutputFractionforContinuousDimmingControl(@hash[:min_light_output])
78
+ else
79
+ os_dl_control.setMinimumLightOutputFractionforContinuousDimmingControl(defaults[:min_light_output][:default])
80
+ end
81
+
82
+ # set whether the lights go off when they reach their minimum
83
+ if @hash[:off_at_minimum]
84
+ os_dl_control.setLightingControlType('Continuous/Off')
85
+ else
86
+ os_dl_control.setLightingControlType('Continuous')
87
+ end
88
+
89
+ # set the fraction of the zone lights that are dimmed
90
+ if @hash[:control_fraction]
91
+ parent_zone.setFractionofZoneControlledbyPrimaryDaylightingControl(@hash[:control_fraction])
92
+ else
93
+ parent_zone.setFractionofZoneControlledbyPrimaryDaylightingControl(defaults[:control_fraction][:default])
94
+ end
95
+
96
+ os_dl_control
97
+ end
98
+
99
+ end #DaylightingControl
100
+ end #Honeybee
@@ -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])