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

# Measure writing: import dependent Objects

Since I was unable to produce the HVAC system in OpenStudio, I'm writing a measure to import my system from an .idf file.

Problem is, when loading an object, or adding it to a workspace, all fields checked if referencing objects exist. References to objects that have not been inserted yet, are therefore deleted. Before I tried fixing this by adjusting the order of my .idf file. However, this does not work when two objects reference each other. Every broken connection would have to be fixed manually in Ruby by finding the offending object and resetting its reference. In my case this would be 100s lines of code.

I've tried looking into the workspace.insertObjects function to see if that would not have this issue, but couldn't get it to work.

Is there another way of disabling this error checking, or inserting multiple objects at the same time into a workspace?

edit retag close merge delete

Sort by » oldest newest most voted

Credits go to macumber and David. Solution was in using the .toIdfFile and adding all objects simulataneously using the .addObjects.

I created an importer for a YAML-file, which holds data for fixing the remaining missing references. This way the connection between the original .osm-file and imported .idf-file can be fixed.

I don't belong to any group in the BCL-library, so for ppl looking for a way to import an .idf file and fixing remaining links, below the full code, including example .yml file.

# YAML style file containing params to be adjusted
---
- class: BuildingSurface:Detailed
name: Surface Name
field: 4
value: OtherSideConditionsModel
- class: BuildingSurface:Detailed
name: Surface Name
field: 5
value: ICS Collector 1 OSCM
- class: SurfaceProperty:ExteriorNaturalVentedCavity
name: ICSRoofPaverExtVentCav1
field: 11
value: Zolder voor
- class: SolarCollector:IntegralCollectorStorage
name: Collector 1
field: 2
value: Surface Name


.

class ImportIDFFile < OpenStudio::Ruleset::WorkspaceUserScript

def name
return " Import IDF-file"
end

def description
return "This measure can be used to import functionality from EnergyPlus, which is not exposed (yet) to OpenStudio. E.g. HVAC-components can be imported."
end

# human readable description of modeling approach
def modeler_description
return "Imports an .idf-file from the /resources folder. Connections from .idf-file which are not satisfied internally (e.g. references to objects in the .osm-file) will be broken. Use the .yml-file to restore these references, example file provided.

Note that this measure is run after the preprocessor. This means you cannot use HVACTemplate objects, instead expand these first and copy from the .expidf-file.

"
end

# define the arguments that the user will input
def arguments(workspace)
args = OpenStudio::Ruleset::OSArgumentVector.new

idf_file = OpenStudio::Ruleset::OSArgument::makeStringArgument('idf_file', false)
idf_file.setDisplayName("IDF-file")
idf_file.setDescription("The name of the IDF-file residing in the resources directory of this measure")
idf_file.setDefaultValue('import.idf')
args << idf_file

yml_file = OpenStudio::Ruleset::OSArgument::makeStringArgument('yml_file', false)
yml_file.setDisplayName("YML-file")
yml_file.setDescription("The name of the YML-file residing in the resources directory of this measure")
yml_file.setDefaultValue('import.yml')
args << yml_file

return args
end

# define what happens when the measure is run
def run(workspace, runner, user_arguments)
super(workspace, runner, user_arguments)

# Use the built-in error checking
if !runner.validateUserArguments(arguments(workspace), user_arguments)
return false
end

# Assign the user inputs to variables
idf_file = runner.getStringArgumentValue("idf_file", user_arguments)
yml_file = runner.getStringArgumentValue("yml_file", user_arguments)

# Parameters
idf_file = "#{File.dirname(__FILE__)}/resources/#{idf_file}"
yml_file = "#{File.dirname(__FILE__)}/resources/#{yml_file}"

# Process .idf file
if !idf_file.empty?
# Load workspace to be imported
if idf.empty?
return false
end
idf = idf.get.toIdfFile()
end

# Process .yml file
if !yml_file.empty?
require 'yaml'

fixfields.each do |f|
o = workspace.getObjectByTypeAndName(f['class'].to_IddObjectType,f['name'])
if o.empty?
runner.registerError("No #{f['class']} found with name #{f['name']}")
return false
end
o = o.get
if !o.setString(f['field'],f['value'])
runner.registerError("Unable to set #{f['value']} to field #{f['field']} in #{f['name']}")
return false
end
end
end

return true

end ...
more

Actually, it appears that I'm not quite there yet. It seems that the calling openstudio/Ruby script passes the workspace by reference. In my last line of code the local workspace variable is reassigned, but not in the caller method.

Calling load on the workspace itself appears to be a private method. Any other way to load the idf file without creating a new workspace?

For now I'm saving the workspace in my script after making the modifications I need, and then run the .idf file manually.

( 2015-02-08 07:10:45 -0500 )edit

Thought I could be smart and remove all objects from the current workspace, and insert them all at once. Turns out this breaks the dependence again...

    # Reload workspace

# Remove existing objects
workspace.removeObjects(workspace.handles)

# Add objects from ws, breaks the objects

( 2015-02-08 08:15:48 -0500 )edit

Hey @LSurf, you are on the right track with this but unfortunately using those methods can be a bit tricky. I'll reply with an answer when I get some time to work something up.

( 2015-02-08 10:52:00 -0500 )edit

That would be great, let me know if you need some sort of input file.

I'm also thinking about setting every field of every object using .setString. I couldn't get that to work because I couldn't find a method of finding the object type (needed for finding the correct original object). But that might be worth a second shot

( 2015-02-08 11:44:48 -0500 )edit

@LSurf, I can't reproduce your issue in a unit test, this test should be doing the same thing you are. The one thing to look at is to make sure that the Workspaces you are working with are using the correct IDD for validation. You can use the command: w.iddFileType.valueName, to print the name of the IddFile your Workspace is using. They should all return 'EnergyPlus' in your case.

( 2015-02-09 16:02:57 -0500 )edit

I'll let @macumber give longer answer but what I think you want is something like this.

idf = workspace.toIdfFile()


I use that method on the Inject OSM Geometry into an External IDF measure. This measure takes the IDF generated from the OSM and merges it with a user specified IDF file. The geometry comes from the OSM generated IDF, and everything else comes from the user specified IDF. I hit the same issue you did.

Here is some of the code from the linked measure.

#get model from path and error if empty
if source_idf.empty?
return false
end
source_idf = source_idf.get

#to preserve links I need to get an IdfFile from Workspace
source_idfFile = source_idf.toIdfFile()

#get source_idf surfaces
source_idf_BuildingSurfaces = source_idfFile.getObjectsByType("BuildingSurface_Detailed".to_IddObjectType)

#reporting initial condition of model
runner.registerInitialCondition("The source IDF has #{source_idf_BuildingSurfaces.size} BuildingSurface_Detail objects.")

more

1

@LSurf, I'll just expand a little on @david-goldwasser's answer. OpenStudio WorkspaceObjects always exist within a Workspace, all references between objects are tracked at all times. If object1 refers to object2 and object2 is removed, the reference in object1 will be cleared. This is nice for working with entire files but make some operations tricky.

( 2015-02-09 09:43:50 -0500 )edit
1

IdfObjects are basically just snippets of text, references between them are not tracked. If object1 refers to object2 and object2 is removed, object1 will remain unchanged. This makes working with files difficult as you have to update multiple references when an object changes, but it is more robust when doing object bulk add operations.

( 2015-02-09 09:44:20 -0500 )edit

I'll add a unit test around your code snippet dealing with Workspace objects, in the meantime you should convert your WorkspaceObjects to IdfObjects to do the add (as in David's example). You can also just load the IdfFile directly using IdfFile::load.

( 2015-02-09 09:45:41 -0500 )edit