Question-and-Answer Resource for the Building Energy Modeling Community
Get started with the Help page
Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

How does one insert daylighting shelves into model using a measure?

Hi, I've searched Unmet Hours for some direction on my question and found only 3 posts.
My goal is to place daylight shelves on the equator-facing wall of a single room, to obtain even light distribution. I only want to see if this will actually work, by installing them, running a simulation, and then studying the DC maps. I altered an existing measure (as understood from this post) to add a single light shelf, and although the measure runs, no light shelves are actually added.

Here's what has happened between my goal and my problem:

With some background in Python, I did some Ruby tutorials and started studying some measures for structure and hierarchy, in particular, the ''AddOverhangsByProjectionFactor' and 'AEDGSmallToMediumOfficeFenestrationAndDaylightingControls' measures. I would like only to see if adding light shelves will in fact help me reach my goal, so I'm not after extra functionality. So I started really small to get a single light shelf on one wall, naming the targeted SubSurface inside the .rb file itself. Studying the SDK documentation and the Measure Writing Guide on Git and on OS website, and taking cues from the measures I studied, addDaylightingDeviceShelf() seemed a good method to choose. But since this method returns an 'OptionalDaylightingShelf'; thus, I checked if it was empty and finding it wasn't, I tried to use the "get" method to see what the variable was pointing to. But the .get method called on this object is not recognized. (However, the measure does run without calling the "get" method, although without any light shelves inserted into the model.)

Here is my code trying the get method (I include the entire measure, so that if anything is missed, it will be obvious):

# start the measure

class AddDaylightShelvesToEquatorFacingWall < OpenStudio::Ruleset::ModelUserScript

# define the name that the user will see ... human readable def name return "Add Daylight Shelves to Equator-facing Wall" # "Add Remove Or Replace Window Overhangs" end

# human readable description def description return "This measure adds daylighting shelves to the equator-facing wall, or to wall specified by the user, at the user-specified width.
Daylighting shelves are useful in creating occupant comfort, both visual and thermal, thus increasing work productivity.
Daylight shelves bounce daylight off ceiling surfaces, thus pushing light further into a building and reducing the need for artificial lighting at certain times of day. If properly designed, they can help to reduce the excessive use of blinds for glare protection, and thus allow for occupant view and more effective use of a space. In combination with exterior shading, daylight shelves evenly distribute light and avoid stark light/dark contrasts and glare over the work plane. Daylighting shelves require high floor to ceiling heights to be effective, given the requirement for daylighting windows (above the daylight shelves positioned above eye level) in addition to view windows below. Note that in some jurisdictions, daylight shelves have a maximum width requirement, due to coordination with sprinkler code requirements. Upper surface of shelves must have high reflectance." end

# human readable description of modeling approach def modeler_description return "More to be added ..." end

# define the arguments that the user will input def arguments(model)

args = OpenStudio::Ruleset::OSArgumentVector.new

function_choices = OpenStudio::StringVector.new
function_choices << "Add"
#function_choices << "Remove"
function_choices << "Replace"
function = OpenStudio::Ruleset::OSArgument::makeChoiceArgument("function", function_choices, true)
function.setDisplayName("Function")
args << function
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 not runner.validateUserArguments(arguments(model), user_arguments)
  return false
end

# assign user arguments to variables, check values, and convert to SI units for simulation
function = runner.getStringArgumentValue("function", user_arguments)

# 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 = sprintf "%.2f", number
  else
    number = number.round
  end
  #regex to add commas
  number.to_s.reverse.gsub(%r{([0-9]{3}(?=([0-9])))}, "\\1,").reverse
end

# helper to make it easier to do unit conversions on the fly. The definition to be called throughout 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

# flag for not applicable
lightshelf_added = false  

# initialize variables
add_replace_count = 0

# get model objects
shading_groups = model.getShadingSurfaceGroups
subsurfaces = model.getSubSurfaces

# report initial condition
number_of_exist_bldg_shading_surf = 0
shading_groups.each do |shading_group|
  if shading_group.shadingSurfaceType == "Space" 
    number_of_exist_bldg_shading_surf = number_of_exist_bldg_shading_surf + shading_group.shadingSurfaces.size
  end
end
runner.registerInitialCondition("number of overhangs = #{number_of_exist_bldg_shading_surf}")

# add window daylight shelves 
# loop through subsurfaces to find a specific subsurface to add daylight shelves to    
subsurfaces.each do |s|

  if s.name.get.match("Sub Surface 7")  

    if function == "Add" or function == "Replace"

      new_lightshelf = s.addDaylightingDeviceShelf()  
      runner.registerInfo("#{new_lightshelf.class}" + s.briefDescription)

      if new_lightshelf.empty? #if new_overhang.empty?
        ok = runner.registerWarning("Unable to add lightshelf to subsurface:" + s.briefDescription) 
        return false if not ok
      else
        new_lightshelf = new_lightshelf.get()  
        #new_lightshelf = s.daylightingDeviceShelf.get
        new_lightshelf.get.setName("#{s.name} - Lightshelf") 
        runner.registerInfo("Added window lightshelf " + new_lightshelf.get.briefDescription + " to " + s.briefDescription)

        lightshelf_added = true
      end

    end  # ends the if function == "Add" ... condition

    add_replace_count += 1

  end  # ends the if s.name.get.match ... condition

end  # ends the subsurfaces.each do loop 

if not lightshelf_added  #if not overhang_added and not function == "Remove"
  runner.registerAsNotApplicable("No windows were found to add lightshelves to.") # on #{facade} exterior walls.")
  return true
end

if function == "Add" or function == "Replace"
  runner.registerInfo("lightshelves added or replaced = #{add_replace_count}")
#elsif function == "Remove"
  #runner.registerInfo("overhangs removed = #{remove_count}")
end


return true

end #run

end #measure

AddDaylightShelvesToEquatorFacingWall.new.registerWithApplication

(NB: function arg left in, really a dummy variable, since no args not feasible)

Here is my error: image description

I'm also wondering why there are no arguments for entering a width/size of light shelf ... I recognize my fledgling understanding of writing measures in Ruby! Also, from the I/O Ref guide, under 1.14.11 DaylightingDevice:Shelf, I learned that if no inside or outside shelf are specified, there is no effect of the daylighting shelf on the simulation. Trouble is, there are no ways for adding an interior shelf via the addDaylightingDeviceShelf method. But given there is the InsideShelf() and setInsideShelf() methods when a new DaylightDeviceShelf object is instantiated from the DaylightingDeviceShelf class, I then tried instantiating a light shelf object like this: new_lightshelf = OpenStudio::Model::DaylightingDeviceShelf.new ...in order to call methods on the new_lightshelf object. But it wouldn't take "model" in its argument... I got this error instead: "Error: Expected argument 0 of type openstudio::model::SubSurface const &, but got OpenStudio::Model::Model #<openstudio::model::model:0x0... in="" swig="" method="" 'daylightingdeviceshelf'"="" i="" fail="" to="" understand="" (nor="" could="" i="" find="" any="" documentation="" that="" explains)="" the="" const="" and="" &amp;="" c="" +="" +="" keywords="" required="" in="" the="" argument="" for="" the="" method:="" daylightingdeviceshelf="" (const="" subsurface="" &amp;subsurface)="" any="" clarification="" would="" be="" greatly="" appreciated!<="" p="">

Then, noting that the same boost::optional<object> result is also returned from the addOverhang method in the "AddOverhangs...' measure (likewise for the 'AEDGSmall...' measure), I ran that measure on my example model, too -- and similarly, the results read that overhangs were added, but no overhangs are actually placed in the example model I ran on. I have not altered these measures in any way, so I'm assuming that the measures ran correctly and made alterations that can be used only during the simulation, but did not alter the model itself. Is there a way to cause the measure to insert those new overhangs/daylight shelves instead of just running them for simulation results? Is it feasible to create a measure that inserts daylighting device shelf objects into the model in the way the user scripts do for overhangs inside the SU plugin?

I realize there are a lot of questions surrounding this issue ... but I would appreciate any answers or suggestions! Thank you!

How does one insert daylighting shelves into model using a measure?

Hi, I've searched Unmet Hours for some direction on my question and found only 3 posts.
My goal is to place daylight shelves on the equator-facing wall of a single room, to obtain even light distribution. I only want to see if this will actually work, by installing them, running a simulation, and then studying the DC maps. I altered an existing measure (as understood from this post) to add a single light shelf, and although the measure runs, no light shelves are actually added.

Here's what has happened between my goal and my problem:

With some background in Python, I did some Ruby tutorials and started studying some measures for structure and hierarchy, in particular, the ''AddOverhangsByProjectionFactor' and 'AEDGSmallToMediumOfficeFenestrationAndDaylightingControls' measures. I would like only to see if adding light shelves will in fact help me reach my goal, so I'm not after extra functionality. So I started really small to get a single light shelf on one wall, naming the targeted SubSurface inside the .rb file itself. Studying the SDK documentation and the Measure Writing Guide on Git and on OS website, and taking cues from the measures I studied, addDaylightingDeviceShelf() seemed a good method to choose. But since this method returns an 'OptionalDaylightingShelf'; thus, I checked if it was empty and finding it wasn't, I tried to use the "get" method to see what the variable was pointing to. But the .get method called on this object is not recognized. (However, the measure does run without calling the "get" method, although without any light shelves inserted into the model.)

Here is my code trying the get method (I include the entire measure, so that if anything is missed, it will be obvious):

# start the measure

class AddDaylightShelvesToEquatorFacingWall < OpenStudio::Ruleset::ModelUserScript

# define the name that the user will see ... human readable def name return "Add Daylight Shelves to Equator-facing Wall" # "Add Remove Or Replace Window Overhangs" end

# human readable description def description return "This measure adds daylighting shelves to the equator-facing wall, or to wall specified by the user, at the user-specified width.
Daylighting shelves are useful in creating occupant comfort, both visual and thermal, thus increasing work productivity.
Daylight shelves bounce daylight off ceiling surfaces, thus pushing light further into a building and reducing the need for artificial lighting at certain times of day. If properly designed, they can help to reduce the excessive use of blinds for glare protection, and thus allow for occupant view and more effective use of a space. In combination with exterior shading, daylight shelves evenly distribute light and avoid stark light/dark contrasts and glare over the work plane. Daylighting shelves require high floor to ceiling heights to be effective, given the requirement for daylighting windows (above the daylight shelves positioned above eye level) in addition to view windows below. Note that in some jurisdictions, daylight shelves have a maximum width requirement, due to coordination with sprinkler code requirements. Upper surface of shelves must have high reflectance." end

# human readable description of modeling approach def modeler_description return "More to be added ..." end

# define the arguments that the user will input def arguments(model)

args = OpenStudio::Ruleset::OSArgumentVector.new

function_choices = OpenStudio::StringVector.new
function_choices << "Add"
#function_choices << "Remove"
function_choices << "Replace"
function = OpenStudio::Ruleset::OSArgument::makeChoiceArgument("function", function_choices, true)
function.setDisplayName("Function")
args << function
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 not runner.validateUserArguments(arguments(model), user_arguments)
  return false
end

# assign user arguments to variables, check values, and convert to SI units for simulation
function = runner.getStringArgumentValue("function", user_arguments)

# 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 = sprintf "%.2f", number
  else
    number = number.round
  end
  #regex to add commas
  number.to_s.reverse.gsub(%r{([0-9]{3}(?=([0-9])))}, "\\1,").reverse
end

# helper to make it easier to do unit conversions on the fly. The definition to be called throughout 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

# flag for not applicable
lightshelf_added = false  

# initialize variables
add_replace_count = 0

# get model objects
shading_groups = model.getShadingSurfaceGroups
subsurfaces = model.getSubSurfaces

# report initial condition
number_of_exist_bldg_shading_surf = 0
shading_groups.each do |shading_group|
  if shading_group.shadingSurfaceType == "Space" 
    number_of_exist_bldg_shading_surf = number_of_exist_bldg_shading_surf + shading_group.shadingSurfaces.size
  end
end
runner.registerInitialCondition("number of overhangs = #{number_of_exist_bldg_shading_surf}")

# add window daylight shelves 
# loop through subsurfaces to find a specific subsurface to add daylight shelves to    
subsurfaces.each do |s|

  if s.name.get.match("Sub Surface 7")  

    if function == "Add" or function == "Replace"

      new_lightshelf = s.addDaylightingDeviceShelf()  
      runner.registerInfo("#{new_lightshelf.class}" + s.briefDescription)

      if new_lightshelf.empty? #if new_overhang.empty?
        ok = runner.registerWarning("Unable to add lightshelf to subsurface:" + s.briefDescription) 
        return false if not ok
      else
        new_lightshelf = new_lightshelf.get()  
        #new_lightshelf = s.daylightingDeviceShelf.get
        new_lightshelf.get.setName("#{s.name} - Lightshelf") 
        runner.registerInfo("Added window lightshelf " + new_lightshelf.get.briefDescription + " to " + s.briefDescription)

        lightshelf_added = true
      end

    end  # ends the if function == "Add" ... condition

    add_replace_count += 1

  end  # ends the if s.name.get.match ... condition

end  # ends the subsurfaces.each do loop 

if not lightshelf_added  #if not overhang_added and not function == "Remove"
  runner.registerAsNotApplicable("No windows were found to add lightshelves to.") # on #{facade} exterior walls.")
  return true
end

if function == "Add" or function == "Replace"
  runner.registerInfo("lightshelves added or replaced = #{add_replace_count}")
#elsif function == "Remove"
  #runner.registerInfo("overhangs removed = #{remove_count}")
end


return true

end #run

end #measure

AddDaylightShelvesToEquatorFacingWall.new.registerWithApplication

(NB: function arg left in, really a dummy variable, since no args not feasible)

Here is my error: image description

I'm also wondering why there are no arguments for entering a width/size of light shelf ... I recognize my fledgling understanding of writing measures in Ruby! Also, from the I/O Ref guide, under 1.14.11 DaylightingDevice:Shelf, I learned that if no inside or outside shelf are specified, there is no effect of the daylighting shelf on the simulation. Trouble is, there are no ways for adding an interior shelf via the addDaylightingDeviceShelf method. But given there is the InsideShelf() and setInsideShelf() methods when a new DaylightDeviceShelf object is instantiated from the DaylightingDeviceShelf class, I then tried instantiating a light shelf object like this: new_lightshelf = OpenStudio::Model::DaylightingDeviceShelf.new ...in order to call methods on the new_lightshelf object. But it wouldn't take "model" in its argument... I got this error instead: "Error: Expected argument 0 of type openstudio::model::SubSurface const &, but got OpenStudio::Model::Model #<openstudio::model::model:0x0... in="" swig="" method="" 'daylightingdeviceshelf'"="" i="" fail="" to="" understand="" (nor="" could="" i="" find="" any="" documentation="" that="" explains)="" the="" const="" and="" &amp;="" c="" +="" +="" keywords="" required="" in="" the="" argument="" for="" the="" method:="" daylightingdeviceshelf="" (const="" subsurface="" &amp;subsurface)="" any="" clarification="" would="" be="" greatly="" appreciated!<="" p="">

Then, noting that the same boost::optional<object> result is also returned from the addOverhang method in the "AddOverhangs...' measure (likewise for the 'AEDGSmall...' measure), I ran that measure on my example model, too -- and similarly, the results read that overhangs were added, but no overhangs are actually placed in the example model I ran on. I have not altered these measures in any way, so I'm assuming that the measures ran correctly and made alterations that can be used only during the simulation, but did not alter the model itself. Is there a way to cause the measure to insert those new overhangs/daylight shelves instead of just running them for simulation results? Is it feasible to create a measure that inserts daylighting device shelf objects into the model in the way the user scripts do for overhangs inside the SU plugin?

I realize there are a lot of questions surrounding this issue ... but I would appreciate any answers or suggestions! Thank you!