# *********************************************************************
# *  Copyright (c) 2008-2015, Natural Resources Canada
# *  All rights reserved.
# *
# *  This library is free software; you can redistribute it and/or
# *  modify it under the terms of the GNU Lesser General Public
# *  License as published by the Free Software Foundation; either
# *  version 2.1 of the License, or (at your option) any later version.
# *
# *  This library is distributed in the hope that it will be useful,
# *  but WITHOUT ANY WARRANTY; without even the implied warranty of
# *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# *  Lesser General Public License for more details.
# *
# *  You should have received a copy of the GNU Lesser General Public
# *  License along with this library; if not, write to the Free Software
# *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
# **********************************************************************/


module BTAP
  module Geometry

    def self.enumerate_spaces_model(model, prepend_name = false)
      #enumerate stories.
      BTAP::Geometry::BuildingStoreys::auto_assign_spaces_to_stories(model)
      #Enumerate spaces
      model.getBuildingStorys.sort.each do |story|
        spaces = Array.new
        spaces.concat(story.spaces)
        spaces.sort! do |a, b|
          (a.xOrigin <=> b.xOrigin).nonzero? ||
              (a.yOrigin <=> b.yOrigin)
        end
        counter = 1
        spaces.sort.each do |space|
          #puts "old space name : #{space.name}"
          if prepend_name == true
            space.setName("#{story.name}-#{counter.to_s}:#{space.name}")
          else
            space.setName("#{story.name}-#{counter.to_s}")
          end
          counter = counter + 1
          p #uts "new space name : #{space.name}"
        end
      end
    end

    #this was a copy of the sketchup plugin method. 
    def self.rename_zones_based_on_spaces(model)

      # loop through thermal zones
      model.getThermalZones.sort.each do |thermal_zone| # this is going through all, not just selection
        #puts "old zone name : #{thermal_zone.name}"
        # reset the array of spaces to be empty
        spaces_in_thermal_zone = []
        # reset length of array of spaces
        number_of_spaces = 0

        # get list of spaces in thermal zone
        spaces = thermal_zone.spaces
        spaces.sort.each do |space|

          # make an array instead of the puts statement
          spaces_in_thermal_zone.push space.name.to_s

        end

        # store length of array
        number_of_spaces = spaces_in_thermal_zone.size

        # sort the array
        spaces_in_thermal_zone = spaces_in_thermal_zone.sort

        # setup a suffix if the thermal zone contains more than one space
        if number_of_spaces > 1
          multi = " - Plus"
        else
          multi = ""
        end

        # rename thermal zone based on first space with prefix added e.g. ThermalZone 203
        if number_of_spaces > 0
          new_name = "ZN:" + spaces_in_thermal_zone[0] + multi
          thermal_zone.setName(new_name)
        else
          puts "#{thermal_zone.name.to_s} did not have any spaces, and will not be renamed."
        end
        #puts "new zone name : #{thermal_zone.name}"
      end
    end

    #This method will rename the zone equipment to have the zone name as a prefix for a model. 
    #It will also rename the hot water coils for: 
    #    AirTerminalSingleDuctVAVReheat
    #    ZoneHVACBaseboardConvectiveWater
    #    ZoneHVACUnitHeater

    def self.prefix_equipment_with_zone_name(model)
      #puts "Renaming zone equipment."
      # get all thermal zones
      thermal_zones = model.getThermalZones

      # loop through thermal zones
      thermal_zones.each do |thermal_zone| # this is going through all, not just selection

        thermal_zone.equipment.each do |equip|

          #For the hydronic conditions below only, it will rename the zonal coils as well. 
          if not equip.to_AirTerminalSingleDuctVAVReheat.empty?

            equip.setName("#{thermal_zone.name}:AirTerminalSingleDuctVAVReheat")
            reheat_coil = equip.to_AirTerminalSingleDuctVAVReheat.get.reheatCoil
            reheat_coil.setName("#{thermal_zone.name}:ReheatCoil")
            #puts reheat_coil.name
          elsif not equip.to_ZoneHVACBaseboardConvectiveWater.empty?
            equip.setName("#{thermal_zone.name}:ZoneHVACBaseboardConvectiveWater")
            heatingCoil = equip.to_ZoneHVACBaseboardConvectiveWater.get.heatingCoil
            heatingCoil.setName("#{thermal_zone.name}:Baseboard HW Htg Coil")
            #puts heatingCoil.name
          elsif not equip.to_ZoneHVACUnitHeater.empty?
            equip.setName("#{thermal_zone.name}:ZoneHVACUnitHeater")
            heatingCoil = equip.to_ZoneHVACUnitHeater.get.heatingCoil
            heatingCoil.setName("#{thermal_zone.name}:Unit Heater Htg Coil")
            #puts heatingCoil.name
            #Add more cases if you wish!!!!!
          else #if the equipment does not follow the above cases, rename
            # it generically and not touch the underlying coils, etc. 
            equip.setName("#{thermal_zone.name}:#{equip.name}")
          end

        end
      end
      #puts "Done zone renaming equipment"
    end


    module Wizards
      def self.create_shape_courtyard(model,
          length = 50.0,
          width = 30.0,
          courtyard_length = 15.0,
          courtyard_width = 5.0,
          num_floors = 3,
          floor_to_floor_height = 3.8,
          plenum_height = 1.0,
          perimeter_zone_depth = 4.57)
        if length <= 1e-4
          raise("Length must be greater than 0.")
          return false
        end

        if width <= 1e-4
          raise("Width must be greater than 0.")
          return false
        end

        if courtyard_length <= 1e-4
          raise("Courtyard length must be greater than 0.")
          return false
        end

        if courtyard_width <= 1e-4
          raise("Courtyard width must be greater than 0.")
          return false
        end

        if num_floors <= 1e-4
          raise("Number of floors must be greater than 0.")
          return false
        end

        if floor_to_floor_height <= 1e-4
          raise("Floor to floor height must be greater than 0.")
          return false
        end

        if plenum_height < 0
          raise("Plenum height must be greater than or equal to 0.")
          return false
        end

        shortest_side = [length, width].min
        if perimeter_zone_depth < 0 or 4 * perimeter_zone_depth >= (shortest_side - 1e-4)
          raise("Perimeter zone depth must be greater than or equal to 0 and less than #{shortest_side / 4.0}m.")
          return false
        end

        if courtyard_length >= (length - 4 * perimeter_zone_depth - 1e-4)
          raise("Courtyard length must be less than #{length - 4.0 * perimeter_zone_depth}m.")
          return false
        end

        if courtyard_width >= (width - 4 * perimeter_zone_depth - 1e-4)
          raise("Courtyard width must be less than #{width - 4.0 * perimeter_zone_depth}m.")
          return false
        end


        # Loop through the number of floors
        for floor in (0..num_floors - 1)

          z = floor_to_floor_height * floor

          #Create a new story within the building
          story = OpenStudio::Model::BuildingStory.new(model)
          story.setNominalFloortoFloorHeight(floor_to_floor_height)
          story.setName("Story #{floor + 1}")


          nw_point = OpenStudio::Point3d.new(0.0, width, z)
          ne_point = OpenStudio::Point3d.new(length, width, z)
          se_point = OpenStudio::Point3d.new(length, 0.0, z)
          sw_point = OpenStudio::Point3d.new(0.0, 0.0, z)

          courtyard_nw_point = OpenStudio::Point3d.new((length - courtyard_length) / 2.0, (width - courtyard_width) / 2.0 + courtyard_width, z)
          courtyard_ne_point = OpenStudio::Point3d.new((length - courtyard_length) / 2.0 + courtyard_length, (width - courtyard_width) / 2.0 + courtyard_width, z)
          courtyard_se_point = OpenStudio::Point3d.new((length - courtyard_length) / 2.0 + courtyard_length, (width - courtyard_width) / 2.0, z)
          courtyard_sw_point = OpenStudio::Point3d.new((length - courtyard_length) / 2.0, (width - courtyard_width) / 2.0, z)

          # Identity matrix for setting space origins
          m = OpenStudio::Matrix.new(4, 4, 0.0)
          m[0, 0] = 1.0
          m[1, 1] = 1.0
          m[2, 2] = 1.0
          m[3, 3] = 1.0

          # Define polygons for a building with a courtyard
          if perimeter_zone_depth > 0
            outer_perimeter_nw_point = nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0.0)
            outer_perimeter_ne_point = ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0.0)
            outer_perimeter_se_point = se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0.0)
            outer_perimeter_sw_point = sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0.0)
            inner_perimeter_nw_point = courtyard_nw_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0.0)
            inner_perimeter_ne_point = courtyard_ne_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0.0)
            inner_perimeter_se_point = courtyard_se_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0.0)
            inner_perimeter_sw_point = courtyard_sw_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0.0)

            west_outer_perimeter_polygon = OpenStudio::Point3dVector.new
            west_outer_perimeter_polygon << sw_point
            west_outer_perimeter_polygon << nw_point
            west_outer_perimeter_polygon << outer_perimeter_nw_point
            west_outer_perimeter_polygon << outer_perimeter_sw_point
            west_outer_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(west_outer_perimeter_polygon, floor_to_floor_height, model)
            west_outer_perimeter_space = west_outer_perimeter_space.get
            m[0, 3] = sw_point.x
            m[1, 3] = sw_point.y
            m[2, 3] = sw_point.z
            west_outer_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_outer_perimeter_space.setBuildingStory(story)
            west_outer_perimeter_space.setName("Story #{floor + 1} West Outer Perimeter Space")


            north_outer_perimeter_polygon = OpenStudio::Point3dVector.new
            north_outer_perimeter_polygon << nw_point
            north_outer_perimeter_polygon << ne_point
            north_outer_perimeter_polygon << outer_perimeter_ne_point
            north_outer_perimeter_polygon << outer_perimeter_nw_point
            north_outer_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(north_outer_perimeter_polygon, floor_to_floor_height, model)
            north_outer_perimeter_space = north_outer_perimeter_space.get
            m[0, 3] = outer_perimeter_nw_point.x
            m[1, 3] = outer_perimeter_nw_point.y
            m[2, 3] = outer_perimeter_nw_point.z
            north_outer_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            north_outer_perimeter_space.setBuildingStory(story)
            north_outer_perimeter_space.setName("Story #{floor + 1} North Outer Perimeter Space")


            east_outer_perimeter_polygon = OpenStudio::Point3dVector.new
            east_outer_perimeter_polygon << ne_point
            east_outer_perimeter_polygon << se_point
            east_outer_perimeter_polygon << outer_perimeter_se_point
            east_outer_perimeter_polygon << outer_perimeter_ne_point
            east_outer_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(east_outer_perimeter_polygon, floor_to_floor_height, model)
            east_outer_perimeter_space = east_outer_perimeter_space.get
            m[0, 3] = outer_perimeter_se_point.x
            m[1, 3] = outer_perimeter_se_point.y
            m[2, 3] = outer_perimeter_se_point.z
            east_outer_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_outer_perimeter_space.setBuildingStory(story)
            east_outer_perimeter_space.setName("Story #{floor + 1} East Outer Perimeter Space")


            south_outer_perimeter_polygon = OpenStudio::Point3dVector.new
            south_outer_perimeter_polygon << se_point
            south_outer_perimeter_polygon << sw_point
            south_outer_perimeter_polygon << outer_perimeter_sw_point
            south_outer_perimeter_polygon << outer_perimeter_se_point
            south_outer_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(south_outer_perimeter_polygon, floor_to_floor_height, model)
            south_outer_perimeter_space = south_outer_perimeter_space.get
            m[0, 3] = sw_point.x
            m[1, 3] = sw_point.y
            m[2, 3] = sw_point.z
            south_outer_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            south_outer_perimeter_space.setBuildingStory(story)
            south_outer_perimeter_space.setName("Story #{floor + 1} South Outer Perimeter Space")


            west_core_polygon = OpenStudio::Point3dVector.new
            west_core_polygon << outer_perimeter_sw_point
            west_core_polygon << outer_perimeter_nw_point
            west_core_polygon << inner_perimeter_nw_point
            west_core_polygon << inner_perimeter_sw_point
            west_core_space = OpenStudio::Model::Space::fromFloorPrint(west_core_polygon, floor_to_floor_height, model)
            west_core_space = west_core_space.get
            m[0, 3] = outer_perimeter_sw_point.x
            m[1, 3] = outer_perimeter_sw_point.y
            m[2, 3] = outer_perimeter_sw_point.z
            west_core_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_core_space.setBuildingStory(story)
            west_core_space.setName("Story #{floor + 1} West Core Space")


            north_core_polygon = OpenStudio::Point3dVector.new
            north_core_polygon << outer_perimeter_nw_point
            north_core_polygon << outer_perimeter_ne_point
            north_core_polygon << inner_perimeter_ne_point
            north_core_polygon << inner_perimeter_nw_point
            north_core_space = OpenStudio::Model::Space::fromFloorPrint(north_core_polygon, floor_to_floor_height, model)
            north_core_space = north_core_space.get
            m[0, 3] = inner_perimeter_nw_point.x
            m[1, 3] = inner_perimeter_nw_point.y
            m[2, 3] = inner_perimeter_nw_point.z
            north_core_space.changeTransformation(OpenStudio::Transformation.new(m))
            north_core_space.setBuildingStory(story)
            north_core_space.setName("Story #{floor + 1} North Core Space")


            east_core_polygon = OpenStudio::Point3dVector.new
            east_core_polygon << outer_perimeter_ne_point
            east_core_polygon << outer_perimeter_se_point
            east_core_polygon << inner_perimeter_se_point
            east_core_polygon << inner_perimeter_ne_point
            east_core_space = OpenStudio::Model::Space::fromFloorPrint(east_core_polygon, floor_to_floor_height, model)
            east_core_space = east_core_space.get
            m[0, 3] = inner_perimeter_se_point.x
            m[1, 3] = inner_perimeter_se_point.y
            m[2, 3] = inner_perimeter_se_point.z
            east_core_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_core_space.setBuildingStory(story)
            east_core_space.setName("Story #{floor + 1} East Core Space")


            south_core_polygon = OpenStudio::Point3dVector.new
            south_core_polygon << outer_perimeter_se_point
            south_core_polygon << outer_perimeter_sw_point
            south_core_polygon << inner_perimeter_sw_point
            south_core_polygon << inner_perimeter_se_point
            south_core_space = OpenStudio::Model::Space::fromFloorPrint(south_core_polygon, floor_to_floor_height, model)
            south_core_space = south_core_space.get
            m[0, 3] = outer_perimeter_sw_point.x
            m[1, 3] = outer_perimeter_sw_point.y
            m[2, 3] = outer_perimeter_sw_point.z
            south_core_space.changeTransformation(OpenStudio::Transformation.new(m))
            south_core_space.setBuildingStory(story)
            south_core_space.setName("Story #{floor + 1} South Core Space")


            west_inner_perimeter_polygon = OpenStudio::Point3dVector.new
            west_inner_perimeter_polygon << inner_perimeter_sw_point
            west_inner_perimeter_polygon << inner_perimeter_nw_point
            west_inner_perimeter_polygon << courtyard_nw_point
            west_inner_perimeter_polygon << courtyard_sw_point
            west_inner_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(west_inner_perimeter_polygon, floor_to_floor_height, model)
            west_inner_perimeter_space = west_inner_perimeter_space.get
            m[0, 3] = inner_perimeter_sw_point.x
            m[1, 3] = inner_perimeter_sw_point.y
            m[2, 3] = inner_perimeter_sw_point.z
            west_inner_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_inner_perimeter_space.setBuildingStory(story)
            west_inner_perimeter_space.setName("Story #{floor + 1} West Inner Perimeter Space")


            north_inner_perimeter_polygon = OpenStudio::Point3dVector.new
            north_inner_perimeter_polygon << inner_perimeter_nw_point
            north_inner_perimeter_polygon << inner_perimeter_ne_point
            north_inner_perimeter_polygon << courtyard_ne_point
            north_inner_perimeter_polygon << courtyard_nw_point
            north_inner_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(north_inner_perimeter_polygon, floor_to_floor_height, model)
            north_inner_perimeter_space = north_inner_perimeter_space.get
            m[0, 3] = courtyard_nw_point.x
            m[1, 3] = courtyard_nw_point.y
            m[2, 3] = courtyard_nw_point.z
            north_inner_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            north_inner_perimeter_space.setBuildingStory(story)
            north_inner_perimeter_space.setName("Story #{floor + 1} North Inner Perimeter Space")


            east_inner_perimeter_polygon = OpenStudio::Point3dVector.new
            east_inner_perimeter_polygon << inner_perimeter_ne_point
            east_inner_perimeter_polygon << inner_perimeter_se_point
            east_inner_perimeter_polygon << courtyard_se_point
            east_inner_perimeter_polygon << courtyard_ne_point
            east_inner_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(east_inner_perimeter_polygon, floor_to_floor_height, model)
            east_inner_perimeter_space = east_inner_perimeter_space.get
            m[0, 3] = courtyard_se_point.x
            m[1, 3] = courtyard_se_point.y
            m[2, 3] = courtyard_se_point.z
            east_inner_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_inner_perimeter_space.setBuildingStory(story)
            east_inner_perimeter_space.setName("Story #{floor + 1} East Inner Perimeter Space")


            south_inner_perimeter_polygon = OpenStudio::Point3dVector.new
            south_inner_perimeter_polygon << inner_perimeter_se_point
            south_inner_perimeter_polygon << inner_perimeter_sw_point
            south_inner_perimeter_polygon << courtyard_sw_point
            south_inner_perimeter_polygon << courtyard_se_point
            south_inner_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(south_inner_perimeter_polygon, floor_to_floor_height, model)
            south_inner_perimeter_space = south_inner_perimeter_space.get
            m[0, 3] = inner_perimeter_sw_point.x
            m[1, 3] = inner_perimeter_sw_point.y
            m[2, 3] = inner_perimeter_sw_point.z
            south_inner_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            south_inner_perimeter_space.setBuildingStory(story)
            south_inner_perimeter_space.setName("Story #{floor + 1} South Inner Perimeter Space")


            # Minimal zones
          else
            west_polygon = OpenStudio::Point3dVector.new
            west_polygon << sw_point
            west_polygon << nw_point
            west_polygon << courtyard_nw_point
            west_polygon << courtyard_sw_point
            west_space = OpenStudio::Model::Space::fromFloorPrint(west_polygon, floor_to_floor_height, model)
            west_space = west_space.get
            m[0, 3] = sw_point.x
            m[1, 3] = sw_point.y
            m[2, 3] = sw_point.z
            west_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_space.setBuildingStory(story)
            west_space.setName("Story #{floor + 1} West Space")


            north_polygon = OpenStudio::Point3dVector.new
            north_polygon << nw_point
            north_polygon << ne_point
            north_polygon << courtyard_ne_point
            north_polygon << courtyard_nw_point
            north_space = OpenStudio::Model::Space::fromFloorPrint(north_polygon, floor_to_floor_height, model)
            north_space = north_space.get
            m[0, 3] = courtyard_nw_point.x
            m[1, 3] = courtyard_nw_point.y
            m[2, 3] = courtyard_nw_point.z
            north_space.changeTransformation(OpenStudio::Transformation.new(m))
            north_space.setBuildingStory(story)
            north_space.setName("Story #{floor + 1} North Space")


            east_polygon = OpenStudio::Point3dVector.new
            east_polygon << ne_point
            east_polygon << se_point
            east_polygon << courtyard_se_point
            east_polygon << courtyard_ne_point
            east_space = OpenStudio::Model::Space::fromFloorPrint(east_polygon, floor_to_floor_height, model)
            east_space = east_space.get
            m[0, 3] = courtyard_se_point.x
            m[1, 3] = courtyard_se_point.y
            m[2, 3] = courtyard_se_point.z
            east_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_space.setBuildingStory(story)
            east_space.setName("Story #{floor + 1} East Space")


            south_polygon = OpenStudio::Point3dVector.new
            south_polygon << se_point
            south_polygon << sw_point
            south_polygon << courtyard_sw_point
            south_polygon << courtyard_se_point
            south_space = OpenStudio::Model::Space::fromFloorPrint(south_polygon, floor_to_floor_height, model)
            south_space = south_space.get
            m[0, 3] = sw_point.x
            m[1, 3] = sw_point.y
            m[2, 3] = sw_point.z
            south_space.changeTransformation(OpenStudio::Transformation.new(m))
            south_space.setBuildingStory(story)
            south_space.setName("Story #{floor + 1} South Space")
          end
          #Set vertical story position
          story.setNominalZCoordinate(z)
        end #End of floor loop
        BTAP::Geometry::match_surfaces(model)
        return model
      end

      def self.create_shape_h(model,
          length = 40.0,
          left_width = 40.0,
          center_width = 10.0,
          right_width = 40.0,
          left_end_length = 15.0,
          right_end_length = 15.0,
          left_upper_end_offset = 15.0,
          right_upper_end_offset = 15.0,
          num_floors = 3,
          floor_to_floor_height = 3.8,
          plenum_height = 1,
          perimeter_zone_depth = 4.57)


        if length <= 1e-4
          raise("Length must be greater than 0.")
          return false
        end

        if left_width <= 1e-4
          raise("Left width must be greater than 0.")
          return false
        end

        if right_width <= 1e-4
          raise("Right width must be greater than 0.")
          return false
        end

        if center_width <= 1e-4 or center_width >= ([left_width, right_width].min - 1e-4)
          raise("Center width must be greater than 0 and less than #{[left_width, right_width].min}m.")
          return false
        end

        if left_end_length <= 1e-4 or left_end_length >= (length - 1e-4)
          raise("Left end length must be greater than 0 and less than #{length}m.")
          return false
        end

        if right_end_length <= 1e-4 or right_end_length >= (length - left_end_length - 1e-4)
          raise("Right end length must be greater than 0 and less than #{length - left_end_length}m.")
          return false
        end

        if left_upper_end_offset <= 1e-4 or left_upper_end_offset >= (left_width - center_width - 1e-4)
          raise("Left upper end offset must be greater than 0 and less than #{left_width - center_width}m.")
          return false
        end

        if right_upper_end_offset <= 1e-4 or right_upper_end_offset >= (right_width - center_width - 1e-4)
          raise("Right upper end offset must be greater than 0 and less than #{right_width - center_width}m.")
          return false
        end

        if num_floors <= 1e-4
          raise("Number of floors must be greater than 0.")
          return false
        end

        if floor_to_floor_height <= 1e-4
          raise("Floor to floor height must be greater than 0.")
          return false
        end

        if plenum_height < 0
          raise("Plenum height must be greater than or equal to 0.")
          return false
        end

        shortest_side = [length / 2, left_width, center_width, right_width, left_end_length, right_end_length].min
        if perimeter_zone_depth < 0 or 2 * perimeter_zone_depth >= (shortest_side - 1e-4)
          raise("Perimeter zone depth must be greater than or equal to 0 and less than #{shortest_side / 2}m.")
          return false
        end


        # Loop through the number of floors
        for floor in (0..num_floors - 1)

          z = floor_to_floor_height * floor

          #Create a new story within the building
          story = OpenStudio::Model::BuildingStory.new(model)
          story.setNominalFloortoFloorHeight(floor_to_floor_height)
          story.setName("Story #{floor + 1}")


          left_origin = (right_width - right_upper_end_offset) > (left_width - left_upper_end_offset) ? (right_width - right_upper_end_offset) - (left_width - left_upper_end_offset) : 0

          left_nw_point = OpenStudio::Point3d.new(0, left_width + left_origin, z)
          left_ne_point = OpenStudio::Point3d.new(left_end_length, left_width + left_origin, z)
          left_se_point = OpenStudio::Point3d.new(left_end_length, left_origin, z)
          left_sw_point = OpenStudio::Point3d.new(0, left_origin, z)
          center_nw_point = OpenStudio::Point3d.new(left_end_length, left_ne_point.y - left_upper_end_offset, z)
          center_ne_point = OpenStudio::Point3d.new(length - right_end_length, center_nw_point.y, z)
          center_se_point = OpenStudio::Point3d.new(length - right_end_length, center_nw_point.y - center_width, z)
          center_sw_point = OpenStudio::Point3d.new(left_end_length, center_se_point.y, z)
          right_nw_point = OpenStudio::Point3d.new(length - right_end_length, center_ne_point.y + right_upper_end_offset, z)
          right_ne_point = OpenStudio::Point3d.new(length, right_nw_point.y, z)
          right_se_point = OpenStudio::Point3d.new(length, right_ne_point.y - right_width, z)
          right_sw_point = OpenStudio::Point3d.new(length - right_end_length, right_se_point.y, z)

          # Identity matrix for setting space origins
          m = OpenStudio::Matrix.new(4, 4, 0)
          m[0, 0] = 1
          m[1, 1] = 1
          m[2, 2] = 1
          m[3, 3] = 1

          # Define polygons for a L-shape building with perimeter core zoning
          if perimeter_zone_depth > 0
            perimeter_left_nw_point = left_nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_left_ne_point = left_ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_left_se_point = left_se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0)
            perimeter_left_sw_point = left_sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0)
            perimeter_center_nw_point = center_nw_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_center_ne_point = center_ne_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_center_se_point = center_se_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0)
            perimeter_center_sw_point = center_sw_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0)
            perimeter_right_nw_point = right_nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_right_ne_point = right_ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_right_se_point = right_se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0)
            perimeter_right_sw_point = right_sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0)

            west_left_perimeter_polygon = OpenStudio::Point3dVector.new
            west_left_perimeter_polygon << left_sw_point
            west_left_perimeter_polygon << left_nw_point
            west_left_perimeter_polygon << perimeter_left_nw_point
            west_left_perimeter_polygon << perimeter_left_sw_point
            west_left_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(west_left_perimeter_polygon, floor_to_floor_height, model)
            west_left_perimeter_space = west_left_perimeter_space.get
            m[0, 3] = left_sw_point.x
            m[1, 3] = left_sw_point.y
            m[2, 3] = left_sw_point.z
            west_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_left_perimeter_space.setBuildingStory(story)
            west_left_perimeter_space.setName("Story #{floor + 1} West Left Perimeter Space")


            north_left_perimeter_polygon = OpenStudio::Point3dVector.new
            north_left_perimeter_polygon << left_nw_point
            north_left_perimeter_polygon << left_ne_point
            north_left_perimeter_polygon << perimeter_left_ne_point
            north_left_perimeter_polygon << perimeter_left_nw_point
            north_left_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(north_left_perimeter_polygon, floor_to_floor_height, model)
            north_left_perimeter_space = north_left_perimeter_space.get
            m[0, 3] = perimeter_left_nw_point.x
            m[1, 3] = perimeter_left_nw_point.y
            m[2, 3] = perimeter_left_nw_point.z
            north_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            north_left_perimeter_space.setBuildingStory(story)
            north_left_perimeter_space.setName("Story #{floor + 1} North Left Perimeter Space")


            east_upper_left_perimeter_polygon = OpenStudio::Point3dVector.new
            east_upper_left_perimeter_polygon << left_ne_point
            east_upper_left_perimeter_polygon << center_nw_point
            east_upper_left_perimeter_polygon << perimeter_center_nw_point
            east_upper_left_perimeter_polygon << perimeter_left_ne_point
            east_upper_left_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(east_upper_left_perimeter_polygon, floor_to_floor_height, model)
            east_upper_left_perimeter_space = east_upper_left_perimeter_space.get
            m[0, 3] = perimeter_center_nw_point.x
            m[1, 3] = perimeter_center_nw_point.y
            m[2, 3] = perimeter_center_nw_point.z
            east_upper_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_upper_left_perimeter_space.setBuildingStory(story)
            east_upper_left_perimeter_space.setName("Story #{floor + 1} East Upper Left Perimeter Space")


            north_center_perimeter_polygon = OpenStudio::Point3dVector.new
            north_center_perimeter_polygon << center_nw_point
            north_center_perimeter_polygon << center_ne_point
            north_center_perimeter_polygon << perimeter_center_ne_point
            north_center_perimeter_polygon << perimeter_center_nw_point
            north_center_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(north_center_perimeter_polygon, floor_to_floor_height, model)
            north_center_perimeter_space = north_center_perimeter_space.get
            m[0, 3] = perimeter_center_nw_point.x
            m[1, 3] = perimeter_center_nw_point.y
            m[2, 3] = perimeter_center_nw_point.z
            north_center_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            north_center_perimeter_space.setBuildingStory(story)
            north_center_perimeter_space.setName("Story #{floor + 1} North Center Perimeter Space")


            west_upper_right_perimeter_polygon = OpenStudio::Point3dVector.new
            west_upper_right_perimeter_polygon << center_ne_point
            west_upper_right_perimeter_polygon << right_nw_point
            west_upper_right_perimeter_polygon << perimeter_right_nw_point
            west_upper_right_perimeter_polygon << perimeter_center_ne_point
            west_upper_right_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(west_upper_right_perimeter_polygon, floor_to_floor_height, model)
            west_upper_right_perimeter_space = west_upper_right_perimeter_space.get
            m[0, 3] = center_ne_point.x
            m[1, 3] = center_ne_point.y
            m[2, 3] = center_ne_point.z
            west_upper_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_upper_right_perimeter_space.setBuildingStory(story)
            west_upper_right_perimeter_space.setName("Story #{floor + 1} West Upper Right Perimeter Space")


            north_right_perimeter_polygon = OpenStudio::Point3dVector.new
            north_right_perimeter_polygon << right_nw_point
            north_right_perimeter_polygon << right_ne_point
            north_right_perimeter_polygon << perimeter_right_ne_point
            north_right_perimeter_polygon << perimeter_right_nw_point
            north_right_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(north_right_perimeter_polygon, floor_to_floor_height, model)
            north_right_perimeter_space = north_right_perimeter_space.get
            m[0, 3] = perimeter_right_nw_point.x
            m[1, 3] = perimeter_right_nw_point.y
            m[2, 3] = perimeter_right_nw_point.z
            north_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            north_right_perimeter_space.setBuildingStory(story)
            north_right_perimeter_space.setName("Story #{floor + 1} North Right Perimeter Space")


            east_right_perimeter_polygon = OpenStudio::Point3dVector.new
            east_right_perimeter_polygon << right_ne_point
            east_right_perimeter_polygon << right_se_point
            east_right_perimeter_polygon << perimeter_right_se_point
            east_right_perimeter_polygon << perimeter_right_ne_point
            east_right_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(east_right_perimeter_polygon, floor_to_floor_height, model)
            east_right_perimeter_space = east_right_perimeter_space.get
            m[0, 3] = perimeter_right_se_point.x
            m[1, 3] = perimeter_right_se_point.y
            m[2, 3] = perimeter_right_se_point.z
            east_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_right_perimeter_space.setBuildingStory(story)
            east_right_perimeter_space.setName("Story #{floor + 1} East Right Perimeter Space")


            south_right_perimeter_polygon = OpenStudio::Point3dVector.new
            south_right_perimeter_polygon << right_se_point
            south_right_perimeter_polygon << right_sw_point
            south_right_perimeter_polygon << perimeter_right_sw_point
            south_right_perimeter_polygon << perimeter_right_se_point
            south_right_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(south_right_perimeter_polygon, floor_to_floor_height, model)
            south_right_perimeter_space = south_right_perimeter_space.get
            m[0, 3] = right_sw_point.x
            m[1, 3] = right_sw_point.y
            m[2, 3] = right_sw_point.z
            south_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            south_right_perimeter_space.setBuildingStory(story)
            south_right_perimeter_space.setName("Story #{floor + 1} South Right Perimeter Space")


            west_lower_right_perimeter_polygon = OpenStudio::Point3dVector.new
            west_lower_right_perimeter_polygon << right_sw_point
            west_lower_right_perimeter_polygon << center_se_point
            west_lower_right_perimeter_polygon << perimeter_center_se_point
            west_lower_right_perimeter_polygon << perimeter_right_sw_point
            west_lower_right_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(west_lower_right_perimeter_polygon, floor_to_floor_height, model)
            west_lower_right_perimeter_space = west_lower_right_perimeter_space.get
            m[0, 3] = right_sw_point.x
            m[1, 3] = right_sw_point.y
            m[2, 3] = right_sw_point.z
            west_lower_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_lower_right_perimeter_space.setBuildingStory(story)
            west_lower_right_perimeter_space.setName("Story #{floor + 1} West Lower Right Perimeter Space")


            south_center_perimeter_polygon = OpenStudio::Point3dVector.new
            south_center_perimeter_polygon << center_se_point
            south_center_perimeter_polygon << center_sw_point
            south_center_perimeter_polygon << perimeter_center_sw_point
            south_center_perimeter_polygon << perimeter_center_se_point
            south_center_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(south_center_perimeter_polygon, floor_to_floor_height, model)
            south_center_perimeter_space = south_center_perimeter_space.get
            m[0, 3] = center_sw_point.x
            m[1, 3] = center_sw_point.y
            m[2, 3] = center_sw_point.z
            south_center_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            south_center_perimeter_space.setBuildingStory(story)
            south_center_perimeter_space.setName("Story #{floor + 1} South Center Perimeter Space")


            east_lower_left_perimeter_polygon = OpenStudio::Point3dVector.new
            east_lower_left_perimeter_polygon << center_sw_point
            east_lower_left_perimeter_polygon << left_se_point
            east_lower_left_perimeter_polygon << perimeter_left_se_point
            east_lower_left_perimeter_polygon << perimeter_center_sw_point
            east_lower_left_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(east_lower_left_perimeter_polygon, floor_to_floor_height, model)
            east_lower_left_perimeter_space = east_lower_left_perimeter_space.get
            m[0, 3] = perimeter_left_se_point.x
            m[1, 3] = perimeter_left_se_point.y
            m[2, 3] = perimeter_left_se_point.z
            east_lower_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_lower_left_perimeter_space.setBuildingStory(story)
            east_lower_left_perimeter_space.setName("Story #{floor + 1} East Lower Left Perimeter Space")


            south_left_perimeter_polygon = OpenStudio::Point3dVector.new
            south_left_perimeter_polygon << left_se_point
            south_left_perimeter_polygon << left_sw_point
            south_left_perimeter_polygon << perimeter_left_sw_point
            south_left_perimeter_polygon << perimeter_left_se_point
            south_left_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(south_left_perimeter_polygon, floor_to_floor_height, model)
            south_left_perimeter_space = south_left_perimeter_space.get
            m[0, 3] = left_sw_point.x
            m[1, 3] = left_sw_point.y
            m[2, 3] = left_sw_point.z
            south_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            south_left_perimeter_space.setBuildingStory(story)
            south_left_perimeter_space.setName("Story #{floor + 1} South Left Perimeter Space")


            west_core_polygon = OpenStudio::Point3dVector.new
            west_core_polygon << perimeter_left_sw_point
            west_core_polygon << perimeter_left_nw_point
            west_core_polygon << perimeter_left_ne_point
            west_core_polygon << perimeter_center_nw_point
            west_core_polygon << perimeter_center_sw_point
            west_core_polygon << perimeter_left_se_point
            west_core_space = OpenStudio::Model::Space::fromFloorPrint(west_core_polygon, floor_to_floor_height, model)
            west_core_space = west_core_space.get
            m[0, 3] = perimeter_left_sw_point.x
            m[1, 3] = perimeter_left_sw_point.y
            m[2, 3] = perimeter_left_sw_point.z
            west_core_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_core_space.setBuildingStory(story)
            west_core_space.setName("Story #{floor + 1} West Core Space")


            center_core_polygon = OpenStudio::Point3dVector.new
            center_core_polygon << perimeter_center_sw_point
            center_core_polygon << perimeter_center_nw_point
            center_core_polygon << perimeter_center_ne_point
            center_core_polygon << perimeter_center_se_point
            center_core_space = OpenStudio::Model::Space::fromFloorPrint(center_core_polygon, floor_to_floor_height, model)
            center_core_space = center_core_space.get
            m[0, 3] = perimeter_center_sw_point.x
            m[1, 3] = perimeter_center_sw_point.y
            m[2, 3] = perimeter_center_sw_point.z
            center_core_space.changeTransformation(OpenStudio::Transformation.new(m))
            center_core_space.setBuildingStory(story)
            center_core_space.setName("Story #{floor + 1} Center Core Space")


            east_core_polygon = OpenStudio::Point3dVector.new
            east_core_polygon << perimeter_right_sw_point
            east_core_polygon << perimeter_center_se_point
            east_core_polygon << perimeter_center_ne_point
            east_core_polygon << perimeter_right_nw_point
            east_core_polygon << perimeter_right_ne_point
            east_core_polygon << perimeter_right_se_point
            east_core_space = OpenStudio::Model::Space::fromFloorPrint(east_core_polygon, floor_to_floor_height, model)
            east_core_space = east_core_space.get
            m[0, 3] = perimeter_right_sw_point.x
            m[1, 3] = perimeter_right_sw_point.y
            m[2, 3] = perimeter_right_sw_point.z
            east_core_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_core_space.setBuildingStory(story)
            east_core_space.setName("Story #{floor + 1} East Core Space")


            # Minimal zones
          else
            west_polygon = OpenStudio::Point3dVector.new
            west_polygon << left_sw_point
            west_polygon << left_nw_point
            west_polygon << left_ne_point
            west_polygon << center_nw_point
            west_polygon << center_sw_point
            west_polygon << left_se_point
            west_space = OpenStudio::Model::Space::fromFloorPrint(west_polygon, floor_to_floor_height, model)
            west_space = west_space.get
            m[0, 3] = left_sw_point.x
            m[1, 3] = left_sw_point.y
            m[2, 3] = left_sw_point.z
            west_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_space.setBuildingStory(story)
            west_space.setName("Story #{floor + 1} West Space")


            center_polygon = OpenStudio::Point3dVector.new
            center_polygon << center_sw_point
            center_polygon << center_nw_point
            center_polygon << center_ne_point
            center_polygon << center_se_point
            center_space = OpenStudio::Model::Space::fromFloorPrint(center_polygon, floor_to_floor_height, model)
            center_space = center_space.get
            m[0, 3] = center_sw_point.x
            m[1, 3] = center_sw_point.y
            m[2, 3] = center_sw_point.z
            center_space.changeTransformation(OpenStudio::Transformation.new(m))
            center_space.setBuildingStory(story)
            center_space.setName("Story #{floor + 1} Center Space")


            east_polygon = OpenStudio::Point3dVector.new
            east_polygon << right_sw_point
            east_polygon << center_se_point
            east_polygon << center_ne_point
            east_polygon << right_nw_point
            east_polygon << right_ne_point
            east_polygon << right_se_point
            east_space = OpenStudio::Model::Space::fromFloorPrint(east_polygon, floor_to_floor_height, model)
            east_space = east_space.get
            m[0, 3] = right_sw_point.x
            m[1, 3] = right_sw_point.y
            m[2, 3] = right_sw_point.z
            east_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_space.setBuildingStory(story)
            east_space.setName("Story #{floor + 1} East Space")


          end

          #Set vertical story position
          story.setNominalZCoordinate(z)

        end #End of floor loop
        BTAP::Geometry::match_surfaces(model)
        return model
      end

      def self.create_shape_l(
          model,
              length = 40.0,
              width = 40.0,
              lower_end_width = 20.0,
              upper_end_length = 20.0,
              num_floors = 3,
              floor_to_floor_height = 3.8,
              plenum_height = 1.0,
              perimeter_zone_depth = 4.57
      )

        if length <= 1e-4
          raise("Length must be greater than 0.")
          return false
        end

        if width <= 1e-4
          raise("Width must be greater than 0.")
          return false
        end

        if lower_end_width <= 1e-4 or lower_end_width >= (width - 1e-4)
          raise("Lower end width must be greater than 0 and less than #{width}m.")
          return false
        end

        if upper_end_length <= 1e-4 or upper_end_length >= (length - 1e-4)
          raise("Upper end length must be greater than 0 and less than #{length}m.")
          return false
        end

        if num_floors <= 1e-4
          raise("Number of floors must be greater than 0.")
          return false
        end

        if floor_to_floor_height <= 1e-4
          raise("Floor to floor height must be greater than 0.")
          return false
        end

        if plenum_height < 0
          raise("Plenum height must be greater than or equal to 0.")
          return false
        end

        shortest_side = [lower_end_width, upper_end_length].min
        if perimeter_zone_depth < 0 or 2 * perimeter_zone_depth >= (shortest_side - 1e-4)
          raise("Perimeter zone depth must be greater than or equal to 0 and less than #{shortest_side / 2}m.")
          return false
        end

        # Create progress bar
        #    runner.createProgressBar("Creating Spaces")
        #    num_total = perimeter_zone_depth>0 ? num_floors*8 : num_floors*2
        #    num_complete = 0

        # Loop through the number of floors
        for floor in (0..num_floors - 1)

          z = floor_to_floor_height * floor

          #Create a new story within the building
          story = OpenStudio::Model::BuildingStory.new(model)
          story.setNominalFloortoFloorHeight(floor_to_floor_height)
          story.setName("Story #{floor + 1}")


          nw_point = OpenStudio::Point3d.new(0, width, z)
          upper_ne_point = OpenStudio::Point3d.new(upper_end_length, width, z)
          upper_sw_point = OpenStudio::Point3d.new(upper_end_length, lower_end_width, z)
          lower_ne_point = OpenStudio::Point3d.new(length, lower_end_width, z)
          se_point = OpenStudio::Point3d.new(length, 0, z)
          sw_point = OpenStudio::Point3d.new(0, 0, z)

          # Identity matrix for setting space origins
          m = OpenStudio::Matrix.new(4, 4, 0)
          m[0, 0] = 1
          m[1, 1] = 1
          m[2, 2] = 1
          m[3, 3] = 1

          # Define polygons for a L-shape building with perimeter core zoning
          if perimeter_zone_depth > 0
            perimeter_nw_point = nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_upper_ne_point = upper_ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_upper_sw_point = upper_sw_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_lower_ne_point = lower_ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_se_point = se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0)
            perimeter_lower_sw_point = sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0)

            west_perimeter_polygon = OpenStudio::Point3dVector.new
            west_perimeter_polygon << sw_point
            west_perimeter_polygon << nw_point
            west_perimeter_polygon << perimeter_nw_point
            west_perimeter_polygon << perimeter_lower_sw_point
            west_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(west_perimeter_polygon, floor_to_floor_height, model)
            west_perimeter_space = west_perimeter_space.get
            m[0, 3] = sw_point.x
            m[1, 3] = sw_point.y
            m[2, 3] = sw_point.z
            west_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_perimeter_space.setBuildingStory(story)
            west_perimeter_space.setName("Story #{floor + 1} West Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            north_upper_perimeter_polygon = OpenStudio::Point3dVector.new
            north_upper_perimeter_polygon << nw_point
            north_upper_perimeter_polygon << upper_ne_point
            north_upper_perimeter_polygon << perimeter_upper_ne_point
            north_upper_perimeter_polygon << perimeter_nw_point
            north_upper_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(north_upper_perimeter_polygon, floor_to_floor_height, model)
            north_upper_perimeter_space = north_upper_perimeter_space.get
            m[0, 3] = perimeter_nw_point.x
            m[1, 3] = perimeter_nw_point.y
            m[2, 3] = perimeter_nw_point.z
            north_upper_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            north_upper_perimeter_space.setBuildingStory(story)
            north_upper_perimeter_space.setName("Story #{floor + 1} North Upper Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            east_upper_perimeter_polygon = OpenStudio::Point3dVector.new
            east_upper_perimeter_polygon << upper_ne_point
            east_upper_perimeter_polygon << upper_sw_point
            east_upper_perimeter_polygon << perimeter_upper_sw_point
            east_upper_perimeter_polygon << perimeter_upper_ne_point
            east_upper_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(east_upper_perimeter_polygon, floor_to_floor_height, model)
            east_upper_perimeter_space = east_upper_perimeter_space.get
            m[0, 3] = perimeter_upper_sw_point.x
            m[1, 3] = perimeter_upper_sw_point.y
            m[2, 3] = perimeter_upper_sw_point.z
            east_upper_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_upper_perimeter_space.setBuildingStory(story)
            east_upper_perimeter_space.setName("Story #{floor + 1} East Upper Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            north_lower_perimeter_polygon = OpenStudio::Point3dVector.new
            north_lower_perimeter_polygon << upper_sw_point
            north_lower_perimeter_polygon << lower_ne_point
            north_lower_perimeter_polygon << perimeter_lower_ne_point
            north_lower_perimeter_polygon << perimeter_upper_sw_point
            north_lower_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(north_lower_perimeter_polygon, floor_to_floor_height, model)
            north_lower_perimeter_space = north_lower_perimeter_space.get
            m[0, 3] = perimeter_upper_sw_point.x
            m[1, 3] = perimeter_upper_sw_point.y
            m[2, 3] = perimeter_upper_sw_point.z
            north_lower_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            north_lower_perimeter_space.setBuildingStory(story)
            north_lower_perimeter_space.setName("Story #{floor + 1} North Lower Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            east_lower_perimeter_polygon = OpenStudio::Point3dVector.new
            east_lower_perimeter_polygon << lower_ne_point
            east_lower_perimeter_polygon << se_point
            east_lower_perimeter_polygon << perimeter_se_point
            east_lower_perimeter_polygon << perimeter_lower_ne_point
            east_lower_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(east_lower_perimeter_polygon, floor_to_floor_height, model)
            east_lower_perimeter_space = east_lower_perimeter_space.get
            m[0, 3] = perimeter_se_point.x
            m[1, 3] = perimeter_se_point.y
            m[2, 3] = perimeter_se_point.z
            east_lower_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_lower_perimeter_space.setBuildingStory(story)
            east_lower_perimeter_space.setName("Story #{floor + 1} East Lower Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            south_perimeter_polygon = OpenStudio::Point3dVector.new
            south_perimeter_polygon << se_point
            south_perimeter_polygon << sw_point
            south_perimeter_polygon << perimeter_lower_sw_point
            south_perimeter_polygon << perimeter_se_point
            south_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(south_perimeter_polygon, floor_to_floor_height, model)
            south_perimeter_space = south_perimeter_space.get
            m[0, 3] = sw_point.x
            m[1, 3] = sw_point.y
            m[2, 3] = sw_point.z
            south_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            south_perimeter_space.setBuildingStory(story)
            south_perimeter_space.setName("Story #{floor + 1} South Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            west_core_polygon = OpenStudio::Point3dVector.new
            west_core_polygon << perimeter_lower_sw_point
            west_core_polygon << perimeter_nw_point
            west_core_polygon << perimeter_upper_ne_point
            west_core_polygon << perimeter_upper_sw_point
            west_core_space = OpenStudio::Model::Space::fromFloorPrint(west_core_polygon, floor_to_floor_height, model)
            west_core_space = west_core_space.get
            m[0, 3] = perimeter_lower_sw_point.x
            m[1, 3] = perimeter_lower_sw_point.y
            m[2, 3] = perimeter_lower_sw_point.z
            west_core_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_core_space.setBuildingStory(story)
            west_core_space.setName("Story #{floor + 1} West Core Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            east_core_polygon = OpenStudio::Point3dVector.new
            east_core_polygon << perimeter_upper_sw_point
            east_core_polygon << perimeter_lower_ne_point
            east_core_polygon << perimeter_se_point
            east_core_polygon << perimeter_lower_sw_point
            east_core_space = OpenStudio::Model::Space::fromFloorPrint(east_core_polygon, floor_to_floor_height, model)
            east_core_space = east_core_space.get
            m[0, 3] = perimeter_lower_sw_point.x
            m[1, 3] = perimeter_lower_sw_point.y
            m[2, 3] = perimeter_lower_sw_point.z
            east_core_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_core_space.setBuildingStory(story)
            east_core_space.setName("Story #{floor + 1} East Core Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            # Minimal zones
          else
            west_polygon = OpenStudio::Point3dVector.new
            west_polygon << sw_point
            west_polygon << nw_point
            west_polygon << upper_ne_point
            west_polygon << upper_sw_point
            west_space = OpenStudio::Model::Space::fromFloorPrint(west_polygon, floor_to_floor_height, model)
            west_space = west_space.get
            m[0, 3] = sw_point.x
            m[1, 3] = sw_point.y
            m[2, 3] = sw_point.z
            west_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_space.setBuildingStory(story)
            west_space.setName("Story #{floor + 1} West Space")

            num_complete += 1
            runner.updateProgress(100 * num_complete / num_total)

            east_polygon = OpenStudio::Point3dVector.new
            east_polygon << sw_point
            east_polygon << upper_sw_point
            east_polygon << lower_ne_point
            east_polygon << se_point
            east_space = OpenStudio::Model::Space::fromFloorPrint(east_polygon, floor_to_floor_height, model)
            east_space = east_space.get
            m[0, 3] = sw_point.x
            m[1, 3] = sw_point.y
            m[2, 3] = sw_point.z
            east_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_space.setBuildingStory(story)
            east_space.setName("Story #{floor + 1} East Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

          end

          #Set vertical story position
          story.setNominalZCoordinate(z)

        end #End of floor loop
        BTAP::Geometry::match_surfaces(model)
        return model
      end

      def self.create_shape_aspect_ratio(model,
          aspect_ratio = 0.5,
          floor_area = 100.0,
          rotation = 0.0,
          num_floors = 3,
          floor_to_floor_height = 3.8,
          plenum_height = 1,
          perimeter_zone_depth = 4.57
      )
        #determine l and w.
        length = MATH::sqrt(floor_area / aspect_ratio)
        width = MATH::sqrt(floor_area * aspect_ratio)
        BTAP::Geometry::Wizards::create_shape_rectangle(model,
                                                        length,
                                                        width,
                                                        num_floors,
                                                        floor_to_floor_height,
                                                        plenum_height,
                                                        perimeter_zone_depth
        )
        BTAP::Geometry::rotate_model(model, rotation)
      end


      def self.create_shape_rectangle(model,
          length = 100.0,
          width = 100.0,
          above_ground_storys = 3,
          under_ground_storys = 1,
          floor_to_floor_height = 3.8,
          plenum_height = 1,
          perimeter_zone_depth = 4.57,
          initial_height = 0.0
      )


        if length <= 1e-4
          raise("Length must be greater than 0.")
          return false
        end

        if width <= 1e-4
          raise("Width must be greater than 0.")
          return false
        end

        if (above_ground_storys + under_ground_storys) <= 1e-4
          raise("Number of floors must be greater than 0.")
          return false
        end

        if floor_to_floor_height <= 1e-4
          raise("Floor to floor height must be greater than 0.")
          return false
        end

        if plenum_height < 0
          raise("Plenum height must be greater than or equal to 0.")
          return false
        end

        shortest_side = [length, width].min
        if perimeter_zone_depth < 0 or 2 * perimeter_zone_depth >= (shortest_side - 1e-4)
          raise("Perimeter zone depth must be greater than or equal to 0 and less than #{shortest_side / 2}m")
          return false
        end

        building_stories = Array.new
        #Loop through the number of floors
        for floor in ((under_ground_storys * -1)..above_ground_storys - 1)

          z = floor_to_floor_height * floor + initial_height

          #Create a new story within the building
          story = OpenStudio::Model::BuildingStory.new(model)
          story.setNominalFloortoFloorHeight(floor_to_floor_height)
          story.setName("Story #{floor + 1}")
          building_stories << story


          nw_point = OpenStudio::Point3d.new(0, width, z)
          ne_point = OpenStudio::Point3d.new(length, width, z)
          se_point = OpenStudio::Point3d.new(length, 0, z)
          sw_point = OpenStudio::Point3d.new(0, 0, z)

          # Identity matrix for setting space origins
          m = OpenStudio::Matrix.new(4, 4, 0)
          m[0, 0] = 1
          m[1, 1] = 1
          m[2, 2] = 1
          m[3, 3] = 1

          #Define polygons for a rectangular building
          if perimeter_zone_depth > 0
            perimeter_nw_point = nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_ne_point = ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_se_point = se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0)
            perimeter_sw_point = sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0)

            west_polygon = OpenStudio::Point3dVector.new
            west_polygon << sw_point
            west_polygon << nw_point
            west_polygon << perimeter_nw_point
            west_polygon << perimeter_sw_point
            west_space = OpenStudio::Model::Space::fromFloorPrint(west_polygon, floor_to_floor_height, model)
            west_space = west_space.get
            m[0, 3] = sw_point.x
            m[1, 3] = sw_point.y
            m[2, 3] = sw_point.z
            west_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_space.setBuildingStory(story)
            west_space.setName("Story #{floor + 1} West Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            north_polygon = OpenStudio::Point3dVector.new
            north_polygon << nw_point
            north_polygon << ne_point
            north_polygon << perimeter_ne_point
            north_polygon << perimeter_nw_point
            north_space = OpenStudio::Model::Space::fromFloorPrint(north_polygon, floor_to_floor_height, model)
            north_space = north_space.get
            m[0, 3] = perimeter_nw_point.x
            m[1, 3] = perimeter_nw_point.y
            m[2, 3] = perimeter_nw_point.z
            north_space.changeTransformation(OpenStudio::Transformation.new(m))
            north_space.setBuildingStory(story)
            north_space.setName("Story #{floor + 1} North Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            east_polygon = OpenStudio::Point3dVector.new
            east_polygon << ne_point
            east_polygon << se_point
            east_polygon << perimeter_se_point
            east_polygon << perimeter_ne_point
            east_space = OpenStudio::Model::Space::fromFloorPrint(east_polygon, floor_to_floor_height, model)
            east_space = east_space.get
            m[0, 3] = perimeter_se_point.x
            m[1, 3] = perimeter_se_point.y
            m[2, 3] = perimeter_se_point.z
            east_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_space.setBuildingStory(story)
            east_space.setName("Story #{floor + 1} East Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            south_polygon = OpenStudio::Point3dVector.new
            south_polygon << se_point
            south_polygon << sw_point
            south_polygon << perimeter_sw_point
            south_polygon << perimeter_se_point
            south_space = OpenStudio::Model::Space::fromFloorPrint(south_polygon, floor_to_floor_height, model)
            south_space = south_space.get
            m[0, 3] = sw_point.x
            m[1, 3] = sw_point.y
            m[2, 3] = sw_point.z
            south_space.changeTransformation(OpenStudio::Transformation.new(m))
            south_space.setBuildingStory(story)
            south_space.setName("Story #{floor + 1} South Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            core_polygon = OpenStudio::Point3dVector.new
            core_polygon << perimeter_sw_point
            core_polygon << perimeter_nw_point
            core_polygon << perimeter_ne_point
            core_polygon << perimeter_se_point
            core_space = OpenStudio::Model::Space::fromFloorPrint(core_polygon, floor_to_floor_height, model)
            core_space = core_space.get
            m[0, 3] = perimeter_sw_point.x
            m[1, 3] = perimeter_sw_point.y
            m[2, 3] = perimeter_sw_point.z
            core_space.changeTransformation(OpenStudio::Transformation.new(m))
            core_space.setBuildingStory(story)
            core_space.setName("Story #{floor + 1} Core Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            # Minimal zones
          else
            core_polygon = OpenStudio::Point3dVector.new
            core_polygon << sw_point
            core_polygon << nw_point
            core_polygon << ne_point
            core_polygon << se_point
            core_space = OpenStudio::Model::Space::fromFloorPrint(core_polygon, floor_to_floor_height, model)
            core_space = core_space.get
            m[0, 3] = sw_point.x
            m[1, 3] = sw_point.y
            m[2, 3] = sw_point.z
            core_space.changeTransformation(OpenStudio::Transformation.new(m))
            core_space.setBuildingStory(story)
            core_space.setName("Story #{floor + 1} Core Space")
            #
            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

          end

          #Set vertical story position
          story.setNominalZCoordinate(z)

          #Ensure that underground stories (when z<0 have Ground set as Boundary conditions). Apply the Ground BC to all surfaces, the top ceiling will be 
          # corrected below when the surface matching algorithm is called. 
          BTAP::Geometry::Surfaces::set_surfaces_boundary_condition(model, BTAP::Geometry::Surfaces::get_surfaces_from_building_stories(model, story), "Ground") if z < 0
        end #End of floor loop

        #    runner.destroyProgressBar
        BTAP::Geometry::match_surfaces(model)
        return building_stories
      end

      def self.create_shape_t(model,
          length = 40.0,
          width = 40.0,
          upper_end_width = 20.0,
          lower_end_length = 20.0,
          left_end_offset = 10.0,
          num_floors = 3,
          floor_to_floor_height = 3.8,
          plenum_height = 1.0,
          perimeter_zone_depth = 4.57
      )

        if length <= 1e-4
          raise("Length must be greater than 0.")
          return false
        end

        if width <= 1e-4
          raise("Width must be greater than 0.")
          return false
        end

        if upper_end_width <= 1e-4 or upper_end_width >= (width - 1e-4)
          raise("Upper end width must be greater than 0 and less than #{width}m.")
          return false
        end

        if lower_end_length <= 1e-4 or lower_end_length >= (length - 1e-4)
          raise("Lower end length must be greater than 0 and less than #{length}m.")
          return false
        end

        if left_end_offset <= 1e-4 or left_end_offset >= (length - lower_end_length - 1e-4)
          raise("Left end offset must be greater than 0 and less than #{length - lower_end_length}m.")
          return false
        end

        if num_floors <= 1e-4
          raise("Number of floors must be greater than 0.")
          return false
        end

        if floor_to_floor_height <= 1e-4
          raise("Floor to floor height must be greater than 0.")
          return false
        end

        if plenum_height < 0
          raise("Plenum height must be greater than or equal to 0.")
          return false
        end

        shortest_side = [length, width, upper_end_width, lower_end_length].min
        if perimeter_zone_depth < 0 or 2 * perimeter_zone_depth >= (shortest_side - 1e-4)
          raise("Perimeter zone depth must be greater than or equal to 0 and less than #{shortest_side / 2}m.")
          return false
        end

        # Create progress bar
        #    runner.createProgressBar("Creating Spaces")
        #    num_total = perimeter_zone_depth>0 ? num_floors*10 : num_floors*2
        #    num_complete = 0

        # Loop through the number of floors
        for floor in (0..num_floors - 1)

          z = floor_to_floor_height * floor

          #Create a new story within the building
          story = OpenStudio::Model::BuildingStory.new(model)
          story.setNominalFloortoFloorHeight(floor_to_floor_height)
          story.setName("Story #{floor + 1}")


          lower_ne_point = OpenStudio::Point3d.new(left_end_offset, width - upper_end_width, z)
          upper_sw_point = OpenStudio::Point3d.new(0, width - upper_end_width, z)
          upper_nw_point = OpenStudio::Point3d.new(0, width, z)
          upper_ne_point = OpenStudio::Point3d.new(length, width, z)
          upper_se_point = OpenStudio::Point3d.new(length, width - upper_end_width, z)
          lower_nw_point = OpenStudio::Point3d.new(left_end_offset + lower_end_length, width - upper_end_width, z)
          lower_se_point = OpenStudio::Point3d.new(left_end_offset + lower_end_length, 0, z)
          lower_sw_point = OpenStudio::Point3d.new(left_end_offset, 0, z)

          # Identity matrix for setting space origins
          m = OpenStudio::Matrix.new(4, 4, 0)
          m[0, 0] = 1
          m[1, 1] = 1
          m[2, 2] = 1
          m[3, 3] = 1

          # Define polygons for a L-shape building with perimeter core zoning
          if perimeter_zone_depth > 0
            perimeter_lower_ne_point = lower_ne_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0)
            perimeter_upper_sw_point = upper_sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0)
            perimeter_upper_nw_point = upper_nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_upper_ne_point = upper_ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_upper_se_point = upper_se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0)
            perimeter_lower_nw_point = lower_nw_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0)
            perimeter_lower_se_point = lower_se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0)
            perimeter_lower_sw_point = lower_sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0)

            west_lower_perimeter_polygon = OpenStudio::Point3dVector.new
            west_lower_perimeter_polygon << lower_sw_point
            west_lower_perimeter_polygon << lower_ne_point
            west_lower_perimeter_polygon << perimeter_lower_ne_point
            west_lower_perimeter_polygon << perimeter_lower_sw_point
            west_lower_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(west_lower_perimeter_polygon, floor_to_floor_height, model)
            west_lower_perimeter_space = west_lower_perimeter_space.get
            m[0, 3] = lower_sw_point.x
            m[1, 3] = lower_sw_point.y
            m[2, 3] = lower_sw_point.z
            west_lower_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_lower_perimeter_space.setBuildingStory(story)
            west_lower_perimeter_space.setName("Story #{floor + 1} West Lower Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            south_upper_left_perimeter_polygon = OpenStudio::Point3dVector.new
            south_upper_left_perimeter_polygon << lower_ne_point
            south_upper_left_perimeter_polygon << upper_sw_point
            south_upper_left_perimeter_polygon << perimeter_upper_sw_point
            south_upper_left_perimeter_polygon << perimeter_lower_ne_point
            south_upper_left_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(south_upper_left_perimeter_polygon, floor_to_floor_height, model)
            south_upper_left_perimeter_space = south_upper_left_perimeter_space.get
            m[0, 3] = upper_sw_point.x
            m[1, 3] = upper_sw_point.y
            m[2, 3] = upper_sw_point.z
            south_upper_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            south_upper_left_perimeter_space.setBuildingStory(story)
            south_upper_left_perimeter_space.setName("Story #{floor + 1} South Upper Left Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            west_upper_perimeter_polygon = OpenStudio::Point3dVector.new
            west_upper_perimeter_polygon << upper_sw_point
            west_upper_perimeter_polygon << upper_nw_point
            west_upper_perimeter_polygon << perimeter_upper_nw_point
            west_upper_perimeter_polygon << perimeter_upper_sw_point
            west_upper_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(west_upper_perimeter_polygon, floor_to_floor_height, model)
            west_upper_perimeter_space = west_upper_perimeter_space.get
            m[0, 3] = upper_sw_point.x
            m[1, 3] = upper_sw_point.y
            m[2, 3] = upper_sw_point.z
            west_upper_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_upper_perimeter_space.setBuildingStory(story)
            west_upper_perimeter_space.setName("Story #{floor + 1} West Upper Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            north_perimeter_polygon = OpenStudio::Point3dVector.new
            north_perimeter_polygon << upper_nw_point
            north_perimeter_polygon << upper_ne_point
            north_perimeter_polygon << perimeter_upper_ne_point
            north_perimeter_polygon << perimeter_upper_nw_point
            north_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(north_perimeter_polygon, floor_to_floor_height, model)
            north_perimeter_space = north_perimeter_space.get
            m[0, 3] = perimeter_upper_nw_point.x
            m[1, 3] = perimeter_upper_nw_point.y
            m[2, 3] = perimeter_upper_nw_point.z
            north_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            north_perimeter_space.setBuildingStory(story)
            north_perimeter_space.setName("Story #{floor + 1} North Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            east_upper_perimeter_polygon = OpenStudio::Point3dVector.new
            east_upper_perimeter_polygon << upper_ne_point
            east_upper_perimeter_polygon << upper_se_point
            east_upper_perimeter_polygon << perimeter_upper_se_point
            east_upper_perimeter_polygon << perimeter_upper_ne_point
            east_upper_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(east_upper_perimeter_polygon, floor_to_floor_height, model)
            east_upper_perimeter_space = east_upper_perimeter_space.get
            m[0, 3] = perimeter_upper_se_point.x
            m[1, 3] = perimeter_upper_se_point.y
            m[2, 3] = perimeter_upper_se_point.z
            east_upper_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_upper_perimeter_space.setBuildingStory(story)
            east_upper_perimeter_space.setName("Story #{floor + 1} East Upper Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            south_upper_right_perimeter_polygon = OpenStudio::Point3dVector.new
            south_upper_right_perimeter_polygon << upper_se_point
            south_upper_right_perimeter_polygon << lower_nw_point
            south_upper_right_perimeter_polygon << perimeter_lower_nw_point
            south_upper_right_perimeter_polygon << perimeter_upper_se_point
            south_upper_right_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(south_upper_right_perimeter_polygon, floor_to_floor_height, model)
            south_upper_right_perimeter_space = south_upper_right_perimeter_space.get
            m[0, 3] = lower_nw_point.x
            m[1, 3] = lower_nw_point.y
            m[2, 3] = lower_nw_point.z
            south_upper_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            south_upper_right_perimeter_space.setBuildingStory(story)
            south_upper_right_perimeter_space.setName("Story #{floor + 1} South Upper Left Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            east_lower_perimeter_polygon = OpenStudio::Point3dVector.new
            east_lower_perimeter_polygon << lower_nw_point
            east_lower_perimeter_polygon << lower_se_point
            east_lower_perimeter_polygon << perimeter_lower_se_point
            east_lower_perimeter_polygon << perimeter_lower_nw_point
            east_lower_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(east_lower_perimeter_polygon, floor_to_floor_height, model)
            east_lower_perimeter_space = east_lower_perimeter_space.get
            m[0, 3] = perimeter_lower_se_point.x
            m[1, 3] = perimeter_lower_se_point.y
            m[2, 3] = perimeter_lower_se_point.z
            east_lower_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_lower_perimeter_space.setBuildingStory(story)
            east_lower_perimeter_space.setName("Story #{floor + 1} East Lower Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            south_lower_perimeter_polygon = OpenStudio::Point3dVector.new
            south_lower_perimeter_polygon << lower_se_point
            south_lower_perimeter_polygon << lower_sw_point
            south_lower_perimeter_polygon << perimeter_lower_sw_point
            south_lower_perimeter_polygon << perimeter_lower_se_point
            south_lower_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(south_lower_perimeter_polygon, floor_to_floor_height, model)
            south_lower_perimeter_space = south_lower_perimeter_space.get
            m[0, 3] = lower_sw_point.x
            m[1, 3] = lower_sw_point.y
            m[2, 3] = lower_sw_point.z
            south_lower_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            south_lower_perimeter_space.setBuildingStory(story)
            south_lower_perimeter_space.setName("Story #{floor + 1} South Lower Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            north_core_polygon = OpenStudio::Point3dVector.new
            north_core_polygon << perimeter_upper_sw_point
            north_core_polygon << perimeter_upper_nw_point
            north_core_polygon << perimeter_upper_ne_point
            north_core_polygon << perimeter_upper_se_point
            north_core_polygon << perimeter_lower_nw_point
            north_core_polygon << perimeter_lower_ne_point
            north_core_space = OpenStudio::Model::Space::fromFloorPrint(north_core_polygon, floor_to_floor_height, model)
            north_core_space = north_core_space.get
            m[0, 3] = perimeter_upper_sw_point.x
            m[1, 3] = perimeter_upper_sw_point.y
            m[2, 3] = perimeter_upper_sw_point.z
            north_core_space.changeTransformation(OpenStudio::Transformation.new(m))
            north_core_space.setBuildingStory(story)
            north_core_space.setName("Story #{floor + 1} North Core Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            south_core_polygon = OpenStudio::Point3dVector.new
            south_core_polygon << perimeter_lower_sw_point
            south_core_polygon << perimeter_lower_ne_point
            south_core_polygon << perimeter_lower_nw_point
            south_core_polygon << perimeter_lower_se_point
            south_core_space = OpenStudio::Model::Space::fromFloorPrint(south_core_polygon, floor_to_floor_height, model)
            south_core_space = south_core_space.get
            m[0, 3] = perimeter_lower_sw_point.x
            m[1, 3] = perimeter_lower_sw_point.y
            m[2, 3] = perimeter_lower_sw_point.z
            south_core_space.changeTransformation(OpenStudio::Transformation.new(m))
            south_core_space.setBuildingStory(story)
            south_core_space.setName("Story #{floor + 1} South Core Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            # Minimal zones
          else
            north_polygon = OpenStudio::Point3dVector.new
            north_polygon << upper_sw_point
            north_polygon << upper_nw_point
            north_polygon << upper_ne_point
            north_polygon << upper_se_point
            north_polygon << lower_nw_point
            north_polygon << lower_ne_point
            north_space = OpenStudio::Model::Space::fromFloorPrint(north_polygon, floor_to_floor_height, model)
            north_space = north_space.get
            m[0, 3] = upper_sw_point.x
            m[1, 3] = upper_sw_point.y
            m[2, 3] = upper_sw_point.z
            north_space.changeTransformation(OpenStudio::Transformation.new(m))
            north_space.setBuildingStory(story)
            north_space.setName("Story #{floor + 1} North Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            south_polygon = OpenStudio::Point3dVector.new
            south_polygon << lower_sw_point
            south_polygon << lower_ne_point
            south_polygon << lower_nw_point
            south_polygon << lower_se_point
            south_space = OpenStudio::Model::Space::fromFloorPrint(south_polygon, floor_to_floor_height, model)
            south_space = south_space.get
            m[0, 3] = lower_sw_point.x
            m[1, 3] = lower_sw_point.y
            m[2, 3] = lower_sw_point.z
            south_space.changeTransformation(OpenStudio::Transformation.new(m))
            south_space.setBuildingStory(story)
            south_space.setName("Story #{floor + 1} South Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

          end

          #Set vertical story position
          story.setNominalZCoordinate(z)

        end #End of floor loop

        BTAP::Geometry::match_surfaces(model)
        return model
      end

      def self.create_shape_u(model,
          length = 40.0,
          left_width = 40.0,
          right_width = 40.0,
          left_end_length = 15.0,
          right_end_length = 15.0,
          left_end_offset = 25.0,
          num_floors = 3.0,
          floor_to_floor_height = 3.8,
          plenum_height = 1.0,
          perimeter_zone_depth = 4.57
      )

        if length <= 1e-4
          raise("Length must be greater than 0.")
          return false
        end

        if left_width <= 1e-4
          raise("Left width must be greater than 0.")
          return false
        end

        if left_end_length <= 1e-4 or left_end_length >= (length - 1e-4)
          raise("Left end length must be greater than 0 and less than #{length}m.")
          return false
        end

        if right_end_length <= 1e-4 or right_end_length >= (length - left_end_length - 1e-4)
          raise("Right end length must be greater than 0 and less than #{length - left_end_length}m.")
          return false
        end

        if left_end_offset <= 1e-4 or left_end_offset >= (left_width - 1e-4)
          raise("Left end offset must be greater than 0 and less than #{left_width}m.")
          return false
        end

        if right_width <= (left_width - left_end_offset - 1e-4)
          raise("Right width must be greater than #{left_width - left_end_offset}m.")
          return false
        end

        if num_floors <= 1e-4
          raise("Number of floors must be greater than 0.")
          return false
        end

        if floor_to_floor_height <= 1e-4
          raise("Floor to floor height must be greater than 0.")
          return false
        end

        if plenum_height < 0
          raise("Plenum height must be greater than or equal to 0.")
          return false
        end

        shortest_side = [length / 2, left_width, right_width, left_end_length, right_end_length, left_width - left_end_offset].min
        if perimeter_zone_depth < 0 or 2 * perimeter_zone_depth >= (shortest_side - 1e-4)
          raise("Perimeter zone depth must be greater than or equal to 0 and less than #{shortest_side / 2}m.")
          return false
        end

        # Create progress bar
        #    runner.createProgressBar("Creating Spaces")
        #    num_total = perimeter_zone_depth>0 ? num_floors*11 : num_floors*3
        #    num_complete = 0

        # Loop through the number of floors
        for floor in (0..num_floors - 1)

          z = floor_to_floor_height * floor

          #Create a new story within the building
          story = OpenStudio::Model::BuildingStory.new(model)
          story.setNominalFloortoFloorHeight(floor_to_floor_height)
          story.setName("Story #{floor + 1}")


          left_nw_point = OpenStudio::Point3d.new(0, left_width, z)
          left_ne_point = OpenStudio::Point3d.new(left_end_length, left_width, z)
          upper_sw_point = OpenStudio::Point3d.new(left_end_length, left_width - left_end_offset, z)
          upper_se_point = OpenStudio::Point3d.new(length - right_end_length, left_width - left_end_offset, z)
          right_nw_point = OpenStudio::Point3d.new(length - right_end_length, right_width, z)
          right_ne_point = OpenStudio::Point3d.new(length, right_width, z)
          lower_se_point = OpenStudio::Point3d.new(length, 0, z)
          lower_sw_point = OpenStudio::Point3d.new(0, 0, z)

          # Identity matrix for setting space origins
          m = OpenStudio::Matrix.new(4, 4, 0)
          m[0, 0] = 1
          m[1, 1] = 1
          m[2, 2] = 1
          m[3, 3] = 1

          # Define polygons for a L-shape building with perimeter core zoning
          if perimeter_zone_depth > 0
            perimeter_left_nw_point = left_nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_left_ne_point = left_ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_upper_sw_point = upper_sw_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_upper_se_point = upper_se_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_right_nw_point = right_nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_right_ne_point = right_ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0)
            perimeter_lower_se_point = lower_se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0)
            perimeter_lower_sw_point = lower_sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0)

            west_left_perimeter_polygon = OpenStudio::Point3dVector.new
            west_left_perimeter_polygon << lower_sw_point
            west_left_perimeter_polygon << left_nw_point
            west_left_perimeter_polygon << perimeter_left_nw_point
            west_left_perimeter_polygon << perimeter_lower_sw_point
            west_left_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(west_left_perimeter_polygon, floor_to_floor_height, model)
            west_left_perimeter_space = west_left_perimeter_space.get
            m[0, 3] = lower_sw_point.x
            m[1, 3] = lower_sw_point.y
            m[2, 3] = lower_sw_point.z
            west_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_left_perimeter_space.setBuildingStory(story)
            west_left_perimeter_space.setName("Story #{floor + 1} West Left Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            north_left_perimeter_polygon = OpenStudio::Point3dVector.new
            north_left_perimeter_polygon << left_nw_point
            north_left_perimeter_polygon << left_ne_point
            north_left_perimeter_polygon << perimeter_left_ne_point
            north_left_perimeter_polygon << perimeter_left_nw_point
            north_left_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(north_left_perimeter_polygon, floor_to_floor_height, model)
            north_left_perimeter_space = north_left_perimeter_space.get
            m[0, 3] = perimeter_left_nw_point.x
            m[1, 3] = perimeter_left_nw_point.y
            m[2, 3] = perimeter_left_nw_point.z
            north_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            north_left_perimeter_space.setBuildingStory(story)
            north_left_perimeter_space.setName("Story #{floor + 1} North Left Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            east_left_perimeter_polygon = OpenStudio::Point3dVector.new
            east_left_perimeter_polygon << left_ne_point
            east_left_perimeter_polygon << upper_sw_point
            east_left_perimeter_polygon << perimeter_upper_sw_point
            east_left_perimeter_polygon << perimeter_left_ne_point
            east_left_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(east_left_perimeter_polygon, floor_to_floor_height, model)
            east_left_perimeter_space = east_left_perimeter_space.get
            m[0, 3] = perimeter_upper_sw_point.x
            m[1, 3] = perimeter_upper_sw_point.y
            m[2, 3] = perimeter_upper_sw_point.z
            east_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_left_perimeter_space.setBuildingStory(story)
            east_left_perimeter_space.setName("Story #{floor + 1} East Left Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            north_lower_perimeter_polygon = OpenStudio::Point3dVector.new
            north_lower_perimeter_polygon << upper_sw_point
            north_lower_perimeter_polygon << upper_se_point
            north_lower_perimeter_polygon << perimeter_upper_se_point
            north_lower_perimeter_polygon << perimeter_upper_sw_point
            north_lower_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(north_lower_perimeter_polygon, floor_to_floor_height, model)
            north_lower_perimeter_space = north_lower_perimeter_space.get
            m[0, 3] = perimeter_upper_sw_point.x
            m[1, 3] = perimeter_upper_sw_point.y
            m[2, 3] = perimeter_upper_sw_point.z
            north_lower_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            north_lower_perimeter_space.setBuildingStory(story)
            north_lower_perimeter_space.setName("Story #{floor + 1} North Lower Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            west_right_perimeter_polygon = OpenStudio::Point3dVector.new
            west_right_perimeter_polygon << upper_se_point
            west_right_perimeter_polygon << right_nw_point
            west_right_perimeter_polygon << perimeter_right_nw_point
            west_right_perimeter_polygon << perimeter_upper_se_point
            west_right_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(west_right_perimeter_polygon, floor_to_floor_height, model)
            west_right_perimeter_space = west_right_perimeter_space.get
            m[0, 3] = upper_se_point.x
            m[1, 3] = upper_se_point.y
            m[2, 3] = upper_se_point.z
            west_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_right_perimeter_space.setBuildingStory(story)
            west_right_perimeter_space.setName("Story #{floor + 1} West Right Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            north_right_perimeter_polygon = OpenStudio::Point3dVector.new
            north_right_perimeter_polygon << right_nw_point
            north_right_perimeter_polygon << right_ne_point
            north_right_perimeter_polygon << perimeter_right_ne_point
            north_right_perimeter_polygon << perimeter_right_nw_point
            north_right_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(north_right_perimeter_polygon, floor_to_floor_height, model)
            north_right_perimeter_space = north_right_perimeter_space.get
            m[0, 3] = perimeter_right_nw_point.x
            m[1, 3] = perimeter_right_nw_point.y
            m[2, 3] = perimeter_right_nw_point.z
            north_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            north_right_perimeter_space.setBuildingStory(story)
            north_right_perimeter_space.setName("Story #{floor + 1} North Right Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            east_right_perimeter_polygon = OpenStudio::Point3dVector.new
            east_right_perimeter_polygon << right_ne_point
            east_right_perimeter_polygon << lower_se_point
            east_right_perimeter_polygon << perimeter_lower_se_point
            east_right_perimeter_polygon << perimeter_right_ne_point
            east_right_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(east_right_perimeter_polygon, floor_to_floor_height, model)
            east_right_perimeter_space = east_right_perimeter_space.get
            m[0, 3] = perimeter_lower_se_point.x
            m[1, 3] = perimeter_lower_se_point.y
            m[2, 3] = perimeter_lower_se_point.z
            east_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_right_perimeter_space.setBuildingStory(story)
            east_right_perimeter_space.setName("Story #{floor + 1} East Right Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            south_lower_perimeter_polygon = OpenStudio::Point3dVector.new
            south_lower_perimeter_polygon << lower_se_point
            south_lower_perimeter_polygon << lower_sw_point
            south_lower_perimeter_polygon << perimeter_lower_sw_point
            south_lower_perimeter_polygon << perimeter_lower_se_point
            south_lower_perimeter_space = OpenStudio::Model::Space::fromFloorPrint(south_lower_perimeter_polygon, floor_to_floor_height, model)
            south_lower_perimeter_space = south_lower_perimeter_space.get
            m[0, 3] = lower_sw_point.x
            m[1, 3] = lower_sw_point.y
            m[2, 3] = lower_sw_point.z
            south_lower_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m))
            south_lower_perimeter_space.setBuildingStory(story)
            south_lower_perimeter_space.setName("Story #{floor + 1} South Lower Perimeter Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            west_core_polygon = OpenStudio::Point3dVector.new
            west_core_polygon << perimeter_lower_sw_point
            west_core_polygon << perimeter_left_nw_point
            west_core_polygon << perimeter_left_ne_point
            west_core_polygon << perimeter_upper_sw_point
            west_core_space = OpenStudio::Model::Space::fromFloorPrint(west_core_polygon, floor_to_floor_height, model)
            west_core_space = west_core_space.get
            m[0, 3] = perimeter_lower_sw_point.x
            m[1, 3] = perimeter_lower_sw_point.y
            m[2, 3] = perimeter_lower_sw_point.z
            west_core_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_core_space.setBuildingStory(story)
            west_core_space.setName("Story #{floor + 1} West Core Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            south_core_polygon = OpenStudio::Point3dVector.new
            south_core_polygon << perimeter_upper_sw_point
            south_core_polygon << perimeter_upper_se_point
            south_core_polygon << perimeter_lower_se_point
            south_core_polygon << perimeter_lower_sw_point
            south_core_space = OpenStudio::Model::Space::fromFloorPrint(south_core_polygon, floor_to_floor_height, model)
            south_core_space = south_core_space.get
            m[0, 3] = perimeter_lower_sw_point.x
            m[1, 3] = perimeter_lower_sw_point.y
            m[2, 3] = perimeter_lower_sw_point.z
            south_core_space.changeTransformation(OpenStudio::Transformation.new(m))
            south_core_space.setBuildingStory(story)
            south_core_space.setName("Story #{floor + 1} South Core Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            east_core_polygon = OpenStudio::Point3dVector.new
            east_core_polygon << perimeter_upper_se_point
            east_core_polygon << perimeter_right_nw_point
            east_core_polygon << perimeter_right_ne_point
            east_core_polygon << perimeter_lower_se_point
            east_core_space = OpenStudio::Model::Space::fromFloorPrint(east_core_polygon, floor_to_floor_height, model)
            east_core_space = east_core_space.get
            m[0, 3] = perimeter_upper_se_point.x
            m[1, 3] = perimeter_upper_se_point.y
            m[2, 3] = perimeter_upper_se_point.z
            east_core_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_core_space.setBuildingStory(story)
            east_core_space.setName("Story #{floor + 1} East Core Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            # Minimal zones
          else
            west_polygon = OpenStudio::Point3dVector.new
            west_polygon << lower_sw_point
            west_polygon << left_nw_point
            west_polygon << left_ne_point
            west_polygon << upper_sw_point
            west_space = OpenStudio::Model::Space::fromFloorPrint(west_polygon, floor_to_floor_height, model)
            west_space = west_space.get
            m[0, 3] = lower_sw_point.x
            m[1, 3] = lower_sw_point.y
            m[2, 3] = lower_sw_point.z
            west_space.changeTransformation(OpenStudio::Transformation.new(m))
            west_space.setBuildingStory(story)
            west_space.setName("Story #{floor + 1} West Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            south_polygon = OpenStudio::Point3dVector.new
            south_polygon << lower_sw_point
            south_polygon << upper_sw_point
            south_polygon << upper_se_point
            south_polygon << lower_se_point
            south_space = OpenStudio::Model::Space::fromFloorPrint(south_polygon, floor_to_floor_height, model)
            south_space = south_space.get
            m[0, 3] = lower_sw_point.x
            m[1, 3] = lower_sw_point.y
            m[2, 3] = lower_sw_point.z
            south_space.changeTransformation(OpenStudio::Transformation.new(m))
            south_space.setBuildingStory(story)
            south_space.setName("Story #{floor + 1} South Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

            east_polygon = OpenStudio::Point3dVector.new
            east_polygon << upper_se_point
            east_polygon << right_nw_point
            east_polygon << right_ne_point
            east_polygon << lower_se_point
            east_space = OpenStudio::Model::Space::fromFloorPrint(east_polygon, floor_to_floor_height, model)
            east_space = east_space.get
            m[0, 3] = upper_se_point.x
            m[1, 3] = upper_se_point.y
            m[2, 3] = upper_se_point.z
            east_space.changeTransformation(OpenStudio::Transformation.new(m))
            east_space.setBuildingStory(story)
            east_space.setName("Story #{floor + 1} East Space")

            #        num_complete += 1
            #        runner.updateProgress(100*num_complete/num_total)

          end

          #Set vertical story position
          story.setNominalZCoordinate(z)

        end #End of floor loop

        #    runner.destroyProgressBar
        BTAP::Geometry::match_surfaces(model)
        return model
      end

      def self.test_geometry()
        courtyard = OpenStudio::Model::Model.new()
        Geometry.create_shape_courtyard(courtyard)
        courtyard.save(OpenStudio::Path.new(BTAP::TESTING_FOLDER + "/courtyard.osm"))

        rectangle = OpenStudio::Model::Model.new()
        Geometry.create_shape_rectangle(rectangle)
        Geometry.scale_model(rectangle, 2, 0.5, 1.0)
        Geometry.rotate_model(rectangle, 45.0)
        File.delete(BTAP::TESTING_FOLDER + "/rectangle.osm")
        rectangle.save(OpenStudio::Path.new(BTAP::TESTING_FOLDER + "/rectangle.osm"))

        l_shape = OpenStudio::Model::Model.new()
        Geometry.create_shape_l(l_shape)
        l_shape.save(OpenStudio::Path.new(BTAP::TESTING_FOLDER + "/l_shape.osm"))

        h_shape = OpenStudio::Model::Model.new()
        Geometry.create_shape_h(h_shape)
        h_shape.save(OpenStudio::Path.new(BTAP::TESTING_FOLDER + "/h_shape.osm"))

        t_shape = OpenStudio::Model::Model.new()
        Geometry.create_shape_t(t_shape)
        t_shape.save(OpenStudio::Path.new(BTAP::TESTING_FOLDER + "/t_shape.osm"))

        u_shape = OpenStudio::Model::Model.new()
        Geometry.create_shape_u(u_shape)
        u_shape.save(OpenStudio::Path.new(BTAP::TESTING_FOLDER + "/u_shape.osm"))
      end
    end

    def self.match_surfaces(model)
      model.getSpaces.sort.each do |space1|
        model.getSpaces.sort.each do |space2|
          space1.matchSurfaces(space2)
        end
      end
      return model
    end

    def self.intersect_surfaces(model)
      model.getSpaces.sort.each do |space1|
        model.getSpaces.sort.each do |space2|
          space1.intersectSurfaces(space2)
        end
      end
      return model
    end

    # This method will scale the model
    # @param model [OpenStudio::Model::Model] the model object.
    # @param x [Float] x scalar multiplier.
    # @param y [Float] y scalar multiplier.
    # @param z [Float] z scalar multiplier.
    # @return [OpenStudio::Model::Model] the model object.
    def self.scale_model(model, x, y, z)
      # Identity matrix for setting space origins
      m = OpenStudio::Matrix.new(4, 4, 0)

      m[0, 0] = 1.0 / x
      m[1, 1] = 1.0 / y
      m[2, 2] = 1.0 / z
      m[3, 3] = 1.0
      t = OpenStudio::Transformation.new(m)
      model.getPlanarSurfaceGroups().each do |planar_surface|
        planar_surface.changeTransformation(t)
      end
      return model
    end

    def self.get_fwdr(model)
      outdoor_surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(model.getSurfaces(), "Outdoors")
      outdoor_walls = BTAP::Geometry::Surfaces::filter_by_surface_types(outdoor_surfaces, "Wall")
      self.get_surface_to_subsurface_ratio(outdoor_walls)
    end

    def self.get_srr(model)
      outdoor_surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(model.getSurfaces(), "Outdoors")
      outdoor_roofs = BTAP::Geometry::Surfaces::filter_by_surface_types(outdoor_surfaces, "RoofCeiling")
      self.get_surface_to_subsurface_ratio(outdoor_roofs)
    end

    def self.get_surface_to_subsurface_ratio(surfaces)
      total_gross_surface_area = 0.0
      total_net_surface_area = 0.0
      surfaces.each do |surface|
        total_gross_surface_area = total_gross_surface_area + surface.grossArea
        total_net_surface_area = total_net_surface_area + surface.netArea
      end
      return 1.0 - (total_net_surface_area / total_gross_surface_area)
    end


    # This method will rotate the model
    # @param model [OpenStudio::Model::Model] the model object.
    # @param degrees [Float] rotation value
    # @return [OpenStudio::Model::Model] the model object.
    def self.rotate_model(model, degrees)
      # Identity matrix for setting space origins
      t = OpenStudio::Transformation::rotation(OpenStudio::Vector3d.new(0, 0, 1), degrees * Math::PI / 180)
      model.getPlanarSurfaceGroups().each {|planar_surface| planar_surface.changeTransformation(t)}
      return model
    end


    def self.rotate_building(model: , degrees: nil)

      # report as not applicable if effective relative rotation is 0
      if degrees == 0 || degrees.nil?
        puts ('The requested rotation was 0 or nil degrees. The model was not rotated.')
        return
      end

      # check the relative_building_rotation for reasonableness
      degrees -= 360.0 * (degrees / 360.0).truncate if (degrees > 360) || (degrees < -360)

      # reporting initial condition of model
      building = model.getBuilding
      # rotate the building
      final_building_angle = building.setNorthAxis(building.northAxis + degrees)
    end




    module BuildingStoreys

      #This method will delete any exisiting stories and then try to assign stories based on 
      # the z-axis origin of the space.
      def self.auto_assign_spaces_to_stories(model)
        #delete existing stories.
        model.getBuildingStorys.sort.each {|buildingstory| buildingstory.remove}
        #create hash of building storeys, index is the Z-axis origin of the space.
        building_story_hash = Hash.new()
        model.getSpaces.sort.each do |space|
          if building_story_hash[space.zOrigin].nil?
            building_story_hash[space.zOrigin] = OpenStudio::Model::BuildingStory.new(model)
            building_story_hash[space.zOrigin].setName(building_story_hash.length.to_s)
          end


          space.setBuildingStory(building_story_hash[space.zOrigin])
        end
      end

      #Get the zones in a storey
      def self.get_zones_from_storey(storey)
        #check to see if the storey has a zone that is part of the zone list. 
        zones = Array.new
        storey.spaces.sort.each do |space|
          if not space.thermalZone.empty? and not zones.include?(space.thermalZone.get)
            zones.push(space.thermalZone.get)
          end
        end
        return zones
      end


      # override run to implement the functionality of your script
      # model is an OpenStudio::Model::Model, runner is a OpenStudio::Ruleset::UserScriptRunner
      def self.auto_assign_stories(model)

        # get all spaces
        spaces = model.getSpaces

        #puts("Assigning Stories to Spaces")

        # make has of spaces and minz values
        sorted_spaces = Hash.new
        spaces.sort.each do |space|
          # loop through space surfaces to find min z value
          z_points = []
          space.surfaces.each do |surface|
            surface.vertices.each do |vertex|
              z_points << vertex.z
            end
          end
          minz = z_points.min + space.zOrigin
          sorted_spaces[space] = minz
        end

        # pre-sort spaces
        sorted_spaces = sorted_spaces.sort {|a, b| a[1] <=> b[1]}


        # this should take the sorted list and make and assign stories
        sorted_spaces.sort.each do |space|
          space_obj = space[0]
          space_minz = space[1]
          if space_obj.buildingStory.empty?

            story = getStoryForNominalZCoordinate(model, space_minz)
            #puts("Setting story of Space " + space_obj.name.get + " to " + story.name.get + ".")
            space_obj.setBuildingStory(story)
          end
        end
      end

      # find the first story with z coordinate, create one if needed
      def self.getStoryForNominalZCoordinate(model, minz)

        model.getBuildingStorys.sort.each do |story|
          z = story.nominalZCoordinate
          if not z.empty?
            if minz == z.get
              return story
            end
          end
        end

        story = OpenStudio::Model::BuildingStory.new(model)
        story.setNominalZCoordinate(minz)
        return story
      end

      def self.getStoryAboveGround(model)
        count = 0
        model.getBuildingStorys.sort.each do |story|
          z = story.nominalZCoordinate
          unless z.empty?
            if z.to_f >= 0
              #puts story.name.get
              count += 1
            end
          end
        end
        return count
      end


      def self.getStoryBelowGround(model)
        count = 0
        model.getBuildingStorys.sort.each do |story|
          z = story.nominalZCoordinate
          unless z.empty?
            if z.to_f < 0
              #puts story.name.get
              count += 1
            end
          end
        end
        return count
      end

    end


    #This module contains helper functions that deal with Space objects.
    module Spaces

      #This method will return the horizontal placement type. (N,S,W,E,C) In the 
      # case of a corner, it will take whatever surface area it faces is the 
      # largest. It will also return the top, bottom or middle conditions.   

      def self.get_space_placement(space)
        horizontal_placement = nil
        vertical_placement = nil
        json_data = nil

        #get all exterior surfaces. 
        surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(space.surfaces,
                                                                          ["Outdoors",
                                                                           "Ground",
                                                                           "GroundFCfactorMethod",
                                                                           "GroundSlabPreprocessorAverage",
                                                                           "GroundSlabPreprocessorCore",
                                                                           "GroundSlabPreprocessorPerimeter",
                                                                           "GroundBasementPreprocessorAverageWall",
                                                                           "GroundBasementPreprocessorAverageFloor",
                                                                           "GroundBasementPreprocessorUpperWall",
                                                                           "GroundBasementPreprocessorLowerWall"])

        #exterior Surfaces
        ext_wall_surfaces = BTAP::Geometry::Surfaces::filter_by_surface_types(surfaces, ["Wall"])
        ext_bottom_surface = BTAP::Geometry::Surfaces::filter_by_surface_types(surfaces, ["Floor"])
        ext_top_surface = BTAP::Geometry::Surfaces::filter_by_surface_types(surfaces, ["RoofCeiling"])

        #Interior Surfaces..if needed....
        internal_surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(space.surfaces, ["Surface"])
        int_wall_surfaces = BTAP::Geometry::Surfaces::filter_by_surface_types(internal_surfaces, ["Wall"])
        int_bottom_surface = BTAP::Geometry::Surfaces::filter_by_surface_types(internal_surfaces, ["Floor"])
        int_top_surface = BTAP::Geometry::Surfaces::filter_by_surface_types(internal_surfaces, ["RoofCeiling"])


        vertical_placement = "NA"
        #determine if space is a top or bottom, both or middle space. 
        if ext_bottom_surface.size > 0 and ext_top_surface.size > 0 and int_bottom_surface.size == 0 and int_top_surface.size == 0
          vertical_placement = "single_story_space"
        elsif int_bottom_surface.size > 0 and ext_top_surface.size > 0 and int_bottom_surface.size > 0
          vertical_placement = "top"
        elsif ext_bottom_surface.size > 0 and ext_top_surface.size == 0
          vertical_placement = "bottom"
        elsif ext_bottom_surface.size == 0 and ext_top_surface.size == 0
          vertical_placement = "middle"
        end


        #determine if what cardinal direction has the majority of external
        #surface area of the space.
        #set this to 'core' by default and change it if it is found to be a space exposed to a cardinal direction.
        horizontal_placement = nil
        #set up summing hashes for each direction.
        json_data = Hash.new
        walls_area_array = Hash.new
        subsurface_area_array = Hash.new
        boundary_conditions = {}
        boundary_conditions[:outdoors] = ["Outdoors"]
        boundary_conditions[:ground] = [
            "Ground",
            "GroundFCfactorMethod",
            "GroundSlabPreprocessorAverage",
            "GroundSlabPreprocessorCore",
            "GroundSlabPreprocessorPerimeter",
            "GroundBasementPreprocessorAverageWall",
            "GroundBasementPreprocessorAverageFloor",
            "GroundBasementPreprocessorUpperWall",
            "GroundBasementPreprocessorLowerWall"]
        #go through all directions.. need to do north twice since that goes around zero degree mark.
        orientations = [
            {:surface_type => 'Wall', :direction => 'north', :azimuth_from => 0.00, :azimuth_to => 45.0, :tilt_from => 0.0, :tilt_to => 180.0},
            {:surface_type => 'Wall', :direction => 'north', :azimuth_from => 315.001, :azimuth_to => 360.0, :tilt_from => 0.0, :tilt_to => 180.0},
            {:surface_type => 'Wall', :direction => 'east', :azimuth_from => 45.001, :azimuth_to => 135.0, :tilt_from => 0.0, :tilt_to => 180.0},
            {:surface_type => 'Wall', :direction => 'south', :azimuth_from => 135.001, :azimuth_to => 225.0, :tilt_from => 0.0, :tilt_to => 180.0},
            {:surface_type => 'Wall', :direction => 'west', :azimuth_from => 225.001, :azimuth_to => 315.0, :tilt_from => 0.0, :tilt_to => 180.0},
            {:surface_type => 'RoofCeiling', :direction => 'top', :azimuth_from => 0.0, :azimuth_to => 360.0, :tilt_from => 0.0, :tilt_to => 180.0},
            {:surface_type => 'Floor', :direction => 'bottom', :azimuth_from => 0.0, :azimuth_to => 360.0, :tilt_from => 0.0, :tilt_to => 180.0}
        ]
        [:outdoors, :ground].each do |bc|
          orientations.each do |orientation|
            walls_area_array[orientation[:direction]] = 0.0
            subsurface_area_array[orientation[:direction]] = 0.0
            json_data[orientation[:direction]] = {} if json_data[orientation[:direction]].nil?
            json_data[orientation[:direction]][bc] = {:surface_area => 0.0,
                                                      :glazed_subsurface_area => 0.0,
                                                      :opaque_subsurface_area => 0.0}

          end
        end


        [:outdoors, :ground].each do |bc|
          orientations.each do |orientation|
            # puts "bc= #{bc}"
            # puts boundary_conditions[bc.to_sym]
            # puts boundary_conditions
            surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(space.surfaces, boundary_conditions[bc])
            selected_surfaces = BTAP::Geometry::Surfaces::filter_by_surface_types(surfaces, [orientation[:surface_type]])
            BTAP::Geometry::Surfaces::filter_by_azimuth_and_tilt(selected_surfaces, orientation[:azimuth_from], orientation[:azimuth_to], orientation[:tilt_from], orientation[:tilt_to]).each do |surface|
              #sum wall area and subsurface area by direction. This is the old way so excluding top and bottom surfaces.
              walls_area_array[orientation[:direction]] += surface.grossArea unless ['RoofCeiling', 'Floor'].include?(orientation[:surface_type])
              subsurface_area_array[orientation[:direction]] += surface.subSurfaces.map {|subsurface| subsurface.grossArea}.inject(0) {|sum, x| sum + x}
              json_data[orientation[:direction]][bc][:surface_area] += surface.grossArea
              glazings = BTAP::Geometry::Surfaces::filter_subsurfaces_by_types(surface.subSurfaces, ["FixedWindow", "OperableWindow", "GlassDoor", "Skylight", "TubularDaylightDiffuser", "TubularDaylightDome"])
              doors = BTAP::Geometry::Surfaces::filter_subsurfaces_by_types(surface.subSurfaces, ["Door", "OverheadDoor"])
              json_data[orientation[:direction]][bc][:glazed_subsurface_area] += glazings.map {|subsurface| subsurface.grossArea}.inject(0) {|sum, x| sum + x}
              json_data[orientation[:direction]][bc][:opaque_subsurface_area] += doors.map {|subsurface| subsurface.grossArea}.inject(0) {|sum, x| sum + x}
            end
          end
        end
        puts JSON.pretty_generate(json_data)

        puts walls_area_array
        #find if no direction
        sum= 0.0
        ['north','east','south','west'].each do |direction|
          [:outdoors,:ground].each do |bc|
            sum += json_data[direction][bc][:surface_area]
          end
        end
        if sum == 0.0
          horizontal_placement = "core"
        else
          #find our which cardinal direction has the most exterior surface and declare it that orientation.
          horizontal_placement = walls_area_array.max_by {|k, v| v}[0] #include ext and ground.
        end

        #save JSON data
        json_data = ({:horizontal_placement => horizontal_placement,
                      :vertical_placement => vertical_placement,
        }).merge(json_data)
        puts JSON.pretty_generate(json_data)

        return json_data
      end


      def self.is_perimeter_space?(model, space)
        exterior_surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(space.surfaces,
                                                                                   ["Outdoors",
                                                                                    "Ground",
                                                                                    "GroundFCfactorMethod",
                                                                                    "GroundSlabPreprocessorAverage",
                                                                                    "GroundSlabPreprocessorCore",
                                                                                    "GroundSlabPreprocessorPerimeter",
                                                                                    "GroundBasementPreprocessorAverageWall",
                                                                                    "GroundBasementPreprocessorAverageFloor",
                                                                                    "GroundBasementPreprocessorUpperWall",
                                                                                    "GroundBasementPreprocessorLowerWall"])

        return BTAP::Geometry::Surfaces::filter_by_surface_types(exterior_surfaces, ["Wall"]).size > 0

      end

      def self.show(model, space)
        if drawing_interface = BTAP::Common::validate_array(model, space, "Space").first.drawing_interface
          if entity = drawing_interface.entity
            entity.visible = true
          end
        end
      end

      def self.hide(model, space)
        if drawing_interface = BTAP::Common::validate_array(model, space, "Space").first.drawing_interface
          if entity = drawing_interface.entity
            entity.visible = false
          end
        end
      end


      # This method will return a Array of surfaces that are contained within the
      # passed spaces. Note: if you wish to avoid to create an array of spaces,
      # simply put the space variable in [] brackets
      # Ex: get_all_surfaces_from_spaces( [space1,space2] )
      # @param spaces_array an array of type [OpenStudio::Model::Space]
      # @return an array of surfaces contained in the passed spaces.
      def self.get_surfaces_from_spaces(model, spaces_array)
        BTAP::Geometry::Surfaces::get_surfaces_from_spaces(spaces_array)
      end

      # This method will return a SpaceArray of surfaces that are contained within the
      # passed floors. Note: if you wish to avoid to create an array of spaces,
      # simply put the space variable in [] brackets
      # Ex: get_all_surfaces_from_spaces( [space1,space2] )
      # @param model
      # @param floors
      # @return [Array<OpenStudio::Model::Space>] an array of spaces
      def self.get_spaces_from_storeys(model, floors)
        floors = BTAP::Common::validate_array(model, floors, "BuildingStory")
        spaces = Array.new()
        floors.each {|floor| spaces.concat(floor.spaces)}
        return spaces
      end

      # This method will filter an array of spaces that have an external wall
      # passed floors. Note: if you wish to avoid to create an array of spaces,
      # simply put the space variable in [] brackets
      # Ex: get_all_surfaces_from_spaces( [space1,space2] )
      # @param spaces_array an array of type [OpenStudio::Model::Space]
      # @return an array of spaces.
      def self.filter_perimeter_spaces(model, spaces_array)
        spaces_array = BTAP::Common::validate_array(model, spaces_array, "Space")
        array = Array.new()
        spaces_array.each do |space|
          if space.is_a_perimeter_space?()
            array.push(space)
          end
        end
        return array
      end

      # This method will filter an array of spaces that have no external wall
      # passed floors. Note: if you wish to avoid to create an array of spaces,
      # simply put the space variable in [] brackets
      # Ex: get_all_surfaces_from_spaces( [space1,space2] )
      # @param spaces_array an array of type [OpenStudio::Model::Space]
      # @return an array of spaces.
      def self.filter_core_spaces(model, spaces_array)
        spaces_array = BTAP::Common::validate_array(model, spaces_array, "Space")
        array = Array.new()
        spaces_array.each do |space|
          unless space.is_a_perimeter_space?()
            array.push(space)
          end
        end
        return array
      end


      def self.filter_spaces_by_space_types(model, spaces_array, spacetype_array)
        spaces_array = BTAP::Common::validate_array(model, spaces_array, "Space")
        spacetype_array = BTAP::Common::validate_array(model, spacetype_array, "SpaceType")
        #validate space array
        returnarray = Array.new()
        spaces_array.each do |space|
          returnarray << spacetype_array.include?(space.spaceType())
        end
        return returnarray
      end

      #to do write test.
      def self.assign_spaces_to_thermal_zone(model, spaces_array, thermal_zone)
        spaces_array = BTAP::Common::validate_array(model, spaces_array, "Space")
        thermal_zone = BTAP::Common::validate_array(model, thermal_zone, "ThermalZone")[0]
        spaces_array.each do |space|
          space.setThermalZone(thermal_zone)
        end
      end

    end

    #This Module contains methods that create, modify and query Thermal zone objects.
    module Zones

      def self.enumerate_model(model)

      end


      # This method will filter an array of zones that have an external wall
      # passed floors. Note: if you wish to avoid to create an array of spaces,
      # simply put the space variable in [] brackets
      # Ex: get_all_surfaces_from_spaces( [space1,space2] )
      # @param thermal_zones [Array<OpenStudio::Model::ThermalZone>] an array of zones 
      # @return [Array<OpenStudio::Model::ThermalZone] an array of thermal zones.
      def self.filter_perimeter_zones(thermal_zones)
        array = Array.new()
        thermal_zones.each do |zone|
          zone.space.each do |space|
            if space.is_a_perimeter_space?()
              array.push(zone)
              next
            end
          end
        end
        return array
      end


      # This method will filter an array of zones that have no external wall
      # passed floors. Note: if you wish to avoid to create an array of spaces,
      # simply put the space variable in [] brackets
      # Ex: ( [space1,space2] )
      # @param thermal_zones [Array<OpenStudio::Model::ThermalZone] an array of zones
      # @return [Array<OpenStudio::Model::ThermalZone] an array of zones
      def self.filter_core_zones(thermal_zones)
        array = Array.new()
        thermal_zones.getThermalZones.sort.each do |zone|
          zone.space.each do |space|
            if not space.is_a_perimeter_space?()
              array.push(zone)
              next
            end
          end
        end
        return array
      end

      def self.get_surfaces_from_thermal_zones(thermal_zone_array)
        BTAP::Geometry::Surfaces::get_all_surfaces_from_thermal_zones(thermal_zone_array)
      end

      def self.create_thermal_zone(model, spaces_array = "")
        thermal_zone = OpenStudio::Model::ThermalZone.new(model)
        BTAP::Geometry::Spaces::assign_spaces_to_thermal_zone(model, spaces_array, thermal_zone)
        return thermal_zone
      end

    end
    module Surfaces

      def self.create_surface(model, name, os_point3d_array, boundary_condition = "", construction = "")
        os_surface = OpenStudio::Model::Surface.new(os_point3d_array, model)
        os_surface.setName(name)
        if OpenStudio::Model::Surface::validOutsideBoundaryConditionValues.include?(boundary_condition)
          self.set_surfaces_boundary_condition([os_surface], boundary_condition)
        else
          puts "boundary condition not set for #{name}"
        end
        self.set_surfaces_construction([os_surface], construction)
        return os_surface
      end

      # This method will rotate a surface
      # @param planar_surfaces [Array<OpenStudio::Model::Surface>] an array of surfaces
      # @param azimuth_degrees [Float] rotation value
      # @param tilt_degrees [Float] rotation value
      # @param translation_vector [OpenStudio::Vector3d] a vector along which to move all surfaces
      # @return [OpenStudio::Model::Model] the model object.
      def self.rotate_tilt_translate_surfaces(planar_surfaces, azimuth_degrees, tilt_degrees = 0.0, translation_vector = OpenStudio::Vector3d.new(0.0, 0.0, 0.0))
        # Identity matrix for setting space origins
        azimuth_matrix = OpenStudio::Transformation::rotation(OpenStudio::Vector3d.new(0, 0, 1), azimuth_degrees * Math::PI / 180)
        tilt_matrix = OpenStudio::Transformation::rotation(OpenStudio::Vector3d.new(0, 0, 1), tilt_degrees * Math::PI / 180)
        translation_matrix = OpenStudio::createTranslation(translation_vector)
        planar_surfaces.each do |surface|
          surface.changeTransformation(azimuth_matrix)
          surface.changeTransformation(tilt_matrix)
          surface.changeTransformation(translation_matrix)
        end
        return planar_surfaces
      end

      def self.set_fenestration_to_wall_ratio(surfaces, ratio, offset = 0, height_offset_from_floor = true, floor = "all")
        surfaces.each do |surface|
          result = surface.setWindowToWallRatio(ratio, offset, height_offset_from_floor)
          raise("Unable to set FWR for surface " +
                    surface.name.get.to_s +
                    " . Possible reasons are  if the surface is not a wall, if the surface
          is not rectangular in face coordinates, if requested ratio is too large
          (window area ~= surface area) or too small (min dimension of window < 1 foot),
          or if the window clips any remaining sub surfaces. Otherwise, removes all
          existing windows and adds new window to meet requested ratio.") unless result
        end
        return surfaces
      end


      # This Method removes all the subsurfaces in a model (Windows, Doors )
      # @author Phylroy A. Lopez
      # @return [OpenStudio::Model::Model] the OpenStudio model object (self reference).
      def self.remove_all_subsurfaces(surfaces)
        surfaces.each do |subsurface|
          subsurface.remove
        end
        return surfaces
      end

      def self.get_surfaces_from_spaces(spaces_array)
        surfaces = Array.new()
        spaces_array.each do |space|
          surfaces.concat(space.surfaces())
        end
        return surfaces
      end

      def self.get_surfaces_from_building_stories(model, story_array)
        surfaces = Array.new()
        BTAP::Geometry::Spaces::get_spaces_from_storeys(model, story_array).each do |space|
          surfaces.concat(space.surfaces())
        end
        return surfaces
      end

      def self.get_surfaces_from_thermal_zones(thermal_zone_array)
        surfaces = Array.new()
        thermal_zone_array.each do |thermal_zone|
          thermal_zone.spaces.sort.each do |space|
            surfaces.concat(space.surfaces())
          end
          return surfaces
        end
      end

      def self.get_subsurfaces_from_surfaces(surface_array)
        subsurfaces = Array.new()
        surface_array.each do |surface|
          subsurfaces.concat(surface.subSurfaces)
        end
        return subsurfaces
      end


      #determine average conductance on set of surfaces or subsurfaces.
      def self.get_weighted_average_surface_conductance(surfaces)
        total_area = 0.0
        temp = 0.0
        surfaces.each do |surface|
          temp = temp + BTAP::Geometry::Surfaces::get_surface_net_area(surface) * BTAP::Geometry::Surfaces::get_surface_construction_conductance(surface)
          total_area = total_area + BTAP::Geometry::Surfaces::get_surface_net_area(surface)
        end
        average_conductance = "NA"
        average_conductance = temp / total_area unless total_area == 0.0
        return average_conductance
      end

      #determine average conductance on set of surfaces or subsurfaces.
      def self.get_weighted_average_surface_shgc(surfaces)
        total_area = 0.0
        temp = 0.0
        surfaces.each do |surface|
          temp = temp + BTAP::Geometry::Surfaces::get_surface_net_area(surface) * BTAP::Geometry::Surfaces::get_surface_construction_shgc(surface)
          total_area = total_area + BTAP::Geometry::Surfaces::get_surface_net_area(surface)
        end
        ave_shgc = "NA"
        ave_shgc = temp / total_area unless total_area == 0.0
        return ave_shgc
      end

      #determine average conductance on set of surfaces or subsurfaces.
      def self.get_weighted_average_surface_tvis(surfaces)
        total_area = 0.0
        temp = 0.0
        surfaces.each do |surface|
          temp = temp + BTAP::Geometry::Surfaces::get_surface_net_area(surface) * BTAP::Geometry::Surfaces::get_surface_construction_tvis(surface)
          total_area = total_area + BTAP::Geometry::Surfaces::get_surface_net_area(surface)
        end
        ave_tvis = "NA"
        ave_tvis = temp / total_area unless total_area == 0.0
        return ave_tvis
      end


      #get total exterior surface area of building.
      def self.get_total_ext_wall_area(model)
        outdoor_surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(model.getSurfaces(), "Outdoors")
        outdoor_walls = BTAP::Geometry::Surfaces::filter_by_surface_types(outdoor_surfaces, "Wall")


      end

      def self.get_total_ext_floor_area(model)

        outdoor_floors = BTAP::Geometry::Surfaces::filter_by_surface_types(outdoor_surfaces, "Floor")
      end

      def self.get_total_ext_fenestration_area(model)

      end

      def self.get_total_ext_roof_area(model)
        outdoor_roofs = BTAP::Geometry::Surfaces::filter_by_surface_types(outdoor_surfaces, "RoofCeiling")

      end


      #["FixedWindow" , "OperableWindow" , "Door" , "GlassDoor", "OverheadDoor" , "Skylight", "TubularDaylightDiffuser","TubularDaylightDome"]
      def self.filter_subsurfaces_by_types(subsurfaces, subSurfaceTypes)

        #check to see if a string or an array was passed.
        if subSurfaceTypes.kind_of?(String)
          temp = subSurfaceTypes
          subSurfaceTypes = Array.new()
          subSurfaceTypes.push(temp)
        end
        subSurfaceTypes.each do |subSurfaceType|
          unless OpenStudio::Model::SubSurface::validSubSurfaceTypeValues.include?(subSurfaceType)
            raise("ERROR: Invalid surface type = #{subSurfaceType} Correct Values are: #{OpenStudio::Model::SubSurface::validSubSurfaceTypeValues}")
          end
        end
        return_array = Array.new()
        if subSurfaceTypes.size == 0 or subSurfaceTypes[0].upcase == "All".upcase
          return_array = self
        else
          subsurfaces.each do |subsurface|
            subSurfaceTypes.each do |subSurfaceType|
              if subsurface.subSurfaceType == subSurfaceType
                return_array.push(subsurface)
              end
            end
          end
        end
        return return_array

      end

      #This method creates a new construction based on the current, changes the rsi and assign the construction to the current surface.
      #Most of the meat of this method is in the construction class. Testing is done there.
      def self.set_surfaces_construction_conductance(surfaces, conductance)
        surfaces.each do |surface|
          #a bit of acrobatics to get the construction object from the ConstrustionBase object's name.
          construction = OpenStudio::Model::getConstructionByName(surface.model, surface.construction.get.name.to_s).get
          #create a new construction with the requested conductance value based on the current construction.

          new_construction = BTAP::Resources::Envelope::Constructions::customize_opaque_construction(surface.model, construction, conductance)
          surface.setConstruction(new_construction)
        end
        return surfaces
      end

      #This method creates a new construction based on the current, changes the rsi and assign the construction to the current surface.
      #Most of the meat of this method is in the construction class. Testing is done there.
      def self.get_surface_construction_conductance(surface)
        #a bit of acrobatics to get the construction object from the ConstrustionBase object's name.
        construction = OpenStudio::Model::getConstructionByName(surface.model, surface.construction.get.name.to_s).get
        #create a new construction with the requested RSI value based on the current construction.
        return BTAP::Resources::Envelope::Constructions::get_conductance(construction)
      end

      #This method gets the shgc for a surface
      def self.get_surface_construction_shgc(surface)
        #a bit of acrobatics to get the construction object from the ConstrustionBase object's name.
        construction = OpenStudio::Model::getConstructionByName(surface.model, surface.construction.get.name.to_s).get
        #create a new construction with the requested RSI value based on the current construction.
        return BTAP::Resources::Envelope::Constructions::get_shgc(surface.model,construction)
      end

      #This method gets the tvis for the surface
      def self.get_surface_construction_tvis(surface)
        #a bit of acrobatics to get the construction object from the ConstrustionBase object's name.
        construction = OpenStudio::Model::getConstructionByName(surface.model, surface.construction.get.name.to_s).get
        #create a new construction with the requested RSI value based on the current construction.
        return BTAP::Resources::Envelope::Constructions::get_tvis(model,construction)
      end


      def self.get_surface_net_area(surface)
        return surface.netArea()
      end

      def self.get_sub_surface_net_area(subsurface)
        return subsurface.netArea()
      end


      def self.set_surfaces_construction(surfaces, construction)
        surfaces.each do |surface|
          surface.setConstruction(construction)
        end
        return true
      end

      #  This method sets the boundary condition for a surface and it's matching surface.
      #  If set to adiabatic, it will remove all subsurfaces since E+ cannot have adiabatic sub surfaces.
      def self.set_surfaces_boundary_condition(model, surfaces, boundaryCondition)
        surfaces = BTAP::Common::validate_array(model, surfaces, "Surface")
        if OpenStudio::Model::Surface::validOutsideBoundaryConditionValues.include?(boundaryCondition)
          surfaces.each do |surface|
            if boundaryCondition == "Adiabatic"
              #need to remove subsurface as you cannot have a adiabatic surface with a
              #subsurface.
              surface.subSurfaces.each do |subsurface|
                subsurface.remove
              end

              #A bug with adiabatic surfaces. They do not hold the default contruction. 
              surface.setConstruction(surface.construction.get()) if surface.isConstructionDefaulted
            end

            surface.setOutsideBoundaryCondition(boundaryCondition)
            adj_surface = surface.adjacentSurface
            unless adj_surface.empty?
              adj_surface.get.setOutsideBoundaryCondition(boundaryCondition)
            end
          end
        else
          puts "ERROR: Invalid Boundary Condition = " + boundary_condition
          puts "Correct Values are:"
          puts OpenStudio::Model::Surface::validOutsideBoundaryConditionValues
        end
      end

      def self.filter_by_non_defaulted_surfaces(surfaces)
        non_defaulted_surfaces = Array.new()
        surfaces.each {|surface| non_defaulted_surfaces << surface unless surface.isConstructionDefaulted}
        return non_defaulted_surfaces
      end


      def self.filter_by_boundary_condition(surfaces, boundary_conditions)
        #check to see if a string or an array was passed.
        if boundary_conditions.kind_of?(String)
          temp = boundary_conditions
          boundary_conditions = Array.new()
          boundary_conditions.push(temp)
        end
        #ensure boundary conditions are valid
        boundary_conditions.each do |boundary_condition|
          unless OpenStudio::Model::Surface::validOutsideBoundaryConditionValues.include?(boundary_condition)
            raise "ERROR: Invalid Boundary Condition = " + boundary_condition + "Correct Values are:" + OpenStudio::Model::Surface::validOutsideBoundaryConditionValues.to_s
          end
        end
        #create return array.
        return_array = Array.new()

        if boundary_conditions.size == 0 or boundary_conditions[0].upcase == "All".upcase
          return_array = surfaces
        else
          surfaces.each do |surface|
            boundary_conditions.each do |condition|
              if surface.outsideBoundaryCondition == condition
                return_array.push(surface)
              end
            end
          end
        end
        return return_array
      end

      def self.filter_by_surface_types(surfaces, surfaceTypes)

        #check to see if a string or an array was passed.
        if surfaceTypes.kind_of?(String)
          temp = surfaceTypes
          surfaceTypes = Array.new()
          surfaceTypes.push(temp)
        end
        surfaceTypes.each do |surfaceType|
          unless OpenStudio::Model::Surface::validSurfaceTypeValues.include?(surfaceType)
            raise("ERROR: Invalid surface type = #{surfaceType} Correct Values are: #{OpenStudio::Model::Surface::validSurfaceTypeValues}")
          end
        end
        return_array = Array.new()
        if surfaceTypes.size == 0 or surfaceTypes[0].upcase == "All".upcase
          return_array = self
        else
          surfaces.each do |surface|
            surfaceTypes.each do |surfaceType|
              if surface.surfaceType == surfaceType
                return_array.push(surface)
              end
            end
          end
        end
        return return_array
      end

      def self.filter_by_interzonal_surface(surfaces)
        return_array = Array.new()
        surfaces.each do |surface|
          unless surface.adjacentSurface().empty?
            return_array.push(surface)
          end
          return return_array
        end
      end

      # Azimuth start from Y axis, Tilts starts from Z-axis
      def self.filter_by_azimuth_and_tilt(surfaces, azimuth_from, azimuth_to, tilt_from, tilt_to, tolerance = 1.0)
        return_surfaces = []
        surfaces.each do |surface|
          unless OpenStudio::Model::PlanarSurface::findPlanarSurfaces([surface], OpenStudio::OptionalDouble.new(azimuth_from), OpenStudio::OptionalDouble.new(azimuth_to), OpenStudio::OptionalDouble.new(tilt_from), OpenStudio::OptionalDouble.new(tilt_to), tolerance).empty?
            return_surfaces << surface
          end
        end
        return return_surfaces
      end


      def self.show(surfaces)
        surfaces.each do |surface|
          if drawing_interface = surface.drawing_interface
            if entity = drawing_interface.entity
              entity.visible = false
            end
          end
        end
      end

      def self.hide(surfaces)
        surfaces.each do |surface|
          if drawing_interface = surface.drawing_interface
            if entity = drawing_interface.entity
              entity.visible = false
            end
          end
        end
      end

      # 2018-09-27 Chris Kirney
      # This method takes a surface in the x-y plane (z coordinates are ignored) with an upwardly pointing normal and
      # turns it into convex quadrialaterals.  If the original surface is already a convex quadrilateral then this method
      # will go to a lot of trouble to return the same thing (only with the coordinates of the points rounded).  If
      # the surface is already a concave surface then this method will return it broken into a bunch of quadrilaters
      # (maybe a triangle here and there). Neither of the above are especially useful.  However, the point of this
      # method is if you pass this a concave surface it will return convex surfaces that you can then use with other
      # methods that only apply to convex surfaces (such as a method which fits skylights into a roof).  Note that
      # surfaces per say are not returned.  Rather, an array containing 4 points arranged in counter clockwise order is
      # returned.  These points are also in the x-y plane with an upwardly pointing normal.  No z coordinate is returned.
      #
      # The method works by first looking for upward pointing lines.  It then looks for cooresponding downward pointing
      # lines.  Since all of the surfaces are closed there should always be enough upward and downward pointing lines.
      # Horizontal lines are ignored.  It then checks to see which y projections of the upward and downward pointing
      # lines overlap.  It then sees which of these overlaping lines overlap.  Ultimately you wind up with a whole bunch
      # of overlapping y projections that coorespond with different upward pointing lines.  These overlapping
      # y projects are either unique, or they precisely match other overlapping y projections.  The point is that, in
      # the case of a convex shape, an upward pointing line may overlap with some lines close, and some far away, with
      # some lines in between.  The method then sorts through the overlapping y projections to see which are closest
      # to a given upward pointing line.  It keeps the unique ones, and the ones that are closest.  The end result
      # should be downward pointing line segments that correspond to an upward pointing line segment with no intervening
      # lines.  The last part of the method assembles the quadrilaterals from the remaining downward pointing line
      # segments which correspond with a given upward pointing line segment.
      def self.make_convex_surfaces(surface:, tol: 12)
        # Note that points on surfaces are given counterclockwise when looking at the surface from the opposite direction as
        # the outward normal (i.e. the outward normal is pointing at you).  I use point_a1, point_a2, point_b1 and point b2
        # lots.  For this, point_a refers to vectors pointing up.  In this case point_a1 is at the top of the vector and
        # point_a2 is at the bottom of the vector.  Contrarily, point_b refers to vectors pointing down.  In this case
        # point_b1 is at the bottom of the vector and point_b2 is at the top.  All of this comes about because I cycle
        # through the points starting at the 2nd point and and going to the last point.  I count vectors as starting from
        # the last point and going toward the current point.
        # See following where P1 through P4 are the points.  When cycling through a is where you start and b is where you
        # end.  the o is the tip of the outward normal pointing at you.
        #    P2b------------aP1
        #     a              b
        #     |              |
        #     |      o       |
        #     |              |
        #     b              a
        #     P3a-----------bP4
        surf_verts = []
        # Get the vertices from the surface, keep the x and y coordinates, and turn the vertices from OpenStudio's
        # data structure to a differet one which is a little easier to deal with.  Also, round them to the given
        # tolerance.  This is done because some numbers that should match don't because of tiny errors.
        surface.vertices.each do |vert|
          surf_vert = {
              x: vert.x.to_f.round(tol),
              y: vert.y.to_f.round(tol),
              z: vert.z.to_f
          }
          surf_verts << surf_vert
        end
        # If the surface is a triangle or less then do nothing and return it.
        return surf_verts if surf_verts.length <= 3
        # Adding the first vertex to the end so that it is accounted for.
        surf_verts << surf_verts[0]
        # Following we go through the points, look for upward pointing lines, then look for downward pointing lines to
        # their left (only to the left because everything goes counter-clockwise).  If there is a line find how much the
        # current upward pointing line overlaps with it in the y direction.
        overlap_segs = []
        new_surfs = []
        for i in 1..(surf_verts.length - 1)
          # Is this line segment pointing up?  If no, then ignore it and go to the next line segment.
          if surf_verts[i][:y] > surf_verts[i - 1][:y]
            # Go through each line segment
            for j in 1..(surf_verts.length - 1)
              # Is the line segment to the left of the current (index i) line segment?  If no, then ignore it and go to the next one.
              # I revised this to check if the start or end of the current (index i) line segment is to the left of the
              # line segment being checked.
              #if surf_verts[j][:x] < surf_verts[i][:x] and surf_verts[j - 1][:x] < surf_verts[i - 1][:x]
              if surf_verts[j][:x] < surf_verts[i][:x] || surf_verts[j - 1][:x] < surf_verts[i - 1][:x]
                # Is the line segment pointing down?  If no, then ignore it and go to the next line segment.
                if surf_verts[j][:y] < surf_verts[j - 1][:y]
                  # Do the y coordinates of the line segment overlap with the current (index i) line segment?  If no
                  # then ignore it and go to the next line segment.
                  overlap_y = line_segment_overlap_y?(point_a1: surf_verts[i][:y], point_a2: surf_verts[i - 1][:y], point_b1: surf_verts[j][:y], point_b2: surf_verts[j - 1][:y])
                  unless overlap_y[:overlap_start].nil? || overlap_y[:overlap_end].nil?
                    unless overlap_y[:overlap_start] == overlap_y[:overlap_end]
                      overlap_seg = {
                          index_a1: i,
                          index_a2: i - 1,
                          index_b1: j,
                          index_b2: j - 1,
                          point_b1: surf_verts[j],
                          point_b2: surf_verts[j - 1],
                          overlap_y: overlap_y
                      }
                      overlap_segs << overlap_seg
                    end
                  end
                end
              end
            end
          end
        end
        # This part:
        # 1. Subdivides the overlapping segments found above into either unique overlaps between the upward and downward
        #    pointing lines or overlapping segments that exactly match one another.
        # 2. Goes through each upward pointing line and finds the closest overlapping downward pointing line segments (if
        #    these downward pointing segments belong together they are re-attached).
        # 3. Makes quadrilaterals (or triangles as the case may be) out of each upward pointing line and the closest
        #    downward pointing line segment.
        if overlap_segs.length > 1
          # Subdivide the overlapping segments found above into either unique overlaps between the upward and downward
          # pointing lines or overlapping segments that exactly match one another.
          overlap_segs = subdivide_overlaps(overlap_segs: overlap_segs)
          # Remove redundant overlapping segments
          recheck = true
          while recheck
            recheck = false
            # Go through each overlapping segment and look for duplicate segments
            overlap_segs.each_with_index do |ind_overlap_seg, seg_index|
              # Find duplicate overlapping segments
              redundant_segs = overlap_segs.select { |check_seg| check_seg == ind_overlap_seg}
              # Remove the first one and then restart the while loop to recompile the seg_index
              if redundant_segs.size > 1
                overlap_segs.delete_at(seg_index)
                recheck = true
              end
            end
          end
          for i in 1..(surf_verts.length - 1)
            # Does the line point up?  No then ignore and go on to the next one.
            if surf_verts[i][:y] > surf_verts[i - 1][:y]
              # Finds the closest overlapping downward pointing line segments that correspond to this upward pointing
              # line (if some of these downward pointing segments belong together then re-attached them).
              closest_overlaps = get_overlapping_segments(overlap_segs: overlap_segs, index: i, point_a1: surf_verts[i], point_a2: surf_verts[i - 1])
              closest_overlaps = closest_overlaps.sort_by {|closest_overlap| closest_overlap[:overlap_y][:overlap_start]}
              # Create the quadrilaterals out of the downward pointing line segments closest to the current upward
              # pointing line.
              for j in 0..(closest_overlaps.length - 1)
                new_surf = []
                z_loc = surf_verts[closest_overlaps[j][:index_a1]][:z]
                y_loc = closest_overlaps[j][:overlap_y][:overlap_start]
                x_loc = line_segment_overlap_x_coord(y_check: y_loc, point_b1: surf_verts[closest_overlaps[j][:index_a1]], point_b2: surf_verts[closest_overlaps[j][:index_a2]])
                new_surf << {x: x_loc.to_f.round(tol), y: y_loc.to_f.round(tol), z: z_loc.to_f.round(tol)}
                x_loc = line_segment_overlap_x_coord(y_check: y_loc, point_b1: closest_overlaps[j][:point_b1], point_b2: closest_overlaps[j][:point_b2])
                z_loc = surf_verts[closest_overlaps[j][:index_b2]][:z]
                new_surf << {x: x_loc.to_f.round(tol), y: y_loc.to_f.round(tol), z: z_loc.to_f.round(tol)}
                y_loc = closest_overlaps[j][:overlap_y][:overlap_end]
                x_loc = line_segment_overlap_x_coord(y_check: y_loc, point_b1: closest_overlaps[j][:point_b1], point_b2: closest_overlaps[j][:point_b2])
                z_loc = surf_verts[closest_overlaps[j][:index_b1]][:z]
                new_surf << {x: x_loc.to_f.round(tol), y: y_loc.to_f.round(tol), z: z_loc.to_f.round(tol)}
                x_loc = line_segment_overlap_x_coord(y_check: y_loc, point_b1: surf_verts[closest_overlaps[j][:index_a1]], point_b2: surf_verts[closest_overlaps[j][:index_a2]])
                z_loc = surf_verts[closest_overlaps[j][:index_a2]][:z]
                new_surf << {x: x_loc.to_f.round(tol), y: y_loc.to_f.round(tol), z: z_loc.to_f.round(tol)}
                # Check if this should be a triangle.
                for k in 0..(new_surf.length - 1)
                  break_now = false
                  for l in 0..(new_surf.length - 1)
                    next if k == l
                    if (new_surf[k][:x] == new_surf[l][:x]) && (new_surf[k][:y] == new_surf[l][:y])
                      new_surf.delete_at(l)
                      break_now = true
                      break
                    end
                  end
                  if break_now == true
                    break
                  end
                end
                new_surfs << new_surf
              end
            end
          end
        elsif overlap_segs.length == 1
          # There is only one overlapping downward line, thus this is a quadrilateral already so just return it.
          # Remove the last vertex as we had artificially added it at the start.
          surf_verts.pop
          new_surfs << surf_verts
        end
        return new_surfs
      end

      # This method takes the y projections of a bunch of overlapping line segments and sorts them to determines which
      # are unique and, if they are not unique, which is closest to the current, upwardly pointing, line.  If several
      # overlapping segments belong to the same line they are put together (after the 'subdivide_overlaps' method broke
      # them apart).  The end result is the method returns the closet point downward pointing line segments closest to
      # the given upward pointing line segment.
      #
      # overlap_segs: This is an array of hashes that looks like:
      #                     overlap_seg = {
      #                         index_a1: i,
      #                         index_a2: i-1,
      #                         index_b1: j,
      #                         index_b2: j-1,
      #                         point_b1: surf_verts[j],
      #                         point_b2: surf_verts[j-1],
      #                         overlap_y: overlap_y
      #                     }
      # index_a1:  The index of the array of points that cooresponds with the top of line a (points up)
      # index_a1:  The index of the array of points that cooresponds with the bottom of line a (points up)
      # index_b1:  The index of the array of points that cooresponds with the bottom of line b (points down)
      # index_b1:  The index of the array of points that cooresponds with the top of line b (points down)
      # point_b1:  The coordinates of the bottom of line b
      # point_b2:  The coordinates of the top of line b
      #
      # overlap_y: A hash that contains the coordinates of the top and bottom of the y projection of the overlapping
      # lines (line a and line b)
      #         overlap_y = {
      #             overlap_start: overlap_start,
      #             overlap_end: overlap_end
      #         }
      # overlap_start:  The y coordinate of the top of the overlap
      # overlap_end:  The y coordinate of the bottom of the overlap
      #
      # index: The index of the array of points that cooresponds with the top of of the current upward pointing line.
      #
      # point_a1:  The coordinates of the top of the first line
      # point_a2:  The coordinates of the  bottom of the first line
      # This naming convention was chosen because this method was originally designed to work with the
      # 'make_concave_surfaces' method (see above).  That method choses lines that point up and then sees where they
      # overlap with lines pointing down.  The point_1 of each line is the end of the line.  In this case a lines point
      # up and b lines point down.
      def self.get_overlapping_segments(overlap_segs:, index:, point_a1:, point_a2:)
        closest_overlaps = []
        linea_overlaps = []
        # This goes through all the line segments and determines which correspond to the current upward pointing line
        # segment(line a).  It also determines the x coordinate distance between the top and bottom of the overlapping
        # portions of the line segments.
        curr_overlap_segs = overlap_segs.select { |seg| (seg[:index_a1] == index) && (seg[:index_a2] == (index - 1)) }
        curr_overlap_segs.each do |overlap_seg|
          line_a_x_top = line_segment_overlap_x_coord(y_check: overlap_seg[:overlap_y][:overlap_start], point_b1: point_a1, point_b2: point_a2)
          line_a_x_bottom = line_segment_overlap_x_coord(y_check: overlap_seg[:overlap_y][:overlap_end], point_b1: point_a1, point_b2: point_a2)
          line_b_x_top = line_segment_overlap_x_coord(y_check: overlap_seg[:overlap_y][:overlap_start], point_b1: overlap_seg[:point_b1], point_b2: overlap_seg[:point_b2])
          line_b_x_bottom = line_segment_overlap_x_coord(y_check: overlap_seg[:overlap_y][:overlap_end], point_b1: overlap_seg[:point_b1], point_b2: overlap_seg[:point_b2])
          x_distance_top = line_a_x_top - line_b_x_top
          x_distance_bottom = line_a_x_bottom - line_b_x_bottom
          linea_overlap = {
            dx_top: x_distance_top,
            dx_bottom: x_distance_bottom,
            overlap: overlap_seg
          }
          linea_overlaps << linea_overlap
        end

        # This sorts through the overlapping downward pointing line segments corresponding to the current upward pointing
        # line a.  The overlapping downward pointing line segments closest to the current upward pointing line segment
        # are kept.  The other are discarded.  Unique overlapping line segments are kept as well.  There should only be
        # unuique overlapping line segments or overlapping line segments that precisely match one another because of
        # the 'subdivide_overlaps' method which this method is supposed to work with.
        linea_overlaps.each do |line_a_overlap|
          overlaps = linea_overlaps.select { |seg| seg[:overlap][:overlap_y] == line_a_overlap[:overlap][:overlap_y]}
          if overlaps.size > 1
            redundant_overlap = closest_overlaps.select { |dup_seg| dup_seg[:overlap_y] == overlaps[0][:overlap][:overlap_y] }
            closest_overlaps << (overlaps.min_by { |dup_seg| dup_seg[:dx_top] })[:overlap] if redundant_overlap.empty?
          elsif overlaps.size == 1
            closest_overlaps << overlaps[0][:overlap]
          end
        end

        # This combines the line segments that belong together.  These were broken apart because of the
        # 'subdivide_overlaps' method.
        overlap_exts = [closest_overlaps[0]]
        for j in 0..(closest_overlaps.length - 1)
          index = 0
          found = false
          for l in 0..(overlap_exts.length - 1)
            if overlap_exts[l][:index_b1] == closest_overlaps[j][:index_b1] && overlap_exts[l][:index_b2] == closest_overlaps[j][:index_b2]
              index = l
              found = true
              break
            end
          end
          if found == false
            overlap_exts << closest_overlaps[j]
            index = overlap_exts.length - 1
          end
          for k in 0..(closest_overlaps.length - 1)
            if (closest_overlaps[j][:index_b1] == closest_overlaps[k][:index_b1]) && (closest_overlaps[j][:index_b2] == closest_overlaps[k][:index_b2])
              if closest_overlaps[k][:overlap_y][:overlap_start] >= overlap_exts[index][:overlap_y][:overlap_start]
                overlap_exts[index][:overlap_y][:overlap_start] = closest_overlaps[k][:overlap_y][:overlap_start]
              end
              if closest_overlaps[k][:overlap_y][:overlap_end] <= overlap_exts[index][:overlap_y][:overlap_end]
                overlap_exts[index][:overlap_y][:overlap_end] = closest_overlaps[k][:overlap_y][:overlap_end]
              end
            end
          end
        end
        return overlap_exts
      end

      # This method was originally written to work with the 'make_concave_surfaces' method above.  It takes the
      # y-components of a bunch of line segemnts and cuts them up until they either are unique (no other overlapping
      # components) or they match the y-components of other line segments.
      # overlap_segs: This is an array of hashes that looks like:
      #                     overlap_seg = {
      #                         index_a1: i,
      #                         index_a2: i-1,
      #                         index_b1: j,
      #                         index_b2: j-1,
      #                         point_b1: surf_verts[j],
      #                         point_b2: surf_verts[j-1],
      #                         overlap_y: overlap_y
      #                     }
      # index_a1:  The index of the array of points that cooresponds with the top of line a (points up)
      # index_a1:  The index of the array of points that cooresponds with the bottom of line a (points up)
      # index_b1:  The index of the array of points that cooresponds with the bottom of line b (points down)
      # index_b1:  The index of the array of points that cooresponds with the top of line b (points down)
      # point_b1:  The coordinates of the bottom of line b
      # point_b2:  The coordinates of the top of line b
      #
      # overlap_y: A hash that contains the coordinates of the top and bottom of the y projection of the overlapping
      # lines (line a and line b)
      #         overlap_y = {
      #             overlap_start: overlap_start,
      #             overlap_end: overlap_end
      #         }
      # overlap_start:  The y coordinate of the top of the overlap
      # overlap_end:  The y coordinate of the bottom of the overlap
      def self.subdivide_overlaps(overlap_segs:)
        restart = true
        # Keep doing this until the y projections of the lines are either unique or the match the y projections of other
        # lines.
        while restart == true
          restart = false
          overlap_segs.each_with_index do |overlap_seg, curr_seg_index|
            for j in 0..(overlap_segs.length - 1)
              # Skip this y projection if it is the same as that in overlap_seg
              if overlap_seg == overlap_segs[j]
                next
              end
              # Check to see if the y projection of line a overlaps with the y projection of line b
              overlap_segs_overlap = line_segment_overlap_y?(point_a1: overlap_seg[:overlap_y][:overlap_start], point_a2: overlap_seg[:overlap_y][:overlap_end], point_b1: overlap_segs[j][:overlap_y][:overlap_end], point_b2: overlap_segs[j][:overlap_y][:overlap_start])
              # If the y projections of the two lines overlap then the components of overlap_segs_overlap should not be
              # nil.
              unless ((overlap_segs_overlap[:overlap_start].nil?) || (overlap_segs_overlap[:overlap_end].nil?))
                # If the two overlaping segments start and end at the same point then do nothing and go to the next segment.
                if (overlap_seg[:overlap_y][:overlap_start] == overlap_segs[j][:overlap_y][:overlap_start]) && (overlap_seg[:overlap_y][:overlap_end] == overlap_segs[j][:overlap_y][:overlap_end])
                  next
                  # If the start point of one overlapping segment shares the end point of the other overlapping segment then
                  # they are not really overlapping.  Ignore and go to the next point.
                elsif overlap_segs_overlap[:overlap_start] == overlap_segs_overlap[:overlap_end]
                  next
                  # If the overlap_seg segment covers beyond the overlap_segs[j] segment then break overlap_seg into three smaller pieces:
                  # -One piece for where overlap_seg starts to where overlap_segs[j] starts;
                  # -One piece to cover overlap_segs[j] (the middle part); and
                  # -One piece for where overlap_segs[j] ends to where overlap_seg ends (the bottom part).
                  # The overlap_segs[j] remains as it is associated with another upward pointing line segment.
                  # If overlap_seg starts at the same point as overlap_segs[j] or ends at the same point as overlap_segs[j]
                  # then overlap_seg is broken into two pieces (no mid piece).
                elsif (overlap_seg[:overlap_y][:overlap_start] >= overlap_segs[j][:overlap_y][:overlap_start]) && (overlap_seg[:overlap_y][:overlap_end] <= overlap_segs[j][:overlap_y][:overlap_end])
                  # If the overlap_seg and overlap_segs[j] start at the same point replace overlap_seg with two segments (
                  # one top and one bottom).
                  if overlap_seg[:overlap_y][:overlap_start] == overlap_segs[j][:overlap_y][:overlap_start]
                    overlap_top = {
                        index_a1: overlap_seg[:index_a1],
                        index_a2: overlap_seg[:index_a2],
                        index_b1: overlap_seg[:index_b1],
                        index_b2: overlap_seg[:index_b2],
                        point_b1: overlap_seg[:point_b1],
                        point_b2: overlap_seg[:point_b2],
                        overlap_y: overlap_segs_overlap
                    }
                    overlap_bottom_over = {
                        overlap_start: overlap_segs_overlap[:overlap_end],
                        overlap_end: overlap_seg[:overlap_y][:overlap_end]
                    }
                    overlap_bottom = {
                        index_a1: overlap_seg[:index_a1],
                        index_a2: overlap_seg[:index_a2],
                        index_b1: overlap_seg[:index_b1],
                        index_b2: overlap_seg[:index_b2],
                        point_b1: overlap_seg[:point_b1],
                        point_b2: overlap_seg[:point_b2],
                        overlap_y: overlap_bottom_over
                    }
                    # delete the existing y projection overlaps and replace it with the ones we just made.
                    overlap_segs.delete_at(curr_seg_index)
                    overlap_segs << overlap_top
                    overlap_segs << overlap_bottom
                  elsif overlap_seg[:overlap_y][:overlap_end] == overlap_segs[j][:overlap_y][:overlap_end]
                    # If the overlap_seg and overlap_segs[j] end at the same point replace overlap_seg with two segments (
                    # one top and one bottom).
                    overlap_top_over = {
                        overlap_start: overlap_seg[:overlap_y][:overlap_start],
                        overlap_end: overlap_segs_overlap[:overlap_start]
                    }
                    overlap_top = {
                        index_a1: overlap_seg[:index_a1],
                        index_a2: overlap_seg[:index_a2],
                        index_b1: overlap_seg[:index_b1],
                        index_b2: overlap_seg[:index_b2],
                        point_b1: overlap_seg[:point_b1],
                        point_b2: overlap_seg[:point_b2],
                        overlap_y: overlap_top_over
                    }
                    overlap_bottom = {
                        index_a1: overlap_seg[:index_a1],
                        index_a2: overlap_seg[:index_a2],
                        index_b1: overlap_seg[:index_b1],
                        index_b2: overlap_seg[:index_b2],
                        point_b1: overlap_seg[:point_b1],
                        point_b2: overlap_seg[:point_b2],
                        overlap_y: overlap_segs_overlap
                    }
                    # delete the existing y projection overlaps and replace it with the ones we just made.
                    overlap_segs.delete_at(curr_seg_index)
                    overlap_segs << overlap_top
                    overlap_segs << overlap_bottom
                  elsif (overlap_seg[:overlap_y][:overlap_start] > overlap_segs[j][:overlap_y][:overlap_start]) && (overlap_seg[:overlap_y][:overlap_end] < overlap_segs[j][:overlap_y][:overlap_end])
                    # If the overlap_seg stretches above and below overlap_segs[j] then break overlap_seg into three pieces
                    # (one top, one middle, one bottom).
                    overlap_top_over = {
                        overlap_start: overlap_seg[:overlap_y][:overlap_start],
                        overlap_end: overlap_segs_overlap[:overlap_start]
                    }
                    overlap_top = {
                        index_a1: overlap_seg[:index_a1],
                        index_a2: overlap_seg[:index_a2],
                        index_b1: overlap_seg[:index_b1],
                        index_b2: overlap_seg[:index_b2],
                        point_b1: overlap_seg[:point_b1],
                        point_b2: overlap_seg[:point_b2],
                        overlap_y: overlap_top_over
                    }
                    overlap_mid = {
                        index_a1: overlap_seg[:index_a1],
                        index_a2: overlap_seg[:index_a2],
                        index_b1: overlap_seg[:index_b1],
                        index_b2: overlap_seg[:index_b2],
                        point_b1: overlap_seg[:point_b1],
                        point_b2: overlap_seg[:point_b2],
                        overlap_y: overlap_segs_overlap
                    }
                    overlap_bottom_over = {
                        overlap_start: overlap_segs_overlap[:overlap_end],
                        overlap_end: overlap_seg[:overlap_y][:overlap_end]
                    }
                    overlap_bottom = {
                        index_a1: overlap_seg[:index_a1],
                        index_a2: overlap_seg[:index_a2],
                        index_b1: overlap_seg[:index_b1],
                        index_b2: overlap_seg[:index_b2],
                        point_b1: overlap_seg[:point_b1],
                        point_b2: overlap_seg[:point_b2],
                        overlap_y: overlap_bottom_over
                    }
                    # delete the existing y projection overlaps and replace it with the ones we just made.
                    overlap_segs.delete_at(curr_seg_index)
                    overlap_segs << overlap_top
                    overlap_segs << overlap_mid
                    overlap_segs << overlap_bottom
                  end
                  restart = true
                  break
                  # If the overlap_segs[j] segment covers beyond the overlap_seg segment then break overlap_segs[j] into three smaller pieces:
                  # -One piece for where overlap_segs[j] starts to where overlap_seg starts;
                  # -One piece to cover overlap_seg (the middle part); and
                  # -One piece for where overlap_seg ends to where overlap_segs[j] ends (the bottom part).
                  # The overlap_seg remains as it is associated with another upward pointing line segment.
                  # If overlap_segs[j] starts at the same point as overlap_seg or ends at the same point as overlap_seg
                  # then overlap_segs[j] is broken into two pieces (no mid piece).
                elsif overlap_seg[:overlap_y][:overlap_start] <= overlap_segs[j][:overlap_y][:overlap_start] && overlap_seg[:overlap_y][:overlap_end] >= overlap_segs[j][:overlap_y][:overlap_end]
                  # If the overlap_seg and overlap_segs[j] start at the same point replace overlap_segs[j] with two segments (
                  # one top and one bottom).
                  if overlap_seg[:overlap_y][:overlap_start] == overlap_segs[j][:overlap_y][:overlap_start]
                    overlap_top = {
                        index_a1: overlap_segs[j][:index_a1],
                        index_a2: overlap_segs[j][:index_a2],
                        index_b1: overlap_segs[j][:index_b1],
                        index_b2: overlap_segs[j][:index_b2],
                        point_b1: overlap_segs[j][:point_b1],
                        point_b2: overlap_segs[j][:point_b2],
                        overlap_y: overlap_segs_overlap
                    }
                    overlap_bottom_over = {
                        overlap_start: overlap_segs_overlap[:overlap_end],
                        overlap_end: overlap_segs[j][:overlap_y][:overlap_end]
                    }
                    overlap_bottom = {
                        index_a1: overlap_segs[j][:index_a1],
                        index_a2: overlap_segs[j][:index_a2],
                        index_b1: overlap_segs[j][:index_b1],
                        index_b2: overlap_segs[j][:index_b2],
                        point_b1: overlap_segs[j][:point_b1],
                        point_b2: overlap_segs[j][:point_b2],
                        overlap_y: overlap_bottom_over
                    }
                    # delete the existing y projection overlaps and replace it with the ones we just made.
                    overlap_segs.delete_at(j)
                    overlap_segs << overlap_top
                    overlap_segs << overlap_bottom
                  elsif overlap_seg[:overlap_y][:overlap_end] == overlap_segs[j][:overlap_y][:overlap_end]
                    # If the overlap_seg and overlap_segs[j] end at the same point replace overlap_segs[j] with two segments (
                    # one top and one bottom).
                    overlap_top_over = {
                        overlap_start: overlap_segs[j][:overlap_y][:overlap_start],
                        overlap_end: overlap_segs_overlap[:overlap_start]
                    }
                    overlap_top = {
                        index_a1: overlap_segs[j][:index_a1],
                        index_a2: overlap_segs[j][:index_a2],
                        index_b1: overlap_segs[j][:index_b1],
                        index_b2: overlap_segs[j][:index_b2],
                        point_b1: overlap_segs[j][:point_b1],
                        point_b2: overlap_segs[j][:point_b2],
                        overlap_y: overlap_top_over
                    }
                    overlap_bottom = {
                        index_a1: overlap_segs[j][:index_a1],
                        index_a2: overlap_segs[j][:index_a2],
                        index_b1: overlap_segs[j][:index_b1],
                        index_b2: overlap_segs[j][:index_b2],
                        point_b1: overlap_segs[j][:point_b1],
                        point_b2: overlap_segs[j][:point_b2],
                        overlap_y: overlap_segs_overlap
                    }
                    # delete the existing y projection overlaps and replace it with the ones we just made.
                    overlap_segs.delete_at(j)
                    overlap_segs << overlap_top
                    overlap_segs << overlap_bottom
                  elsif overlap_seg[:overlap_y][:overlap_start] < overlap_segs[j][:overlap_y][:overlap_start] && overlap_seg[:overlap_y][:overlap_end] > overlap_segs[j][:overlap_y][:overlap_end]
                    # If the overlap_segs[j] stretches above and below overlap_seg then break overlap_segs[j] into three pieces
                    # (one top, one middle, one bottom).
                    overlap_top_over = {
                        overlap_start: overlap_segs[j][:overlap_y][:overlap_start],
                        overlap_end: overlap_segs_overlap[:overlap_start]
                    }
                    overlap_top = {
                        index_a1: overlap_segs[j][:index_a1],
                        index_a2: overlap_segs[j][:index_a2],
                        index_b1: overlap_segs[j][:index_b1],
                        index_b2: overlap_segs[j][:index_b2],
                        point_b1: overlap_segs[j][:point_b1],
                        point_b2: overlap_segs[j][:point_b2],
                        overlap_y: overlap_top_over
                    }
                    overlap_mid = {
                        index_a1: overlap_segs[j][:index_a1],
                        index_a2: overlap_segs[j][:index_a2],
                        index_b1: overlap_segs[j][:index_b1],
                        index_b2: overlap_segs[j][:index_b2],
                        point_b1: overlap_segs[j][:point_b1],
                        point_b2: overlap_segs[j][:point_b2],
                        overlap_y: overlap_segs_overlap
                    }
                    overlap_bottom_over = {
                        overlap_start: overlap_segs_overlap[:overlap_end],
                        overlap_end: overlap_segs[j][:overlap_y][:overlap_end]
                    }
                    overlap_bottom = {
                        index_a1: overlap_segs[j][:index_a1],
                        index_a2: overlap_segs[j][:index_a2],
                        index_b1: overlap_segs[j][:index_b1],
                        index_b2: overlap_segs[j][:index_b2],
                        point_b1: overlap_segs[j][:point_b1],
                        point_b2: overlap_segs[j][:point_b2],
                        overlap_y: overlap_bottom_over
                    }
                    # delete the existing y projection overlaps and replace it with the ones we just made.
                    overlap_segs.delete_at(j)
                    overlap_segs << overlap_top
                    overlap_segs << overlap_mid
                    overlap_segs << overlap_bottom
                  end
                  restart = true
                  break
                  # if overlap_seg covers the top of overlap_segs[j] then break overlap_seg into a top and an overlap portion
                  # ond break overlap_segs[j] into an overlap portion and a bottom portion.
                elsif (overlap_seg[:overlap_y][:overlap_start] >= overlap_segs[j][:overlap_y][:overlap_start]) && (overlap_seg[:overlap_y][:overlap_end] <= overlap_segs[j][:overlap_y][:overlap_start]) && (overlap_seg[:overlap_y][:overlap_end] > overlap_segs[j][:overlap_y][:overlap_end])
                  overlap_top_over = {
                      overlap_start: overlap_seg[:overlap_y][:overlap_start],
                      overlap_end: overlap_segs_overlap[:overlap_start]
                  }
                  overlap_top = {
                      index_a1: overlap_seg[:index_a1],
                      index_a2: overlap_seg[:index_a2],
                      index_b1: overlap_seg[:index_b1],
                      index_b2: overlap_seg[:index_b2],
                      point_b1: overlap_seg[:point_b1],
                      point_b2: overlap_seg[:point_b2],
                      overlap_y: overlap_top_over
                  }
                  overlap_mid_seg = {
                      index_a1: overlap_seg[:index_a1],
                      index_a2: overlap_seg[:index_a2],
                      index_b1: overlap_seg[:index_b1],
                      index_b2: overlap_seg[:index_b2],
                      point_b1: overlap_seg[:point_b1],
                      point_b2: overlap_seg[:point_b2],
                      overlap_y: overlap_segs_overlap
                  }
                  overlap_mid_segs = {
                      index_a1: overlap_segs[j][:index_a1],
                      index_a2: overlap_segs[j][:index_a2],
                      index_b1: overlap_segs[j][:index_b1],
                      index_b2: overlap_segs[j][:index_b2],
                      point_b1: overlap_segs[j][:point_b1],
                      point_b2: overlap_segs[j][:point_b2],
                      overlap_y: overlap_segs_overlap
                  }
                  overlap_bottom_over = {
                      overlap_start: overlap_segs_overlap[:overlap_end],
                      overlap_end: overlap_segs[j][:overlap_y][:overlap_end]
                  }
                  overlap_bottom = {
                      index_a1: overlap_segs[j][:index_a1],
                      index_a2: overlap_segs[j][:index_a2],
                      index_b1: overlap_segs[j][:index_b1],
                      index_b2: overlap_segs[j][:index_b2],
                      point_b1: overlap_segs[j][:point_b1],
                      point_b2: overlap_segs[j][:point_b2],
                      overlap_y: overlap_bottom_over
                  }
                  # delete the existing y projection overlaps and replace it with the ones we just made.
                  if curr_seg_index > j
                    overlap_segs.delete_at(curr_seg_index)
                    overlap_segs.delete_at(j)
                  else
                    overlap_segs.delete_at(j)
                    overlap_segs.delete_at(curr_seg_index)
                  end
                  overlap_segs << overlap_top
                  overlap_segs << overlap_mid_seg
                  overlap_segs << overlap_mid_segs
                  overlap_segs << overlap_bottom
                  restart = true
                  break
                elsif (overlap_seg[:overlap_y][:overlap_start] >= overlap_segs[j][:overlap_y][:overlap_end]) && (overlap_seg[:overlap_end] < overlap_segs[j][:overlap_end]) && (overlap_seg[:overlap_y][:overlap_start] <= overlap_segs[j][:overlap_y][:overlap_start])
                  # if overlap_seg covers the bottom of overlap_segs[j] then break overlap_segs[j] into a top and an overlap portion
                  # ond break overlap_seg into an overlap portion and a bottom portion.
                  overlap_top_over = {
                      overlap_start: overlap_segs[j][:overlap_y][:overlap_start],
                      overlap_end: overlap_segs_overlap[:overlap_start]
                  }
                  overlap_top = {
                      index_a1: overlap_segs[j][:index_a1],
                      index_a2: overlap_segs[j][:index_a2],
                      index_b1: overlap_segs[j][:index_b1],
                      index_b2: overlap_segs[j][:index_b2],
                      point_b1: overlap_segs[j][:point_b1],
                      point_b2: overlap_segs[j][:point_b2],
                      overlap_y: overlap_top_over
                  }
                  overlap_mid_seg = {
                      index_a1: overlap_seg[:index_a1],
                      index_a2: overlap_seg[:index_a2],
                      index_b1: overlap_seg[:index_b1],
                      index_b2: overlap_seg[:index_b2],
                      point_b1: overlap_seg[:point_b1],
                      point_b2: overlap_seg[:point_b2],
                      overlap_y: overlap_segs_overlap
                  }
                  overlap_mid_segs = {
                      index_a1: overlap_segs[j][:index_a1],
                      index_a2: overlap_segs[j][:index_a2],
                      index_b1: overlap_segs[j][:index_b1],
                      index_b2: overlap_segs[j][:index_b2],
                      point_b1: overlap_segs[j][:point_b1],
                      point_b2: overlap_segs[j][:point_b2],
                      overlap_y: overlap_segs_overlap
                  }
                  overlap_bottom_over = {
                      overlap_start: overlap_segs_overlap[:overlap_end],
                      overlap_end: overlap_seg[:overlap_y][:overlap_end]
                  }
                  overlap_bottom = {
                      index_a1: overlap_seg[:index_a1],
                      index_a2: overlap_seg[:index_a2],
                      index_b1: overlap_seg[:index_b1],
                      index_b2: overlap_seg[:index_b2],
                      point_b1: overlap_seg[:point_b1],
                      point_b2: overlap_seg[:point_b2],
                      overlap_y: overlap_bottom_over
                  }
                  # delete the existing y projection overlaps and replace it with the ones we just made.
                  if curr_seg_index > j
                    overlap_segs.delete_at(curr_seg_index)
                    overlap_segs.delete_at(j)
                  else
                    overlap_segs.delete_at(j)
                    overlap_segs.delete_at(curr_seg_index)
                  end
                  overlap_segs << overlap_top
                  overlap_segs << overlap_mid_seg
                  overlap_segs << overlap_mid_segs
                  overlap_segs << overlap_bottom
                  restart = true
                  break
                end
              end
            end
            if restart == true
              break
            end
          end
        end
        return overlap_segs
      end

      # This method determines if the y component of 2 lines overlap.
      # point_a1:  The top of the first line
      # point_a2:  The bottom of the first line
      # point_b1:  The bottom of the second line
      # point_b2:  The top of the second line.
      # This naming convention was chosen because this method was originally designed to work with the
      # 'make_concave_surfaces' method (see above).  That method choses lines that point up and then sees where they
      # overlap with lines pointing down.  The point_1 of each line is the end of the line.  In this case a lines point
      # up and b lines point down.
      def self.line_segment_overlap_y?(point_a1:, point_a2:, point_b1:, point_b2:)
        overlap_start = nil
        overlap_end = nil
        # If line a overlaps with the bottom of line b do this:
        if (point_a1 >= point_b1) && (point_a2 <= point_b1)
          overlap_start = point_a1
          overlap_end = point_b1
          # This checks if all of line b is overlapped by line a
          if point_a1 >= point_b2
            overlap_start = point_b2
          end
          # If line a overlaps with the top of line b do this:
        elsif (point_a1 >= point_b2) && (point_a2 <= point_b2)
          overlap_start = point_b2
          overlap_end = point_a2
          # This checks if all of line b is overlapped by line a
          if point_a2 <= point_b1
            overlap_end = point_b1
          end
          # This checks if all of line a fits in line b
        elsif (point_a1 <= point_b2) && (point_a2 >= point_b1)
          overlap_start = point_a1
          overlap_end = point_a2
        end
        # Overlap vectors always point down.  Thus overlap_start is the y location of the top of the overlap vector and
        # overlap_end is the y location of the bottom of the overlap vector.  The overlap vector will later be constructed
        # using point_b1 and point_b2 and checking which overlaps are closest (and not obstructed) by other overlaps.
        overlap_y = {
            overlap_start: overlap_start,
            overlap_end: overlap_end
        }
        return overlap_y
      end

      # This method determines the x coordinate of where a given y coordinate crosses a given line.
      # y_check:  The y coordinate that you want to determine the x coordinate for on a line
      # point_b1: The coordinates of the bottom of the line
      # point_b2: The coordinates of the top of the line
      def self.line_segment_overlap_x_coord(y_check:, point_b1:, point_b2:)
        # If the line is vertical then all x coordinates are the same
        if point_b1[:x] == point_b2[:x]
          xcross = point_b2[:x]
          # If the line is horizontal you cannot find the y intercept
        elsif (point_b1[:y] == point_b2[:y])
          raise("This line is horizontal so no y intercept can be found.")
          # Otherwise determine the line coefficients and get the intercept
        else
          a = (point_b1[:y] - point_b2[:y]) / (point_b1[:x] - point_b2[:x])
          b = point_b1[:y] - a * point_b1[:x]
          xcross = (y_check - b) / a
        end
        return xcross
      end

      # This method finds the centroid of a surface using the point averaging method.  OpenStudio already has something
      # which does this but you have to turn something into a special OpenStudio surface first which you may not want
      # to do.
      def self.surf_centroid(surf:)
        new_surf_cent = {
            x: 0,
            y: 0,
            z: 0
        }
        surf.each do |surf_vert|
          new_surf_cent[:x] += surf_vert[:x]
          new_surf_cent[:y] += surf_vert[:y]
          new_surf_cent[:z] += surf_vert[:z]
        end
        new_surf_cent[:x] /= surf.length
        new_surf_cent[:y] /= surf.length
        new_surf_cent[:z] /= surf.length
        return new_surf_cent
      end

      # This method calculates the surface area of a 2-D polygon from an array of OpenStudio vertices.  It ignores any z
      # vertices.  This method assumes that the polygon is complete, has no holes, does not cross itself, and that the
      # vertices are provided in counter-clockwise order.  This method is used in cases when you want to find the area
      # of something before creating an OpenStudio surface/subsurface.
      #
      # Input arguments:
      # vetices:  Array of openstudio vertices.
      #
      # Output:
      # Area:  Float, area of polygot represented by the vertices.
      def self.getSurfaceAreafromVertices(vertices:)
        area = 0.0
        numberVertices = vertices.size

        # Check that a polygon is actually provided and not just a line or point.  Return 0 if the vertices are a line
        # or point.
        return 0.0 if numberVertices < 3

        # Go through the vertices and get the cross product.  This adopted from:
        # https://web.archive.org/web/20100405070507/http://valis.cs.uiuc.edu/~sariel/research/CG/compgeom/msg00831.html
        vertices.each_with_index do |vertex, i|
          j = (i + 1) % numberVertices
          area += vertex.x.to_f * vertices[j].y.to_f
          area -= vertex.y.to_f * vertices[j].x.to_f
        end
        return area
      end
    end #Module Surfaces
  end #module Geometry
end