# ******************************************************************************* # OpenStudio(R), Copyright (c) 2008-2021, 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://nrel.github.io/OpenStudio-user-documentation/reference/measure_writing_guide/ # load OpenStudio measure libraries from openstudio-extension gem require 'openstudio-extension' require 'openstudio/extension/core/os_lib_helper_methods' # start the measure class MakeShadingSurfacesBasedOnZoneMultipliers < OpenStudio::Measure::ModelMeasure # human readable name def name return 'Make Shading Surfaces Based on Zone Multipliers' end # human readable description def description return 'Initially this will jsut focus on Z shifting of geometry, but in future could work on x,z or y,z multiplier grids like what is use don the large hotel' end # human readable description of modeling approach def modeler_description return 'Not sure how I will handle arguments. Maybe lump together all spaces on same sotry that have the same multilier value. This will have variable number of arguments basd on the model pased in. Alternative is to either only allo w one group to be chosen at at time, or allow a comlex string that describes everything. Also need to see how to define shirting. There is an offset but it may be above and below and may not be equal. In Some cases a mid floor is halfway betwen floors which makes just copying the base surfaces as shading multiple times probemeatic, since there is overlap. It coudl be nice to stretch one surface over many stories. If I check for vertial adn orthogonal surface that may work fine. ' end # define the arguments that the user will input def arguments(model) args = OpenStudio::Measure::OSArgumentVector.new # TODO: - make arguments for each group of common non 1 multipliers on the same story # TODO: - optionally could also list individual zones with non 1 multiliers as well # TODO: - argument for z offset distance per zone z_offset_dist = OpenStudio::Measure::OSArgument.makeDoubleArgument('z_offset_dist', true) z_offset_dist.setDisplayName('Z offset distance for selcected zones.') z_offset_dist.setDefaultValue(10.0) z_offset_dist.setUnits('ft') args << z_offset_dist # TODO: - argument for z start offset starting position (0 is equal above and below) z_num_pos = OpenStudio::Measure::OSArgument.makeIntegerArgument('z_num_pos', true) z_num_pos.setDisplayName('Number of copies in the positive direction.') z_num_pos.setDescription('Should be integer no more than the multiplier - 1') z_num_pos.setDefaultValue(1) # TODO: - replace with half of multiplier rounded up args << z_num_pos # TODO: - argument for x offset distance per zone # TODO: - argument for x start offset starting position (0 is equal above and below) # TODO: - argument for y offset distance per zone # TODO: - argument for y start offset starting position (0 is equal above and below) return args end # define what happens when the measure is run def run(model, runner, user_arguments) super(model, runner, user_arguments) # assign the user inputs to variables args = OsLib_HelperMethods.createRunVariables(runner, model, user_arguments, arguments(model)) if !args then return false end # report initial condition of model runner.registerInitialCondition("The building started with #{model.getShadingSurfaces.size} shading surfaces.") # find thermal zones with multiplier greater than 1 zones_to_alter = {} model.getThermalZones.each do |zone| if zone.multiplier > 1 puts "#{zone.name} has a multiplier of #{zone.multiplier}" zones_to_alter[zone.spaces] = zone.multiplier end end # gather inputs z_offset_si = OpenStudio.convert(args['z_offset_dist'], 'ft', 'm').get # gather surfaces to copy surfaces_to_copy = {} zones_to_alter.each do |spaces, multiplier| spaces.each do |space| # space_origin origin = [space.xOrigin, space.yOrigin, space.zOrigin] origin_pos_z = space.zOrigin args['z_num_pos'].times do origin_pos_z += z_offset_si # make shading surface group and set origin shading_surface_group = OpenStudio::Model::ShadingSurfaceGroup.new(model) shading_surface_group.setXOrigin(origin[0]) shading_surface_group.setYOrigin(origin[1]) shading_surface_group.setZOrigin(origin_pos_z) space.surfaces.each do |surface| next if surface.outsideBoundaryCondition != 'Outdoors' surfaces_to_copy[surface] = multiplier # store vertices vertices = surface.vertices # make shading surface for new group shading_surface = OpenStudio::Model::ShadingSurface.new(vertices, model) shading_surface.setShadingSurfaceGroup(shading_surface_group) shading_surface.setName("mult - #{surface.name}") end end origin_neg_z = space.zOrigin num_nug = (multiplier - args['z_num_pos']) - 1 # one copy already exist, so only need multiplier - 1 num_nug.times do origin_neg_z -= z_offset_si # make shading surface group and set origin shading_surface_group = OpenStudio::Model::ShadingSurfaceGroup.new(model) shading_surface_group.setXOrigin(origin[0]) shading_surface_group.setYOrigin(origin[1]) shading_surface_group.setZOrigin(origin_neg_z) space.surfaces.each do |surface| next if surface.outsideBoundaryCondition != 'Outdoors' surfaces_to_copy[surface] = multiplier # store vertices vertices = surface.vertices # make shading surface for new group shading_surface = OpenStudio::Model::ShadingSurface.new(vertices, model) shading_surface.setShadingSurfaceGroup(shading_surface_group) shading_surface.setName("mult - #{surface.name}") end end end end # TODO: - stretching on non orthogonal won't work, take different approach in those cases. # report final condition of model runner.registerFinalCondition("The building finished with #{model.getShadingSurfaces.size} surfaces.") return true end end # register the measure to be used by the application MakeShadingSurfacesBasedOnZoneMultipliers.new.registerWithApplication