openstudio-extension 0.2.1 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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