Saturday, March 22, 2014

Easy Visualizations of PHPP Energy Performance With A Sketchup Ruby Script

Cooling-Period Losses & Gains (kWh)



We're always on the lookout for new ways to work with our tools and get them to interact better with one another. Like most designers, we can often use 5 or 10 different pieces of software for any given job. Two of our favorites are PHPP (the Passive House Planning Package Energy modeling software) and Sketchup - the simple 3D Modeler. While we certainly love the 3DMaxe-s and V-Ray-s of the world, Sketchup has a definite place in our workflow,, especially when it comes to the diagram. The simple line-work and flexibility of the modeler make Sketchup a great tool for simple 3D diagrams.

Recently we've begun working with Ruby to interact with Sketchup a bit more. I caution you - we're in no ways experts in (or even all that good at) Ruby, but we thought we'd post a bit about one of our recent tools we made. This script uses Ruby to import values, calculated in the PHPP Energy Modeling software, and then apply them as attributes to 3D Geometry in Sketchup.

This, of course, will probably all be entirely obsolete as soon as they release the PHPP Sketchup tool DesignPH (though who knows when that will ever happen in the US ..... ) But for now this is a nice way to perform both data verification (by using color to spot abnormalities and errors) and to display information to clients and others working on the project.

PHPP is an incredibly robust energy modeling package, but of course its completely devoid of any graphical content save for a couple little graphs and a chart or two buried in there. While you engineers out there might love that kind of thing, those of us on the more architect-y end of the spectrum can have a tough time with nothing but page after page of numbers. Its also often difficult to translate this data into something meaningful for our clients or design-partners. 

recently, out of necessity we built a pair of Ruby scripts which can take some of this mush of data and bring it into Sketchup. The image at the top of this post shows you the kind of thing we're after - using visuals to more intuitively show energy performance in a flexible way which allows us to move quickly through multiple versions and options. 

So  - how does it work?

First, the data in PHPP needs to be consolidated into an export sheet. While the 'Windows' sheet does a decent enough job giving some info about the window performance - we actually like to get a finer grain of feedback separately for the heating (winter) and cooling (summer) periods for our analysis. 

The image below is from one of our custom sheets from a recent project - its mostly all reporting from different locations within PHPP (sort of), but collected in one place so we can more effectively assess the window performance. 

Custom sheet Heating Period Reporting Table

Once you set this up for the heating and cooling periods, you can pull out whatever data your interested in. In this case, its the Window Heating and Cooling Period's Net Gain (or Loss) values. These are exported to a simple CSV file which is what we use to move data into Sketchup.

CSV of the Data we want to bring into Sketchup - these numbers are all kWh / period

Ok - so far pretty easy right? Its just messing around in Excel really. So now that you have the CSV, we move to Ruby. We are still learning Ruby - so please don't yell at us if (when) you see us doing something not quite right.

The Sketchup workflow actually has two scripts - one which imports data, and the other which colors elements

First, we select the PHPP CSV Importer  tool from the plugins menu...



Next enter the path for the CSV file you just saved out from the PHPP Excel model.

Enter the CSV columns for the data types you are importing (column 1 in the CSV is column 0 in the ruby here) - yes, this is all pretty User-Unfriendly - you need to know a lot about how the data is set up before this can work - I'll build it out so its nicer to work with . . . someday. . .



This now takes the data and applies it to the model. It basically looks around at the objects and matches the names of the objects in the Sketchup model with the Names on the CSV list. The code looks like this:

mod = Sketchup.active_model # Open model
ent = mod.entities # All entities in model
sel = mod.selection # Current selection

#This is the Method to call for importing data
def PHPP_CSV_Importer.assignAttribute(imported_array, column, attrib_name)
      mod = Sketchup.active_model # Open model
      ent = mod.entities # All entities in model
      sel = mod.selection # Current selection
      _imported_data_array = imported_array
      _column = column  
      _attrib_name = attrib_name
      
      #This part ‘clears’ all the existing attribute values
      ent.each do |e|
        e.set_attribute("dynamic_attributes", _attrib_name, 0)
      end
           
      #This part steps through every Entity (in Ruby, all Sketchup objects are called 'Entities'
      ent.each do |e|
        entName = e.get_attribute("dynamic_attributes", "_name")
        limit = _imported_data_array.length
        i = 0
        j = 0
        
          for counter in 0..limit-1
            if entName == _imported_data_array[j][0]
              _importedValue = _imported_data_array[j][_column]
              e.set_attribute("dynamic_attributes", _attrib_name, _importedValue.chomp.to_i )
              j+=1
            else
              j+=1
            end
          end 
        
      end
      
    end
    
    #Below this is what runs when you select from the PLUGINS menu
    #This part is to get the file location and Columns for import
    prompts = ["File Location", "Heating Col.", "Cooling Col.", "Yearly Col."]
    defaults = ["/Users/edwinmay/Desktop/Workbook1.csv", "1", "2", "3"]
    inputValue = UI.inputbox(prompts, defaults, "File to Import")
    heatingCSVcolumn = inputValue[1].chomp.to_i
    coolingCSVcolumn = inputValue[2].chomp.to_i
    yearlyCSVcolumn = inputValue[3].chomp.to_i
      
    
    #This part tells where the file to read is
    fname = "/Users/edwinmay/Desktop/Workbook1.csv" #inputValue[0]
    f = File.new(fname, "r");
    lines = f.readlines(); #This makes an array of each line - each line is a string
    f.close();

    #This part splits the imported file(array) into a 2D array at the commas
    imported_data_array = []
    lines.each { |r|
      line_array = []
      line_array = r.split(',')
      imported_data_array << line_array
      }

###########
# Main body of the Program - definition calls
###########    
    assignAttribute(imported_data_array, heatingCSVcolumn, "summernet")
    assignAttribute(imported_data_array, coolingCSVcolumn, "winternet")
    assignAttribute(imported_data_array, yearlyCSVcolumn, "yearnet")
  end
end


As you can see, this will make attributes and apply the imported values. So far so good right? But still no colors yet. But f you look at any of the window objects you'll see that the values have been applied.

Attributes from PHPP applied to the 3D Geometry as an attribute

So, if you select the windows or elements you want to colorize, then select the PHPP Visualizer from the plugins menu, you should be prompted which period you want to see - simple select one and the colors will be applied and show up. You can run this as many times as you like and it will re-style everything accordingly.


Select the PHPP Visualizer Tool from the Menu

Select the Period you want to see represented

For those interested, the code for the Visualizer looks like this:

###########
# VARIABLES
###########
module PHPP_Vis
  def PHPP_Vis.main
    mod = Sketchup.active_model # Open model
    ent = mod.entities # All entities in model
    sel = mod.selection # Current selection
    maxCount = sel.count-1
    maxSumNetGains = 1810
    maxSumNetLosses = 330
    maxWintNetGains = 1070
    maxWintNetLosses = 400
    maxNetGains = 2870
    maxNetLosses = 640
    inputValue = []
    textArray = []

###########
# METHODS
###########
    #method for displaying the Summer Net Gains/Losses of selected entities
    def PHPP_Vis.displaySumNet(sumNetGains, sumNetLosses)
      mod = Sketchup.active_model
      maxNetGains = sumNetGains
      maxNetLosses = sumNetLosses
      sel = mod.selection
        sel.each do |e|
          netValue = e.get_attribute("dynamic_attributes", "summernet")
          if netValue > 0
            r = 255
            g = 255-(((netValue)*255) / maxNetGains)
            b = 255-(((netValue)*255) / maxNetGains)
          else
            r = 255+(((netValue)*255) / maxNetLosses)
            g = 255+(((netValue)*255) / maxNetLosses)
            b = 255
          end
          e.material = r,g,b
        end
    end
    
    #method for displaying the Winter Net Gains/Losses of selected entities
    def PHPP_Vis.displayWintNet(wintNetGains, wintNetLosses)
    mod = Sketchup.active_model
      maxNetGains = wintNetGains
      maxNetLosses = wintNetLosses
      sel = mod.selection
  
      sel.each do |e|
        netValue = e.get_attribute("dynamic_attributes", "winternet")
          if netValue > 0
            r = 255
            g = 255-(((netValue)*255) / maxNetGains)
            b = 255-(((netValue)*255) / maxNetGains)
        else
            r = 255+(((netValue)*255) / maxNetLosses)
            g = 255+(((netValue)*255) / maxNetLosses)
            b = 255
        end
      e.material = r,g,b
      end
    end 
    
    #method for displaying the Yearly Net Gains/Losses of selected entities
    def PHPP_Vis.displayNet(netGains, netLosses)
      mod = Sketchup.active_model
      maxNetGains = netGains
      maxNetLosses = netLosses
      sel = mod.selection
      
      sel.each do |e|
        netValue = e.get_attribute("dynamic_attributes", "yearnet")
        if netValue > 0
          r = 255
          g = 255-(((netValue)*255) / maxNetGains)
          b = 255-(((netValue)*255) / maxNetGains)
        else
          r = 255+(((netValue)*255) / maxNetLosses)
          g = 255+(((netValue)*255) / maxNetLosses)
          b = 255
        end
    e.material = r,g,b
      end
    end

    #method for displaying Attribute Value on the Selected Elements
    def PHPP_Vis.displayText(inputValue)
      mod = Sketchup.active_model
      ent = mod.entities
      sel = mod.selection
      _inputValue = inputValue
      
      #This part applies the attribute value to the face
      sel.each do |e|
        textToDisplay = e.get_attribute("dynamic_attributes", _inputValue)
        face = sel
        cntrPoint = e.bounds.center
        point = Geom::Point3d.new cntrPoint
        vector = Geom::Vector3d.new(10,10,10) #the 10,10,10 part should use the normal to ‘push'
        pointNew = point.offset! vector
        text = ent.add_text textToDisplay, point
      end
    end
      
    #This part ‘clears’ the material colors from the last run
    ent.each do |e|  
      e.material = 255,255,255 
    end  

    #Input Box def inPutBox
    prompts = ["Display"]    
    defaults = [" "]
    list = ["Summer Net|Winter Net|Yearly Net"]
    inputValue = UI.inputbox(prompts, defaults, list, "Display")
    
    #Main Body of the Program that calls functions to color faces
    _inputValue = inputValue[0]
    if _inputValue == "Summer Net"
      displaySumNet(maxSumNetGains, maxSumNetLosses)
      #displayText("summernet")
    elsif _inputValue == "Winter Net"
      displayWintNet(maxWintNetGains, maxWintNetLosses)
    elsif _inputValue == "Yearly Net" 
      displayNet(maxNetGains,maxNetLosses)
    end
  end
 end

As you can see, there a bunch of hard-coded elements that should be more flexible (things like adjusting the scale max and min, etc..) Maybe someday I'll get to it. For now I just change the code depending on the data coming in. Hasn't been a huge pain.... yet.

Mostly its pretty simple though - normalizes the value from the attribute and uses a color scale to select the color value in R,G and B - then applies that color to the face.


Colorized Windows and Doors based on values calculated in PHPP

It works pretty well and we've used it for the last couple projects. It could certainly use some 'making-nice', but it gives us the feedback we need for these kinds of projects. One side benefit is that you instantly note outliers / errors easily in this workflow because they visually stick out like a sore thumb. 

And while it may not seem like much when your looking at a single small house with 15 windows, once you get to the larger multifamily or commercial jobs tools like this become critical. We have one Passive House in the pipeline with 70+ windows and you wouldn't want to go applying data by hand every time you made a change.

If you think you have a better idea for the code or the procedures here let us know! We're always refining things and love to hear new ideas.