Skip to content

Instantly share code, notes, and snippets.

@omarh119
Created June 18, 2021 20:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save omarh119/3b6e9dc9f2035ec6216fb9591b215a4a to your computer and use it in GitHub Desktop.
Save omarh119/3b6e9dc9f2035ec6216fb9591b215a4a to your computer and use it in GitHub Desktop.
Create Geometry
# *******************************************************************************
# OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC.
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# (1) Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# (2) Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# (3) Neither the name of the copyright holder nor the names of any contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission from the respective party.
#
# (4) Other than as required in clauses (1) and (2), distributions in any form
# of modifications or other derivative works may not use the "OpenStudio"
# trademark, "OS", "os", or any other confusingly similar designation without
# specific prior written permission from Alliance for Sustainable Energy, LLC.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
# UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
# THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# *******************************************************************************
# see the URL below for information on how to write OpenStudio measures
# http://openstudio.nrel.gov/openstudio-measure-writing-guide
# see the URL below for information on using life cycle cost objects in OpenStudio
# http://openstudio.nrel.gov/openstudio-life-cycle-examples
# see the URL below for access to C++ documentation on model objects (click on "model" in the main window to view model objects)
# http://openstudio.nrel.gov/sites/openstudio.nrel.gov/files/nv_data/cpp_documentation_it/model/html/namespaces.html
# start the measure
class BarAspectRatioStudy < OpenStudio::Measure::ModelMeasure
# define the name that a user will see, this method may be deprecated as
# the display name in PAT comes from the name field in measure.xml
def name
return "Bar Aspect Ratio Study"
end
# define the arguments that the user will input
def arguments(model)
args = OpenStudio::Measure::OSArgumentVector.new
# Main Building Params
#--------------------------------------------------------------------------------------------------------------
# make an argument for Building Long Dimension
bldg_long_len_ip = OpenStudio::Measure::OSArgument.makeDoubleArgument('bldg_long_len_ip', true)
bldg_long_len_ip.setDisplayName('Building Long Dimension')
bldg_long_len_ip.setUnits('ft')
bldg_long_len_ip.setDefaultValue(600.0)
args << bldg_long_len_ip
# make an argument for Building Short Dimension
bldg_short_len_ip = OpenStudio::Measure::OSArgument.makeDoubleArgument('bldg_short_len_ip', true)
bldg_short_len_ip.setDisplayName('Building Short Dimension')
bldg_short_len_ip.setUnits('ft')
bldg_short_len_ip.setDefaultValue(200.0)
args << bldg_short_len_ip
# make an argument for floor height
floor_to_floor_height_ip = OpenStudio::Measure::OSArgument.makeDoubleArgument('floor_to_floor_height_ip', true)
floor_to_floor_height_ip.setDisplayName('Floor to Floor Height')
floor_to_floor_height_ip.setUnits('ft')
floor_to_floor_height_ip.setDefaultValue(30.0)
args << floor_to_floor_height_ip
# North Fenestrations
#--------------------------------------------------------------------------------------------------------------
# make an argument for Man Door Area
mandoor_north_area = OpenStudio::Measure::OSArgument.makeDoubleArgument('mandoor_north_area', true)
mandoor_north_area.setDisplayName('North Man Door Area')
mandoor_north_area.setUnits('ft^2')
mandoor_north_area.setDefaultValue(160.0)
args << mandoor_north_area
# make an argument for Overhead Door Area
overheadDoor_north_area = OpenStudio::Measure::OSArgument.makeDoubleArgument('overheadDoor_north_area', true)
overheadDoor_north_area.setDisplayName('North Overhead Door Area')
overheadDoor_north_area.setUnits('ft^2')
overheadDoor_north_area.setDefaultValue(2400.0)
args << overheadDoor_north_area
# make an argument for Window Area
window_north_area = OpenStudio::Measure::OSArgument.makeDoubleArgument('window_north_area', true)
window_north_area.setDisplayName('North Window Area')
window_north_area.setUnits('ft^2')
window_north_area.setDefaultValue(100.0)
args << window_north_area
# South Fenestrations
#--------------------------------------------------------------------------------------------------------------
# make an argument for Man Door Area
mandoor_south_area = OpenStudio::Measure::OSArgument.makeDoubleArgument('mandoor_south_area', true)
mandoor_south_area.setDisplayName('South Man Door Area')
mandoor_south_area.setUnits('ft^2')
mandoor_south_area.setDefaultValue(160.0)
args << mandoor_south_area
# make an argument for Overhead Door Area
overheadDoor_south_area = OpenStudio::Measure::OSArgument.makeDoubleArgument('overheadDoor_south_area', true)
overheadDoor_south_area.setDisplayName('South Overhead Door Area')
overheadDoor_south_area.setUnits('ft^2')
overheadDoor_south_area.setDefaultValue(2400.0)
args << overheadDoor_south_area
# make an argument for Window Area
window_south_area = OpenStudio::Measure::OSArgument.makeDoubleArgument('window_south_area', true)
window_south_area.setDisplayName('South Window Area')
window_south_area.setUnits('ft^2')
window_south_area.setDefaultValue(100.0)
args << window_south_area
# East Fenestrations
#--------------------------------------------------------------------------------------------------------------
# make an argument for Man Door Area
mandoor_east_area = OpenStudio::Measure::OSArgument.makeDoubleArgument('mandoor_east_area', true)
mandoor_east_area.setDisplayName('East Man Door Area')
mandoor_east_area.setUnits('ft^2')
mandoor_east_area.setDefaultValue(160.0)
args << mandoor_east_area
# make an argument for Overhead Door Area
overheadDoor_east_area = OpenStudio::Measure::OSArgument.makeDoubleArgument('overheadDoor_east_area', true)
overheadDoor_east_area.setDisplayName('East Overhead Door Area')
overheadDoor_east_area.setUnits('ft^2')
overheadDoor_east_area.setDefaultValue(2400.0)
args << overheadDoor_east_area
# make an argument for Window Area
window_east_area = OpenStudio::Measure::OSArgument.makeDoubleArgument('window_east_area', true)
window_east_area.setDisplayName('East Window Area')
window_east_area.setUnits('ft^2')
window_east_area.setDefaultValue(100.0)
args << window_east_area
# West Fenestrations
#--------------------------------------------------------------------------------------------------------------
# make an argument for Man Door Area
mandoor_west_area = OpenStudio::Measure::OSArgument.makeDoubleArgument('mandoor_west_area', true)
mandoor_west_area.setDisplayName('West Man Door Area')
mandoor_west_area.setUnits('ft^2')
mandoor_west_area.setDefaultValue(160.0)
args << mandoor_west_area
# make an argument for Overhead Door Area
overheadDoor_west_area = OpenStudio::Measure::OSArgument.makeDoubleArgument('overheadDoor_west_area', true)
overheadDoor_west_area.setDisplayName('West Overhead Door Area')
overheadDoor_west_area.setUnits('ft^2')
overheadDoor_west_area.setDefaultValue(2400.0)
args << overheadDoor_west_area
# make an argument for Window Area
window_west_area = OpenStudio::Measure::OSArgument.makeDoubleArgument('window_west_area', true)
window_west_area.setDisplayName('West Window Area')
window_west_area.setUnits('ft^2')
window_west_area.setDefaultValue(100.0)
args << window_west_area
# Misc Params
#--------------------------------------------------------------------------------------------------------------
# make an argument to surface match
surface_matching = OpenStudio::Measure::OSArgument.makeBoolArgument('surface_matching', true)
surface_matching.setDisplayName('Surface Matching?')
surface_matching.setDefaultValue(true)
args << surface_matching
# make an argument to create zones from spaces
make_zones = OpenStudio::Measure::OSArgument.makeBoolArgument('make_zones', true)
make_zones.setDisplayName('Make Thermal Zones from Spaces?')
make_zones.setDefaultValue(true)
args << make_zones
# Make an argument for enabling debug messages
debug = OpenStudio::Measure::OSArgument.makeBoolArgument('debug', true)
debug.setDisplayName('Show debug messages?')
debug.setDefaultValue(false)
args << debug
return args
end
# define what happens when the measure is run
def run(model, runner, user_arguments)
super(model, runner, user_arguments)
# use the built-in error checking
if !runner.validateUserArguments(arguments(model), user_arguments)
return false
end
# assign the user inputs to variables
bldg_long_len_ip = runner.getDoubleArgumentValue('bldg_long_len_ip', user_arguments)
bldg_short_len_ip = runner.getDoubleArgumentValue('bldg_short_len_ip', user_arguments)
total_bldg_area_ip = bldg_long_len_ip * bldg_short_len_ip
ns_to_ew_ratio = bldg_long_len_ip/bldg_short_len_ip
num_floors = 1
floor_to_floor_height_ip = runner.getDoubleArgumentValue('floor_to_floor_height_ip', user_arguments)
surface_matching = runner.getBoolArgumentValue('surface_matching', user_arguments)
make_zones = runner.getBoolArgumentValue('make_zones', user_arguments)
# North fenestrations
mandoor_north_area_ip = runner.getDoubleArgumentValue('mandoor_north_area', user_arguments)
overheadDoor_north_area_ip = runner.getDoubleArgumentValue('overheadDoor_north_area', user_arguments)
window_north_area_ip = runner.getDoubleArgumentValue('window_north_area', user_arguments)
# South Fenestrations
mandoor_south_area_ip = runner.getDoubleArgumentValue('mandoor_south_area', user_arguments)
overheadDoor_south_area_ip = runner.getDoubleArgumentValue('overheadDoor_south_area', user_arguments)
window_south_area_ip = runner.getDoubleArgumentValue('window_south_area', user_arguments)
# East Fenestrations
mandoor_east_area_ip = runner.getDoubleArgumentValue('mandoor_east_area', user_arguments)
overheadDoor_east_area_ip = runner.getDoubleArgumentValue('overheadDoor_east_area', user_arguments)
window_east_area_ip = runner.getDoubleArgumentValue('window_east_area', user_arguments)
# West Fenestrations
mandoor_west_area_ip = runner.getDoubleArgumentValue('mandoor_west_area', user_arguments)
overheadDoor_west_area_ip = runner.getDoubleArgumentValue('overheadDoor_west_area', user_arguments)
window_west_area_ip = runner.getDoubleArgumentValue('window_west_area', user_arguments)
# test for positive inputs
if total_bldg_area_ip <= 0
runner.registerError('Enter a total building area greater than 0.')
end
if ns_to_ew_ratio <= 0
runner.registerError('Enter ratio grater than 0.')
end
if num_floors <= 0
runner.registerError('Enter a number of stories 1 or greater.')
end
if floor_to_floor_height_ip <= 0
runner.registerError('Enter a positive floor height.')
end
# helper to make numbers pretty (converts 4125001.25641 to 4,125,001.26 or 4,125,001). The definition be called through this measure.
def neat_numbers(number, roundto = 2) # round to 0 or 2)
if roundto == 2
number = format '%.2f', number
else
number = number.round
end
# regex to add commas
number.to_s.reverse.gsub(/([0-9]{3}(?=([0-9])))/, '\\1,').reverse
end
# helper to make it easier to do unit conversions on the fly. The definition be called through this measure.
def unit_helper(number, from_unit_string, to_unit_string)
converted_number = OpenStudio.convert(OpenStudio::Quantity.new(number, OpenStudio.createUnit(from_unit_string).get), OpenStudio.createUnit(to_unit_string).get).get.value
end
# calculate needed variables
footprint_ip = total_bldg_area_ip / num_floors
footprint_si = unit_helper(footprint_ip, 'ft^2', 'm^2')
floor_to_floor_height = unit_helper(floor_to_floor_height_ip, 'ft', 'm')
mandoor_north_area_si = unit_helper(mandoor_north_area_ip,'ft^2','m^2')
overheadDoor_north_area_si = unit_helper(overheadDoor_north_area_ip,'ft^2','m^2')
window_north_area_si = unit_helper(window_north_area_ip,'ft^2','m^2')
mandoor_south_area_si = unit_helper(mandoor_south_area_ip,'ft^2','m^2')
overheadDoor_south_area_si = unit_helper(overheadDoor_south_area_ip,'ft^2','m^2')
window_south_area_si = unit_helper(window_south_area_ip,'ft^2','m^2')
mandoor_east_area_si = unit_helper(mandoor_east_area_ip,'ft^2','m^2')
overheadDoor_east_area_si = unit_helper(overheadDoor_east_area_ip,'ft^2','m^2')
window_east_area_si = unit_helper(window_east_area_ip,'ft^2','m^2')
mandoor_west_area_si = unit_helper(mandoor_west_area_ip,'ft^2','m^2')
overheadDoor_west_area_si = unit_helper(overheadDoor_west_area_ip,'ft^2','m^2')
window_west_area_si = unit_helper(window_west_area_ip,'ft^2','m^2')
# variables from original rectangle script not exposed in this measure
width = Math.sqrt(footprint_si / ns_to_ew_ratio)
length = footprint_si / width
plenum_height = 0 # this doesn't look like it is used anywhere
# determine if core and perimeter zoning can be used
perimeter_zone_depth = 0
# reporting initial condition of model
starting_spaces = model.getSpaces
runner.registerInitialCondition("The building started with #{starting_spaces.size} spaces.")
# Delete current model
model.getSpaces.each do |space|
space.remove()
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, 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
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")
# Set vertical story position
story.setNominalZCoordinate(z)
end
# Helper to get Bounding Coordinates of a surface
def GetBoundingCoordinates(surface)
maxX = 0
maxY = 0
minX = 0
minY = 0
counter = 0
surface.vertices.each do |v|
if counter == 0
minX = v.x
minY = v.y
maxX = v.x
maxY = v.y
else
minX = v.x < minX ? v.x : minX
minY = v.y < minY ? v.y : minY
maxX = v.x > maxX ? v.x : maxX
maxY = v.y > maxY ? v.y : maxY
end
counter += 1
end
boundingConditions = {}
boundingConditions = {
"minX" => minX,
"minY" => minY,
"maxX" => maxX,
"maxY" => maxY,
}
return boundingConditions
end
# Create a Point3D Vector and push the subsurface vertices into an array of arrays
subs = [] # this will hold 'x' amount of subsurfaces
pts = [] # this will hold the subsurface vertices
pts.push(OpenStudio::Point3d.new(36, 0, 1))
pts.push(OpenStudio::Point3d.new(36, 0, 5))
pts.push(OpenStudio::Point3d.new(20, 0, 1))
pts.push(OpenStudio::Point3d.new(20, 0, 5))
subs.push(pts)
# put all of the spaces in the model into a vector
spaces = OpenStudio::Model::SpaceVector.new
model.getSpaces.each do |space|
# Find the appropriate surfaces to apply sub-surfaces too
space.surfaces.each do |surface|
puts surface.name
boundingCoords = GetBoundingCoordinates(surface)
# Determine which surface it is
if boundingCoords["minX"] == boundingCoords["maxX"]
# This is East or West
if boundingCoords["minX"] == 0
# This is West Wall
surface.setWindowToWallRatio(window_west_area_si / surface.grossArea())
else
# This is East Wall
surface.setWindowToWallRatio(window_east_area_si / surface.grossArea())
end
elsif boundingCoords["minY"] == boundingCoords["maxY"]
# This is North or South
if boundingCoords["minY"] == 0
# This is South Wall
surface.setWindowToWallRatio(window_south_area_si / surface.grossArea())
# surface.createSubSurfaces(subs,0.1) # this errors out for some reason...
else
# This is North Wall
surface.setWindowToWallRatio(window_north_area_si / surface.grossArea())
end
end
end
spaces << space
if make_zones
# create zones
new_zone = OpenStudio::Model::ThermalZone.new(model)
space.setThermalZone(new_zone)
zone_name = space.name.get.gsub('Space', 'Zone')
new_zone.setName(zone_name)
end
space.surfaces.each do |surface|
puts surface.subSurfaces
end
end
# the struggle to determine if im providing the correct params to createSubSurfaces by seeing what GenerateSkylights provides
mObjs = model.getModelObjects
mObjs.each do |obj|
nm = obj.name.to_s
if nm.include? "Building"
bldg = obj
bldg = bldg.to_Building.get
puts bldg.class
pattern = bldg.generateSkylightPattern(0.2,1.5,1)
#puts pattern.class
#puts pattern
#puts pattern[0].class
#puts pattern[0]
#puts pattern[0][0].class
#puts pattern[0][0]
end
end
if surface_matching
# match surfaces for each space in the vector
OpenStudio::Model.matchSurfaces(spaces)
end
# reporting final condition of model
finishing_spaces = model.getSpaces
runner.registerFinalCondition("The building finished with #{finishing_spaces.size} spaces.")
return true
end
end
# this allows the measure to be use by the application
BarAspectRatioStudy.new.registerWithApplication
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment