openstudio-extension 0.2.1 → 0.2.6

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.
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  # *******************************************************************************
4
2
  # OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC.
5
3
  # All rights reserved.
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  # *******************************************************************************
4
2
  # OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC.
5
3
  # All rights reserved.
@@ -228,5 +226,26 @@ module OpenStudio
228
226
 
229
227
  return osw
230
228
  end
229
+
230
+ ##
231
+ # Module method used to check whether a measure is present in an OSW file
232
+ ##
233
+ # @param [Hash] in_osw Initial OSW object as a Hash, keys should be symbolized
234
+ # @param [String] measure_dir_name Directory name of measure to set argument on
235
+ # @param [String] step_name Optional argument, if present used to further identify the measure
236
+ #
237
+ # @return [Boolean] true or false
238
+ def self.measure_in_osw(osw, measure_dir_name, step_name = nil)
239
+ result = false
240
+ osw[:steps].each do |step|
241
+ if step[:measure_dir_name] == measure_dir_name
242
+ if step_name.nil? || step[:name] == step_name
243
+ result = true
244
+ end
245
+ end
246
+ end
247
+
248
+ return result
249
+ end
231
250
  end
232
251
  end
@@ -79,6 +79,11 @@ module OsLib_ModelGeneration
79
79
  array << 'Hospital'
80
80
  array << 'Outpatient'
81
81
  array << 'SuperMarket'
82
+ array << 'Laboratory'
83
+ array << 'LargeDataCenterLowITE'
84
+ array << 'LargeDataCenterHighITE'
85
+ array << 'SmallDataCenterLowITE'
86
+ array << 'SmallDataCenterHighITE'
82
87
 
83
88
  return array
84
89
  end
@@ -328,7 +333,7 @@ module OsLib_ModelGeneration
328
333
  supermarket_a = 45001.0
329
334
  supermarket_p = 866.0
330
335
  supermarket_wwr = 1880.0 / (supermarket_p * 20.0)
331
- supermarket_aspet_ratio = calc_aspect_ratio(supermarket_a, supermarket_p)
336
+ supermarket_aspect_ratio = calc_aspect_ratio(supermarket_a, supermarket_p)
332
337
 
333
338
  hash['SmallOffice'] = { aspect_ratio: 1.5, wwr: 0.15, typical_story: 10.0, perim_mult: 1.0 }
334
339
  hash['MediumOffice'] = { aspect_ratio: 1.5, wwr: 0.33, typical_story: 13.0, perim_mult: 1.0 }
@@ -351,7 +356,14 @@ module OsLib_ModelGeneration
351
356
  hash['MidriseApartment'] = { aspect_ratio: 2.75, wwr: 0.15, typical_story: 10.0, perim_mult: 1.0 }
352
357
  hash['HighriseApartment'] = { aspect_ratio: 2.75, wwr: 0.15, typical_story: 10.0, perim_mult: 1.0 }
353
358
  # SuperMarket inputs come from prototype model
354
- hash['SuperMarket'] = { aspect_ratio: supermarket_aspet_ratio.round(1), wwr: supermarket_wwr.round(2), typical_story: 20.0, perim_mult: 1.0 }
359
+ hash['SuperMarket'] = { aspect_ratio: supermarket_aspect_ratio.round(1), wwr: supermarket_wwr.round(2), typical_story: 20.0, perim_mult: 1.0 }
360
+
361
+ # Add Laboratory and Data Centers
362
+ hash['Laboratory'] = { aspect_ratio: 1.33, wwr: 0.12, typical_story: 10.0, perim_mult: 1.0 }
363
+ hash['LargeDataCenterLowITE'] = { aspect_ratio: 1.67, wwr: 0.0, typical_story: 14.0, perim_mult: 1.0 }
364
+ hash['LargeDataCenterHighITE'] = { aspect_ratio: 1.67, wwr: 0.0, typical_story: 14.0, perim_mult: 1.0 }
365
+ hash['SmallDataCenterLowITE'] = { aspect_ratio: 1.5, wwr: 0.0, typical_story: 14.0, perim_mult: 1.0 }
366
+ hash['SmallDataCenterHighITE'] = { aspect_ratio: 1.5, wwr: 0.0, typical_story: 14.0, perim_mult: 1.0 }
355
367
 
356
368
  # DEER Prototypes
357
369
  hash['Asm'] = { aspect_ratio: 1.0, wwr: 0.19, typical_story: 15.0 }
@@ -664,6 +676,19 @@ module OsLib_ModelGeneration
664
676
  hash['Meeting'] = { ratio: 0.99, space_type_gen: true, default: true }
665
677
  hash['Restroom'] = { ratio: 0.99, space_type_gen: true, default: true }
666
678
  hash['Vestibule'] = { ratio: 0.99, space_type_gen: true, default: true }
679
+ elsif building_type == 'Laboratory'
680
+ hash['Office'] = { ratio: 0.50, space_type_gen: true, default: true }
681
+ hash['Open lab'] = { ratio: 0.35, space_type_gen: true, default: true }
682
+ hash['Equipment corridor'] = { ratio: 0.05, space_type_gen: true, default: true }
683
+ hash['Lab with fume hood'] = { ratio: 0.10, space_type_gen: true, default: true }
684
+ elsif building_type == 'LargeDataCenterHighITE'
685
+ hash['StandaloneDataCenter'] = { ratio: 1.0, space_type_gen: true, default: true }
686
+ elsif building_type == 'LargeDataCenterLowITE'
687
+ hash['StandaloneDataCenter'] = { ratio: 1.0, space_type_gen: true, default: true }
688
+ elsif building_type == 'SmallDataCenterHighITE'
689
+ hash['ComputerRoom'] = { ratio: 1.0, space_type_gen: true, default: true }
690
+ elsif building_type == 'SmallDataCenterLowITE'
691
+ hash['ComputerRoom'] = { ratio: 1.0, space_type_gen: true, default: true }
667
692
  # DEER Prototypes
668
693
  elsif building_type == 'Asm'
669
694
  hash['Auditorium'] = { ratio: 0.7658, space_type_gen: true, default: true }
@@ -2560,6 +2585,19 @@ module OsLib_ModelGeneration
2560
2585
  # Make the standard applier
2561
2586
  standard = Standard.build((args['template']).to_s)
2562
2587
 
2588
+ # validate climate zone
2589
+ if !args.has_key?('climate_zone') || args['climate_zone'] == 'Lookup From Model'
2590
+ climate_zone = standard.model_get_building_climate_zone_and_building_type(model)['climate_zone']
2591
+ runner.registerInfo("Using climate zone #{climate_zone} from model")
2592
+ else
2593
+ climate_zone = args['climate_zone']
2594
+ runner.registerInfo("Using climate zone #{climate_zone} from user arguments")
2595
+ end
2596
+ if climate_zone == ''
2597
+ runner.registerError("Could not determine climate zone from measure arguments or model.")
2598
+ return false
2599
+ end
2600
+
2563
2601
  # make sure daylight savings is turned on up prior to any sizing runs being done.
2564
2602
  if args['enable_dst']
2565
2603
  start_date = '2nd Sunday in March'
@@ -2646,13 +2684,6 @@ module OsLib_ModelGeneration
2646
2684
  else
2647
2685
  is_residential = 'No'
2648
2686
  end
2649
- if !args.has_key?('climate_zone') || args['climate_zone'] == 'Lookup From Model'
2650
- climate_zone = standard.model_get_building_climate_zone_and_building_type(model)['climate_zone']
2651
- runner.registerInfo("Using climate zone #{climate_zone} from model")
2652
- else
2653
- climate_zone = args['climate_zone']
2654
- runner.registerInfo("Using climate zone #{climate_zone} from user arguments")
2655
- end
2656
2687
  bldg_def_const_set = standard.model_add_construction_set(model, climate_zone, lookup_building_type, nil, is_residential)
2657
2688
  if bldg_def_const_set.is_initialized
2658
2689
  bldg_def_const_set = bldg_def_const_set.get
@@ -2797,16 +2828,25 @@ module OsLib_ModelGeneration
2797
2828
  end
2798
2829
  end
2799
2830
 
2800
- # TODO: - when add methods below add bool to enable/disable them with default value to true
2831
+ # add_daylighting_controls (since outdated measure don't have this default to true if arg not found)
2832
+ if !args.has_key?('add_daylighting_controls')
2833
+ args['add_daylighting_controls'] = true
2834
+ end
2835
+ if args['add_daylighting_controls']
2836
+ # remove add_daylighting_controls objects
2837
+ if args['remove_objects']
2838
+ model.getDaylightingControls.each(&:remove)
2839
+ end
2801
2840
 
2802
- # add daylight controls, need to perform a sizing run for 2010
2803
- if args['template'] == '90.1-2010'
2804
- if standard.model_run_sizing_run(model, "#{Dir.pwd}/SRvt") == false
2805
- log_messages_to_runner(runner, debug = true)
2806
- return false
2841
+ # add daylight controls, need to perform a sizing run for 2010
2842
+ if args['template'] == '90.1-2010'
2843
+ if standard.model_run_sizing_run(model, "#{Dir.pwd}/SRvt") == false
2844
+ log_messages_to_runner(runner, debug = true)
2845
+ return false
2846
+ end
2807
2847
  end
2808
- end
2809
2848
  standard.model_add_daylighting_controls(model)
2849
+ end
2810
2850
 
2811
2851
  # add refrigeration
2812
2852
  if args['add_refrigeration']
@@ -3034,20 +3074,6 @@ module OsLib_ModelGeneration
3034
3074
  end
3035
3075
  end
3036
3076
 
3037
- # add internal mass
3038
- if args['add_internal_mass']
3039
-
3040
- if args['remove_objects']
3041
- model.getSpaceLoads.each do |instance|
3042
- next unless instance.to_InternalMass.is_initialized
3043
- instance.remove
3044
- end
3045
- end
3046
-
3047
- # add internal mass to conditioned spaces; needs to happen after thermostats are applied
3048
- standard.model_add_internal_mass(model, primary_bldg_type)
3049
- end
3050
-
3051
3077
  # set unmet hours tolerance
3052
3078
  unmet_hrs_tol_r = args['unmet_hours_tolerance']
3053
3079
  unmet_hrs_tol_k = OpenStudio.convert(unmet_hrs_tol_r, 'R', 'K').get
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  # *******************************************************************************
4
2
  # OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC.
5
3
  # All rights reserved.
@@ -51,14 +49,17 @@ module OpenStudio
51
49
  setup_subtasks(@name)
52
50
  end
53
51
 
54
- def set_extension_class(extension_class)
52
+ def set_extension_class(extension_class, github_repo = '')
55
53
  @extension_class = extension_class
56
54
  @extension = extension_class.new
57
55
  @root_dir = @extension.root_dir
58
- @measures_dir = @extension.measures_dir
56
+ # Catch if measures_dir is nil, then just make it an empty string
57
+ @measures_dir = @extension.measures_dir || ''
58
+ @staged_path = @measures_dir + '/staged'
59
59
  @core_dir = @extension.core_dir
60
60
  @doc_templates_dir = @extension.doc_templates_dir
61
61
  @files_dir = @extension.files_dir
62
+ @github_repo = github_repo
62
63
  end
63
64
 
64
65
  private
@@ -67,22 +68,20 @@ module OpenStudio
67
68
  namespace name do
68
69
  desc 'Run the CLI task to check for measure updates'
69
70
  task update_measures: ['measures:add_license', 'measures:add_readme', 'measures:copy_resources', 'update_copyright'] do
70
- puts 'updating measures...'
71
+ puts 'updating measures'
71
72
  runner = OpenStudio::Extension::Runner.new(Dir.pwd)
72
73
  runner.update_measures(@measures_dir)
73
74
  end
74
75
 
75
76
  desc 'List measures'
76
77
  task :list_measures do
77
- puts 'Listing measures...'
78
+ puts 'Listing measures'
78
79
  runner = OpenStudio::Extension::Runner.new(Dir.pwd)
79
80
  runner.list_measures(@measures_dir)
80
81
  end
81
82
 
82
83
  desc 'Use openstudio system ruby to run tests'
83
84
  task :test_with_openstudio do
84
- # puts Dir.pwd
85
- # puts Rake.original_dir
86
85
  puts 'testing with openstudio'
87
86
  runner = OpenStudio::Extension::Runner.new(Dir.pwd)
88
87
  result = runner.test_measures_with_cli(@measures_dir)
@@ -92,12 +91,6 @@ module OpenStudio
92
91
  end
93
92
  end
94
93
 
95
- # TODO: Implement this eventually... comment out for now.
96
- # desc 'Use openstudio docker image to run tests'
97
- # task :test_with_docker do
98
- # puts 'testing with docker'
99
- # end
100
-
101
94
  # namespace for measure operations
102
95
  namespace 'measures' do
103
96
  desc 'Copy the resources files to individual measures'
@@ -145,14 +138,233 @@ module OpenStudio
145
138
  runner.update_copyright(@root_dir, @doc_templates_dir)
146
139
  end
147
140
 
148
- desc 'Copy the measures to a location that can be uploaded to BCL'
149
- task :stage_bcl do
150
- puts 'Staging measures for BCL'
141
+ desc 'Print the change log from GitHub. Date format: yyyy-mm-dd'
142
+ task :change_log, [:start_date, :end_date, :apikey] do |t, args|
143
+ require 'change_log'
144
+ cl = ChangeLog.new(@github_repo, *args)
145
+ cl.process
146
+ cl.print_issues
151
147
  end
152
148
 
153
- desc 'Upload measures from the specified location.'
154
- task :push_bcl do
155
- puts 'Push measures to BCL'
149
+ namespace 'bcl' do
150
+ desc 'Test BCL login'
151
+ task :test_login do
152
+ puts 'test BCL login'
153
+ bcl = ::BCL::ComponentMethods.new
154
+ bcl.login
155
+ end
156
+
157
+ # for custom search, populate env var: bcl_search_keyword
158
+ desc 'Search BCL'
159
+ task :search_measures do
160
+ puts 'test search BCL'
161
+ bcl = ::BCL::ComponentMethods.new
162
+ bcl.login
163
+
164
+ # check for env var specifying keyword first
165
+ if ENV['bcl_search_keyword']
166
+ keyword = ENV['bcl_search_keyword']
167
+ else
168
+ keyword = 'Space'
169
+ end
170
+ num_results = 10
171
+ # bcl.search params: search_string, filter_string, return_all_results?
172
+ puts "searching BCL measures for keyword: #{keyword}"
173
+ results = bcl.search(keyword, "fq[]=bundle:nrel_measure&show_rows=#{num_results}", false)
174
+ puts "there are #{results[:result].count} results"
175
+ results[:result].each do |res|
176
+ puts(res[:measure][:name]).to_s
177
+ end
178
+ end
179
+
180
+ # to call with argument: "openstudio:bcl:stage[true]" (true = remove existing staged content)
181
+ desc 'Copy the measures/components to a location that can be uploaded to BCL'
182
+ task :stage, [:reset] do |t, args|
183
+ puts 'Staging measures for BCL'
184
+ # initialize BCL and login
185
+ bcl = ::BCL::ComponentMethods.new
186
+ bcl.login
187
+
188
+ # process reset options: true to clear out old staged content
189
+ options = { reset: false }
190
+ if args[:reset].to_s == 'true'
191
+ options[:reset] = true
192
+ end
193
+
194
+ # ensure staged dir exists
195
+ FileUtils.mkdir_p(@staged_path)
196
+
197
+ # delete existing tarballs if reset is true
198
+ if options[:reset]
199
+ puts 'Deleting existing staged content'
200
+ FileUtils.rm_rf(Dir.glob("#{@staged_path}/*"))
201
+ end
202
+
203
+ # create new and existing directories
204
+ FileUtils.mkdir_p(@staged_path.to_s + '/update')
205
+ FileUtils.mkdir_p(@staged_path.to_s + '/push/component')
206
+ FileUtils.mkdir_p(@staged_path.to_s + '/push/measure')
207
+
208
+ # keep track of noop, update, push
209
+ noops = 0
210
+ new_ones = 0
211
+ updates = 0
212
+
213
+ # get all content directories to process
214
+ dirs = Dir.glob("#{@measures_dir}/*")
215
+
216
+ dirs.each do |dir|
217
+ next if dir.include?('Rakefile') || File.basename(dir) == 'staged'
218
+ current_d = Dir.pwd
219
+ content_name = File.basename(dir)
220
+ puts '', '---'
221
+ puts "Generating #{content_name}"
222
+
223
+ Dir.chdir(dir)
224
+
225
+ # figure out whether to upload new or update existing
226
+ files = Pathname.glob('**/*')
227
+ uuid = nil
228
+ vid = nil
229
+ content_type = 'measure'
230
+
231
+ paths = []
232
+ files.each do |file|
233
+ # don't tar tests/outputs directory
234
+ next if file.to_s.start_with?('tests/output') # From measure testing process
235
+ next if file.to_s.start_with?('tests/test') # From openstudio-measure-tester-gem
236
+ next if file.to_s.start_with?('tests/coverage') # From openstudio-measure-tester-gem
237
+ next if file.to_s.start_with?('test_results') # From openstudio-measure-tester-gem
238
+ paths << file.to_s
239
+ if file.to_s =~ /^.{0,2}component.xml$/ || file.to_s =~ /^.{0,2}measure.xml$/
240
+ if file.to_s.match?(/^.{0,2}component.xml$/)
241
+ content_type = 'component'
242
+ end
243
+ # extract uuid and vid
244
+ uuid, vid = bcl.uuid_vid_from_xml(file)
245
+ end
246
+ end
247
+ puts "UUID: #{uuid}, VID: #{vid}"
248
+
249
+ # note: if uuid is missing, will assume new content
250
+ action = bcl.search_by_uuid(uuid, vid)
251
+ puts "#{content_name} ACTION TO TAKE: #{action}"
252
+ # new content functionality needs to know if measure or component. update is agnostic.
253
+ if action == 'noop' # ignore up-to-date content
254
+ puts " - WARNING: local #{content_name} uuid and vid match BCL... no update will be performed"
255
+ noops += 1
256
+ next
257
+ elsif action == 'update'
258
+ # puts "#{content_name} labeled as update for BCL"
259
+ destination = @staged_path + '/' + action + '/' + "#{content_name}.tar.gz"
260
+ updates += 1
261
+ elsif action == 'push'
262
+ # puts "#{content_name} labeled as new content for BCL"
263
+ destination = @staged_path + '/' + action + '/' + content_type + "/#{content_name}.tar.gz"
264
+ new_ones += 1
265
+ end
266
+
267
+ puts "destination: #{destination}"
268
+
269
+ # copy over only if 'reset_receipts' is set to TRUE. otherwise ignore if file exists already
270
+ if File.exist?(destination)
271
+ if options[:reset]
272
+ FileUtils.rm(destination)
273
+ ::BCL.tarball(destination, paths)
274
+ else
275
+ puts "*** WARNING: File #{content_name}.tar.gz already exists in staged directory... keeping existing file. To overwrite, set reset_receipts arg to true ***"
276
+ end
277
+ else
278
+ ::BCL.tarball(destination, paths)
279
+ end
280
+ Dir.chdir(current_d)
281
+ end
282
+ puts '', "****STAGING DONE**** #{new_ones} new content, #{updates} updates, #{noops} skipped (already up-to-date on BCL)", ''
283
+ end
284
+
285
+ desc 'Upload measures from the specified location.'
286
+ task :push do
287
+ puts 'Push measures to BCL'
288
+
289
+ # initialize BCL and login
290
+ bcl = ::BCL::ComponentMethods.new
291
+ bcl.login
292
+ reset = false
293
+
294
+ total_count = 0
295
+ successes = 0
296
+ errors = 0
297
+ skipped = 0
298
+
299
+ # grab all the new measure and component tar files and push to bcl
300
+ ['measure', 'component'].each do |content_type|
301
+ items = []
302
+ paths = Pathname.glob(@staged_path.to_s + "/push/#{content_type}/*.tar.gz")
303
+ paths.each do |path|
304
+ # puts path
305
+ items << path.to_s
306
+ end
307
+
308
+ items.each do |item|
309
+ puts item.split('/').last
310
+ total_count += 1
311
+
312
+ receipt_file = File.dirname(item) + '/' + File.basename(item, '.tar.gz') + '.receipt'
313
+ if !reset && File.exist?(receipt_file)
314
+ skipped += 1
315
+ puts 'SKIP: receipt file found'
316
+ next
317
+ end
318
+
319
+ valid, res = bcl.push_content(item, true, "nrel_#{content_type}")
320
+ if valid
321
+ successes += 1
322
+ else
323
+ errors += 1
324
+ if res.key?(:error)
325
+ puts " ERROR MESSAGE: #{res[:error]}"
326
+ else
327
+ puts "ERROR: #{res.inspect.chomp}"
328
+ end
329
+ end
330
+ puts '', '---'
331
+ end
332
+ end
333
+
334
+ # grab all the updated content (measures and components) tar files and push to bcl
335
+ items = []
336
+ paths = Pathname.glob(@staged_path.to_s + '/update/*.tar.gz')
337
+ paths.each do |path|
338
+ # puts path
339
+ items << path.to_s
340
+ end
341
+ items.each do |item|
342
+ puts item.split('/').last
343
+ total_count += 1
344
+
345
+ receipt_file = File.dirname(item) + '/' + File.basename(item, '.tar.gz') + '.receipt'
346
+ if !reset && File.exist?(receipt_file)
347
+ skipped += 1
348
+ puts 'SKIP: receipt file found'
349
+ next
350
+ end
351
+
352
+ valid, res = bcl.update_content(item, true)
353
+ if valid
354
+ successes += 1
355
+ else
356
+ errors += 1
357
+ if res.key?(:error)
358
+ puts " ERROR MESSAGE: #{res[:error]}"
359
+ else
360
+ puts "ERROR MESSAGE: #{res.inspect.chomp}"
361
+ end
362
+ end
363
+ puts '', '---'
364
+ end
365
+
366
+ puts "****UPLOAD DONE**** #{total_count} total, #{successes} success, #{errors} failures, #{skipped} skipped"
367
+ end
156
368
  end
157
369
  end
158
370
  end