openstudio-extension 0.2.0 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,17 +1,18 @@
1
+ <?xml version="1.0"?>
1
2
  <measure>
2
3
  <schema_version>3.0</schema_version>
3
4
  <name>openstudio_extension_test_measure</name>
4
5
  <uid>36b99a29-41e1-4d85-9272-85d43b966e5a</uid>
5
- <version_id>e9fedf04-7619-4c93-a865-0e54adfe3507</version_id>
6
- <version_modified>20190430T155209Z</version_modified>
6
+ <version_id>edf7f103-9f51-4944-a5af-1d8fa2b0c9b7</version_id>
7
+ <version_modified>20200427T230714Z</version_modified>
7
8
  <xml_checksum>49BEF039</xml_checksum>
8
9
  <class_name>OpenStudioExtensionTestMeasure</class_name>
9
10
  <display_name>OpenStudio Extension Test Measure</display_name>
10
- <description>Rotate your building relative to its current orientation. This will not rotate site shading objects.</description>
11
- <modeler_description>Get the North Axis field for the OS:Building object and adjusted it based on the user specified value. If the starting value is 20 degrees and the user value is 5 degrees, then the resulting value is 25 degrees.</modeler_description>
12
- <arguments/>
13
- <outputs/>
14
- <provenances/>
11
+ <description>A measure that tests OpenStudio Extension gem functionality</description>
12
+ <modeler_description>This is a test measure that tests OpenStudio Extension gem functionality.</modeler_description>
13
+ <arguments />
14
+ <outputs />
15
+ <provenances />
15
16
  <tags>
16
17
  <tag>Envelope.Form</tag>
17
18
  </tags>
@@ -38,12 +39,6 @@
38
39
  </attribute>
39
40
  </attributes>
40
41
  <files>
41
- <file>
42
- <filename>LICENSE.md</filename>
43
- <filetype>md</filetype>
44
- <usage_type>license</usage_type>
45
- <checksum>9640B6CB</checksum>
46
- </file>
47
42
  <file>
48
43
  <filename>README.md.erb</filename>
49
44
  <filetype>erb</filetype>
@@ -51,16 +46,16 @@
51
46
  <checksum>703C9964</checksum>
52
47
  </file>
53
48
  <file>
54
- <filename>README.md</filename>
49
+ <filename>LICENSE.md</filename>
55
50
  <filetype>md</filetype>
56
- <usage_type>readme</usage_type>
57
- <checksum>7258830F</checksum>
51
+ <usage_type>license</usage_type>
52
+ <checksum>E0468DD6</checksum>
58
53
  </file>
59
54
  <file>
60
55
  <filename>OpenStudioExtensionTestMeasure_Test.rb</filename>
61
56
  <filetype>rb</filetype>
62
57
  <usage_type>test</usage_type>
63
- <checksum>8452EA9A</checksum>
58
+ <checksum>66A00EA8</checksum>
64
59
  </file>
65
60
  <file>
66
61
  <version>
@@ -71,13 +66,19 @@
71
66
  <filename>measure.rb</filename>
72
67
  <filetype>rb</filetype>
73
68
  <usage_type>script</usage_type>
74
- <checksum>E5793A49</checksum>
69
+ <checksum>51BB85EF</checksum>
75
70
  </file>
76
71
  <file>
77
72
  <filename>os_lib_helper_methods.rb</filename>
78
73
  <filetype>rb</filetype>
79
74
  <usage_type>resource</usage_type>
80
- <checksum>22515A49</checksum>
75
+ <checksum>07B01D67</checksum>
76
+ </file>
77
+ <file>
78
+ <filename>README.md</filename>
79
+ <filetype>md</filetype>
80
+ <usage_type>readme</usage_type>
81
+ <checksum>7258830F</checksum>
81
82
  </file>
82
83
  </files>
83
84
  </measure>
@@ -101,11 +101,9 @@ module OpenStudio
101
101
  def self.all_measure_dirs
102
102
  result = []
103
103
  all_extensions.each do |obj|
104
- begin
105
- dir = obj.new.measures_dir
106
- result << dir if dir
107
- rescue StandardError
108
- end
104
+ dir = obj.new.measures_dir
105
+ result << dir if dir
106
+ rescue StandardError
109
107
  end
110
108
  return result.uniq
111
109
  end
@@ -117,11 +115,9 @@ module OpenStudio
117
115
  def self.all_file_dirs
118
116
  result = []
119
117
  all_extensions.each do |obj|
120
- begin
121
- dir = obj.new.files_dir
122
- result << dir if dir
123
- rescue StandardError
124
- end
118
+ dir = obj.new.files_dir
119
+ result << dir if dir
120
+ rescue StandardError
125
121
  end
126
122
  return result.uniq
127
123
  end
@@ -230,5 +226,26 @@ module OpenStudio
230
226
 
231
227
  return osw
232
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
233
250
  end
234
251
  end
@@ -45,6 +45,23 @@ module OsLib_CreateResults
45
45
  # @param end_mo [String] the end month for the peak demand window
46
46
  # @param end_day [Integer] the end day for the peak demand window
47
47
  # @param end_hr [Integer] the end hour for the peak demand window, using 24-hr clock
48
+ # @param electricity_consumption_tou_periods [Array<Hash>] optional array of hashes to add
49
+ # time-of-use electricity consumption values to the annual consumption information.
50
+ # Periods may overlap, but should be listed in the order in which they must be checked,
51
+ # where the value will be assigned to the first encountered period it falls into.
52
+ # An example hash looks like this:
53
+ # {
54
+ # 'tou_name' => 'system_peak',
55
+ # 'tou_id' => 1,
56
+ # 'skip_weekends' => true,
57
+ # 'skip_holidays' => true,
58
+ # 'start_mo' => 'July',
59
+ # 'start_day' => 1,
60
+ # 'start_hr' => 14,
61
+ # 'end_mo' => 'August',
62
+ # 'end_day' => 31,
63
+ # 'end_hr' => 18
64
+ # }
48
65
  # @return [OpenStudio::AttributeVector] a vector of results needed by EDAPT
49
66
  def create_results(skip_weekends = true,
50
67
  skip_holidays = true,
@@ -53,7 +70,8 @@ module OsLib_CreateResults
53
70
  start_hr = 14,
54
71
  end_mo = 'September',
55
72
  end_day = 30,
56
- end_hr = 18)
73
+ end_hr = 18,
74
+ electricity_consumption_tou_periods = [])
57
75
 
58
76
  # get the current version of OS being used to determine if sql query
59
77
  # changes are needed (for when E+ changes).
@@ -497,6 +515,83 @@ module OsLib_CreateResults
497
515
  @runner.registerValue('annual_demand_electricity_peak_demand', 0.0, 'kW')
498
516
  end
499
517
 
518
+ # Describe the TOU periods
519
+ electricity_consumption_tou_periods.each do |tou_pd|
520
+ @runner.registerInfo("TOU period #{tou_pd['tou_id']} represents #{tou_pd['tou_name']} and covers #{tou_pd['start_mo']}-#{tou_pd['start_day']} to #{tou_pd['end_mo']}-#{tou_pd['end_day']} from #{tou_pd['start_hr']} to #{tou_pd['end_hr']}, skip weekends = #{tou_pd['skip_weekends']}, skip holidays = #{tou_pd['skip_holidays']}")
521
+ end
522
+
523
+ # electricity time-of-use periods
524
+ elec = @sql.timeSeries(ann_env_pd, 'Zone Timestep', 'Electricity:Facility', '')
525
+ if elec.is_initialized && day_types
526
+ elec = elec.get
527
+ # Put timeseries into array
528
+ elec_vals = []
529
+ ann_elec_vals = elec.values
530
+ for i in 0..(ann_elec_vals.size - 1)
531
+ elec_vals << ann_elec_vals[i]
532
+ end
533
+
534
+ # Put values into array
535
+ elec_times = []
536
+ ann_elec_times = elec.dateTimes
537
+ for i in 0..(ann_elec_times.size - 1)
538
+ elec_times << ann_elec_times[i]
539
+ end
540
+
541
+ # Loop through the time/value pairs and find the peak
542
+ # excluding the times outside of the Xcel peak demand window
543
+ electricity_tou_vals = Hash.new(0)
544
+ elec_times.zip(elec_vals).each_with_index do |vs, ind|
545
+ date_time = vs[0]
546
+ joules = vs[1]
547
+ day_type = day_types[ind]
548
+ time = date_time.time
549
+ date = date_time.date
550
+
551
+ # puts("#{val_kW}kW; #{date}; #{time}; #{day_of_week.valueName}")
552
+
553
+ # Determine which TOU period this hour falls into
554
+ tou_period_assigned = false
555
+ electricity_consumption_tou_periods.each do |tou_pd|
556
+ pd_start_date = OpenStudio::DateTime.new(OpenStudio::Date.new(OpenStudio::MonthOfYear.new(tou_pd['start_mo']), tou_pd['start_day'], timeseries_yr), OpenStudio::Time.new(0, 0, 0, 0))
557
+ pd_end_date = OpenStudio::DateTime.new(OpenStudio::Date.new(OpenStudio::MonthOfYear.new(tou_pd['end_mo']), tou_pd['end_day'], timeseries_yr), OpenStudio::Time.new(0, 24, 0, 0))
558
+ pd_start_time = OpenStudio::Time.new(0, tou_pd['start_hr'], 0, 0)
559
+ pd_end_time = OpenStudio::Time.new(0, tou_pd['end_hr'], 0, 0)
560
+ # Skip times outside of the correct months
561
+ next if date_time < pd_start_date || date_time > pd_end_date
562
+ # Skip times before some time and after another time
563
+ next if time < pd_start_time || time > pd_end_time
564
+ # Skip weekends if asked
565
+ if tou_pd['skip_weekends']
566
+ # Sunday = 1, Saturday = 7
567
+ next if day_type == 1 || day_type == 7
568
+ end
569
+ # Skip holidays if asked
570
+ if tou_pd['skip_holidays']
571
+ # Holiday = 8
572
+ next if day_type == 8
573
+ end
574
+ # If here, this hour falls into the specified period
575
+ tou_period_assigned = true
576
+ electricity_tou_vals[tou_pd['tou_id']] += joules
577
+ break
578
+ end
579
+ # Ensure that the value fell into a period
580
+ unless tou_period_assigned
581
+ @runner.registerError("Did not find a TOU period covering #{time} on #{date}, kWh will not be included in any TOU period.")
582
+ end
583
+ end
584
+ # Register values for any time-of-use period with kWh
585
+ electricity_tou_vals.each do |tou_pd_id, joules_in_pd|
586
+ gj_in_pd = OpenStudio.convert(joules_in_pd, 'J', 'GJ').get
587
+ kwh_in_pd = OpenStudio.convert(joules_in_pd, 'J', 'kWh').get
588
+ @runner.registerValue("annual_consumption_electricity_tou_#{tou_pd_id}", gj_in_pd, 'GJ')
589
+ @runner.registerInfo("TOU period #{tou_pd_id} annual electricity consumption = #{kwh_in_pd} kWh.")
590
+ end
591
+ else
592
+ @runner.registerError('Electricity timeseries (Electricity:Facility at zone timestep) could not be found, cannot determine the information needed to calculate savings or incentives.')
593
+ end
594
+
500
595
  # electricity_annual_avg_peak_demand
501
596
  val = @sql.electricityTotalEndUses
502
597
  if val.is_initialized
@@ -587,6 +682,88 @@ module OsLib_CreateResults
587
682
  @runner.registerValue('annual_demand_district_cooling_peak_demand', 0.0, 'kW')
588
683
  end
589
684
 
685
+ # district cooling time-of-use periods
686
+ dist_clg = @sql.timeSeries(ann_env_pd, 'Zone Timestep', 'DistrictCooling:Facility', '')
687
+ if dist_clg.is_initialized && day_types
688
+ dist_clg = dist_clg.get
689
+ # Put timeseries into array
690
+ dist_clg_vals = []
691
+ ann_dist_clg_vals = dist_clg.values
692
+ for i in 0..(ann_dist_clg_vals.size - 1)
693
+ dist_clg_vals << ann_dist_clg_vals[i]
694
+ end
695
+
696
+ # Put values into array
697
+ dist_clg_times = []
698
+ ann_dist_clg_times = dist_clg.dateTimes
699
+ for i in 0..(ann_dist_clg_times.size - 1)
700
+ dist_clg_times << ann_dist_clg_times[i]
701
+ end
702
+
703
+ # Loop through the time/value pairs and find the peak
704
+ # excluding the times outside of the Xcel peak demand window
705
+ dist_clg_tou_vals = Hash.new(0)
706
+ dist_clg_times.zip(dist_clg_vals).each_with_index do |vs, ind|
707
+ date_time = vs[0]
708
+ joules = vs[1]
709
+ day_type = day_types[ind]
710
+ time = date_time.time
711
+ date = date_time.date
712
+
713
+ # puts("#{val_kW}kW; #{date}; #{time}; #{day_of_week.valueName}")
714
+
715
+ # Determine which TOU period this hour falls into
716
+ tou_period_assigned = false
717
+ electricity_consumption_tou_periods.each do |tou_pd|
718
+ pd_start_date = OpenStudio::DateTime.new(OpenStudio::Date.new(OpenStudio::MonthOfYear.new(tou_pd['start_mo']), tou_pd['start_day'], timeseries_yr), OpenStudio::Time.new(0, 0, 0, 0))
719
+ pd_end_date = OpenStudio::DateTime.new(OpenStudio::Date.new(OpenStudio::MonthOfYear.new(tou_pd['end_mo']), tou_pd['end_day'], timeseries_yr), OpenStudio::Time.new(0, 24, 0, 0))
720
+ pd_start_time = OpenStudio::Time.new(0, tou_pd['start_hr'], 0, 0)
721
+ pd_end_time = OpenStudio::Time.new(0, tou_pd['end_hr'], 0, 0)
722
+ # Skip times outside of the correct months
723
+ next if date_time < pd_start_date || date_time > pd_end_date
724
+ # Skip times before some time and after another time
725
+ next if time < pd_start_time || time > pd_end_time
726
+ # Skip weekends if asked
727
+ if tou_pd['skip_weekends']
728
+ # Sunday = 1, Saturday = 7
729
+ next if day_type == 1 || day_type == 7
730
+ end
731
+ # Skip holidays if asked
732
+ if tou_pd['skip_holidays']
733
+ # Holiday = 8
734
+ next if day_type == 8
735
+ end
736
+ # If here, this hour falls into the specified period
737
+ tou_period_assigned = true
738
+ dist_clg_tou_vals[tou_pd['tou_id']] += joules
739
+ break
740
+ end
741
+ # Ensure that the value fell into a period
742
+ unless tou_period_assigned
743
+ @runner.registerError("Did not find a TOU period covering #{time} on #{date}, kWh will not be included in any TOU period.")
744
+ end
745
+ end
746
+ # Register values for any time-of-use period with kWh
747
+ dist_clg_tou_vals.each do |tou_pd_id, joules_in_pd|
748
+ gj_in_pd = OpenStudio.convert(joules_in_pd, 'J', 'GJ').get
749
+ kwh_in_pd = OpenStudio.convert(joules_in_pd, 'J', 'kWh').get
750
+ @runner.registerValue("annual_consumption_district_cooling_tou_#{tou_pd_id}", gj_in_pd, 'GJ')
751
+ @runner.registerInfo("TOU period #{tou_pd_id} annual district cooling consumption = #{kwh_in_pd} kWh.")
752
+ end
753
+ else
754
+ # If TOU periods were specified but this model has no district cooling, report zeroes
755
+ if electricity_consumption_tou_periods.size > 0
756
+ # Get the TOU ids
757
+ tou_ids = []
758
+ electricity_consumption_tou_periods.each do |tou_pd|
759
+ tou_ids << tou_pd['tou_id']
760
+ end
761
+ tou_ids.uniq.each do |tou_id|
762
+ @runner.registerValue("annual_consumption_district_cooling_tou_#{tou_id}", 0.0, 'GJ')
763
+ end
764
+ end
765
+ end
766
+
590
767
  else
591
768
  @runner.registerError('Could not find an annual run period')
592
769
  return OpenStudio::Attribute.new('report', result_elems)
@@ -283,7 +283,12 @@ module OsLib_Constructions
283
283
  # create info message
284
284
  if !runner.nil? # todo - need to look for bad visible transmittance here
285
285
  uFactorSiToIpConversion = OpenStudio.convert(material.uFactor, 'W/m^2*K', 'Btu/ft^2*h*R').get
286
- runner.registerInfo("Created #{construction.name} construction. U-factor: #{OpenStudio.toNeatString(uFactorSiToIpConversion, 2, true)}(Btu/ft^2*h*R), SHGC: #{material.getSolarHeatGainCoefficient}, VT: #{material.getVisibleTransmittance.get}.")
286
+ # version check to support 2.x and 3.x
287
+ if Gem::Version.new(OpenStudio::openStudioVersion) > Gem::Version.new("2.9.1")
288
+ runner.registerInfo("Created #{construction.name} construction. U-factor: #{OpenStudio.toNeatString(uFactorSiToIpConversion, 2, true)}(Btu/ft^2*h*R), SHGC: #{material.solarHeatGainCoefficient}, VT: #{material.getVisibleTransmittance.get}.")
289
+ else
290
+ runner.registerInfo("Created #{construction.name} construction. U-factor: #{OpenStudio.toNeatString(uFactorSiToIpConversion, 2, true)}(Btu/ft^2*h*R), SHGC: #{material.getSolarHeatGainCoefficient}, VT: #{material.getVisibleTransmittance.get}.")
291
+ end
287
292
  end
288
293
 
289
294
  result = construction
@@ -327,7 +327,8 @@ module OsLib_Geometry
327
327
  end
328
328
 
329
329
  # sort array by floor area, this hash will be altered to reduce floor area for each space type to 0
330
- space_types_running_count = space_types.sort_by { |k, v| v[:floor_area] }
330
+ #space_types_running_count = space_types.sort_by { |k, v| v[:floor_area] }
331
+ space_types_running_count = space_types
331
332
 
332
333
  # array entry for each story
333
334
  footprints = []
@@ -354,8 +355,10 @@ module OsLib_Geometry
354
355
 
355
356
  space_types_running_count.each do |space_type, space_type_hash|
356
357
  # next if floor area is full or space type is empty
357
- next if current_footprint_area >= target_footprint_area
358
- next if space_type_hash[:floor_area] <= 0.0
358
+
359
+ tol_value = 0.0001
360
+ next if current_footprint_area + tol_value >= target_footprint_area
361
+ next if space_type_hash[:floor_area] <= tol_value
359
362
 
360
363
  # special test for when total floor area is smaller than valid_bar_area_min, just make bar smaller that valid min and warn user
361
364
  if target_per_space_type[space_type] < valid_bar_area_min
@@ -367,20 +370,18 @@ module OsLib_Geometry
367
370
 
368
371
  # add entry for space type if it doesn't have one yet
369
372
  if !space_types_local_count.key?(space_type)
370
- space_types_local_count[space_type] = { floor_area: 0.0 }
373
+ if space_type_hash.has_key?(:children)
374
+ space_type = space_type_hash[:children][:default][:space_type] # will re-using space type create issue
375
+ space_types_local_count[space_type] = { floor_area: 0.0 }
376
+ space_types_local_count[space_type][:children] = space_type_hash[:children]
377
+ else
378
+ space_types_local_count[space_type] = { floor_area: 0.0 }
379
+ end
371
380
  end
372
381
 
373
382
  # if there is enough of this space type to fill rest of floor area
374
383
  remaining_in_footprint = target_footprint_area - current_footprint_area
375
- if space_type_hash[:floor_area] > remaining_in_footprint
376
-
377
- # add to local count for story and remove from running count from space type
378
- raw_footprint_area_used = remaining_in_footprint
379
-
380
- else
381
- # if not then use up the rest of the floor area and move on to next space type
382
- raw_footprint_area_used = space_type_hash[:floor_area]
383
- end
384
+ raw_footprint_area_used = [space_type_hash[:floor_area],remaining_in_footprint].min
384
385
 
385
386
  # add to local hash
386
387
  space_types_local_count[space_type][:floor_area] = raw_footprint_area_used / v[:multiplier].to_f
@@ -389,13 +390,13 @@ module OsLib_Geometry
389
390
  current_footprint_area += raw_footprint_area_used
390
391
  space_type_hash[:floor_area] -= raw_footprint_area_used
391
392
 
392
- # test if think slice left on current floor.
393
+ # test if think sliver left on current floor.
393
394
  # fix by moving smallest space type to next floor and and the same amount more of the sliver space type to this story
394
395
  raw_footprint_area_used < valid_bar_area_min && sliver_override == false ? (test_a = true) : (test_a = false)
395
396
 
396
397
  # test if what would be left of the current space type would result in a sliver on the next story.
397
398
  # fix by removing some of this space type so their is enough left for the next story, and replace the removed amount with the largest space type in the model
398
- (space_type_hash[:floor_area] < valid_bar_area_min) && (space_type_hash[:floor_area] > 0.0001) ? (test_b = true) : (test_b = false)
399
+ (space_type_hash[:floor_area] < valid_bar_area_min) && (space_type_hash[:floor_area] > tol_value) ? (test_b = true) : (test_b = false)
399
400
 
400
401
  # identify very small slices and re-arrange spaces to different stories to avoid this
401
402
  if test_a
@@ -422,6 +423,10 @@ module OsLib_Geometry
422
423
 
423
424
  # swap size
424
425
  swap_size = valid_bar_area_min * 5 # currently equal to default perimeter zone depth of 15'
426
+ # this prevents too much area from being swapped resulting in a negative number for floor area
427
+ if swap_size > space_types_local_count[space_type][:floor_area] * v[:multiplier].to_f
428
+ swap_size = space_types_local_count[space_type][:floor_area] * v[:multiplier].to_f
429
+ end
425
430
 
426
431
  # adjust running count for current space type
427
432
  space_type_hash[:floor_area] += swap_size
@@ -445,12 +450,18 @@ module OsLib_Geometry
445
450
  end
446
451
 
447
452
  # sliced bar simple creates a single sliced bar for space types passed in
448
- # todo - look at length and width to adjust slicing direction
453
+ # look at length and width to adjust slicing direction
449
454
  def self.make_sliced_bar_simple_polygons(runner, space_types, length, width, footprint_origin = OpenStudio::Point3d.new(0, 0, 0), perimeter_zone_depth = OpenStudio.convert(15, 'ft', 'm').get)
450
455
  hash_of_point_vectors = {} # key is name, value is a hash, one item of which is polygon. Another could be space type
451
456
 
457
+ reverse_slice = false
458
+ if length < width
459
+ reverse_slice = true
460
+ #runner.registerInfo("reverse typical slice direction for bar because of aspect ratio less than 1.0.")
461
+ end
462
+
452
463
  # determine if core and perimeter zoning can be used
453
- if !(length > perimeter_zone_depth * 2.5 && width > perimeter_zone_depth * 2.5)
464
+ if !([length,width].min > perimeter_zone_depth * 2.5 && [length,width].min > perimeter_zone_depth * 2.5)
454
465
  perimeter_zone_depth = 0 # if any size is to small then just model floor as single zone, issue warning
455
466
  runner.registerWarning('Not modeling core and perimeter zones for some portion of the model.')
456
467
  end
@@ -461,6 +472,7 @@ module OsLib_Geometry
461
472
  # this represents the entire bar, not individual space type slices
462
473
  nw_point = OpenStudio::Point3d.new(x_delta, y_delta + width, z)
463
474
  sw_point = OpenStudio::Point3d.new(x_delta, y_delta, z)
475
+ se_point = OpenStudio::Point3d.new(x_delta + length, y_delta, z) # used when length is less than width
464
476
 
465
477
  # total building floor area to calculate ratios from space type floor areas
466
478
  total_floor_area = 0.0
@@ -470,7 +482,7 @@ module OsLib_Geometry
470
482
 
471
483
  # sort array by floor area but shift largest object to front
472
484
  space_types = space_types.sort_by { |k, v| v[:floor_area] }
473
- space_types.insert(0, space_types.delete_at(space_types.size - 1))
485
+ space_types.insert(0, space_types.delete_at(space_types.size - 1)) #.to_h
474
486
 
475
487
  # min and max bar end values
476
488
  min_bar_end_multiplier = 0.75
@@ -484,20 +496,20 @@ module OsLib_Geometry
484
496
  start_perimeter_width_deduction = 0.0
485
497
  end_perimeter_width_deduction = 0.0
486
498
  if space_type == space_types.first[0]
487
- if length * space_type_hash[:floor_area] / total_floor_area > max_bar_end_multiplier * perimeter_zone_depth
499
+ if [length,width].max * space_type_hash[:floor_area] / total_floor_area > max_bar_end_multiplier * perimeter_zone_depth
488
500
  start_perimeter_width_deduction = perimeter_zone_depth
489
501
  end
490
502
  # see if last space type is too small for perimeter. If it is then save some of this space type
491
- if length * space_types.last[1][:floor_area] / total_floor_area < perimeter_zone_depth * min_bar_end_multiplier
503
+ if [length,width].max * space_types.last[1][:floor_area] / total_floor_area < perimeter_zone_depth * min_bar_end_multiplier
492
504
  re_apply_largest_space_type_at_end = true
493
505
  end
494
506
  end
495
507
  if space_type == space_types.last[0]
496
- if length * space_type_hash[:floor_area] / total_floor_area > max_bar_end_multiplier * perimeter_zone_depth
508
+ if [length,width].max * space_type_hash[:floor_area] / total_floor_area > max_bar_end_multiplier * perimeter_zone_depth
497
509
  end_perimeter_width_deduction = perimeter_zone_depth
498
510
  end
499
511
  end
500
- non_end_adjusted_width = (length * space_type_hash[:floor_area] / total_floor_area) - start_perimeter_width_deduction - end_perimeter_width_deduction
512
+ non_end_adjusted_width = ([length,width].max * space_type_hash[:floor_area] / total_floor_area) - start_perimeter_width_deduction - end_perimeter_width_deduction
501
513
 
502
514
  # adjustment of end space type is too small and is replaced with largest space type
503
515
  if (space_type == space_types.first[0]) && re_apply_largest_space_type_at_end
@@ -506,69 +518,223 @@ module OsLib_Geometry
506
518
  end
507
519
  if (space_type == space_types.last[0]) && re_apply_largest_space_type_at_end
508
520
  end_perimeter_width_deduction = space_types.first[0]
521
+ end_b_flag = true
522
+ else
523
+ end_b_flag = false
509
524
  end
510
525
 
511
- # poulate data for core and perimeter of slice
526
+ # populate data for core and perimeter of slice
512
527
  section_hash_for_space_type = {}
513
528
  section_hash_for_space_type['end_a'] = start_perimeter_width_deduction
514
529
  section_hash_for_space_type[''] = non_end_adjusted_width
515
530
  section_hash_for_space_type['end_b'] = end_perimeter_width_deduction
516
531
 
532
+ # determine if this space+type is double loaded corridor, and if so what the perimeter zone depth should be based on building width
533
+ # look at reverse_slice to see if length or width should be used to determine perimeter depth
534
+ if space_type_hash.has_key?(:children)
535
+ core_ratio = space_type_hash[:children][:circ][:orig_ratio]
536
+ perim_ratio = space_type_hash[:children][:default][:orig_ratio]
537
+ core_ratio_adj = core_ratio / (core_ratio + perim_ratio)
538
+ perim_ratio_adj = perim_ratio / (core_ratio + perim_ratio)
539
+ core_space_type = space_type_hash[:children][:circ][:space_type]
540
+ perim_space_type = space_type_hash[:children][:default][:space_type]
541
+ if !reverse_slice
542
+ custom_cor_val = width * core_ratio_adj
543
+ custom_perim_val = (width - custom_cor_val)/2.0
544
+ else
545
+ custom_cor_val = length * core_ratio_adj
546
+ custom_perim_val = (length - custom_cor_val)/2.0
547
+ end
548
+ actual_perim = custom_perim_val
549
+ double_loaded_corridor = true
550
+ else
551
+ actual_perim = perimeter_zone_depth
552
+ double_loaded_corridor = false
553
+ end
554
+
555
+ # may overwrite
556
+ first_space_type_hash = space_types.first[1]
557
+ if end_b_flag && first_space_type_hash.has_key?(:children)
558
+ end_b_core_ratio = first_space_type_hash[:children][:circ][:orig_ratio]
559
+ end_b_perim_ratio = first_space_type_hash[:children][:default][:orig_ratio]
560
+ end_b_core_ratio_adj = end_b_core_ratio / (end_b_core_ratio + end_b_perim_ratio)
561
+ end_b_perim_ratio_adj = end_b_perim_ratio / (end_b_core_ratio + end_b_perim_ratio)
562
+ end_b_core_space_type = first_space_type_hash[:children][:circ][:space_type]
563
+ end_b_perim_space_type = first_space_type_hash[:children][:default][:space_type]
564
+ if !reverse_slice
565
+ end_b_custom_cor_val = width * end_b_core_ratio_adj
566
+ end_b_custom_perim_val = (width - end_b_custom_cor_val)/2.0
567
+ else
568
+ end_b_custom_cor_val = length * end_b_core_ratio_adj
569
+ end_b_custom_perim_val = (length - end_b_custom_cor_val)/2.0
570
+ end
571
+ end_b_actual_perim = end_b_custom_perim_val
572
+ end_b_double_loaded_corridor = true
573
+ else
574
+ end_b_actual_perim = perimeter_zone_depth
575
+ end_b_double_loaded_corridor = false
576
+ end
577
+
517
578
  # loop through sections for space type (main and possibly one or two end perimeter sections)
518
- section_hash_for_space_type.each do |k, width|
519
- if width.class.to_s == 'OpenStudio::Model::SpaceType' # confirm this
520
- space_type = width
579
+ section_hash_for_space_type.each do |k, slice|
580
+
581
+ # need to use different space type for end_b
582
+ if end_b_flag && k == "end_b" && space_types.first[1].has_key?(:children)
583
+ slice = space_types.first[0]
584
+ actual_perim = end_b_actual_perim
585
+ double_loaded_corridor = end_b_double_loaded_corridor
586
+ core_ratio = end_b_core_ratio
587
+ perim_ratio = end_b_perim_ratio
588
+ core_ratio_adj = end_b_core_ratio_adj
589
+ perim_ratio_adj = end_b_perim_ratio_adj
590
+ core_space_type = end_b_core_space_type
591
+ perim_space_type = end_b_perim_space_type
592
+ end
593
+
594
+ if slice.class.to_s == 'OpenStudio::Model::SpaceType' || slice.class.to_s == 'OpenStudio::Model::Building'
595
+ space_type = slice
521
596
  max_reduction = [perimeter_zone_depth, max_reduction].min
522
- width = max_reduction
597
+ slice = max_reduction
523
598
  end
524
- if width == 0
599
+ if slice == 0
525
600
  next
526
601
  end
527
602
 
528
- ne_point = nw_point + OpenStudio::Vector3d.new(width, 0, 0)
529
- se_point = sw_point + OpenStudio::Vector3d.new(width, 0, 0)
530
-
531
- if perimeter_zone_depth > 0
532
- polygon_a = OpenStudio::Point3dVector.new
533
- polygon_a << sw_point
534
- polygon_a << sw_point + OpenStudio::Vector3d.new(0, perimeter_zone_depth, 0)
535
- polygon_a << se_point + OpenStudio::Vector3d.new(0, perimeter_zone_depth, 0)
536
- polygon_a << se_point
537
- hash_of_point_vectors["#{space_type.name} A #{k}"] = {}
538
- hash_of_point_vectors["#{space_type.name} A #{k}"][:space_type] = space_type
539
- hash_of_point_vectors["#{space_type.name} A #{k}"][:polygon] = polygon_a
540
-
541
- polygon_b = OpenStudio::Point3dVector.new
542
- polygon_b << sw_point + OpenStudio::Vector3d.new(0, perimeter_zone_depth, 0)
543
- polygon_b << nw_point + OpenStudio::Vector3d.new(0, - perimeter_zone_depth, 0)
544
- polygon_b << ne_point + OpenStudio::Vector3d.new(0, - perimeter_zone_depth, 0)
545
- polygon_b << se_point + OpenStudio::Vector3d.new(0, perimeter_zone_depth, 0)
546
- hash_of_point_vectors["#{space_type.name} B #{k}"] = {}
547
- hash_of_point_vectors["#{space_type.name} B #{k}"][:space_type] = space_type
548
- hash_of_point_vectors["#{space_type.name} B #{k}"][:polygon] = polygon_b
549
-
550
- polygon_c = OpenStudio::Point3dVector.new
551
- polygon_c << nw_point + OpenStudio::Vector3d.new(0, - perimeter_zone_depth, 0)
552
- polygon_c << nw_point
553
- polygon_c << ne_point
554
- polygon_c << ne_point + OpenStudio::Vector3d.new(0, - perimeter_zone_depth, 0)
555
- hash_of_point_vectors["#{space_type.name} C #{k}"] = {}
556
- hash_of_point_vectors["#{space_type.name} C #{k}"][:space_type] = space_type
557
- hash_of_point_vectors["#{space_type.name} C #{k}"][:polygon] = polygon_c
603
+ if !reverse_slice
604
+
605
+ ne_point = nw_point + OpenStudio::Vector3d.new(slice, 0, 0)
606
+ se_point = sw_point + OpenStudio::Vector3d.new(slice, 0, 0)
607
+
608
+ if actual_perim > 0 && (actual_perim * 2.0) < width
609
+ polygon_a = OpenStudio::Point3dVector.new
610
+ polygon_a << sw_point
611
+ polygon_a << sw_point + OpenStudio::Vector3d.new(0, actual_perim, 0)
612
+ polygon_a << se_point + OpenStudio::Vector3d.new(0, actual_perim, 0)
613
+ polygon_a << se_point
614
+ if double_loaded_corridor
615
+ hash_of_point_vectors["#{perim_space_type.name} A #{k}"] = {}
616
+ hash_of_point_vectors["#{perim_space_type.name} A #{k}"][:space_type] = perim_space_type
617
+ hash_of_point_vectors["#{perim_space_type.name} A #{k}"][:polygon] = polygon_a
618
+ else
619
+ hash_of_point_vectors["#{space_type.name} A #{k}"] = {}
620
+ hash_of_point_vectors["#{space_type.name} A #{k}"][:space_type] = space_type
621
+ hash_of_point_vectors["#{space_type.name} A #{k}"][:polygon] = polygon_a
622
+ end
623
+
624
+ polygon_b = OpenStudio::Point3dVector.new
625
+ polygon_b << sw_point + OpenStudio::Vector3d.new(0, actual_perim, 0)
626
+ polygon_b << nw_point + OpenStudio::Vector3d.new(0, - actual_perim, 0)
627
+ polygon_b << ne_point + OpenStudio::Vector3d.new(0, - actual_perim, 0)
628
+ polygon_b << se_point + OpenStudio::Vector3d.new(0, actual_perim, 0)
629
+ if double_loaded_corridor
630
+ hash_of_point_vectors["#{core_space_type.name} B #{k}"] = {}
631
+ hash_of_point_vectors["#{core_space_type.name} B #{k}"][:space_type] = core_space_type
632
+ hash_of_point_vectors["#{core_space_type.name} B #{k}"][:polygon] = polygon_b
633
+ else
634
+ hash_of_point_vectors["#{space_type.name} B #{k}"] = {}
635
+ hash_of_point_vectors["#{space_type.name} B #{k}"][:space_type] = space_type
636
+ hash_of_point_vectors["#{space_type.name} B #{k}"][:polygon] = polygon_b
637
+ end
638
+
639
+ polygon_c = OpenStudio::Point3dVector.new
640
+ polygon_c << nw_point + OpenStudio::Vector3d.new(0, - actual_perim, 0)
641
+ polygon_c << nw_point
642
+ polygon_c << ne_point
643
+ polygon_c << ne_point + OpenStudio::Vector3d.new(0, - actual_perim, 0)
644
+ if double_loaded_corridor
645
+ hash_of_point_vectors["#{perim_space_type.name} C #{k}"] = {}
646
+ hash_of_point_vectors["#{perim_space_type.name} C #{k}"][:space_type] = perim_space_type
647
+ hash_of_point_vectors["#{perim_space_type.name} C #{k}"][:polygon] = polygon_c
648
+ else
649
+ hash_of_point_vectors["#{space_type.name} C #{k}"] = {}
650
+ hash_of_point_vectors["#{space_type.name} C #{k}"][:space_type] = space_type
651
+ hash_of_point_vectors["#{space_type.name} C #{k}"][:polygon] = polygon_c
652
+ end
653
+ else
654
+ polygon_a = OpenStudio::Point3dVector.new
655
+ polygon_a << sw_point
656
+ polygon_a << nw_point
657
+ polygon_a << ne_point
658
+ polygon_a << se_point
659
+ hash_of_point_vectors["#{space_type.name} #{k}"] = {}
660
+ hash_of_point_vectors["#{space_type.name} #{k}"][:space_type] = space_type
661
+ hash_of_point_vectors["#{space_type.name} #{k}"][:polygon] = polygon_a
662
+ end
663
+
664
+ # update west points
665
+ nw_point = ne_point
666
+ sw_point = se_point
667
+
558
668
  else
559
- polygon_a = OpenStudio::Point3dVector.new
560
- polygon_a << sw_point
561
- polygon_a << nw_point
562
- polygon_a << ne_point
563
- polygon_a << se_point
564
- hash_of_point_vectors["#{space_type.name} #{k}"] = {}
565
- hash_of_point_vectors["#{space_type.name} #{k}"][:space_type] = space_type
566
- hash_of_point_vectors["#{space_type.name} #{k}"][:polygon] = polygon_a
567
- end
568
669
 
569
- # update west points
570
- nw_point = ne_point
571
- sw_point = se_point
670
+ # create_bar at 90 degrees if aspect ration is less than 1.0
671
+ # typical order (sw,nw,ne,se)
672
+ # order used here (se,sw,nw,ne)
673
+
674
+ nw_point = sw_point + OpenStudio::Vector3d.new(0, slice, 0)
675
+ ne_point = se_point + OpenStudio::Vector3d.new(0, slice, 0)
676
+
677
+ if actual_perim > 0 && (actual_perim * 2.0) < length
678
+ polygon_a = OpenStudio::Point3dVector.new
679
+ polygon_a << se_point
680
+ polygon_a << se_point + OpenStudio::Vector3d.new(- actual_perim, 0, 0)
681
+ polygon_a << ne_point + OpenStudio::Vector3d.new(- actual_perim, 0, 0)
682
+ polygon_a << ne_point
683
+ if double_loaded_corridor
684
+ hash_of_point_vectors["#{perim_space_type.name} A #{k}"] = {}
685
+ hash_of_point_vectors["#{perim_space_type.name} A #{k}"][:space_type] = perim_space_type
686
+ hash_of_point_vectors["#{perim_space_type.name} A #{k}"][:polygon] = polygon_a
687
+ else
688
+ hash_of_point_vectors["#{space_type.name} A #{k}"] = {}
689
+ hash_of_point_vectors["#{space_type.name} A #{k}"][:space_type] = space_type
690
+ hash_of_point_vectors["#{space_type.name} A #{k}"][:polygon] = polygon_a
691
+ end
692
+
693
+ polygon_b = OpenStudio::Point3dVector.new
694
+ polygon_b << se_point + OpenStudio::Vector3d.new(- actual_perim, 0, 0)
695
+ polygon_b << sw_point + OpenStudio::Vector3d.new(actual_perim, 0, 0)
696
+ polygon_b << nw_point + OpenStudio::Vector3d.new(actual_perim, 0, 0)
697
+ polygon_b << ne_point + OpenStudio::Vector3d.new(- actual_perim, 0, 0)
698
+ if double_loaded_corridor
699
+ hash_of_point_vectors["#{core_space_type.name} B #{k}"] = {}
700
+ hash_of_point_vectors["#{core_space_type.name} B #{k}"][:space_type] = core_space_type
701
+ hash_of_point_vectors["#{core_space_type.name} B #{k}"][:polygon] = polygon_b
702
+ else
703
+ hash_of_point_vectors["#{space_type.name} B #{k}"] = {}
704
+ hash_of_point_vectors["#{space_type.name} B #{k}"][:space_type] = space_type
705
+ hash_of_point_vectors["#{space_type.name} B #{k}"][:polygon] = polygon_b
706
+ end
707
+
708
+ polygon_c = OpenStudio::Point3dVector.new
709
+ polygon_c << sw_point + OpenStudio::Vector3d.new(actual_perim, 0, 0)
710
+ polygon_c << sw_point
711
+ polygon_c << nw_point
712
+ polygon_c << nw_point + OpenStudio::Vector3d.new(actual_perim, 0, 0)
713
+ if double_loaded_corridor
714
+ hash_of_point_vectors["#{perim_space_type.name} C #{k}"] = {}
715
+ hash_of_point_vectors["#{perim_space_type.name} C #{k}"][:space_type] = perim_space_type
716
+ hash_of_point_vectors["#{perim_space_type.name} C #{k}"][:polygon] = polygon_c
717
+ else
718
+ hash_of_point_vectors["#{space_type.name} C #{k}"] = {}
719
+ hash_of_point_vectors["#{space_type.name} C #{k}"][:space_type] = space_type
720
+ hash_of_point_vectors["#{space_type.name} C #{k}"][:polygon] = polygon_c
721
+ end
722
+ else
723
+ polygon_a = OpenStudio::Point3dVector.new
724
+ polygon_a << se_point
725
+ polygon_a << sw_point
726
+ polygon_a << nw_point
727
+ polygon_a << ne_point
728
+ hash_of_point_vectors["#{space_type.name} #{k}"] = {}
729
+ hash_of_point_vectors["#{space_type.name} #{k}"][:space_type] = space_type
730
+ hash_of_point_vectors["#{space_type.name} #{k}"][:polygon] = polygon_a
731
+ end
732
+
733
+ # update west points
734
+ sw_point = nw_point
735
+ se_point = ne_point
736
+
737
+ end
572
738
  end
573
739
  end
574
740
 
@@ -592,13 +758,24 @@ module OsLib_Geometry
592
758
  end
593
759
  end
594
760
 
761
+ # hash of new spaces (only change boundary conditions for these)
762
+ new_spaces = []
763
+
595
764
  # loop through story_hash and polygons to generate all of the spaces
596
765
  story_hash.each_with_index do |(story_name, story_data), index|
597
- # make new story
598
- story = OpenStudio::Model::BuildingStory.new(model)
599
- story.setNominalFloortoFloorHeight(story_data[:space_height]) # not used for anything
600
- story.setNominalZCoordinate (story_data[:space_origin_z]) # not used for anything
601
- story.setName("Story #{story_name}")
766
+ # make new story unless story at requested height already exists.
767
+ story = nil
768
+ model.getBuildingStorys.each do |ext_story|
769
+ if (ext_story.nominalZCoordinate.to_f - story_data[:space_origin_z].to_f).abs < 0.01
770
+ story = ext_story
771
+ end
772
+ end
773
+ if story.nil?
774
+ story = OpenStudio::Model::BuildingStory.new(model)
775
+ story.setNominalFloortoFloorHeight(story_data[:space_height]) # not used for anything
776
+ story.setNominalZCoordinate (story_data[:space_origin_z]) # not used for anything
777
+ story.setName("Story #{story_name}")
778
+ end
602
779
 
603
780
  # multiplier values for adjacent stories to be altered below as needed
604
781
  multiplier_story_above = 1
@@ -647,6 +824,7 @@ module OsLib_Geometry
647
824
 
648
825
  # make space
649
826
  space = OsLib_Geometry.makeSpaceFromPolygon(model, space_data[:polygon].first, space_data[:polygon], options)
827
+ new_spaces << space
650
828
 
651
829
  # set z origin to proper position
652
830
  space.setZOrigin(story_data[:space_origin_z])
@@ -676,7 +854,7 @@ module OsLib_Geometry
676
854
  # any changes to wall boundary conditions will be handled by same code that calls this method.
677
855
  # this method doesn't need to know about basements and party walls.
678
856
 
679
- return model
857
+ return new_spaces
680
858
  end
681
859
 
682
860
  # add def to create a space from input, optionally take a name, space type, story and thermal zone.
@@ -714,7 +892,7 @@ module OsLib_Geometry
714
892
  space.setName(options['name'])
715
893
  end
716
894
 
717
- if !options['spaceType'].nil?
895
+ if !options['spaceType'].nil? && options['spaceType'].class.to_s == 'OpenStudio::Model::SpaceType'
718
896
  space.setSpaceType(options['spaceType'])
719
897
  end
720
898