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

Changing a constant speed pump to a variable speed pump with an OpenStudio/PAT measure

asked 2014-12-09 19:13:41 -0600

alenm gravatar image

updated 2017-08-20 15:08:15 -0600

I'm trying to put together a measure that will switch a selected Constant Speed Pump to a Variable Speed Pump (VFD). I'm still learning how these are written, but I have the skeleton of the script together. The actual pump replacement process has been a struggle. As far as I can see, the PumpConstantSpeed and PumpVariableSpeed classes and their associated functions only manage the values of their respective attributes, but have no way of adding or removing the overall pump objects.

I've tried using .remove() on the current constant speed pump and then creating a new variable speed pump with, but can't seem to find a way to connect the new pump object to the rest of the system (or substitute it in place of the previous one).

I've also tried breaking up the pump object with .split() and manually substituting in the Handle, Input Node, and Output Node of the previous pump, but I again can't seem to find a way to push this information back into the model to complete the replacement. Does the Connection class offer any potential solutions?

The script is available here, with some commented blocks of failed attempts at solutions:

It should work with "Apply Measure Now" in OpenStudio on any model that has an OS:Pump:ConstantSpeed object.

Here is the def run() snippet of the variable speed pump set-up:

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

    #assign the user inputs to variables
    pumps = runner.getOptionalWorkspaceObjectChoiceValue("pumps",user_arguments,model)
    if pumps.empty?
        runner.registerError("PumpConstantSpeed not found...")
        return false
    pump_rtdflow = runner.getDoubleArgumentValue("pump_rtdflow",user_arguments)
    pump_rtdpump = runner.getIntegerArgumentValue("pump_rtdpump",user_arguments)
    pump_rtdpwr = runner.getIntegerArgumentValue("pump_rtdpwr",user_arguments)
    pump_motor = runner.getDoubleArgumentValue("pump_motor",user_arguments)
    pump_frac = runner.getDoubleArgumentValue("pump_frac",user_arguments)
    pump_coeff1 = runner.getDoubleArgumentValue("pump_coeff1",user_arguments)
    pump_coeff2 = runner.getDoubleArgumentValue("pump_coeff2",user_arguments)
    pump_coeff3 = runner.getDoubleArgumentValue("pump_coeff3",user_arguments)
    pump_coeff4 = runner.getDoubleArgumentValue("pump_coeff4",user_arguments)
    pump_ctrl_type_widget = runner.getOptionalWorkspaceObjectChoiceValue("pump_ctrl_type_widget",user_arguments,model)
    cost = runner.getDoubleArgumentValue("material_cost",user_arguments)

    selected_pump = nil

    if pumps.empty?
        handle = runner.getStringArgumentValue("pumps",user_arguments)
        if handle.empty?
            runner.registerError("No pump was chosen.")
            return false
            runner.registerError("The selected pump with handle '#{handle}' was not found in the model. It may have been removed by another measure.")
            return false
        selected_pump = pumps.get.to_PumpConstantSpeed.get

        runner.registerInfo("Current pump: #{selected_pump}")

        new_pump =

        new_pump.setName("VFD Pump")

        runner.registerInfo("New pump: #{new_pump}")
    end  #end of if pumps.empty?
    return true
edit retag flag offensive close merge delete

2 Answers

Sort by ยป oldest newest most voted

answered 2014-12-10 04:24:05 -0600

There are several different ways to add objects to loops in OpenStudio. I typically use the .addToNode method that all HVAC Components (Pumps, Chillers, Fans, etc) have. You don't see this method in the documentation for the PumpVariableSpeed class because this method is inherited from its base class, HVAC Component.

This code should work to disconnect the existing pump and add the new one to the same place on the loop:

  # Get the nodes before and after the existing pump on the loop     
  prev_node = selected_pump.inletModelObject.get.to_Node.get
  next_node = selected_pump.outletModelObject.get.to_Node.get

  # Remove the existing pump.  When this happens, either the pump's
  # inlet or outlet node will be deleted and the other will remain

  # Get the node that remains after deleting the existing pump
  remaining_node = nil
  if prev_node.outletModelObject.is_initialized
    remaining_node = prev_node
  elsif next_node.inletModelObject.is_initialized
    remaining_node = next_node

  # Add the new pump to the node where the old pump was
  if remaining_node.nil?
    runner.registerError("Couldn't add the new pump to the loop after removing existing pump.")
    return false
edit flag offensive delete link more


Ah I figured .addToNode was the key, but I couldn't quite figure out how to locate the correct node after deleting the existing pump. This is great, thanks Andrew!

alenm gravatar image alenm  ( 2014-12-10 14:56:45 -0600 )edit

Andrew's code is accounting for the fact that OpenStudio is managing the nodes for you. When a component is removed OpenStudio normally needs to remove at least one unnecessary node. Often times the node that OpenStudio takes away will be the component outlet node. But there are exceptions, most notably if the component is the last component on the loop and the outlet component is the supply outlet node, then OpenStudio will choose to remove the component inlet node instead. This is because supply outlet often has important setpoint managers attached to it.

Kyle Benne gravatar image Kyle Benne  ( 2014-12-14 20:12:21 -0600 )edit

answered 2014-12-14 20:14:36 -0600

updated 2014-12-15 11:20:28 -0600

To simplify things and avoid need to check what node has been removed, you might add the new pump first to the existing pump outlet node, then remove the unwanted pump.

next_node = selected_pump.outletModelObject.get.to_Node.get
edit flag offensive delete link more


@Kyle Benne this is clever, I may just have to adopt it!

aparker gravatar image aparker  ( 2014-12-15 11:22:18 -0600 )edit

The only case I can think of where it won't work is if you are swapping fans and there are already two fans on the system. OpenStudio won't allow three, even momentarily before you remove one. I am increasingly motivated to add a swap function in the HVACCompnent API. That swap function would almost certainly be implemented generically this way. And from within the Model we could use private functions to bypass the fan limit issue.

Kyle Benne gravatar image Kyle Benne  ( 2014-12-15 11:27:41 -0600 )edit

Yeah, a .swap method would be good. Probably have to think about what types it would work with (water to Air to air-to-air, etc).

aparker gravatar image aparker  ( 2014-12-15 13:50:43 -0600 )edit

The fan limit has been removed.

MatthewSteen gravatar image MatthewSteen  ( 2018-04-09 22:04:06 -0600 )edit

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer


Question Tools

1 follower


Asked: 2014-12-09 19:13:41 -0600

Seen: 643 times

Last updated: Dec 15 '14