Module: OpenstudioStandards::CreateTypical

Defined in:
lib/openstudio-standards/create_typical/enumerations.rb,
lib/openstudio-standards/create_typical/create_typical.rb,
lib/openstudio-standards/create_typical/space_type_blend.rb,
lib/openstudio-standards/create_typical/space_type_ratios.rb

Overview

The CreateTypical module provides methods to create and modify an entire building energy model of a typical building

CreateTypicalEnumerations collapse

CreateTypical collapse

Space Type Blending collapse

Class Method Summary collapse

Class Method Details

.blend_internal_loads(model, source_space_or_space_type, target_space_type, ratios, collection_floor_area, space_hash) ⇒ Array<OpenStudio::Model::SpaceLoad>

blend internal loads used when working from existing model

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • source_space_or_space_type (OpenStudio::Model::Space, OpenStuido::Model::SpaceType)

    OpenStudio Space or SpaceType object

  • target_space_type (OpenStuido::Model::SpaceType)

    target OpenStudio SpaceType object

  • ratios (Hash)

    Hash of ratios

  • collection_floor_area (Double)

    floor area in m^2

  • space_hash (Hash)

    has of space load properties with space_hash = { int_loads: space_loads_hash, totals: space_totals }

Options Hash (ratios):

  • :floor_area_ratio (Double)
  • :num_people_ratio (Double)
  • :ext_surface_area_ratio (Double)
  • :ext_wall_area_ratio (Double)
  • :volume_ratio (Double)

Returns:

  • (Array<OpenStudio::Model::SpaceLoad>)

    Array of OpenStudio SpaceLoad objects



404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
# File 'lib/openstudio-standards/create_typical/space_type_blend.rb', line 404

def self.blend_internal_loads(model, source_space_or_space_type, target_space_type, ratios, collection_floor_area, space_hash)
  OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', 'WARNING: This method blends space types together. We recommend against blending space types. Blending space types averages internal loads that determine peak loads and therefore HVAC sizing. DO NOT use this method across thermal zones coupled with autosized HVAC.')

  # ratios
  floor_area_ratio = ratios[:floor_area_ratio]
  num_people_ratio = ratios[:num_people_ratio]
  ext_surface_area_ratio = ratios[:ext_surface_area_ratio]
  ext_wall_area_ratio = ratios[:ext_wall_area_ratio]
  volume_ratio = ratios[:volume_ratio]

  # for normalizing design level loads I need to know effective number of spaces instance is applied to
  if source_space_or_space_type.to_Space.is_initialized
    eff_num_spaces = source_space_or_space_type.multiplier
  else
    eff_num_spaces = 0
    source_space_or_space_type.spaces.each do |space|
      eff_num_spaces += space.multiplier
    end
  end

  # array of load instacnes re-assigned to blended space
  instances_array = []

  # internal_mass
  source_space_or_space_type.internalMass.each do |load_inst|
    load_def = load_inst.definition.to_InternalMassDefinition.get
    if load_def.surfaceArea.is_initialized
      # edit and assign a clone of definition and normalize per area based on floor area ratio
      if collection_floor_area == 0
        OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
      else
        cloned_load_def = load_def.clone(model).to_InternalMass.get
        orig_design_level = cloned_load_def.surfaceArea.get
        cloned_load_def.setSurfaceAreaperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
        cloned_load_def.setName("#{cloned_load_def.name} - pre-normalized value was #{orig_design_level.round} m^2.")
        load_inst.setInternalMassDefinition(cloned_load_def)
      end
    elsif load_def.surfaceAreaperSpaceFloorArea.is_initialized
      load_inst.setMultiplier(load_inst.multiplier * floor_area_ratio)
    elsif load_def.surfaceAreaperPerson.is_initialized
      if num_people_ratio.nil?
        OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "#{load_def} has value defined per person, but people ratio wasn't passed in")
        return false
      else
        load_inst.setMultiplier(load_inst.multiplier * num_people_ratio)
      end
    else
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Unexpected value type for #{load_def.name}")
      return false
    end
    load_inst.setSpaceType(target_space_type)
    instances_array << load_inst
  end

  # people
  source_space_or_space_type.people.each do |load_inst|
    load_def = load_inst.definition.to_PeopleDefinition.get
    if load_def.numberofPeople.is_initialized
      # edit and assign a clone of definition and normalize per area based on floor area ratio
      if collection_floor_area == 0
        OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
      else
        cloned_load_def = load_def.clone(model).to_PeopleDefinition.get
        orig_design_level = cloned_load_def.numberofPeople.get
        cloned_load_def.setPeopleperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
        cloned_load_def.setName("#{cloned_load_def.name} - pre-normalized value was #{orig_design_level.round} people.")
        load_inst.setPeopleDefinition(cloned_load_def)
      end
    elsif load_def.peopleperSpaceFloorArea.is_initialized || load_def.spaceFloorAreaperPerson.is_initialized
      load_inst.setMultiplier(load_inst.multiplier * floor_area_ratio)
    else
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Unexpected value type for #{load_def.name}")
      return false
    end
    load_inst.setSpaceType(target_space_type)
    instances_array << load_inst
  end

  # lights
  source_space_or_space_type.lights.each do |load_inst|
    load_def = load_inst.definition.to_LightsDefinition.get
    if load_def.lightingLevel.is_initialized
      # edit and assign a clone of definition and normalize per area based on floor area ratio
      if collection_floor_area == 0
        OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
      else
        cloned_load_def = load_def.clone(model).to_LightsDefinition.get
        orig_design_level = cloned_load_def.lightingLevel.get
        cloned_load_def.setWattsperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
        cloned_load_def.setName("#{cloned_load_def.name} - pre-normalized value was #{orig_design_level.round} W.")
        load_inst.setLightsDefinition(cloned_load_def)
      end
    elsif load_def.wattsperSpaceFloorArea.is_initialized
      load_inst.setMultiplier(load_inst.multiplier * floor_area_ratio)
    elsif load_def.wattsperPerson.is_initialized
      if num_people_ratio.nil?
        OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "#{load_def} has value defined per person, but people ratio wasn't passed in")
        return false
      else
        load_inst.setMultiplier(load_inst.multiplier * num_people_ratio)
      end
    else
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Unexpected value type for #{load_def.name}")
      return false
    end
    load_inst.setSpaceType(target_space_type)
    instances_array << load_inst
  end

  # luminaires
  source_space_or_space_type.luminaires.each do |load_inst|
    # @todo can't normalize luminaire. Replace it with similar normalized lights def and instance
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "Can't area normalize luminaire. Instance will be applied to every space using the blended space type")
    instances_array << load_inst
  end

  # electric_equipment
  source_space_or_space_type.electricEquipment.each do |load_inst|
    load_def = load_inst.definition.to_ElectricEquipmentDefinition.get
    if load_def.designLevel.is_initialized
      # edit and assign a clone of definition and normalize per area based on floor area ratio
      if collection_floor_area == 0
        OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
      else
        cloned_load_def = load_def.clone(model).to_ElectricEquipmentDefinition.get
        orig_design_level = cloned_load_def.designLevel.get
        cloned_load_def.setWattsperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
        cloned_load_def.setName("#{cloned_load_def.name} - pre-normalized value was #{orig_design_level.round} W.")
        load_inst.setElectricEquipmentDefinition(cloned_load_def)
      end
    elsif load_def.wattsperSpaceFloorArea.is_initialized
      load_inst.setMultiplier(load_inst.multiplier * floor_area_ratio)
    elsif load_def.wattsperPerson.is_initialized
      if num_people_ratio.nil?
        OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "#{load_def} has value defined per person, but people ratio wasn't passed in")
        return false
      else
        load_inst.setMultiplier(load_inst.multiplier * num_people_ratio)
      end
    else
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Unexpected value type for #{load_def.name}")
      return false
    end
    load_inst.setSpaceType(target_space_type)
    instances_array << load_inst
  end

  # gas_equipment
  source_space_or_space_type.gasEquipment.each do |load_inst|
    load_def = load_inst.definition.to_GasEquipmentDefinition.get
    if load_def.designLevel.is_initialized
      # edit and assign a clone of definition and normalize per area based on floor area ratio
      if collection_floor_area == 0
        OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
      else
        cloned_load_def = load_def.clone(model).to_GasEquipmentDefinition.get
        orig_design_level = cloned_load_def.designLevel.get
        cloned_load_def.setWattsperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
        cloned_load_def.setName("#{cloned_load_def.name} - pre-normalized value was #{orig_design_level.round} W.")
        load_inst.setGasEquipmentDefinition(cloned_load_def)
      end
    elsif load_def.wattsperSpaceFloorArea.is_initialized
      load_inst.setMultiplier(load_inst.multiplier * floor_area_ratio)
    elsif load_def.wattsperPerson.is_initialized
      if num_people_ratio.nil?
        OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "#{load_def} has value defined per person, but people ratio wasn't passed in")
        return false
      else
        load_inst.setMultiplier(load_inst.multiplier * num_people_ratio)
      end
    else
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Unexpected value type for #{load_def.name}")
      return false
    end
    load_inst.setSpaceType(target_space_type)
    instances_array << load_inst
  end

  # hot_water_equipment
  source_space_or_space_type.hotWaterEquipment.each do |load_inst|
    load_def = load_inst.definition.to_HotWaterDefinition.get
    if load_def.designLevel.is_initialized
      # edit and assign a clone of definition and normalize per area based on floor area ratio
      if collection_floor_area == 0
        OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
      else
        cloned_load_def = load_def.clone(model).to_HotWaterEquipmentDefinition.get
        orig_design_level = cloned_load_def.designLevel.get
        cloned_load_def.setWattsperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
        cloned_load_def.setName("#{cloned_load_def.name} - pre-normalized value was #{orig_design_level.round} W.")
        load_inst.setHotWaterEquipmentDefinition(cloned_load_def)
      end
    elsif load_def.wattsperSpaceFloorArea.is_initialized
      load_inst.setMultiplier(load_inst.multiplier * floor_area_ratio)
    elsif load_def.wattsperPerson.is_initialized
      if num_people_ratio.nil?
        OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "#{load_def} has value defined per person, but people ratio wasn't passed in")
        return false
      else
        load_inst.setMultiplier(load_inst.multiplier * num_people_ratio)
      end
    else
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Unexpected value type for #{load_def.name}")
      return false
    end
    load_inst.setSpaceType(target_space_type)
    instances_array << load_inst
  end

  # steam_equipment
  source_space_or_space_type.steamEquipment.each do |load_inst|
    load_def = load_inst.definition.to_SteamDefinition.get
    if load_def.designLevel.is_initialized
      # edit and assign a clone of definition and normalize per area based on floor area ratio
      if collection_floor_area == 0
        OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
      else
        cloned_load_def = load_def.clone(model).to_SteamEquipmentDefinition.get
        orig_design_level = cloned_load_def.designLevel.get
        cloned_load_def.setWattsperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
        cloned_load_def.setName("#{cloned_load_def.name} - pre-normalized value was #{orig_design_level.round} W.")
        load_inst.setSteamEquipmentDefinition(cloned_load_def)
      end
    elsif load_def.wattsperSpaceFloorArea.is_initialized
      load_inst.setMultiplier(load_inst.multiplier * floor_area_ratio)
    elsif load_def.wattsperPerson.is_initialized
      if num_people_ratio.nil?
        OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "#{load_def} has value defined per person, but people ratio wasn't passed in")
        return false
      else
        load_inst.setMultiplier(load_inst.multiplier * num_people_ratio)
      end
    else
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Unexpected value type for #{load_def.name}")
      return false
    end
    load_inst.setSpaceType(target_space_type)
    instances_array << load_inst
  end

  # other_equipment
  source_space_or_space_type.otherEquipment.each do |load_inst|
    load_def = load_inst.definition.to_OtherDefinition.get
    if load_def.designLevel.is_initialized
      # edit and assign a clone of definition and normalize per area based on floor area ratio
      if collection_floor_area == 0
        OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
      else
        cloned_load_def = load_def.clone(model).to_OtherEquipmentDefinition.get
        orig_design_level = cloned_load_def.designLevel.get
        cloned_load_def.setWattsperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
        cloned_load_def.setName("#{cloned_load_def.name} - pre-normalized value was #{orig_design_level.round} W.")
        load_inst.setOtherEquipmentDefinition(cloned_load_def)
      end
    elsif load_def.wattsperSpaceFloorArea.is_initialized
      load_inst.setMultiplier(load_inst.multiplier * floor_area_ratio)
    elsif load_def.wattsperPerson.is_initialized
      if num_people_ratio.nil?
        OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "#{load_def} has value defined per person, but people ratio wasn't passed in")
        return false
      else
        load_inst.setMultiplier(load_inst.multiplier * num_people_ratio)
      end
    else
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Unexpected value type for #{load_def.name}")
      return false
    end
    load_inst.setSpaceType(target_space_type)
    instances_array << load_inst
  end

  # space_infiltration_design_flow_rates
  source_space_or_space_type.spaceInfiltrationDesignFlowRates.each do |load_inst|
    case load_inst.designFlowRateCalculationMethod
    when 'Flow/Space'
      # edit load so normalized for building area
      if collection_floor_area == 0
        OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
      else
        orig_design_level = load_inst.designFlowRate.get
        load_inst.setFlowperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
        load_inst.setName("#{load_inst.name} -  pre-normalized value was #{orig_design_level} m^3/sec")
      end
    when 'Flow/Area'
      load_inst.setFlowperSpaceFloorArea(load_inst.flowperSpaceFloorArea.get * floor_area_ratio)
    when 'Flow/ExteriorArea'
      load_inst.setFlowperExteriorSurfaceArea(load_inst.flowperExteriorSurfaceArea.get * ext_surface_area_ratio)
    when 'Flow/ExteriorWallArea'
      load_inst.setFlowperExteriorWallArea(load_inst.flowperExteriorWallArea.get * ext_wall_area_ratio)
    when 'AirChanges/Hour'
      load_inst.setAirChangesperHour(load_inst.airChangesperHour.get * volume_ratio)
    else
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Unexpected value type for #{load_inst.name}")
      return false
    end
    load_inst.setSpaceType(target_space_type)
    instances_array << load_inst
  end

  # space_infiltration_effective_leakage_areas
  source_space_or_space_type.spaceInfiltrationEffectiveLeakageAreas.each do |load|
    # @todo can't normalize space_infiltration_effective_leakage_areas. Come up with logic to address this
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "Can't area normalize space_infiltration_effective_leakage_areas. It will be applied to every space using the blended space type")
    load.setSpaceType(target_space_type)
    instances_array << load
  end

  # add OA object if it doesn't already exist
  if target_space_type.designSpecificationOutdoorAir.is_initialized
    blended_oa = target_space_type.designSpecificationOutdoorAir.get
  else
    blended_oa = OpenStudio::Model::DesignSpecificationOutdoorAir.new(model)
    blended_oa.setName('Blended OA')
    blended_oa.setOutdoorAirMethod('Sum')
    target_space_type.setDesignSpecificationOutdoorAir(blended_oa)
    instances_array << blended_oa
  end

  # update OA object
  if source_space_or_space_type.designSpecificationOutdoorAir.is_initialized
    oa = source_space_or_space_type.designSpecificationOutdoorAir.get
    oa_sch = nil
    if oa.outdoorAirFlowRateFractionSchedule.is_initialized
      # @todo improve logic to address multiple schedules
      OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "Schedule #{oa.outdoorAirFlowRateFractionSchedule.get.name} assigned to #{oa.name} will be ignored. New OA object will not have a schedule assigned")
    end
    if oa.outdoorAirMethod == 'Maximum'
      # @todo  see if way to address this by pre-calculating the max and only entering that value for space type
      OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "Outdoor air method of Maximum will be ignored for #{oa.name}. New OA object will have outdoor air method of Sum.")
    end
    # adjusted ratios for oa (lowered for space type if there is hard assigned oa load for one or more spaces)
    oa_floor_area_ratio = floor_area_ratio
    oa_num_people_ratio = num_people_ratio
    if source_space_or_space_type.class.to_s == 'OpenStudio::Model::SpaceType'
      source_space_or_space_type.spaces.each do |space|
        if !space.isDesignSpecificationOutdoorAirDefaulted
          if space_hash.nil?
            OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', 'No space_hash passed in and model has OA designed at space level.')
          else
            oa_floor_area_ratio -= space_hash[space][:floor_area_ratio]
            oa_num_people_ratio -= space_hash[space][:num_people_ratio]
          end
        end
      end
    end
    # add to values of blended OA load
    if oa.outdoorAirFlowperPerson > 0
      blended_oa.setOutdoorAirFlowperPerson(blended_oa.outdoorAirFlowperPerson + (oa.outdoorAirFlowperPerson * oa_num_people_ratio))
    end
    if oa.outdoorAirFlowperFloorArea > 0
      blended_oa.setOutdoorAirFlowperFloorArea(blended_oa.outdoorAirFlowperFloorArea + (oa.outdoorAirFlowperFloorArea * oa_floor_area_ratio))
    end
    if oa.outdoorAirFlowRate > 0

      # calculate quantity for instance (doesn't exist as a method in api)
      if source_space_or_space_type.class.to_s == 'OpenStudio::Model::SpaceType'
        quantity = 0
        source_space_or_space_type.spaces.each do |space|
          if !space.isDesignSpecificationOutdoorAirDefaulted
            quantity += space.multiplier
          end
        end
      else
        quantity = source_space_or_space_type.multiplier
      end

      # can't normalize air flow rate, convert to air flow rate per floor area
      blended_oa.setOutdoorAirFlowperFloorArea(blended_oa.outdoorAirFlowperFloorArea + (quantity * oa.outdoorAirFlowRate / collection_floor_area))
    end
    if oa.outdoorAirFlowAirChangesperHour > 0
      # floor area should be good approximation of area for multiplier
      blended_oa.setOutdoorAirFlowAirChangesperHour(blended_oa.outdoorAirFlowAirChangesperHour + (oa.outdoorAirFlowAirChangesperHour * oa_floor_area_ratio))
    end
  end

  # @note water_use_equipment can't be assigned to a space type. Leave it as is, if assigned to space type
  # @todo if we use this measure with new geometry need to find a way to pull water use equipment loads into new model

  return instances_array
end

.blend_space_type_collections(model, space_type_hash) ⇒ Array<OpenStudio::Model::SpaceType>

takes in space type hash where each hash value is a collection of space types. Each collection is blended into it’s own space type If key for any collection is “Building” it will also opererate on spaces that don’t have space type assigned where a space assigned to a space type from a collection has space loads, those space loads are normalized and added to the blended space type load instances are maintained so that they can haave unique schedules, and can have EE measures selectivly applied.

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • space_type_hash (Hash)

    Hash of space type information

Returns:

  • (Array<OpenStudio::Model::SpaceType>)

    Array of OpenStudio SpaceType objects



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
# File 'lib/openstudio-standards/create_typical/space_type_blend.rb', line 126

def self.blend_space_type_collections(model, space_type_hash)
  # loop through building type hash to create multiple blends
  space_type_hash.each do |collection_name, space_types|
    if collection_name == 'Building'
      space_array = model.getSpaces.sort # use all space types, not just space types passed in
    else
      space_array = []
      space_types.each do |space_type|
        space_array.concat(space_type.spaces)
      end
    end

    # calculate metrics for all spaces included in building area to pass into space_type and space hash
    # @note in the future this may be a subset of spaces if blending into multiple space types vs. just one.
    collection_totals = {}
    collection_totals[:floor_area] = 0.0
    collection_totals[:num_people] = 0.0
    collection_totals[:ext_surface_area] = 0.0
    collection_totals[:ext_wall_area] = 0.0
    collection_totals[:volume] = 0.0
    space_array.each do |space|
      next if !space.partofTotalFloorArea

      collection_totals[:floor_area] += space.floorArea * space.multiplier
      collection_totals[:num_people] += space.numberOfPeople * space.multiplier
      collection_totals[:ext_surface_area] += space.exteriorArea * space.multiplier
      collection_totals[:ext_wall_area] += space.exteriorWallArea * space.multiplier
      collection_totals[:volume] += space.volume * space.multiplier
    end
    area_ip = OpenStudio.convert(collection_totals[:floor_area], 'm^2', 'ft^2').get
    area_ip_neat = OpenStudio.toNeatString(area_ip, 2, true)
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "#{collection_name} area is #{area_ip_neat} ft^2, number of people is #{collection_totals[:num_people].round(0)}.")

    # create hash of space types and floor area for all space types with area > 0 when spaces included in floor area
    # code to gather space type areas came from openstudio_results measure.
    space_type_hash = {}
    largest_space_type = nil
    largest_space_type_ratio = 0.00
    space_types.each do |space_type|
      next if space_type.floorArea == 0

      space_type_totals = {}
      space_type_totals[:floor_area] = 0.0
      space_type_totals[:num_people] = 0.0
      space_type_totals[:ext_surface_area] = 0.0
      space_type_totals[:ext_wall_area] = 0.0
      space_type_totals[:volume] = 0.0
      # loop through spaces so I can skip if not included in floor area
      space_type.spaces.each do |space|
        next if !space.partofTotalFloorArea

        space_type_totals[:floor_area] += space.floorArea * space.multiplier
        space_type_totals[:num_people] += space.numberOfPeople * space.multiplier
        space_type_totals[:ext_surface_area] += space.exteriorArea * space.multiplier
        space_type_totals[:ext_wall_area] += space.exteriorWallArea * space.multiplier
        space_type_totals[:volume] += space.volume * space.multiplier
      end

      # update largest space type values
      if largest_space_type.nil? || space_type_totals[:floor_area] > largest_space_type_ratio
        largest_space_type = space_type
        largest_space_type_ratio = space_type_totals[:floor_area]
      end

      # gather internal loads
      space_type_loads_hash = OpenstudioStandards::CreateTypical.space_or_space_type_gather_internal_loads(space_type)

      # don't add to hash if no spaces used for space type are included in building area (e.g. plenum and attic)
      # @todo log these and decide what to do for them. Leave loads alone or remove, do they add to blend at all?
      next if space_type_totals[:floor_area] == 0

      if !space_type_totals[:floor_area] = space_type.floorArea
        # @todo not sure if these would ever show as different
        OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "Some but not all spaces of #{space_type.name} space type are not included in the building floor area. May have unexpected results")
      end

      # populate space type hash
      space_type_hash[space_type] = { int_loads: space_type_loads_hash, totals: space_type_totals }
    end

    # report initial condition of model
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "#{collection_name} accounts for #{space_type_hash.size} space types.")

    if collection_name == 'Building'
      # count area of spaces that have no space type
      no_space_type_area_counter = 0
      model.getSpaces.sort.each do |space|
        if space.spaceType.empty?
          next if !space.partofTotalFloorArea

          no_space_type_area_counter += space.floorArea * space.multiplier
        end
      end
      floor_area_ratio = no_space_type_area_counter / collection_totals[:floor_area]
      if floor_area_ratio > 0
        OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "#{floor_area_ratio} fraction of building area is composed of spaces without space type assignments.")
      end
    end

    # report the space ratio for hard spaces
    space_hash = {}
    space_array.each do |space|
      next if !space.partofTotalFloorArea

      space_loads_hash = OpenstudioStandards::CreateTypical.space_or_space_type_gather_internal_loads(space)
      space_totals = {}
      space_totals[:floor_area] = space.floorArea * space.multiplier
      space_totals[:num_people] = space.numberOfPeople * space.multiplier
      space_totals[:ext_surface_area] = space.exteriorArea * space.multiplier
      space_totals[:ext_wall_area] = space.exteriorWallArea * space.multiplier
      space_totals[:volume] = space.volume * space.multiplier
      if !space_loads_hash[:daylighting_controls].empty?
        OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "#{space.name} has one or more daylighting controls. Lighting loads from blended space type may affect lighting reduction from daylighting controls.")
      end
      if !space_loads_hash[:water_use_equipment].empty?
        OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "One or more water use equipment objects are associated with space #{space.name}. This can't be moved to a space type.")
      end
      # @note If generating ratios without geometry can calculate people_ratio given space_types floor_area_ratio
      space_hash[space] = { int_loads: space_loads_hash, totals: space_totals }
    end

    # create stub blended space type
    blended_space_type = OpenStudio::Model::SpaceType.new(model)
    blended_space_type.setName("#{collection_name} Blended Space Type")

    # set standards info for space type based on largest ratio (for use to apply HVAC system)
    standards_building_type = largest_space_type.standardsBuildingType
    standards_space_type = largest_space_type.standardsSpaceType
    if standards_building_type.is_initialized
      blended_space_type.setStandardsBuildingType(standards_building_type.get)
    end
    if standards_space_type.is_initialized
      blended_space_type.setStandardsSpaceType(standards_space_type.get)
    end

    # values from collection hash
    collection_floor_area = collection_totals[:floor_area]
    collection_num_people = collection_totals[:num_people]
    collection_ext_surface_area = collection_totals[:ext_surface_area]
    collection_ext_wall_area = collection_totals[:ext_wall_area]
    collection_volume = collection_totals[:volume]

    # loop through space that have one or more spaces included in the building area
    space_type_hash.each do |space_type, hash|
      # hard assign space load schedules before re-assign instances to blended space type
      space_type.hardApplySpaceLoadSchedules

      # vaules from space or space_type
      floor_area = hash[:totals][:floor_area]
      num_people = hash[:totals][:num_people]
      ext_surface_area = hash[:totals][:ext_surface_area]
      ext_wall_area = hash[:totals][:ext_wall_area]
      volume = hash[:totals][:volume]

      # ratios
      ratios = {}
      if collection_floor_area > 0
        ratios[:floor_area_ratio] = floor_area / collection_floor_area
      else
        ratios[:floor_area_ratio] = 0.0
      end
      if collection_num_people > 0
        ratios[:num_people_ratio] = num_people / collection_num_people
      else
        ratios[:num_people_ratio] = 0.0
      end
      if collection_ext_surface_area > 0
        ratios[:ext_surface_area_ratio] = ext_surface_area / collection_ext_surface_area
      else
        ratios[:ext_surface_area_ratio] = 0.0
      end
      if collection_ext_wall_area > 0
        ratios[:ext_wall_area_ratio] = ext_wall_area / collection_ext_wall_area
      else
        ratios[:ext_wall_area_ratio] = 0.0
      end
      if collection_volume > 0
        ratios[:volume_ratio] = volume / collection_volume
      else
        ratios[:volume_ratio] = 0.0
      end

      # populate blended space type with space type loads
      space_type_load_instances = OpenstudioStandards::CreateTypical.blend_internal_loads(model, space_type, blended_space_type, ratios, collection_floor_area, space_hash)
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Blending space type #{space_type.name}. Floor area ratio is #{(hash[:totals][:floor_area] / collection_totals[:floor_area]).round(3)}. People ratio is #{(hash[:totals][:num_people] / collection_totals[:num_people]).round(3)}")

      # hard assign any constructions assigned by space types, except for space not included in the building area
      if space_type.defaultConstructionSet.is_initialized
        OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Hard assigning constructions for #{space_type.name}.")
        space_type.spaces.each(&:hardApplyConstructions)
      end

      # remove all space type assignments, except for spaces not included in building area.
      space_type.spaces.each do |space|
        next if !space.partofTotalFloorArea

        space.resetSpaceType
      end

      # delete space type. Don't want to leave in model since internal loads  have been removed from it
      space_type.remove
    end

    # loop through spaces that are included in building area
    space_hash.each do |space, hash|
      # hard assign space load schedules before re-assign instances to blended space type
      space.hardApplySpaceLoadSchedules

      # vaules from space or space_type
      floor_area = hash[:totals][:floor_area]
      num_people = hash[:totals][:num_people]
      ext_surface_area = hash[:totals][:ext_surface_area]
      ext_wall_area = hash[:totals][:ext_wall_area]
      volume = hash[:totals][:volume]

      # ratios
      ratios = {}
      if collection_floor_area > 0
        ratios[:floor_area_ratio] = floor_area / collection_floor_area
      else
        ratios[:floor_area_ratio] = 0.0
      end
      if collection_num_people > 0
        ratios[:num_people_ratio] = num_people / collection_num_people
      else
        ratios[:num_people_ratio] = 0.0
      end
      if collection_ext_surface_area > 0
        ratios[:ext_surface_area_ratio] = ext_surface_area / collection_ext_surface_area
      else
        ratios[:ext_surface_area_ratio] = 0.0
      end
      if collection_ext_wall_area > 0
        ratios[:ext_wall_area_ratio] = ext_wall_area / collection_ext_wall_area
      else
        ratios[:ext_wall_area_ratio] = 0.0
      end
      if collection_volume > 0
        ratios[:volume_ratio] = volume / collection_volume
      else
        ratios[:volume_ratio] = 0.0
      end

      # populate blended space type with space loads
      space_load_instances = OpenstudioStandards::CreateTypical.blend_internal_loads(model, space, blended_space_type, ratios, collection_floor_area, space_hash)
      next if space_load_instances.empty?

      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Blending space #{space.name}. Floor area ratio is #{(hash[:totals][:floor_area] / collection_totals[:floor_area]).round(3)}. People ratio is #{(hash[:totals][:num_people] / collection_totals[:num_people]).round(3)}")
    end

    if collection_name == 'Building'
      # assign blended space type to building
      model.getBuilding.setSpaceType(blended_space_type)
      building_space_type = model.getBuilding.spaceType
    else
      space_array.each do |space|
        space.setSpaceType(blended_space_type)
      end
    end
  end

  return model.getSpaceTypes.sort
end

.blend_space_types_from_floor_area_ratio(model, space_type_ratio_hash) ⇒ OpenStudio::Model::SpaceType

blend_space_types_from_floor_area_ratio used when working from space type ratio and un-assigned space types

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • space_type_ratio_hash (Hash)

    Hash where the keys are OpenStudio SpaceType objects and hash includes the floor area ratio

Options Hash (space_type_ratio_hash):

  • :floor_area_ratio (Double)

Returns:

  • (OpenStudio::Model::SpaceType)

    Blended OpenStudio SpaceType object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/openstudio-standards/create_typical/space_type_blend.rb', line 47

def self.blend_space_types_from_floor_area_ratio(model, space_type_ratio_hash)
  # create stub blended space type
  blended_space_type = OpenStudio::Model::SpaceType.new(model)
  blended_space_type.setName('Blended Space Type')

  # @todo inspect people instances and see if any defs are not normalized per area. If find any issue warning

  # gather inputs
  sum_of_num_people_per_m_2 = 0.0
  space_type_ratio_hash.each do |space_type, ratios|
    # get number of peple per m 2 for space type. Can do this without looking at instances
    sum_of_num_people_per_m_2 += space_type.getPeoplePerFloorArea(1.0)
  end

  # raw num_people_ratios
  sum_area_adj_num_people_ratio = 0.0
  space_type_ratio_hash.each do |space_type, ratios|
    # calculate num_people_ratios
    area_adj_num_people_ratio = (space_type.getPeoplePerFloorArea(1.0) / sum_of_num_people_per_m_2) * ratios[:floor_area_ratio]
    sum_area_adj_num_people_ratio += area_adj_num_people_ratio
  end

  # set ratios
  largest_space_type = nil
  largest_space_type_ratio = 0.00
  space_type_ratio_hash.each do |space_type, ratios|
    # calculate num_people_ratios
    area_adj_num_people_ratio = (space_type.getPeoplePerFloorArea(1.0) / sum_of_num_people_per_m_2) * ratios[:floor_area_ratio]
    normalized_area_adj_num_people_ratio = area_adj_num_people_ratio / sum_area_adj_num_people_ratio

    # ratios[:floor_area_ratio] is already defined
    ratios[:num_people_ratio] = normalized_area_adj_num_people_ratio.round(4)
    ratios[:ext_surface_area_ratio] = ratios[:floor_area_ratio]
    ratios[:ext_wall_area_ratio] = ratios[:floor_area_ratio]
    ratios[:volume_ratio] = ratios[:floor_area_ratio]

    # update largest space type values
    if largest_space_type.nil? || ratios[:floor_area_ratio] > largest_space_type_ratio
      largest_space_type = space_type
      largest_space_type_ratio = ratios[:floor_area_ratio]
    end
  end

  if largest_space_type.nil?
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Didn't find any space types in model matching user argument string.")
    return false
  end

  # set standards info for space type based on largest ratio (for use to apply HVAC system)
  standards_building_type = largest_space_type.standardsBuildingType
  standards_space_type = largest_space_type.standardsSpaceType
  if standards_building_type.is_initialized
    blended_space_type.setStandardsBuildingType(standards_building_type.get)
  end
  if standards_space_type.is_initialized
    blended_space_type.setStandardsSpaceType(standards_space_type.get)
  end

  # loop therough space types to get instances from and then remove
  space_type_ratio_hash.each do |space_type, ratios|
    # blend internal loads (nil is space_hash)
    space_type_load_instances = OpenstudioStandards::CreateTypical.blend_internal_loads(model, space_type, blended_space_type, ratios, model.getBuilding.floorArea, nil)
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Blending #{space_type.name.get} with floor area ratio of #{ratios[:floor_area_ratio]} and number of people ratio of #{ratios[:num_people_ratio]}.")

    # delete space type. Don't want to leave in model since internal loads have been removed from it
    space_type.remove
  end

  return blended_space_type
end

.create_space_types_and_constructions(model, building_type, template, climate_zone, create_space_types: true, create_construction_set: true, set_building_defaults: true) ⇒ Boolean

creates spaces types and construction objects in the model for the given building type, template, and climate zone

Parameters:

  • building_type (String)

    standard building type

  • template (String)

    standard template

  • climate_zone (String)

    ASHRAE climate zone, e.g. ‘ASHRAE 169-2013-4A’

  • create_space_types (Boolean) (defaults to: true)

    Create space types

  • create_construction_set (Boolean) (defaults to: true)

    Create the construction set

  • set_building_defaults (Boolean) (defaults to: true)

    Set the climate zone, newly generated construction set, and first newly generated space type as the building default

Returns:

  • (Boolean)

    returns true if successful, false if not



854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
# File 'lib/openstudio-standards/create_typical/create_typical.rb', line 854

def self.create_space_types_and_constructions(model,
                                              building_type,
                                              template,
                                              climate_zone,
                                              create_space_types: true,
                                              create_construction_set: true,
                                              set_building_defaults: true)
  # reporting initial condition of model
  starting_space_types = model.getSpaceTypes.sort
  starting_construction_sets = model.getDefaultConstructionSets.sort
  OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "The building started with #{starting_space_types.size} space types and #{starting_construction_sets.size} construction sets.")

  # lookup space types for specified building type (false indicates not to use whole building type only)
  space_type_hash = OpenstudioStandards::CreateTypical.get_space_types_from_building_type(building_type, template: template, whole_building: false)
  if space_type_hash == false
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "#{building_type} is an unexpected building type.")
    return false
  end

  # create space_type_map from array
  space_type_map = {}
  default_space_type_name = nil
  space_type_hash.each do |space_type_name, hash|
    # skip space types like undeveloped and basement
    next if hash[:space_type_gen] == false

    # no spaces to pass in
    space_type_map[space_type_name] = []
    if hash[:default]
      default_space_type_name = space_type_name
    end
  end

  # Make the standard applier
  standard = Standard.build(template)

  # mapping building_type name is needed for a few methods
  lookup_building_type = standard.model_get_lookup_name(building_type)

  # remap small medium and large office to office
  if building_type.include?('Office')
    building_type = 'Office'
  end

  # get array of new space types
  space_types_new = []

  # create_space_types
  if create_space_types

    # array of starting space types
    space_types_starting = model.getSpaceTypes.sort

    # create stub space types
    space_type_hash.each do |space_type_name, hash|
      # skip space types like undeveloped and basement
      next if hash[:space_type_gen] == false

      # create space type
      space_type = OpenStudio::Model::SpaceType.new(model)
      space_type.setStandardsBuildingType(lookup_building_type)
      space_type.setStandardsSpaceType(space_type_name)
      space_type.setName("#{lookup_building_type} #{space_type_name}")

      # add to array of new space types
      space_types_new << space_type

      # add internal loads (the nil check isn't necessary, but I will keep it in as a warning instad of an error)
      test = standard.space_type_apply_internal_loads(space_type, true, true, true, true, true, true)
      if test.nil?
        OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "Could not add loads for #{space_type.name}. Not expected for #{template} #{lookup_building_type}")
      end

      # the last bool test it to make thermostat schedules. They are added to the model but not assigned
      standard.space_type_apply_internal_load_schedules(space_type, true, true, true, true, true, true, true)

      # assign colors
      standard.space_type_apply_rendering_color(space_type)

      # exend space type name to include the template. Consider this as well for load defs
      space_type.setName("#{space_type.name} - #{template}")
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Added space type named #{space_type.name}")
    end

  end

  # add construction sets
  bldg_def_const_set = nil
  if create_construction_set

    # Make the default construction set for the building
    is_residential = 'No' # default is nonresidential for building level
    bldg_def_const_set = standard.model_add_construction_set(model, climate_zone, lookup_building_type, nil, is_residential)
    if bldg_def_const_set.is_initialized
      bldg_def_const_set = bldg_def_const_set.get
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Added default construction set named #{bldg_def_const_set.name}")
    else
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', 'Could not create default construction set for the building.')
      return false
    end

    # make residential construction set as unused resource
    if ['SmallHotel', 'LargeHotel', 'MidriseApartment', 'HighriseApartment'].include?(building_type)
      res_const_set = standard.model_add_construction_set(model, climate_zone, lookup_building_type, nil, 'Yes')
      if res_const_set.is_initialized
        res_const_set = res_const_set.get
        res_const_set.setName("#{bldg_def_const_set.name} - Residential ")
        OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Added residential construction set named #{res_const_set.name}")
      else
        OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', 'Could not create residential construction set for the building.')
        return false
      end
    end

  end

  # set_building_defaults
  if set_building_defaults

    # identify default space type
    default_space_type = nil
    space_types_new.each do |space_type|
      standards_building_type = space_type.standardsBuildingType.is_initialized ? space_type.standardsBuildingType.get : nil
      standards_space_type = space_type.standardsSpaceType.is_initialized ? space_type.standardsSpaceType.get : nil
      if default_space_type_name == standards_space_type
        default_space_type = space_type
      end
    end

    # set default space type
    building = model.getBuilding
    if !default_space_type.nil?
      building.setSpaceType(default_space_type)
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Setting default Space Type for building to #{building.spaceType.get.name}")
    end

    # default construction
    if !bldg_def_const_set.nil?
      building.setDefaultConstructionSet(bldg_def_const_set)
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Setting default Construction Set for building to #{building.defaultConstructionSet.get.name}")
    end

    # set climate zone
    os_climate_zone = climate_zone.gsub('ASHRAE 169-2013-', '')
    # trim off letter from climate zone 7 or 8
    if (os_climate_zone[0] == '7') || (os_climate_zone[0] == '8')
      os_climate_zone = os_climate_zone[0]
    end
    climate_zone = model.getClimateZones.setClimateZone('ASHRAE', os_climate_zone)
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Setting #{climate_zone.institution} Climate Zone to #{climate_zone.value}")

    # set building type
    # use lookup_building_type so spaces like MediumOffice will map to Office (Supports baseline automation)
    building.setStandardsBuildingType(lookup_building_type)
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Setting Standards Building Type to #{building.standardsBuildingType}")

    # rename building if it is named "Building 1"
    if model.getBuilding.name.to_s == 'Building 1'
      model.getBuilding.setName("#{building_type} #{template} #{os_climate_zone}")
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Renaming building to #{model.getBuilding.name}")
    end
  end

  # reporting final condition of model
  finishing_space_types = model.getSpaceTypes.sort
  finishing_construction_sets = model.getDefaultConstructionSets.sort
  OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "The building finished with #{finishing_space_types.size} space types and #{finishing_construction_sets.size} construction sets.")

  return true
end

.create_typical_building_from_model(model, template, climate_zone: 'Lookup From Model', add_hvac: true, hvac_system_type: 'Inferred', hvac_delivery_type: 'Forced Air', heating_fuel: 'NaturalGas', service_water_heating_fuel: 'NaturalGas', cooling_fuel: 'Electricity', kitchen_makeup: 'Adjacent', exterior_lighting_zone: '3 - All Other Areas', add_constructions: true, wall_construction_type: 'Inferred', add_space_type_loads: true, add_daylighting_controls: true, add_elevators: true, add_internal_mass: true, add_exterior_lights: true, onsite_parking_fraction: 1.0, add_exhaust: true, add_swh: true, add_thermostat: true, add_refrigeration: true, modify_wkdy_op_hrs: false, wkdy_op_hrs_start_time: 8.0, wkdy_op_hrs_duration: 8.0, modify_wknd_op_hrs: false, wknd_op_hrs_start_time: 8.0, wknd_op_hrs_duration: 8.0, hoo_var_method: 'hours', enable_dst: true, unmet_hours_tolerance_r: 1.0, remove_objects: true, user_hvac_mapping: nil, sizing_run_directory: nil) ⇒ Boolean

create typical building from model creates a complete energy model from model with defined geometry and standards space type assignments

Parameters:

  • template (String)

    standard template

  • climate_zone (String) (defaults to: 'Lookup From Model')

    ASHRAE climate zone, e.g. ‘ASHRAE 169-2013-4A’.

  • add_hvac (Boolean) (defaults to: true)

    Add HVAC systems to the model

  • hvac_system_type (String) (defaults to: 'Inferred')

    HVAC system type

  • hvac_delivery_type (String) (defaults to: 'Forced Air')

    HVAC delivery type, how the system delivers heating or cooling to zones. Options are ‘Forced Air’ or ‘Hydronic’.

  • heating_fuel (String) (defaults to: 'NaturalGas')

    The primary HVAC heating fuel type. Options are ‘Electricity’, ‘NaturalGas’, ‘DistrictHeating’, ‘DistrictHeatingWater’, ‘DistrictHeatingSteam’, ‘DistrictAmbient’

  • service_water_heating_fuel (String) (defaults to: 'NaturalGas')

    The primary service water heating fuel type. Options are ‘Inferred’, ‘Electricity’, ‘NaturalGas’, ‘DistrictHeating’, ‘DistrictHeatingWater’, ‘DistrictHeatingSteam’, ‘HeatPump’

  • cooling_fuel (String) (defaults to: 'Electricity')

    The primary HVAC cooling fuel type Options are ‘Electricity’, ‘DistrictCooling’, ‘DistrictAmbient’

  • kitchen_makeup (String) (defaults to: 'Adjacent')

    Source of makeup air for kitchen exhaust Options are ‘None’, ‘Largest Zone’, ‘Adjacent’

  • exterior_lighting_zone (String) (defaults to: '3 - All Other Areas')

    The exterior lighting zone for exterior lighting allowance. Options are ‘0 - Undeveloped Areas Parks’, ‘1 - Developed Areas Parks’, ‘2 - Neighborhood’, ‘3 - All Other Areas’, ‘4 - High Activity’

  • add_constructions (Boolean) (defaults to: true)

    Create and apply default construction set

  • wall_construction_type (String) (defaults to: 'Inferred')

    wall construction type. Options are ‘Inferred’, ‘Mass’, ‘Metal Building’, ‘WoodFramed’, ‘SteelFramed’

  • add_space_type_loads (Boolean) (defaults to: true)

    Populate existing standards space types in the model with internal loads

  • add_daylighting_controls (Boolean) (defaults to: true)

    Add daylighting controls

  • add_elevators (Boolean) (defaults to: true)

    Apply elevators directly to a space in the model instead of to a space type

  • add_internal_mass (Boolean) (defaults to: true)

    Add internal mass to each space

  • add_exterior_lights (Boolean) (defaults to: true)

    Add exterior lightings objects to parking, canopies, and facades

  • onsite_parking_fraction (Double) (defaults to: 1.0)

    Fraction of allowable exterior parking lighting applied. Set to 0 to add no parking lighting.

  • add_exhaust (Boolean) (defaults to: true)

    Add exhaust fans to the models. Primarly kitchen exhaust fans.

  • add_swh (Boolean) (defaults to: true)

    Add service water heating supply and demand objects

  • add_thermostat (Boolean) (defaults to: true)

    Add thermostats to thermal zones based on the standards space type

  • add_refrigeration (Boolean) (defaults to: true)

    Add refrigerated cases and walkin refrigeration

  • modify_wkdy_op_hrs (Boolean) (defaults to: false)

    Modify the default weekday hours of operation

  • wkdy_op_hrs_start_time (Double) (defaults to: 8.0)

    Weekday operating hours start time. Enter as a fractional value, e.g. 5:15pm is 17.25. Only used if modify_wkdy_op_hrs is true.

  • wkdy_op_hrs_duration (Double) (defaults to: 8.0)

    Weekday operating hours duration from start time. Enter as a fractional value, e.g. 5:15pm is 17.25. Only used if modify_wkdy_op_hrs is true.

  • modify_wknd_op_hrs (Boolean) (defaults to: false)

    Modify the default weekend hours of operation

  • wknd_op_hrs_start_time (Double) (defaults to: 8.0)

    Weekend operation hours start time. Enter as a fractional value, e.g. 5:15pm is 17.25. Only used if modify_wknd_op_hrs is true.

  • wknd_op_hrs_duration (Double) (defaults to: 8.0)

    Weekend operating hours duration from start time. Enter as a fractional value, e.g. 5:15pm is 17.25. Only used if modify_wknd_op_hrs is true.

  • hoo_var_method (String) (defaults to: 'hours')

    hours of operation variable method. Options are ‘hours’ or ‘fractional’.

  • enable_dst (Boolean) (defaults to: true)

    Enable daylight savings

  • unmet_hours_tolerance_r (Double) (defaults to: 1.0)

    Thermostat setpoint tolerance for unmet hours in degrees Rankine

  • remove_objects (Boolean) (defaults to: true)

    Clean model of non-geometry objects. Only removes the same objects types as those added to the model.

  • user_hvac_mapping (Hash) (defaults to: nil)

    Hash defining a mapping of system types to zones. Structure is:

    ['systems'][N]['system_type'] = 'MY_CBECS_HVAC_TYPE' as defined in lib/openstudio-standards/hvac/cbecs_hvac.rb
    ['systems'][N]['thermal_zones'] = ['Zone 1', 'Zone 2', ...]
    

Returns:

  • (Boolean)

    returns true if successful, false if not



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
# File 'lib/openstudio-standards/create_typical/create_typical.rb', line 54

def self.create_typical_building_from_model(model,
                                            template,
                                            climate_zone: 'Lookup From Model',
                                            add_hvac: true,
                                            hvac_system_type: 'Inferred',
                                            hvac_delivery_type: 'Forced Air',
                                            heating_fuel: 'NaturalGas',
                                            service_water_heating_fuel: 'NaturalGas',
                                            cooling_fuel: 'Electricity',
                                            kitchen_makeup: 'Adjacent',
                                            exterior_lighting_zone: '3 - All Other Areas',
                                            add_constructions: true,
                                            wall_construction_type: 'Inferred',
                                            add_space_type_loads: true,
                                            add_daylighting_controls: true,
                                            add_elevators: true,
                                            add_internal_mass: true,
                                            add_exterior_lights: true,
                                            onsite_parking_fraction: 1.0,
                                            add_exhaust: true,
                                            add_swh: true,
                                            add_thermostat: true,
                                            add_refrigeration: true,
                                            modify_wkdy_op_hrs: false,
                                            wkdy_op_hrs_start_time: 8.0,
                                            wkdy_op_hrs_duration: 8.0,
                                            modify_wknd_op_hrs: false,
                                            wknd_op_hrs_start_time: 8.0,
                                            wknd_op_hrs_duration: 8.0,
                                            hoo_var_method: 'hours',
                                            enable_dst: true,
                                            unmet_hours_tolerance_r: 1.0,
                                            remove_objects: true,
                                            user_hvac_mapping: nil,
                                            sizing_run_directory: nil)
  # sizing run directory
  sizing_run_directory = Dir.pwd if sizing_run_directory.nil?

  # report initial condition of model
  initial_object_size = model.getModelObjects.size
  OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "The building started with #{initial_object_size} objects.")

  # create a new standard class
  standard = Standard.build(template)

  # validate climate zone
  if climate_zone == 'Lookup From Model' || climate_zone.nil?
    climate_zone = standard.model_get_building_properties(model)['climate_zone']
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Using climate zone #{climate_zone} from model")
  else
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Using climate zone #{climate_zone} from user arguments")
  end
  if climate_zone == ''
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', 'Could not determine climate zone from measure arguments or model.')
    return false
  end

  # validate weekday hours of operation
  wkdy_op_hrs_start_time_hr = nil
  wkdy_op_hrs_start_time_min = nil
  wkdy_op_hrs_duration_hr = nil
  wkdy_op_hrs_duration_min = nil
  if modify_wkdy_op_hrs
    # weekday start time hr
    wkdy_op_hrs_start_time_hr = wkdy_op_hrs_start_time.floor
    if wkdy_op_hrs_start_time_hr < 0 || wkdy_op_hrs_start_time_hr > 24
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Weekday operating hours start time hrs must be between 0 and 24.  #{wkdy_op_hrs_start_time} was entered.")
      return false
    end

    # weekday start time min
    wkdy_op_hrs_start_time_min = (60.0 * (wkdy_op_hrs_start_time - wkdy_op_hrs_start_time.floor)).floor
    if wkdy_op_hrs_start_time_min < 0 || wkdy_op_hrs_start_time_min > 59
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Weekday operating hours start time mins must be between 0 and 59.  #{wkdy_op_hrs_start_time} was entered.")
      return false
    end

    # weekday duration hr
    wkdy_op_hrs_duration_hr = wkdy_op_hrs_duration.floor
    if wkdy_op_hrs_duration_hr < 0 || wkdy_op_hrs_duration_hr > 24
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Weekday operating hours duration hrs must be between 0 and 24.  #{wkdy_op_hrs_duration} was entered.")
      return false
    end

    # weekday duration min
    wkdy_op_hrs_duration_min = (60.0 * (wkdy_op_hrs_duration - wkdy_op_hrs_duration.floor)).floor
    if wkdy_op_hrs_duration_min < 0 || wkdy_op_hrs_duration_min > 59
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Weekday operating hours duration mins must be between 0 and 59.  #{wkdy_op_hrs_duration} was entered.")
      return false
    end

    # check that weekday start time plus duration does not exceed 24 hrs
    if (wkdy_op_hrs_start_time_hr + wkdy_op_hrs_duration_hr + ((wkdy_op_hrs_start_time_min + wkdy_op_hrs_duration_min) / 60.0)) > 24.0
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Weekday start time of #{wkdy_op_hrs_start_time} plus duration of #{wkdy_op_hrs_duration} is more than 24 hrs, hours of operation overlap midnight.")
    end
  end

  # validate weekend hours of operation
  wknd_op_hrs_start_time_hr = nil
  wknd_op_hrs_start_time_min = nil
  wknd_op_hrs_duration_hr = nil
  wknd_op_hrs_duration_min = nil
  if modify_wknd_op_hrs
    # weekend start time hr
    wknd_op_hrs_start_time_hr = wknd_op_hrs_start_time.floor
    if wknd_op_hrs_start_time_hr < 0 || wknd_op_hrs_start_time_hr > 24
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Weekend operating hours start time hrs must be between 0 and 24.  #{wknd_op_hrs_start_time} was entered.")
      return false
    end

    # weekend start time min
    wknd_op_hrs_start_time_min = (60.0 * (wknd_op_hrs_start_time - wknd_op_hrs_start_time.floor)).floor
    if wknd_op_hrs_start_time_min < 0 || wknd_op_hrs_start_time_min > 59
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Weekend operating hours start time mins must be between 0 and 59.  #{wknd_op_hrs_start_time} was entered.")
      return false
    end

    # weekend duration hr
    wknd_op_hrs_duration_hr = wknd_op_hrs_duration.floor
    if wknd_op_hrs_duration_hr < 0 || wknd_op_hrs_duration_hr > 24
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Weekend operating hours duration hrs must be between 0 and 24.  #{wknd_op_hrs_duration} was entered.")
      return false
    end

    # weekend duration min
    wknd_op_hrs_duration_min = (60.0 * (wknd_op_hrs_duration - wknd_op_hrs_duration.floor)).floor
    if wknd_op_hrs_duration_min < 0 || wknd_op_hrs_duration_min > 59
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Weekend operating hours duration min smust be between 0 and 59.  #{wknd_op_hrs_duration} was entered.")
      return false
    end

    # check that weekend start time plus duration does not exceed 24 hrs
    if (wknd_op_hrs_start_time_hr + wknd_op_hrs_duration_hr + ((wknd_op_hrs_start_time_min + wknd_op_hrs_duration_min) / 60.0)) > 24.0
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Weekend start time of #{wknd_op_hrs_start} plus duration of #{wknd_op_hrs_duration} is more than 24 hrs, hours of operation overlap midnight.")
    end
  end

  # validate unmet hours tolerance
  if unmet_hours_tolerance_r < 0
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', 'unmet_hours_tolerance_r must be greater than or equal to 0 Rankine.')
    return false
  elsif unmet_hours_tolerance_r > 5.0
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', 'unmet_hours_tolerance_r must be less than or equal to 5 Rankine.')
    return false
  end

  # make sure daylight savings is turned on up prior to any sizing runs being done.
  if enable_dst
    start_date = '2nd Sunday in March'
    end_date = '1st Sunday in November'

    runperiodctrl_daylightsaving = model.getRunPeriodControlDaylightSavingTime
    runperiodctrl_daylightsaving.setStartDate(start_date)
    runperiodctrl_daylightsaving.setEndDate(end_date)
  end

  # add internal loads to space types
  if add_space_type_loads

    # remove internal loads
    if remove_objects
      model.getSpaceLoads.sort.each do |instance|
        # most prototype building types model exterior elevators with name Elevator
        next if instance.name.to_s.include?('Elevator')
        next if instance.to_InternalMass.is_initialized
        next if instance.to_WaterUseEquipment.is_initialized

        instance.remove
      end
      model.getDesignSpecificationOutdoorAirs.each(&:remove)
      model.getDefaultScheduleSets.each(&:remove)
    end

    model.getSpaceTypes.sort.each do |space_type|
      # Don't add infiltration here; will be added later in the script
      test = standard.space_type_apply_internal_loads(space_type, true, true, true, true, true, false)
      if test == false
        OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "Could not add loads for #{space_type.name}. Not expected for #{template}")
        next
      end

      # apply internal load schedules
      # the last bool test it to make thermostat schedules. They are now added in HVAC section instead of here
      standard.space_type_apply_internal_load_schedules(space_type, true, true, true, true, true, true, false)

      # extend space type name to include the template. Consider this as well for load defs
      space_type.setName("#{space_type.name} - #{template}")
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Adding loads to space type named #{space_type.name}")
    end

    # warn if spaces in model without space type
    spaces_without_space_types = []
    model.getSpaces.sort.each do |space|
      next if space.spaceType.is_initialized

      spaces_without_space_types << space
    end
    if !spaces_without_space_types.empty?
      OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "#{spaces_without_space_types.size} spaces do not have space types assigned, and wont' receive internal loads from standards space type lookups.")
    end
  end

  # identify primary building type (used for construction, and ideally HVAC as well)
  building_types = {}
  model.getSpaceTypes.sort.each do |space_type|
    # populate hash of building types
    if space_type.standardsBuildingType.is_initialized
      bldg_type = space_type.standardsBuildingType.get
      if building_types.key?(bldg_type)
        building_types[bldg_type] += space_type.floorArea
      else
        building_types[bldg_type] = space_type.floorArea
      end
    else
      OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CreateTypical', "Can't identify building type for #{space_type.name}")
    end
  end
  # @todo this fails if no space types, or maybe just no space types with standards
  primary_bldg_type = building_types.key(building_types.values.max)
  # Used for some lookups in the standards gem
  lookup_building_type = standard.model_get_lookup_name(primary_bldg_type)
  model.getBuilding.setStandardsBuildingType(primary_bldg_type)

  # set FC factor constructions before adding other constructions
  standard.model_set_below_grade_wall_constructions(model, lookup_building_type, climate_zone)
  standard.model_set_floor_constructions(model, lookup_building_type, climate_zone)
  if model.getFFactorGroundFloorConstructions.empty?
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', 'Unable to determine FC factor value to use. Using default ground construction instead.')
  else
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', 'Set FC factor constructions for slab and below grade walls.')
  end

  # adjust F factor constructions to avoid simulation errors
  model.getFFactorGroundFloorConstructions.each do |cons|
    # Rfilm_in = 0.135, Rfilm_out = 0.03, Rcons for 6" heavy concrete = 0.15m / 1.95 W/mK, 0.001 minimum resistance of Rfic resistive layer
    if cons.area <= (0.135 + 0.03 + (0.15 / 1.95) + 0.001) * cons.perimeterExposed * cons.fFactor
      # set minimum Rfic to ~ R1 = 0.18 m^2K/W
      new_area = 0.422 * cons.perimeterExposed * cons.fFactor
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "F-factor fictitious resistance for #{cons.name.get} with Area=#{cons.area.round(2)}, Exposed Perimeter=#{cons.perimeterExposed.round(2)}, and F-factor=#{cons.fFactor.round(2)} will result in a negative value and a failed simulation. Construction area is adjusted to be #{new_area.round(2)} m2.")
      cons.setArea(new_area)
    end
  end

  # make construction set and apply to building
  if add_constructions

    # remove default construction sets
    if remove_objects
      model.getDefaultConstructionSets.each(&:remove)
    end

    if ['SmallHotel', 'LargeHotel', 'MidriseApartment', 'HighriseApartment'].include?(primary_bldg_type)
      is_residential = 'Yes'
      occ_type = 'Residential'
    else
      is_residential = 'No'
      occ_type = 'Nonresidential'
    end
    bldg_def_const_set = standard.model_add_construction_set(model, climate_zone, lookup_building_type, nil, is_residential)
    if bldg_def_const_set.is_initialized
      bldg_def_const_set = bldg_def_const_set.get
      if is_residential == 'Yes'
        bldg_def_const_set.setName("Res #{bldg_def_const_set.name}")
      end
      model.getBuilding.setDefaultConstructionSet(bldg_def_const_set)
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Adding default construction set named #{bldg_def_const_set.name}")
    else
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "Could not create default construction set for the building type #{lookup_building_type} in climate zone #{climate_zone} with template #{template}.")
      return false
    end

    # Replace the construction of exterior walls with user-specified wall construction type
    unless wall_construction_type == 'Inferred'
      # Check that a default exterior construction set is defined
      if bldg_def_const_set.defaultExteriorSurfaceConstructions.empty?
        OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', 'Default construction set has no default exterior surface constructions.')
        return false
      end
      ext_surf_consts = bldg_def_const_set.defaultExteriorSurfaceConstructions.get

      # Check that a default exterior wall is defined
      if ext_surf_consts.wallConstruction.empty?
        OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', 'Default construction set has no default exterior wall construction.')
        return false
      end
      old_construction = ext_surf_consts.wallConstruction.get
      standards_info = old_construction.standardsInformation

      # Get the old wall construction type
      if standards_info.standardsConstructionType.empty?
        old_wall_construction_type = 'Not defined'
      else
        old_wall_construction_type = standards_info.standardsConstructionType.get
      end

      # Modify the default wall construction if different from measure input
      if old_wall_construction_type == wall_construction_type
        # Don't modify if the default matches the user-specified wall construction type
        OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Exterior wall construction type #{wall_construction_type} is the default for this building type.")
      else
        climate_zone_set = standard.model_find_climate_zone_set(model, climate_zone)
        new_construction = standard.model_find_and_add_construction(model,
                                                                    climate_zone_set,
                                                                    'ExteriorWall',
                                                                    wall_construction_type,
                                                                    occ_type)
        ext_surf_consts.setWallConstruction(new_construction)
        OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Set exterior wall construction to #{new_construction.name}, replacing building type default #{old_construction.name}.")
      end
    end

    # Replace the construction of any outdoor-facing "AtticFloor" surfaces
    # with the "ExteriorRoof" - "IEAD" construction for the specific climate zone and template.
    # This prevents creation of buildings where the DOE Prototype building construction set
    # assumes an attic but the supplied geometry used does not have an attic.
    new_construction = nil
    climate_zone_set = standard.model_find_climate_zone_set(model, climate_zone)
    model.getSurfaces.sort.each do |surf|
      next unless surf.outsideBoundaryCondition == 'Outdoors'
      next unless surf.surfaceType == 'RoofCeiling'
      next if surf.construction.empty?

      construction = surf.construction.get
      standards_info = construction.standardsInformation
      next if standards_info.intendedSurfaceType.empty?
      next unless standards_info.intendedSurfaceType.get == 'AtticFloor'

      if new_construction.nil?
        new_construction = standard.model_find_and_add_construction(model,
                                                                    climate_zone_set,
                                                                    'ExteriorRoof',
                                                                    'IEAD',
                                                                    occ_type)
      end
      surf.setConstruction(new_construction)
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Changed the construction for #{surf.name} from #{construction.name} to #{new_construction.name} to avoid outdoor-facing attic floor constructions in buildings with no attic space.")
    end

    # address any adiabatic surfaces that don't have hard assigned constructions
    model.getSurfaces.sort.each do |surface|
      next if surface.outsideBoundaryCondition != 'Adiabatic'
      next if surface.construction.is_initialized

      surface.setAdjacentSurface(surface)
      surface.setConstruction(surface.construction.get)
      surface.setOutsideBoundaryCondition('Adiabatic')
    end

    # modify the infiltration rates
    if remove_objects
      model.getSpaceInfiltrationDesignFlowRates.each(&:remove)
    end
    standard.model_apply_infiltration_standard(model)
    standard.model_modify_infiltration_coefficients(model, primary_bldg_type, climate_zone)

    # set ground temperatures from DOE prototype buildings
    OpenstudioStandards::Weather.model_set_ground_temperatures(model, climate_zone: climate_zone)
  end

  # add elevators (returns ElectricEquipment object)
  if add_elevators

    # remove elevators as spaceLoads or exteriorLights
    model.getSpaceLoads.sort.each do |instance|
      next if !instance.name.to_s.include?('Elevator') # most prototype building types model exterior elevators with name Elevator

      instance.remove
    end
    model.getExteriorLightss.sort.each do |ext_light|
      next if !ext_light.name.to_s.include?('Fuel equipment') # some prototype building types model exterior elevators by this name

      ext_light.remove
    end

    elevators = standard.model_add_elevators(model)
    if elevators.nil?
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', 'No elevators added to the building.')
    else
      elevator_def = elevators.electricEquipmentDefinition
      design_level = elevator_def.designLevel.get
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Adding #{elevators.multiplier.round(1)} elevators each with power of #{OpenStudio.toNeatString(design_level, 0, true)} (W), plus lights and fans.")
      elevator_def.setFractionLatent(0.0)
      elevator_def.setFractionRadiant(0.0)
      elevator_def.setFractionLost(1.0)
    end
  end

  # add exterior lights (returns a hash where key is lighting type and value is exteriorLights object)
  if add_exterior_lights

    if remove_objects
      model.getExteriorLightss.sort.each do |ext_light|
        next if ext_light.name.to_s.include?('Fuel equipment') # some prototype building types model exterior elevators by this name

        ext_light.remove
      end
    end

    exterior_lights = standard.model_add_typical_exterior_lights(model, exterior_lighting_zone.chars[0].to_i, onsite_parking_fraction)
    exterior_lights.each do |k, v|
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Adding Exterior Lights named #{v.exteriorLightsDefinition.name} with design level of #{v.exteriorLightsDefinition.designLevel} * #{OpenStudio.toNeatString(v.multiplier, 0, true)}.")
    end
  end

  # add_exhaust
  if add_exhaust

    # remove exhaust objects
    if remove_objects
      model.getFanZoneExhausts.each(&:remove)
    end

    zone_exhaust_fans = standard.model_add_exhaust(model, kitchen_makeup) # second argument is strategy for finding makeup zones for exhaust zones
    zone_exhaust_fans.each do |k, v|
      max_flow_rate_ip = OpenStudio.convert(k.maximumFlowRate.get, 'm^3/s', 'cfm').get
      if v.key?(:zone_mixing)
        zone_mixing = v[:zone_mixing]
        mixing_source_zone_name = zone_mixing.sourceZone.get.name
        mixing_design_flow_rate_ip = OpenStudio.convert(zone_mixing.designFlowRate.get, 'm^3/s', 'cfm').get
        OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Adding #{OpenStudio.toNeatString(max_flow_rate_ip, 0, true)} (cfm) of exhaust to #{k.thermalZone.get.name}, with #{OpenStudio.toNeatString(mixing_design_flow_rate_ip, 0, true)} (cfm) of makeup air from #{mixing_source_zone_name}")
      else
        OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Adding #{OpenStudio.toNeatString(max_flow_rate_ip, 0, true)} (cfm) of exhaust to #{k.thermalZone.get.name}")
      end
    end
  end

  # add service water heating demand and supply
  if add_swh

    # remove water use equipment and water use connections
    if remove_objects
      # @todo remove plant loops used for service water heating
      model.getWaterUseEquipments.each(&:remove)
      model.getWaterUseConnectionss.each(&:remove)
    end

    # Infer the SWH type
    if service_water_heating_fuel == 'Inferred'
      if heating_fuel == 'NaturalGas' || heating_fuel.include?('DistrictHeating')
        # If building has gas service, probably uses natural gas for SWH
        service_water_heating_fuel = 'NaturalGas'
      elsif heating_fuel == 'Electricity'
        # If building is doing space heating with electricity, probably used for SWH
        service_water_heating_fuel = 'Electricity'
      elsif heating_fuel == 'DistrictAmbient'
        # If building has district ambient loop, it is fancy and probably uses HPs for SWH
        service_water_heating_fuel = 'HeatPump'
      else
        # Use inferences built into OpenStudio Standards for each building and space type
        service_water_heating_fuel = nil
      end
    end

    typical_swh = standard.model_add_typical_swh(model, water_heater_fuel: service_water_heating_fuel)
    midrise_swh_loops = []
    stripmall_swh_loops = []
    typical_swh.each do |loop|
      if loop.name.get.include?('MidriseApartment')
        midrise_swh_loops << loop
      elsif loop.name.get.include?('RetailStripmall')
        stripmall_swh_loops << loop
      else
        water_use_connections = []
        loop.demandComponents.each do |component|
          next if !component.to_WaterUseConnections.is_initialized

          water_use_connections << component
        end
        OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Adding #{loop.name} to the building. It has #{water_use_connections.size} water use connections.")
      end
    end
    if !midrise_swh_loops.empty?
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Adding #{midrise_swh_loops.size} MidriseApartment service water heating loops.")
    end
    if !stripmall_swh_loops.empty?
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Adding #{stripmall_swh_loops.size} RetailStripmall service water heating loops.")
    end
  end

  # add_daylighting_controls
  if add_daylighting_controls
    # remove add_daylighting_controls objects
    if remove_objects
      model.getDaylightingControls.each(&:remove)
    end

    # add daylight controls, need to perform a sizing run for 2010
    if (template == '90.1-2010' || template == 'ComStock 90.1-2010') && (standard.model_run_sizing_run(model, "#{sizing_run_directory}/create_typical_building_from_model_SR0") == false)
      return false
    end

    standard.model_add_daylighting_controls(model)
  end

  # add refrigeration
  if add_refrigeration

    # remove refrigeration equipment
    if remove_objects
      model.getRefrigerationSystems.each(&:remove)
    end

    # Add refrigerated cases and walkins
    standard.model_add_typical_refrigeration(model, primary_bldg_type)
  end

  # @todo add slab modeling and slab insulation
  # @todo fuel customization for cooking and laundry
  # works by switching some fraction of electric loads to gas if requested (assuming base load is electric)

  # add thermostats
  if add_thermostat

    # remove thermostats
    if remove_objects
      model.getThermostatSetpointDualSetpoints.each(&:remove)
    end

    model.getSpaceTypes.sort.each do |space_type|
      # create thermostat schedules
      # skip un-recognized space types
      next if standard.space_type_get_standards_data(space_type).empty?

      # the last bool test it to make thermostat schedules. They are added to the model but not assigned
      standard.space_type_apply_internal_load_schedules(space_type, false, false, false, false, false, false, true)

      # identify thermal thermostat and apply to zones (apply_internal_load_schedules names )
      model.getThermostatSetpointDualSetpoints.sort.each do |thermostat|
        next if thermostat.name.to_s != "#{space_type.name} Thermostat"
        next if !thermostat.coolingSetpointTemperatureSchedule.is_initialized
        next if !thermostat.heatingSetpointTemperatureSchedule.is_initialized

        OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Assigning #{thermostat.name} to thermal zones with #{space_type.name} assigned.")
        space_type.spaces.sort.each do |space|
          next if !space.thermalZone.is_initialized

          space.thermalZone.get.setThermostatSetpointDualSetpoint(thermostat)
        end
      end
    end
  end

  # add internal mass
  if add_internal_mass

    if remove_objects
      model.getSpaceLoads.sort.each do |instance|
        next unless instance.to_InternalMass.is_initialized

        instance.remove
      end
    end

    # add internal mass to conditioned spaces; needs to happen after thermostats are applied
    standard.model_add_internal_mass(model, primary_bldg_type)
  end

  # add hvac system
  if add_hvac

    # remove HVAC objects
    if remove_objects
      standard.model_remove_prm_hvac(model)
    end

    # If user does not map HVAC types to zones with a JSON file, run conventional approach to HVAC assignment
    if user_hvac_mapping.nil?
      case hvac_system_type
      when 'Inferred'

        # Get the hvac delivery type enum
        hvac_delivery = case hvac_delivery_type
                        when 'Forced Air'
                          'air'
                        when 'Hydronic'
                          'hydronic'
                        end

        # Group the zones by occupancy type.  Only split out non-dominant groups if their total area exceeds the limit.
        min_area_m2 = OpenStudio.convert(20_000, 'ft^2', 'm^2').get
        sys_groups = OpenstudioStandards::Geometry.model_group_thermal_zones_by_occupancy_type(model, min_area_m2: min_area_m2)

        # For each group, infer the HVAC system type.
        sys_groups.each do |sys_group|
          # Infer the primary system type
          sys_type, central_htg_fuel, zone_htg_fuel, clg_fuel = standard.model_typical_hvac_system_type(model,
                                                                                                        climate_zone,
                                                                                                        sys_group['type'],
                                                                                                        hvac_delivery,
                                                                                                        heating_fuel,
                                                                                                        cooling_fuel,
                                                                                                        OpenStudio.convert(sys_group['area_ft2'], 'ft^2', 'm^2').get,
                                                                                                        sys_group['stories'])

          # Infer the secondary system type for multizone systems
          sec_sys_type = case sys_type
                         when 'PVAV Reheat', 'VAV Reheat'
                           'PSZ-AC'
                         when 'PVAV PFP Boxes', 'VAV PFP Boxes'
                           'PSZ-HP'
                         else
                           sys_type # same as primary system type
                         end

          # group zones
          story_zone_lists = OpenstudioStandards::Geometry.model_group_thermal_zones_by_building_story(model, sys_group['zones'])

          # On each story, add the primary system to the primary zones
          # and add the secondary system to any zones that are different.
          story_zone_lists.each do |story_group|
            # Differentiate primary and secondary zones, based on
            # operating hours and internal loads (same as 90.1 PRM)
            pri_sec_zone_lists = standard.model_differentiate_primary_secondary_thermal_zones(model, story_group)
            system_zones = pri_sec_zone_lists['primary']

            # if the primary system type is PTAC, filter to cooled zones to prevent sizing error if no cooling
            if sys_type == 'PTAC'
              heated_and_cooled_zones = system_zones.select { |zone| OpenstudioStandards::ThermalZone.thermal_zone_heated?(zone) && OpenstudioStandards::ThermalZone.thermal_zone_cooled?(zone) }
              cooled_only_zones = system_zones.select { |zone| !OpenstudioStandards::ThermalZone.thermal_zone_heated?(zone) && OpenstudioStandards::ThermalZone.thermal_zone_cooled?(zone) }
              system_zones = heated_and_cooled_zones + cooled_only_zones
            end

            # Add the primary system to the primary zones
            unless standard.model_add_hvac_system(model, sys_type, central_htg_fuel, zone_htg_fuel, clg_fuel, system_zones)
              OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "HVAC system type '#{sys_type}' not recognized. Check input system type argument against Model.hvac.rb for valid hvac system type names.")
              return false
            end

            # Add the secondary system to the secondary zones (if any)
            if !pri_sec_zone_lists['secondary'].empty?
              system_zones = pri_sec_zone_lists['secondary']
              if (sec_sys_type == 'PTAC') || (sec_sys_type == 'PSZ-AC')
                heated_and_cooled_zones = system_zones.select { |zone| OpenstudioStandards::ThermalZone.thermal_zone_heated?(zone) && OpenstudioStandards::ThermalZone.thermal_zone_cooled?(zone) }
                cooled_only_zones = system_zones.select { |zone| !OpenstudioStandards::ThermalZone.thermal_zone_heated?(zone) && OpenstudioStandards::ThermalZone.thermal_zone_cooled?(zone) }
                system_zones = heated_and_cooled_zones + cooled_only_zones
              end
              unless standard.model_add_hvac_system(model, sec_sys_type, central_htg_fuel, zone_htg_fuel, clg_fuel, system_zones)
                OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "HVAC system type '#{sys_type}' not recognized. Check input system type argument against Model.hvac.rb for valid hvac system type names.")
                return false
              end
            end
          end
        end

      else
        # HVAC system_type specified
        # Group the zones by occupancy type.  Only split out non-dominant groups if their total area exceeds the limit.
        min_area_m2 = OpenStudio.convert(20_000, 'ft^2', 'm^2').get
        sys_groups = OpenstudioStandards::Geometry.model_group_thermal_zones_by_occupancy_type(model, min_area_m2: min_area_m2)
        sys_groups.each do |sys_group|
          # group zones
          story_zone_groups = OpenstudioStandards::Geometry.model_group_thermal_zones_by_building_story(model, sys_group['zones'])

          # Add the user specified HVAC system for each story.
          # Single-zone systems will get one per zone.
          story_zone_groups.each do |zones|
            unless OpenstudioStandards::HVAC.add_cbecs_hvac_system(model, standard, hvac_system_type, zones)
              OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "HVAC system type '#{hvac_system_type}' not recognized. Check input system type argument against cbecs_hvac.rb in the HVAC module for valid HVAC system type names.")
              return false
            end
          end
        end
      end
    else
      # If user specified a mapping of HVAC systems to zones
      user_hvac_mapping['systems'].each do |system_hash|
        hvac_system_type = system_hash['system_type']
        zone_names = system_hash['thermal_zones']

        # Get OS:ThermalZone objects
        zones = zone_names.map do |zone_name|
          model.getThermalZoneByName(zone_name).get
        end

        puts "Adding #{hvac_system_type} to #{zone_names.join(', ')}"

        unless OpenstudioStandards::HVAC.add_cbecs_hvac_system(model, standard, hvac_system_type, zones)
          OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.CreateTypical', "HVAC system type '#{hvac_system_type}' not recognized. Check input system type argument against cbecs_hvac.rb in the HVAC module for valid HVAC system type names.")
          return false
        end
      end
    end
  end

  # hours of operation
  if modify_wkdy_op_hrs || modify_wknd_op_hrs
    # Infer the current hours of operation schedule for the building
    op_sch = OpenstudioStandards::Schedules.model_infer_hours_of_operation_building(model)

    # Convert existing schedules in the model to parametric schedules based on current hours of operation
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Generating parametric schedules from ruleset schedules using #{hoo_var_method} variable method for hours of operation formula.")
    OpenstudioStandards::Schedules.model_setup_parametric_schedules(model, hoo_var_method: hoo_var_method)

    # Create start and end times from start time and duration supplied
    wkdy_start_time = nil
    wkdy_end_time = nil
    wknd_start_time = nil
    wknd_end_time = nil
    # weekdays
    if modify_wkdy_op_hrs
      wkdy_start_time = OpenStudio::Time.new(0, wkdy_op_hrs_start_time_hr, wkdy_op_hrs_start_time_min, 0)
      wkdy_end_time = wkdy_start_time + OpenStudio::Time.new(0, wkdy_op_hrs_duration_hr, wkdy_op_hrs_duration_min, 0)
    end
    # weekends
    if modify_wknd_op_hrs
      wknd_start_time = OpenStudio::Time.new(0, wknd_op_hrs_start_time_hr, wknd_op_hrs_start_time_min, 0)
      wknd_end_time = wknd_start_time + OpenStudio::Time.new(0, wknd_op_hrs_duration_hr, wknd_op_hrs_duration_min, 0)
    end

    # Modify hours of operation, using weekdays values for all weekdays and weekend values for Saturday and Sunday
    OpenstudioStandards::Schedules.schedule_ruleset_set_hours_of_operation(op_sch,
                                                                           wkdy_start_time: wkdy_start_time,
                                                                           wkdy_end_time: wkdy_end_time,
                                                                           sat_start_time: wknd_start_time,
                                                                           sat_end_time: wknd_end_time,
                                                                           sun_start_time: wknd_start_time,
                                                                           sun_end_time: wknd_end_time)

    # Apply new operating hours to parametric schedules to make schedules in model reflect modified hours of operation
    parametric_schedules = OpenstudioStandards::Schedules.model_apply_parametric_schedules(model, error_on_out_of_order: false)
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Updated #{parametric_schedules.size} schedules with new hours of operation.")
  end

  # set hvac controls and efficiencies (this should be last model articulation element)
  if add_hvac
    # set additional properties for building
    props = model.getBuilding.additionalProperties
    props.setFeature('hvac_system_type', hvac_system_type)

    case hvac_system_type
    when 'Ideal Air Loads'

    else
      # Set the heating and cooling sizing parameters
      standard.model_apply_prm_sizing_parameters(model)

      # Perform a sizing run
      if standard.model_run_sizing_run(model, "#{sizing_run_directory}/create_typical_building_from_model_SR1") == false
        return false
      end

      # If there are any multizone systems, reset damper positions
      # to achieve a 60% ventilation effectiveness minimum for the system
      # following the ventilation rate procedure from 62.1
      standard.model_apply_multizone_vav_outdoor_air_sizing(model)

      # Apply the prototype HVAC assumptions
      standard.model_apply_prototype_hvac_assumptions(model, primary_bldg_type, climate_zone)

      # Apply the HVAC efficiency standard
      standard.model_apply_hvac_efficiency_standard(model, climate_zone)
    end
  end

  # set unmet hours tolerance
  unmet_hrs_tol_k = OpenStudio.convert(unmet_hours_tolerance_r, 'R', 'K').get
  tolerances = model.getOutputControlReportingTolerances
  tolerances.setToleranceforTimeHeatingSetpointNotMet(unmet_hrs_tol_k)
  tolerances.setToleranceforTimeCoolingSetpointNotMet(unmet_hrs_tol_k)

  # remove everything but spaces, zones, and stub space types (extend as needed for additional objects, may make bool arg for this)
  if remove_objects
    model.purgeUnusedResourceObjects
    objects_after_cleanup = initial_object_size - model.getModelObjects.size
    if objects_after_cleanup > 0
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Removing #{objects_after_cleanup} objects from model")
    end
  end

  # change night cycling control to "Thermostat" cycling and increase thermostat tolerance to 1.99999
  manager_night_cycles = model.getAvailabilityManagerNightCycles
  OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "Changing thermostat tolerance to 1.99999 for #{manager_night_cycles.size} night cycle manager objects.")
  manager_night_cycles.each do |night_cycle|
    night_cycle.setThermostatTolerance(1.9999)
    night_cycle.setCyclingRunTimeControlType('Thermostat')
  end

  # disable HVAC Sizing Simulation for Sizing Periods, not used for the type of PlantLoop sizing used in ComStock
  if model.version >= OpenStudio::VersionString.new('3.0.0')
    sim_control = model.getSimulationControl
    sim_control.setDoHVACSizingSimulationforSizingPeriodsNoFail(false)
  end

  # report final condition of model
  OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CreateTypical', "The building finished with #{model.getModelObjects.size} objects.")

  return true
end

.deer_building_type_to_hvac_systems(deer_building_type_short) ⇒ Array<String>

Valid building type/hvac type combos

Parameters:

  • deer_building_type_short (String)

    DEER building type in short format

Returns:

  • (Array<String>)

    Allowable HVAC systems for the DEER building type



422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
# File 'lib/openstudio-standards/create_typical/enumerations.rb', line 422

def self.deer_building_type_to_hvac_systems(deer_building_type_short)
  case deer_building_type_short
  when 'Asm', 'ERC', 'Gro', 'Mtl', 'MLI', 'RFF', 'RSD', 'RtL', 'RtS', 'SCn'
    ['DXEH', 'DXGF', 'DXHP', 'NCEH', 'NCGF']
  when 'ECC', 'ESe', 'Htl', 'MBT', 'OfL', 'OfS', 'Rt3'
    ['DXEH', 'DXGF', 'DXHP', 'NCEH', 'NCGF', 'PVVE', 'PVVG', 'SVVE', 'SVVG', 'WLHP']
  when 'EPr'
    ['DXEH', 'DXGF', 'DXHP', 'NCEH', 'NCGF', 'WLHP']
  when 'EUn', 'Hsp'
    ['DXEH', 'DXGF', 'DXHP', 'NCEH', 'NCGF', 'PVVE', 'PVVG', 'SVVE', 'SVVG']
  when 'Nrs'
    ['DXEH', 'DXGF', 'DXHP', 'FPFC', 'NCEH', 'NCGF', 'PVVE', 'PVVG', 'SVVE', 'SVVG']
  when 'SUn'
    ['Unc']
  when 'WRf'
    ['DXGF']
  end
end

.deer_building_type_to_long(deer_building_type_short) ⇒ String

Building type abbreviation to long name map

Parameters:

  • deer_building_type_short (String)

    DEER building type in short format

Returns:

  • (String)

    DEER building type in long format



359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/openstudio-standards/create_typical/enumerations.rb', line 359

def self.deer_building_type_to_long(deer_building_type_short)
  dict = {}
  dict['Asm'] = 'Assembly'
  dict['DMo'] = 'Residential Mobile Home'
  dict['ECC'] = 'Education - Community College'
  dict['EPr'] = 'Education - Primary School'
  dict['ERC'] = 'Education - Relocatable Classroom'
  dict['ESe'] = 'Education - Secondary School'
  dict['EUn'] = 'Education - University'
  dict['GHs'] = 'Greenhouse'
  dict['Gro'] = 'Grocery'
  dict['Hsp'] = 'Health/Medical - Hospital'
  dict['Htl'] = 'Lodging - Hotel'
  dict['MBT'] = 'Manufacturing Biotech'
  dict['MFm'] = 'Residential Multi-family'
  dict['MLI'] = 'Manufacturing Light Industrial'
  dict['Mtl'] = 'Lodging - Motel'
  dict['Nrs'] = 'Health/Medical - Nursing Home'
  dict['OfL'] = 'Office - Large'
  dict['OfS'] = 'Office - Small'
  dict['RFF'] = 'Restaurant - Fast-Food'
  dict['RSD'] = 'Restaurant - Sit-Down'
  dict['Rt3'] = 'Retail - Multistory Large'
  dict['RtL'] = 'Retail - Single-Story Large'
  dict['RtS'] = 'Retail - Small'
  dict['SCn'] = 'Storage - Conditioned'
  dict['SFm'] = 'Residential Single Family'
  dict['SUn'] = 'Storage - Unconditioned'
  dict['WRf'] = 'Warehouse - Refrigerated'

  return dict[deer_building_type_short]
end

.deer_hvac_system_to_long(deer_hvac_system_type_short) ⇒ String

HVAC type abbreviation to long name map

Parameters:

  • deer_hvac_system_type_short (String)

    DEER HVAC system type in short format

Returns:

  • (String)

    DEER HVAC system type in long format



396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/openstudio-standards/create_typical/enumerations.rb', line 396

def self.deer_hvac_system_to_long(deer_hvac_system_type_short)
  dict = {}
  dict['DXGF'] = 'Split or Packaged DX Unit with Gas Furnace'
  dict['DXEH'] = 'Split or Packaged DX Unit with Electric Heat'
  dict['DXHP'] = 'Split or Packaged DX Unit with Heat Pump'
  dict['WLHP'] = 'Water Loop Heat Pump'
  dict['NCEH'] = 'No Cooling with Electric Heat'
  dict['NCGF'] = 'No Cooling with Gas Furnace'
  dict['PVVG'] = 'Packaged VAV System with Gas Boiler'
  dict['PVVE'] = 'Packaged VAV System with Electric Heat'
  dict['SVVG'] = 'Built-Up VAV System with Gas Boiler'
  dict['SVVE'] = 'Built-Up VAV System with Electric Reheat'
  dict['Unc'] = 'No HVAC (Unconditioned)'
  dict['PTAC'] = 'Packaged Terminal Air Conditioner'
  dict['PTHP'] = 'Packaged Terminal Heat Pump'
  dict['FPFC'] = 'Four Pipe Fan Coil'
  dict['DDCT'] = 'Dual Duct System'
  dict['EVAP'] = 'Evaporative Cooling with Separate Gas Furnace'

  return dict[deer_hvac_system_type_short]
end

.deer_template_to_age_range(deer_template) ⇒ String

Age range to DEER template

Parameters:

  • deer_template (String)

    DEER template

Returns:



444
445
446
447
448
449
450
451
452
453
454
455
456
457
# File 'lib/openstudio-standards/create_typical/enumerations.rb', line 444

def self.deer_template_to_age_range(deer_template)
  dict = {}
  dict['DEER Pre-1975'] = 'Before 1978'
  dict['DEER 1985'] = '1978-1992'
  dict['DEER 1996'] = '1993-2001'
  dict['DEER 2003'] = '2002-2005'
  dict['DEER 2007'] = '2006-2009'
  dict['DEER 2011'] = '2010-2013'
  dict['DEER 2014'] = '2014'
  dict['DEER 2015'] = '2015-2016'
  dict['DEER 2017'] = '2017 or Later'

  return dict[deer_template]
end

.doe_to_deer_building_type(doe_building_type) ⇒ String

Map a DOE building type to the corresponding DEER building type. DEER to DEER mappings included for some use cases

Parameters:

  • doe_building_type (String)

    DOE building type

Returns:

  • (String)

    DEER building type in short format



305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
# File 'lib/openstudio-standards/create_typical/enumerations.rb', line 305

def self.doe_to_deer_building_type(doe_building_type)
  dict = {}
  dict['SecondarySchool'] = 'ESe'
  dict['PrimarySchool'] = 'EPr'
  dict['SmallOffice'] = 'OfS'
  dict['MediumOffice'] = 'OfL'
  dict['LargeOffice'] = 'OfL'
  dict['SmallHotel'] = 'Mtl'
  dict['LargeHotel'] = 'Htl'
  dict['Warehouse'] = 'SUn' # Unconditioned Storage (SUn) is nearly identical to SCn
  dict['RetailStandalone'] = 'RtL'
  dict['RetailStripmall'] = 'RtS'
  dict['QuickServiceRestaurant'] = 'RFF'
  dict['FullServiceRestaurant'] = 'RSD'
  dict['MidriseApartment'] = 'MFm'
  dict['HighriseApartment'] = 'OfL'
  dict['Hospital'] = 'Hsp'
  dict['Outpatient'] = 'OfL'
  dict['SuperMarket'] = 'Gro'
  dict['Asm'] = 'Asm'
  dict['DMo'] = 'DMo'
  dict['ECC'] = 'ECC'
  dict['EPr'] = 'EPr'
  dict['ERC'] = 'ERC'
  dict['ESe'] = 'ESe'
  dict['EUn'] = 'EUn'
  dict['GHs'] = 'GHs'
  dict['Gro'] = 'Gro'
  dict['Hsp'] = 'Hsp'
  dict['Htl'] = 'Htl'
  dict['MBT'] = 'MBT'
  dict['MFm'] = 'MFm'
  dict['MLI'] = 'MLI'
  dict['Mtl'] = 'Mtl'
  dict['Nrs'] = 'Nrs'
  dict['OfL'] = 'OfL'
  dict['OfS'] = 'OfS'
  dict['RFF'] = 'RFF'
  dict['RSD'] = 'RSD'
  dict['Rt3'] = 'Rt3'
  dict['RtL'] = 'RtL'
  dict['RtS'] = 'RtS'
  dict['SCn'] = 'SCn'
  dict['SFm'] = 'SFm'
  dict['SUn'] = 'SUn'
  dict['WRf'] = 'WRf'

  return dict[doe_building_type]
end

.get_building_types(extended = false) ⇒ OpenStudio::StringVector

list of building types that are valid for get_space_types_from_building_type

Parameters:

  • extended (Boolean) (defaults to: false)

    set to true to return additional building types

Returns:

  • (OpenStudio::StringVector)

    array of building type strings



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/openstudio-standards/create_typical/enumerations.rb', line 84

def self.get_building_types(extended = false)
  # get building_types
  if extended
    doe = get_doe_building_types(true)
    deer = get_deer_building_types(true)
  else
    doe = get_doe_building_types
    deer = get_deer_building_types
  end

  # combine building_types
  array = OpenStudio::StringVector.new
  temp_array = doe.to_a + deer.to_a
  temp_array.each do |i|
    array << i
  end

  return array
end

.get_climate_zones(extended = false, extra = nil) ⇒ OpenStudio::StringVector

Get climate zones

Parameters:

  • extended (Boolean) (defaults to: false)

    set to true to return additional climate zones

  • extra (String) (defaults to: nil)

    extra climate zone to append to list

Returns:

  • (OpenStudio::StringVector)

    array of available climate zones as strings



274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/openstudio-standards/create_typical/enumerations.rb', line 274

def self.get_climate_zones(extended = false, extra = nil)
  # get climate_zones
  if extended && !extra.nil?
    doe = get_doe_climate_zones(true, extra)
    deer = get_deer_climate_zones(true, nil)
  elsif extended
    doe = get_doe_climate_zones(true, nil)
    deer = get_deer_climate_zones(true, nil)
  elsif !extra.nil?
    doe = get_doe_climate_zones(false, extra)
    deer = get_deer_climate_zones(false, nil)
  else
    doe = get_doe_climate_zones
    deer = get_deer_climate_zones
  end

  # combine climate zones
  array = OpenStudio::StringVector.new
  temp_array = doe.to_a + deer.to_a
  temp_array.each do |i|
    array << i
  end

  return array
end

.get_deer_building_types(extended = false) ⇒ OpenStudio::StringVector

Get DEER building types

Parameters:

  • extended (Boolean) (defaults to: false)

    set to true to return additional building types

Returns:

  • (OpenStudio::StringVector)

    array of building type strings



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/openstudio-standards/create_typical/enumerations.rb', line 46

def self.get_deer_building_types(extended = false)
  # DOE Prototypes
  array = OpenStudio::StringVector.new
  array << 'Asm'
  array << 'DMo'
  array << 'ECC'
  array << 'EPr'
  array << 'ERC'
  array << 'ESe'
  array << 'EUn'
  array << 'GHs'
  array << 'Gro'
  array << 'Hsp'
  array << 'Htl'
  array << 'MBT'
  array << 'MFm'
  array << 'MLI'
  array << 'Mtl'
  array << 'Nrs'
  array << 'OfL'
  array << 'OfS'
  array << 'RFF'
  array << 'RSD'
  array << 'Rt3'
  array << 'RtL'
  array << 'RtS'
  array << 'SCn'
  array << 'SFm'
  array << 'SUn'
  array << 'WRf'

  return array
end

.get_deer_climate_zones(extended = false, extra = nil) ⇒ OpenStudio::StringVector

Get DEER climate zones

Parameters:

  • extended (Boolean) (defaults to: false)

    set to true to return additional climate zones

  • extra (String) (defaults to: nil)

    extra climate zone to append to list

Returns:

  • (OpenStudio::StringVector)

    array of available climate zones as strings



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/openstudio-standards/create_typical/enumerations.rb', line 243

def self.get_deer_climate_zones(extended = false, extra = nil)
  # Lookup From Model should be added as an option where appropriate in the measure
  cz_choices = OpenStudio::StringVector.new
  if !extra.nil?
    cz_choices << extra
  end
  cz_choices << 'CEC T24-CEC1'
  cz_choices << 'CEC T24-CEC2'
  cz_choices << 'CEC T24-CEC3'
  cz_choices << 'CEC T24-CEC4'
  cz_choices << 'CEC T24-CEC5'
  cz_choices << 'CEC T24-CEC6'
  cz_choices << 'CEC T24-CEC7'
  cz_choices << 'CEC T24-CEC8'
  cz_choices << 'CEC T24-CEC9'
  cz_choices << 'CEC T24-CEC10'
  cz_choices << 'CEC T24-CEC11'
  cz_choices << 'CEC T24-CEC12'
  cz_choices << 'CEC T24-CEC13'
  cz_choices << 'CEC T24-CEC14'
  cz_choices << 'CEC T24-CEC15'
  cz_choices << 'CEC T24-CEC16'

  return cz_choices
end

.get_deer_templates(extended = false) ⇒ OpenStudio::StringVector

Get DEER templates

Parameters:

  • extended (Boolean) (defaults to: false)

    set to true to return additional templates

Returns:

  • (OpenStudio::StringVector)

    array of available standard templates as strings



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/openstudio-standards/create_typical/enumerations.rb', line 138

def self.get_deer_templates(extended = false)
  array = OpenStudio::StringVector.new
  array << 'DEER Pre-1975'
  array << 'DEER 1985'
  array << 'DEER 1996'
  array << 'DEER 2003'
  array << 'DEER 2007'
  array << 'DEER 2011'
  array << 'DEER 2014'
  array << 'DEER 2015'
  array << 'DEER 2017'
  array << 'DEER 2020'
  if extended
    array << 'DEER 2025'
    array << 'DEER 2030'
    array << 'DEER 2035'
    array << 'DEER 2040'
    array << 'DEER 2045'
    array << 'DEER 2050'
    array << 'DEER 2055'
    array << 'DEER 2060'
    array << 'DEER 2065'
    array << 'DEER 2070'
    array << 'DEER 2075'
  end

  array << 'ComStock DEER Pre-1975'
  array << 'ComStock DEER 1985'
  array << 'ComStock DEER 1996'
  array << 'ComStock DEER 2003'
  array << 'ComStock DEER 2007'
  array << 'ComStock DEER 2011'
  array << 'ComStock DEER 2014'
  array << 'ComStock DEER 2015'
  array << 'ComStock DEER 2017'
  array << 'ComStock DEER 2020'

  return array
end

.get_doe_building_types(extended = false) ⇒ OpenStudio::StringVector

Get DOE building types

Parameters:

  • extended (Boolean) (defaults to: false)

    set to true to return additional building types

Returns:

  • (OpenStudio::StringVector)

    array of building type strings



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/openstudio-standards/create_typical/enumerations.rb', line 11

def self.get_doe_building_types(extended = false)
  # DOE Prototypes
  array = OpenStudio::StringVector.new
  array << 'SecondarySchool'
  array << 'PrimarySchool'
  array << 'SmallOffice'
  array << 'MediumOffice'
  array << 'LargeOffice'
  array << 'SmallHotel'
  array << 'LargeHotel'
  array << 'Warehouse'
  array << 'RetailStandalone'
  array << 'RetailStripmall'
  array << 'QuickServiceRestaurant'
  array << 'FullServiceRestaurant'
  array << 'MidriseApartment'
  array << 'HighriseApartment'
  array << 'Hospital'
  array << 'Outpatient'
  array << 'SuperMarket'
  array << 'Laboratory'
  array << 'LargeDataCenterLowITE'
  array << 'LargeDataCenterHighITE'
  array << 'SmallDataCenterLowITE'
  array << 'SmallDataCenterHighITE'
  array << 'Courthouse'
  array << 'College'

  return array
end

.get_doe_climate_zones(extended = false, extra = nil) ⇒ OpenStudio::StringVector

Get DOE climate zones

Parameters:

  • extended (Boolean) (defaults to: false)

    set to true to return additional climate zones

  • extra (String) (defaults to: nil)

    extra climate zone to append to list

Returns:

  • (OpenStudio::StringVector)

    array of available climate zones as strings



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/openstudio-standards/create_typical/enumerations.rb', line 207

def self.get_doe_climate_zones(extended = false, extra = nil)
  # Lookup From Model should be added as an option where appropriate in the measure
  cz_choices = OpenStudio::StringVector.new
  if !extra.nil?
    cz_choices << extra
  end
  cz_choices << 'ASHRAE 169-2013-1A'
  cz_choices << 'ASHRAE 169-2013-1B'
  cz_choices << 'ASHRAE 169-2013-2A'
  cz_choices << 'ASHRAE 169-2013-2B'
  cz_choices << 'ASHRAE 169-2013-3A'
  cz_choices << 'ASHRAE 169-2013-3B'
  cz_choices << 'ASHRAE 169-2013-3C'
  cz_choices << 'ASHRAE 169-2013-4A'
  cz_choices << 'ASHRAE 169-2013-4B'
  cz_choices << 'ASHRAE 169-2013-4C'
  cz_choices << 'ASHRAE 169-2013-5A'
  cz_choices << 'ASHRAE 169-2013-5B'
  cz_choices << 'ASHRAE 169-2013-5C'
  cz_choices << 'ASHRAE 169-2013-6A'
  cz_choices << 'ASHRAE 169-2013-6B'
  cz_choices << 'ASHRAE 169-2013-7A'
  cz_choices << 'ASHRAE 169-2013-8A'
  if extended
    cz_choices << 'ASHRAE 169-2013-0A'
    cz_choices << 'ASHRAE 169-2013-0B'
  end

  return cz_choices
end

.get_doe_templates(extended = false) ⇒ OpenStudio::StringVector

Get DOE templates

Parameters:

  • extended (Boolean) (defaults to: false)

    set to true to return additional templates

Returns:

  • (OpenStudio::StringVector)

    array of available standard templates as strings



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/openstudio-standards/create_typical/enumerations.rb', line 108

def self.get_doe_templates(extended = false)
  array = OpenStudio::StringVector.new
  array << 'DOE Ref Pre-1980'
  array << 'DOE Ref 1980-2004'
  array << '90.1-2004'
  array << '90.1-2007'
  array << '90.1-2010'
  array << '90.1-2013'
  array << '90.1-2016'
  array << '90.1-2019'
  array << 'ComStock DOE Ref Pre-1980'
  array << 'ComStock DOE Ref 1980-2004'
  array << 'ComStock 90.1-2004'
  array << 'ComStock 90.1-2007'
  array << 'ComStock 90.1-2010'
  array << 'ComStock 90.1-2013'
  array << 'ComStock 90.1-2016'
  array << 'ComStock 90.1-2019'
  if extended
    # array << '189.1-2009' # if turn this on need to update space_type_array for RetailStripmall
    array << 'NREL ZNE Ready 2017'
  end

  return array
end

.get_space_types_from_building_type(building_type, building_subtype: nil, template: nil, whole_building: true) ⇒ Hash

TODO:

this method will be replaced with space type specific edits

TODO:

enable each building type and template combination

create hash of space types and generic ratios of building floor area. some building type and template combination are incompatible

Parameters:

  • building_type (String)

    standard building type

  • building_subtype (String) (defaults to: nil)

    building subtype for large offices or warehouses

  • template (String) (defaults to: nil)

    standard template

  • whole_building (Boolean) (defaults to: true)

    use a whole building space type for Office types

Returns:

  • (Hash)

    hash of space types



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
# File 'lib/openstudio-standards/create_typical/space_type_ratios.rb', line 16

def self.get_space_types_from_building_type(building_type,
                                            building_subtype: nil,
                                            template: nil,
                                            whole_building: true)
  hash = {}

  # DOE Prototypes
  case building_type
  when 'SecondarySchool'
    if ['DOE Ref Pre-1980', 'DOE Ref 1980-2004', 'ComStock DOE Ref Pre-1980', 'ComStock DOE Ref 1980-2004'].include?(template)
      hash['Auditorium'] = { ratio: 0.0504, space_type_gen: true, default: false, story_height: 26.0 }
      hash['Cafeteria'] = { ratio: 0.0319, space_type_gen: true, default: false }
      hash['Classroom'] = { ratio: 0.3528, space_type_gen: true, default: true }
      hash['Corridor'] = { ratio: 0.2144, space_type_gen: true, default: false, circ: true }
      hash['Gym'] = { ratio: 0.1009, space_type_gen: true, default: false, story_height: 26.0 }
      hash['Gym - audience'] = { ratio: 0.0637, space_type_gen: true, default: false, story_height: 26.0 }
      hash['Kitchen'] = { ratio: 0.0110, space_type_gen: true, default: false }
      hash['Library'] = { ratio: 0.0429, space_type_gen: true, default: false }
      hash['Lobby'] = { ratio: 0.0214, space_type_gen: true, default: false }
      hash['Mechanical'] = { ratio: 0.0349, space_type_gen: true, default: false }
      hash['Office'] = { ratio: 0.0543, space_type_gen: true, default: false }
      hash['Restroom'] = { ratio: 0.0214, space_type_gen: true, default: false }
    else
      hash['Auditorium'] = { ratio: 0.0504, space_type_gen: true, default: false, story_height: 26.0 }
      hash['Cafeteria'] = { ratio: 0.0319, space_type_gen: true, default: false }
      hash['Classroom'] = { ratio: 0.3041, space_type_gen: true, default: true }
      hash['ComputerRoom'] = { ratio: 0.0487, space_type_gen: true, default: true }
      hash['Corridor'] = { ratio: 0.2144, space_type_gen: true, default: false, circ: true }
      hash['Gym'] = { ratio: 0.1646, space_type_gen: true, default: false, story_height: 26.0 }
      hash['Kitchen'] = { ratio: 0.0110, space_type_gen: true, default: false }
      hash['Library'] = { ratio: 0.0429, space_type_gen: true, default: false }
      hash['Lobby'] = { ratio: 0.0214, space_type_gen: true, default: false }
      hash['Mechanical'] = { ratio: 0.0349, space_type_gen: true, default: false }
      hash['Office'] = { ratio: 0.0543, space_type_gen: true, default: false }
      hash['Restroom'] = { ratio: 0.0214, space_type_gen: true, default: false }
    end
  when 'PrimarySchool'
    if ['DOE Ref Pre-1980', 'DOE Ref 1980-2004', 'ComStock DOE Ref Pre-1980', 'ComStock DOE Ref 1980-2004'].include?(template)
      # updated to 2004 which includes library vs. pre-1980
      hash['Cafeteria'] = { ratio: 0.0458, space_type_gen: true, default: false }
      hash['Classroom'] = { ratio: 0.5610, space_type_gen: true, default: true }
      hash['Corridor'] = { ratio: 0.1633, space_type_gen: true, default: false, circ: true }
      hash['Gym'] = { ratio: 0.0520, space_type_gen: true, default: false }
      hash['Kitchen'] = { ratio: 0.0244, space_type_gen: true, default: false }
      hash['Library'] = { ratio: 0.0, space_type_gen: true, default: false } # no library in model
      hash['Lobby'] = { ratio: 0.0249, space_type_gen: true, default: false }
      hash['Mechanical'] = { ratio: 0.0367, space_type_gen: true, default: false }
      hash['Office'] = { ratio: 0.0642, space_type_gen: true, default: false }
      hash['Restroom'] = { ratio: 0.0277, space_type_gen: true, default: false }
    else
      # updated to 2004 which includes library vs. pre-1980
      hash['Cafeteria'] = { ratio: 0.0458, space_type_gen: true, default: false }
      hash['Classroom'] = { ratio: 0.4793, space_type_gen: true, default: true }
      hash['ComputerRoom'] = { ratio: 0.0236, space_type_gen: true, default: true }
      hash['Corridor'] = { ratio: 0.1633, space_type_gen: true, default: false, circ: true }
      hash['Gym'] = { ratio: 0.0520, space_type_gen: true, default: false }
      hash['Kitchen'] = { ratio: 0.0244, space_type_gen: true, default: false }
      hash['Library'] = { ratio: 0.0581, space_type_gen: true, default: false }
      hash['Lobby'] = { ratio: 0.0249, space_type_gen: true, default: false }
      hash['Mechanical'] = { ratio: 0.0367, space_type_gen: true, default: false }
      hash['Office'] = { ratio: 0.0642, space_type_gen: true, default: false }
      hash['Restroom'] = { ratio: 0.0277, space_type_gen: true, default: false }
    end
  when 'SmallOffice'
    # @todo populate Small, Medium, and Large office for whole_building false
    if whole_building
      hash['WholeBuilding - Sm Office'] = { ratio: 1.0, space_type_gen: true, default: true }
    else
      hash['SmallOffice - ClosedOffice'] = { ratio: 0.3325, space_type_gen: true, default: false }
      hash['SmallOffice - Conference'] = { ratio: 0.0818, space_type_gen: true, default: false }
      hash['SmallOffice - Corridor'] = { ratio: 0.1213, space_type_gen: true, default: false, circ: true }
      hash['SmallOffice - Elec/MechRoom'] = { ratio: 0.0201, space_type_gen: true, default: false }
      hash['SmallOffice - Lobby'] = { ratio: 0.0818, space_type_gen: true, default: false }
      hash['SmallOffice - OpenOffice'] = { ratio: 0.1659, space_type_gen: true, default: true }
      hash['SmallOffice - Restroom'] = { ratio: 0.0402, space_type_gen: true, default: false }
      hash['SmallOffice - Stair'] = { ratio: 0.0201, space_type_gen: true, default: false }
      hash['SmallOffice - Storage'] = { ratio: 0.1363, space_type_gen: true, default: false }
    end
  when 'MediumOffice'
    if whole_building
      hash['WholeBuilding - Md Office'] = { ratio: 1.0, space_type_gen: true, default: true }
    else
      hash['MediumOffice - Classroom'] = { ratio: 0.0060, space_type_gen: true, default: false }
      hash['MediumOffice - ClosedOffice'] = { ratio: 0.1866, space_type_gen: true, default: false }
      hash['MediumOffice - Conference'] = { ratio: 0.0519, space_type_gen: true, default: false }
      hash['MediumOffice - Corridor'] = { ratio: 0.0896, space_type_gen: true, default: false, circ: true }
      hash['MediumOffice - Dining'] = { ratio: 0.0138, space_type_gen: true, default: false }
      hash['MediumOffice - Elec/MechRoom'] = { ratio: 0.0300, space_type_gen: true, default: false }
      hash['MediumOffice - Lobby'] = { ratio: 0.0550, space_type_gen: true, default: false }
      hash['MediumOffice - OpenOffice'] = { ratio: 0.4255, space_type_gen: true, default: true }
      hash['MediumOffice - Restroom'] = { ratio: 0.0360, space_type_gen: true, default: false }
      hash['MediumOffice - Stair'] = { ratio: 0.0370, space_type_gen: true, default: false }
      hash['MediumOffice - Storage'] = { ratio: 0.0686, space_type_gen: true, default: false }
    end
  when 'LargeOffice'
    case building_subtype
    when 'largeoffice_datacenter'
      hash['WholeBuilding - Lg Office'] = { ratio: 0.9737, space_type_gen: true, default: true }
      hash['OfficeLarge Data Center'] = { ratio: 0.0094, space_type_gen: true, default: false }
      hash['OfficeLarge Main Data Center'] = { ratio: 0.0169, space_type_gen: true, default: false }
    when 'largeoffice_datacenteronly'
      hash['OfficeLarge Data Center'] = { ratio: 1.0, space_type_gen: true, default: false }
    when 'largeoffice_nodatacenter'
      hash['WholeBuilding - Lg Office'] = { ratio: 1.0, space_type_gen: true, default: true }
    else # 'largeoffice_default'
      if ['DOE Ref Pre-1980', 'DOE Ref 1980-2004', 'ComStock DOE Ref Pre-1980', 'ComStock DOE Ref 1980-2004'].include?(template)
        if whole_building
          hash['WholeBuilding - Lg Office'] = { ratio: 1.0, space_type_gen: true, default: true }
        else
          hash['BreakRoom'] = { ratio: 0.0178, space_type_gen: true, default: false }
          hash['Classroom'] = { ratio: 0.0040, space_type_gen: true, default: false }
          hash['ClosedOffice'] = { ratio: 0.16, space_type_gen: true, default: false }
          hash['Conference'] = { ratio: 0.0153, space_type_gen: true, default: false }
          hash['Corridor'] = { ratio: 0.0460, space_type_gen: true, default: false, circ: true }
          hash['Dining'] = { ratio: 0.0161, space_type_gen: true, default: false }
          hash['Elec/MechRoom'] = { ratio: 0.0944, space_type_gen: true, default: false }
          hash['Lobby'] = { ratio: 0.0554, space_type_gen: true, default: false }
          hash['OpenOffice'] = { ratio: 0.5230, space_type_gen: true, default: true }
          hash['Restroom'] = { ratio: 0.0310, space_type_gen: true, default: false }
          hash['Stair'] = { ratio: 0.0180, space_type_gen: true, default: false }
          hash['Storage'] = { ratio: 0.0190, space_type_gen: true, default: false }
        end
      else
        if whole_building
          hash['WholeBuilding - Lg Office'] = { ratio: 0.9737, space_type_gen: true, default: true }
          hash['OfficeLarge Data Center'] = { ratio: 0.0094, space_type_gen: true, default: false }
          hash['OfficeLarge Main Data Center'] = { ratio: 0.0169, space_type_gen: true, default: false }
        else
          hash['BreakRoom'] = { ratio: 0.0167, space_type_gen: true, default: false }
          hash['Classroom'] = { ratio: 0.0038, space_type_gen: true, default: false }
          hash['ClosedOffice'] = { ratio: 0.1500, space_type_gen: true, default: false }
          hash['Conference'] = { ratio: 0.0144, space_type_gen: true, default: false }
          hash['Corridor'] = { ratio: 0.0431, space_type_gen: true, default: false, circ: true }
          hash['Dining'] = { ratio: 0.0151, space_type_gen: true, default: false }
          hash['Elec/MechRoom'] = { ratio: 0.0885, space_type_gen: true, default: false }
          hash['Lobby'] = { ratio: 0.0520, space_type_gen: true, default: false }
          hash['OfficeLarge Data Center'] = { ratio: 0.0077, space_type_gen: true, default: false }
          hash['OfficeLarge Main Data Center'] = { ratio: 0.0550, space_type_gen: true, default: false }
          hash['OpenOffice'] = { ratio: 0.4900, space_type_gen: true, default: true }
          hash['Restroom'] = { ratio: 0.0290, space_type_gen: true, default: false }
          hash['Stair'] = { ratio: 0.0169, space_type_gen: true, default: false }
          hash['Storage'] = { ratio: 0.0178, space_type_gen: true, default: false }
        end
      end
    end
  when 'SmallHotel'
    hash['Corridor'] = { ratio: 0.1313, space_type_gen: true, default: false, circ: true }
    hash['Elec/MechRoom'] = { ratio: 0.0038, space_type_gen: true, default: false }
    hash['ElevatorCore'] = { ratio: 0.0113, space_type_gen: true, default: false }
    hash['Exercise'] = { ratio: 0.0081, space_type_gen: true, default: false }
    hash['GuestLounge'] = { ratio: 0.0406, space_type_gen: true, default: false }
    hash['GuestRoom123Occ'] = { ratio: 0.4081, space_type_gen: true, default: true }
    hash['GuestRoom123Vac'] = { ratio: 0.2231, space_type_gen: true, default: false }
    hash['Laundry'] = { ratio: 0.0244, space_type_gen: true, default: false }
    hash['Mechanical'] = { ratio: 0.0081, space_type_gen: true, default: false }
    hash['Meeting'] = { ratio: 0.0200, space_type_gen: true, default: false }
    hash['Office'] = { ratio: 0.0325, space_type_gen: true, default: false }
    hash['PublicRestroom'] = { ratio: 0.0081, space_type_gen: true, default: false }
    hash['StaffLounge'] = { ratio: 0.0081, space_type_gen: true, default: false }
    hash['Stair'] = { ratio: 0.0400, space_type_gen: true, default: false }
    hash['Storage'] = { ratio: 0.0325, space_type_gen: true, default: false }
  when 'LargeHotel'
    hash['Banquet'] = { ratio: 0.0585, space_type_gen: true, default: false }
    hash['Basement'] = { ratio: 0.1744, space_type_gen: false, default: false }
    hash['Cafe'] = { ratio: 0.0166, space_type_gen: true, default: false }
    hash['Corridor'] = { ratio: 0.1736, space_type_gen: true, default: false, circ: true }
    hash['GuestRoom'] = { ratio: 0.4099, space_type_gen: true, default: true }
    hash['Kitchen'] = { ratio: 0.0091, space_type_gen: true, default: false }
    hash['Laundry'] = { ratio: 0.0069, space_type_gen: true, default: false }
    hash['Lobby'] = { ratio: 0.1153, space_type_gen: true, default: false }
    hash['Mechanical'] = { ratio: 0.0145, space_type_gen: true, default: false }
    hash['Retail'] = { ratio: 0.0128, space_type_gen: true, default: false }
    hash['Storage'] = { ratio: 0.0084, space_type_gen: true, default: false }
  when 'Warehouse'
    case building_subtype
    when 'warehouse_bulk100'
      hash['Bulk'] = { ratio: 1.0, space_type_gen: true, default: true }
    when 'warehouse_fine100'
      hash['Fine'] = { ratio: 1.0, space_type_gen: true, default: true }
    when 'warehouse_bulk80'
      hash['Bulk'] = { ratio: 0.80, space_type_gen: true, default: true }
      hash['Fine'] = { ratio: 0.151, space_type_gen: true, default: false }
      hash['Office'] = { ratio: 0.0490, space_type_gen: true, default: false, wwr: 0.71, story_height: 14.0 }
    when 'warehouse_bulk40'
      hash['Bulk'] = { ratio: 0.40, space_type_gen: true, default: true }
      hash['Fine'] = { ratio: 0.551, space_type_gen: true, default: false }
      hash['Office'] = { ratio: 0.0490, space_type_gen: true, default: false, wwr: 0.71, story_height: 14.0 }
    when 'warehouse_bulk20'
      hash['Bulk'] = { ratio: 0.20, space_type_gen: true, default: true }
      hash['Fine'] = { ratio: 0.751, space_type_gen: true, default: false }
      hash['Office'] = { ratio: 0.0490, space_type_gen: true, default: false, wwr: 0.71, story_height: 14.0 }
    else # 'warehouse_default'
      hash['Bulk'] = { ratio: 0.6628, space_type_gen: true, default: true }
      hash['Fine'] = { ratio: 0.2882, space_type_gen: true, default: false }
      hash['Office'] = { ratio: 0.0490, space_type_gen: true, default: false, wwr: 0.71, story_height: 14.0 }
    end
  when 'RetailStandalone'
    hash['Back_Space'] = { ratio: 0.1656, space_type_gen: true, default: false }
    hash['Entry'] = { ratio: 0.0052, space_type_gen: true, default: false }
    hash['Point_of_Sale'] = { ratio: 0.0657, space_type_gen: true, default: false }
    hash['Retail'] = { ratio: 0.7635, space_type_gen: true, default: true }
  when 'RetailStripmall'
    hash['Strip mall - type 1'] = { ratio: 0.25, space_type_gen: true, default: false }
    hash['Strip mall - type 2'] = { ratio: 0.25, space_type_gen: true, default: false }
    hash['Strip mall - type 3'] = { ratio: 0.50, space_type_gen: true, default: true }
  when 'QuickServiceRestaurant'
    hash['Dining'] = { ratio: 0.5, space_type_gen: true, default: true }
    hash['Kitchen'] = { ratio: 0.5, space_type_gen: true, default: false }
  when 'FullServiceRestaurant'
    hash['Dining'] = { ratio: 0.7272, space_type_gen: true, default: true }
    hash['Kitchen'] = { ratio: 0.2728, space_type_gen: true, default: false }
  when 'MidriseApartment'
    hash['Apartment'] = { ratio: 0.8727, space_type_gen: true, default: true }
    hash['Corridor'] = { ratio: 0.0991, space_type_gen: true, default: false, circ: true }
    hash['Office'] = { ratio: 0.0282, space_type_gen: true, default: false }
  when 'HighriseApartment'
    hash['Apartment'] = { ratio: 0.8896, space_type_gen: true, default: true }
    hash['Corridor'] = { ratio: 0.0991, space_type_gen: true, default: false, circ: true }
    hash['Office'] = { ratio: 0.0113, space_type_gen: true, default: false }
  when 'Hospital'
    hash['Basement'] = { ratio: 0.1667, space_type_gen: false, default: false }
    hash['Corridor'] = { ratio: 0.1741, space_type_gen: true, default: false, circ: true }
    hash['Dining'] = { ratio: 0.0311, space_type_gen: true, default: false }
    hash['ER_Exam'] = { ratio: 0.0099, space_type_gen: true, default: false }
    hash['ER_NurseStn'] = { ratio: 0.0551, space_type_gen: true, default: false }
    hash['ER_Trauma'] = { ratio: 0.0025, space_type_gen: true, default: false }
    hash['ER_Triage'] = { ratio: 0.0050, space_type_gen: true, default: false }
    hash['ICU_NurseStn'] = { ratio: 0.0298, space_type_gen: true, default: false }
    hash['ICU_Open'] = { ratio: 0.0275, space_type_gen: true, default: false }
    hash['ICU_PatRm'] = { ratio: 0.0115, space_type_gen: true, default: false }
    hash['Kitchen'] = { ratio: 0.0414, space_type_gen: true, default: false }
    hash['Lab'] = { ratio: 0.0236, space_type_gen: true, default: false }
    hash['Lobby'] = { ratio: 0.0657, space_type_gen: true, default: false }
    hash['NurseStn'] = { ratio: 0.1723, space_type_gen: true, default: false }
    hash['Office'] = { ratio: 0.0286, space_type_gen: true, default: false }
    hash['OR'] = { ratio: 0.0273, space_type_gen: true, default: false }
    hash['PatCorridor'] = { ratio: 0.0, space_type_gen: true, default: false } # not in prototype
    hash['PatRoom'] = { ratio: 0.0845, space_type_gen: true, default: true }
    hash['PhysTherapy'] = { ratio: 0.0217, space_type_gen: true, default: false }
    hash['Radiology'] = { ratio: 0.0217, space_type_gen: true, default: false }
  when 'Outpatient'
    hash['Anesthesia'] = { ratio: 0.0026, space_type_gen: true, default: false }
    hash['BioHazard'] = { ratio: 0.0014, space_type_gen: true, default: false }
    hash['Cafe'] = { ratio: 0.0103, space_type_gen: true, default: false }
    hash['CleanWork'] = { ratio: 0.0071, space_type_gen: true, default: false }
    hash['Conference'] = { ratio: 0.0082, space_type_gen: true, default: false }
    hash['DressingRoom'] = { ratio: 0.0021, space_type_gen: true, default: false }
    hash['Elec/MechRoom'] = { ratio: 0.0109, space_type_gen: true, default: false }
    hash['ElevatorPumpRoom'] = { ratio: 0.0022, space_type_gen: true, default: false }
    hash['Exam'] = { ratio: 0.1029, space_type_gen: true, default: true }
    hash['Hall'] = { ratio: 0.1924, space_type_gen: true, default: false, circ: true }
    hash['IT_Room'] = { ratio: 0.0027, space_type_gen: true, default: false }
    hash['Janitor'] = { ratio: 0.0672, space_type_gen: true, default: false }
    hash['Lobby'] = { ratio: 0.0152, space_type_gen: true, default: false }
    hash['LockerRoom'] = { ratio: 0.0190, space_type_gen: true, default: false }
    hash['Lounge'] = { ratio: 0.0293, space_type_gen: true, default: false }
    hash['MedGas'] = { ratio: 0.0014, space_type_gen: true, default: false }
    hash['MRI'] = { ratio: 0.0107, space_type_gen: true, default: false }
    hash['MRI_Control'] = { ratio: 0.0041, space_type_gen: true, default: false }
    hash['NurseStation'] = { ratio: 0.0189, space_type_gen: true, default: false }
    hash['Office'] = { ratio: 0.1828, space_type_gen: true, default: false }
    hash['OR'] = { ratio: 0.0346, space_type_gen: true, default: false }
    hash['PACU'] = { ratio: 0.0232, space_type_gen: true, default: false }
    hash['PhysicalTherapy'] = { ratio: 0.0462, space_type_gen: true, default: false }
    hash['PreOp'] = { ratio: 0.0129, space_type_gen: true, default: false }
    hash['ProcedureRoom'] = { ratio: 0.0070, space_type_gen: true, default: false }
    hash['Reception'] = { ratio: 0.0365, space_type_gen: true, default: false }
    hash['Soil Work'] = { ratio: 0.0088, space_type_gen: true, default: false }
    hash['Stair'] = { ratio: 0.0146, space_type_gen: true, default: false }
    hash['Toilet'] = { ratio: 0.0193, space_type_gen: true, default: false }
    hash['Undeveloped'] = { ratio: 0.0835, space_type_gen: false, default: false }
    hash['Xray'] = { ratio: 0.0220, space_type_gen: true, default: false }
  when 'SuperMarket'
    # @todo populate ratios for SuperMarket
    hash['Bakery'] = { ratio: 0.99, space_type_gen: true, default: false }
    hash['Deli'] = { ratio: 0.99, space_type_gen: true, default: false }
    hash['DryStorage'] = { ratio: 0.99, space_type_gen: true, default: false }
    hash['Office'] = { ratio: 0.99, space_type_gen: true, default: false }
    hash['Produce'] = { ratio: 0.99, space_type_gen: true, default: true }
    hash['Sales'] = { ratio: 0.99, space_type_gen: true, default: true }
    hash['Corridor'] = { ratio: 0.99, space_type_gen: true, default: true }
    hash['Dining'] = { ratio: 0.99, space_type_gen: true, default: true }
    hash['Elec/MechRoom'] = { ratio: 0.99, space_type_gen: true, default: true }
    hash['Meeting'] = { ratio: 0.99, space_type_gen: true, default: true }
    hash['Restroom'] = { ratio: 0.99, space_type_gen: true, default: true }
    hash['Vestibule'] = { ratio: 0.99, space_type_gen: true, default: true }
  when 'Laboratory'
    hash['Office'] = { ratio: 0.50, space_type_gen: true, default: true }
    hash['Open lab'] = { ratio: 0.35, space_type_gen: true, default: true }
    hash['Equipment corridor'] = { ratio: 0.05, space_type_gen: true, default: true }
    hash['Lab with fume hood'] = { ratio: 0.10, space_type_gen: true, default: true }
  when 'LargeDataCenterHighITE', 'LargeDataCenterLowITE'
    hash['StandaloneDataCenter'] = { ratio: 1.0, space_type_gen: true, default: true }
  when 'SmallDataCenterHighITE', 'SmallDataCenterLowITE'
    hash['ComputerRoom'] = { ratio: 1.0, space_type_gen: true, default: true }
  when 'Courthouse'
    hash['Courthouse - Break Room'] = { ratio: 0.0067, space_type_gen: true, default: false }
    hash['Courthouse - Cell'] = { ratio: 0.0731, space_type_gen: true, default: false }
    hash['Courthouse - Conference'] = { ratio: 0.0203, space_type_gen: true, default: false }
    hash['Courthouse - Corridor'] = { ratio: 0.0829, space_type_gen: true, default: false }
    hash['Courthouse - Courtroom'] = { ratio: 0.1137, space_type_gen: true, default: false }
    hash['Courthouse - Courtroom Waiting'] = { ratio: 0.051, space_type_gen: true, default: false }
    hash['Courthouse - Elevator Lobby'] = { ratio: 0.0085, space_type_gen: true, default: false }
    hash['Courthouse - Elevator Shaft'] = { ratio: 0.0047, space_type_gen: true, default: false }
    hash['Courthouse - Entrance Lobby'] = { ratio: 0.0299, space_type_gen: true, default: false }
    hash['Courthouse - Judges Chamber'] = { ratio: 0.0261, space_type_gen: true, default: false }
    hash['Courthouse - Jury Assembly'] = { ratio: 0.0355, space_type_gen: true, default: false }
    hash['Courthouse - Jury Deliberation'] = { ratio: 0.0133, space_type_gen: true, default: false }
    hash['Courthouse - Library'] = { ratio: 0.0302, space_type_gen: true, default: false }
    hash['Courthouse - Office'] = { ratio: 0.1930, space_type_gen: true, default: true }
    hash['Courthouse - Parking'] = { ratio: 0.1083, space_type_gen: true, default: false }
    hash['Courthouse - Restrooms'] = { ratio: 0.04, space_type_gen: true, default: false }
    hash['Courthouse - Security Screening'] = { ratio: 0.0132, space_type_gen: true, default: false }
    hash['Courthouse - Service Shaft'] = { ratio: 0.0019, space_type_gen: true, default: false }
    hash['Courthouse - Stairs'] = { ratio: 0.0111, space_type_gen: true, default: false }
    hash['Courthouse - Storage'] = { ratio: 0.0882, space_type_gen: true, default: false }
    hash['Courthouse - Utility'] = { ratio: 0.0484, space_type_gen: true, default: false }
  when 'College'
    hash['College - Art Classroom'] = { ratio: 0.1868, space_type_gen: true, default: false }
    hash['College - Classroom'] = { ratio: 0.2348, space_type_gen: true, default: true }
    hash['College - Conference'] = { ratio: 0.0215, space_type_gen: true, default: false }
    hash['College - Corridor'] = { ratio: 0.0716, space_type_gen: true, default: false }
    hash['College - Elevator Shaft'] = { ratio: 0.0074, space_type_gen: true, default: false }
    hash['College - Entrance Lobby'] = { ratio: 0.0117, space_type_gen: true, default: false }
    hash['College - Laboratory'] = { ratio: 0.0843, space_type_gen: true, default: false }
    hash['College - Lecture Hall'] = { ratio: 0.0421, space_type_gen: true, default: false }
    hash['College - Lounge'] = { ratio: 0.028, space_type_gen: true, default: false }
    hash['College - Media Center'] = { ratio: 0.0421, space_type_gen: true, default: false }
    hash['College - Office'] = { ratio: 0.1894, space_type_gen: true, default: false }
    hash['College - Restroom'] = { ratio: 0.0363, space_type_gen: true, default: false }
    hash['College - Stairs'] = { ratio: 0.0272, space_type_gen: true, default: false }
    hash['College - Storage'] = { ratio: 0.0117, space_type_gen: true, default: false }
    hash['College - Utility'] = { ratio: 0.0051, space_type_gen: true, default: false }

  # DEER Prototypes
  when 'Asm'
    hash['Auditorium'] = { ratio: 0.7658, space_type_gen: true, default: true }
    hash['OfficeGeneral'] = { ratio: 0.2342, space_type_gen: true, default: false }
  when 'ECC'
    hash['Classroom'] = { ratio: 0.5558, space_type_gen: true, default: true }
    hash['CompRoomClassRm'] = { ratio: 0.0319, space_type_gen: true, default: false }
    hash['Shop'] = { ratio: 0.1249, space_type_gen: true, default: false }
    hash['Dining'] = { ratio: 0.0876, space_type_gen: true, default: false }
    hash['Kitchen'] = { ratio: 0.0188, space_type_gen: true, default: false }
    hash['OfficeGeneral'] = { ratio: 0.181, space_type_gen: true, default: false }
  when 'EPr'
    hash['Classroom'] = { ratio: 0.53, space_type_gen: true, default: true }
    hash['CorridorStairway'] = { ratio: 0.1, space_type_gen: true, default: false }
    hash['Dining'] = { ratio: 0.15, space_type_gen: true, default: false }
    hash['Gymnasium'] = { ratio: 0.15, space_type_gen: true, default: false }
    hash['Kitchen'] = { ratio: 0.07, space_type_gen: true, default: false }
  when 'ERC'
    hash['Classroom'] = { ratio: 0.5, space_type_gen: true, default: true }
  when 'ESe'
    hash['Classroom'] = { ratio: 0.488, space_type_gen: true, default: true }
    hash['CompRoomClassRm'] = { ratio: 0.021, space_type_gen: true, default: false }
    hash['CorridorStairway'] = { ratio: 0.1, space_type_gen: true, default: false }
    hash['Dining'] = { ratio: 0.15, space_type_gen: true, default: false }
    hash['Gymnasium'] = { ratio: 0.15, space_type_gen: true, default: false }
    hash['Kitchen'] = { ratio: 0.07, space_type_gen: true, default: false }
    hash['OfficeGeneral'] = { ratio: 0.021, space_type_gen: true, default: true }
  when 'EUn'
    hash['Dining'] = { ratio: 0.0238, space_type_gen: true, default: false }
    hash['Classroom'] = { ratio: 0.3056, space_type_gen: true, default: false }
    hash['OfficeGeneral'] = { ratio: 0.3422, space_type_gen: true, default: true }
    hash['CompRoomClassRm'] = { ratio: 0.038, space_type_gen: true, default: false }
    hash['Kitchen'] = { ratio: 0.0105, space_type_gen: true, default: false }
    hash['CorridorStairway'] = { ratio: 0.03, space_type_gen: true, default: false }
    hash['FacMaint'] = { ratio: 0.08, space_type_gen: true, default: false }
    hash['DormitoryRoom'] = { ratio: 0.1699, space_type_gen: true, default: false }
  when 'Gro'
    hash['GrocSales'] = { ratio: 0.8002, space_type_gen: true, default: true }
    hash['RefWalkInCool'] = { ratio: 0.0312, space_type_gen: true, default: false }
    hash['OfficeGeneral'] = { ratio: 0.07, space_type_gen: true, default: false }
    hash['RefFoodPrep'] = { ratio: 0.0253, space_type_gen: true, default: false }
    hash['RefWalkInFreeze'] = { ratio: 0.0162, space_type_gen: true, default: false }
    hash['IndLoadDock'] = { ratio: 0.057, space_type_gen: true, default: false }
  when 'Hsp'
    hash['HspSurgOutptLab'] = { ratio: 0.2317, space_type_gen: true, default: false }
    hash['Dining'] = { ratio: 0.0172, space_type_gen: true, default: false }
    hash['Kitchen'] = { ratio: 0.0075, space_type_gen: true, default: false }
    hash['OfficeGeneral'] = { ratio: 0.3636, space_type_gen: true, default: false }
    hash['PatientRoom'] = { ratio: 0.38, space_type_gen: true, default: true }
  when 'Htl'
    hash['Dining'] = { ratio: 0.004, space_type_gen: true, default: false }
    hash['BarCasino'] = { ratio: 0.005, space_type_gen: true, default: false }
    hash['HotelLobby'] = { ratio: 0.0411, space_type_gen: true, default: false }
    hash['OfficeGeneral'] = { ratio: 0.0205, space_type_gen: true, default: false }
    hash['GuestRmCorrid'] = { ratio: 0.1011, space_type_gen: true, default: false }
    hash['Laundry'] = { ratio: 0.0205, space_type_gen: true, default: false }
    hash['GuestRmOcc'] = { ratio: 0.64224, space_type_gen: true, default: true }
    hash['GuestRmUnOcc'] = { ratio: 0.16056, space_type_gen: true, default: true }
    hash['Kitchen'] = { ratio: 0.005, space_type_gen: true, default: false }
  when 'MBT'
    hash['CompRoomData'] = { ratio: 0.02, space_type_gen: true, default: false }
    hash['Laboratory'] = { ratio: 0.4534, space_type_gen: true, default: true }
    hash['CorridorStairway'] = { ratio: 0.2, space_type_gen: true, default: false }
    hash['Conference'] = { ratio: 0.02, space_type_gen: true, default: false }
    hash['Dining'] = { ratio: 0.03, space_type_gen: true, default: false }
    hash['OfficeOpen'] = { ratio: 0.2666, space_type_gen: true, default: false }
    hash['Kitchen'] = { ratio: 0.01, space_type_gen: true, default: false }
  when 'MFm'
    hash['ResLiving'] = { ratio: 0.9297, space_type_gen: true, default: true }
    hash['ResPublicArea'] = { ratio: 0.0725, space_type_gen: true, default: false }
  when 'MLI'
    hash['StockRoom'] = { ratio: 0.2, space_type_gen: true, default: false }
    hash['Work'] = { ratio: 0.8, space_type_gen: true, default: true }
  when 'Mtl'
    hash['OfficeGeneral'] = { ratio: 0.02, space_type_gen: true, default: false }
    hash['GuestRmCorrid'] = { ratio: 0.649, space_type_gen: true, default: true }
    hash['Laundry'] = { ratio: 0.016, space_type_gen: true, default: false }
    hash['GuestRmOcc'] = { ratio: 0.25208, space_type_gen: true, default: false }
    hash['GuestRmUnOcc'] = { ratio: 0.06302, space_type_gen: true, default: false }
  when 'Nrs'
    hash['CorridorStairway'] = { ratio: 0.0555, space_type_gen: true, default: false }
    hash['Dining'] = { ratio: 0.105, space_type_gen: true, default: false }
    hash['Kitchen'] = { ratio: 0.045, space_type_gen: true, default: false }
    hash['OfficeGeneral'] = { ratio: 0.35, space_type_gen: true, default: false }
    hash['PatientRoom'] = { ratio: 0.4445, space_type_gen: true, default: true }
  when 'OfL'
    hash['LobbyWaiting'] = { ratio: 0.0412, space_type_gen: true, default: false }
    hash['OfficeSmall'] = { ratio: 0.3704, space_type_gen: true, default: false }
    hash['OfficeOpen'] = { ratio: 0.5296, space_type_gen: true, default: true }
    hash['MechElecRoom'] = { ratio: 0.0588, space_type_gen: true, default: false }
  when 'OfS'
    hash['Hall'] = { ratio: 0.3141, space_type_gen: true, default: false }
    hash['OfficeSmall'] = { ratio: 0.6859, space_type_gen: true, default: true }
  when 'RFF'
    hash['Dining'] = { ratio: 0.3997, space_type_gen: true, default: false }
    hash['Kitchen'] = { ratio: 0.4, space_type_gen: true, default: true }
    hash['LobbyWaiting'] = { ratio: 0.1501, space_type_gen: true, default: false }
    hash['Restroom'] = { ratio: 0.0501, space_type_gen: true, default: false }
  when 'RSD'
    hash['Restroom'] = { ratio: 0.0357, space_type_gen: true, default: false }
    hash['Dining'] = { ratio: 0.5353, space_type_gen: true, default: true }
    hash['LobbyWaiting'] = { ratio: 0.1429, space_type_gen: true, default: false }
    hash['Kitchen'] = { ratio: 0.2861, space_type_gen: true, default: false }
  when 'Rt3'
    hash['RetailSales'] = { ratio: 1.0, space_type_gen: true, default: true }
  when 'RtL'
    hash['OfficeGeneral'] = { ratio: 0.0363, space_type_gen: true, default: false }
    hash['Work'] = { ratio: 0.0405, space_type_gen: true, default: false }
    hash['StockRoom'] = { ratio: 0.0920, space_type_gen: true, default: false }
    hash['RetailSales'] = { ratio: 0.8312, space_type_gen: true, default: true }
    # hash['Kitchen'] = { ratio: 0.0113, space_type_gen: true, default: false }
  when 'RtS'
    hash['RetailSales'] = { ratio: 0.8, space_type_gen: true, default: true }
    hash['StockRoom'] = { ratio: 0.2, space_type_gen: true, default: false }
  when 'SCn'
    hash['WarehouseCond'] = { ratio: 1.0, space_type_gen: true, default: true }
  when 'SUn'
    hash['WarehouseUnCond'] = { ratio: 1.0, space_type_gen: true, default: true }
  when 'WRf'
    hash['IndLoadDock'] = { ratio: 0.08, space_type_gen: true, default: false }
    hash['OfficeGeneral'] = { ratio: 0.02, space_type_gen: true, default: false }
    hash['RefStorFreezer'] = { ratio: 0.4005, space_type_gen: true, default: false }
    hash['RefStorCooler'] = { ratio: 0.4995, space_type_gen: true, default: true }
  else
    return false
  end

  return hash
end

.get_templates(extended = false) ⇒ OpenStudio::StringVector

list of templates that are valid for get_space_types_from_building_type

Parameters:

  • extended (Boolean) (defaults to: false)

    set to true to return additional templates

Returns:

  • (OpenStudio::StringVector)

    array of available standard templates as strings



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/openstudio-standards/create_typical/enumerations.rb', line 182

def self.get_templates(extended = false)
  # get templates
  if extended
    doe = get_doe_templates(true)
    deer = get_deer_templates(true)
  else
    doe = get_doe_templates
    deer = get_deer_templates
  end

  # combine templates
  array = OpenStudio::StringVector.new
  temp_array = doe.to_a + deer.to_a
  temp_array.each do |i|
    array << i
  end

  return array
end

.space_or_space_type_gather_internal_loads(space_or_space_type) ⇒ Hash

get all loads for a space or space type and place in hash by type

Parameters:

  • space_or_space_type (OpenStudio::Model::Space, OpenStuido::Model::SpaceType)

    OpenStudio Space or SpaceType object

Returns:

  • (Hash)

    Hash of internal load properties



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/openstudio-standards/create_typical/space_type_blend.rb', line 11

def self.space_or_space_type_gather_internal_loads(space_or_space_type)
  internal_load_hash = {}

  # gather different load types (all vectors except dsoa which will be turned into an array)
  internal_load_hash[:internal_mass] = space_or_space_type.internalMass
  internal_load_hash[:people] = space_or_space_type.people
  internal_load_hash[:lights] = space_or_space_type.lights
  internal_load_hash[:luminaires] = space_or_space_type.luminaires
  internal_load_hash[:electric_equipment] = space_or_space_type.electricEquipment
  internal_load_hash[:gas_equipment] = space_or_space_type.gasEquipment
  internal_load_hash[:hot_water_equipment] = space_or_space_type.hotWaterEquipment
  internal_load_hash[:steam_equipment] = space_or_space_type.steamEquipment
  internal_load_hash[:other_equipment] = space_or_space_type.otherEquipment
  internal_load_hash[:space_infiltration_design_flow_rates] = space_or_space_type.spaceInfiltrationDesignFlowRates
  internal_load_hash[:space_infiltration_effective_leakage_areas] = space_or_space_type.spaceInfiltrationEffectiveLeakageAreas
  if space_or_space_type.designSpecificationOutdoorAir.nil?
    internal_load_hash[:design_specification_outdoor_air] = []
  else
    internal_load_hash[:design_specification_outdoor_air] = [space_or_space_type.designSpecificationOutdoorAir]
  end
  if space_or_space_type.class.to_s == 'OpenStudio::Model::Space'
    internal_load_hash[:water_use_equipment] = space_or_space_type.waterUseEquipment # don't think this reports
    internal_load_hash[:daylighting_controls] = space_or_space_type.daylightingControls
  end

  # @todo warn if daylighting controls in spaces (should I alter fraction controled based on lighting per area ratio)

  return internal_load_hash
end