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

OpenStudio Optional Objects

asked 2019-07-16 11:45:14 -0500

updated 2019-07-16 11:47:39 -0500

I stumbled upon a so-called OptionalCoilCoolingDXVariableRefrigerantFlow object when working on a measure. I found a reference in the SDK but there were no details.

The steps to reproduce are as follow:

model = OpenStudio::Model::Model.new
indoor_unit = OpenStudio::Model::ZoneHVACTerminalUnitVariableRefrigerantFlow.new(model)
coil = OpenStudio::Model::CoilCoolingDXVariableRefrigerantFlow.new(model)
indoor_unit.coolingCoil(coil)
new_unit = indoor_unit.clone
new_unit = new_unit.to_ZoneHVACTerminalUnitVariableRefrigerantFlow.get
new_unit.coolingCoil

The terminal will return

=> #<OpenStudio::Model::OptionalCoilCoolingDXVariableRefrigerantFlow:0x00000006292e48 @__swigtype__="_p_boost__optionalT_openstudio__model__CoilCoolingDXVariableRefrigerantFlow_t">

I understand that every piece of ZoneHVAC needs different coils and fans. However, why does it return an Optional version of the ModelObject, instead of just the object itself? It adds the extra step to call .get, but I am more interested in the reason behind this implementation.

edit retag flag offensive close merge delete

3 Answers

Sort by ยป oldest newest most voted
2

answered 2019-07-17 00:58:43 -0500

If you really are interested in the reason behind this implementation, I'll try to explain exactly why. After all, the use of OptionalXXX classes is paramount in properly using the openstudio SDK via any bindings, so it might be a good idea to really understand why.

The OpenStudio SDK is written in C++. When for example you use an OpenStudio Measure, that is written in Ruby, you use for example the Ruby bindings. OpenStudio makes use of something called SWIG - Simplified Wrapper and Interface Generator - to expose this C++ SDK to a variety of programming languages such as C#, Ruby, (Java, Python, etc...). SWIG basically writes (almost) automatically "spaghetti code" to make the link between Ruby and the calls to the C++ library.

C++ is strongly typed, and in particular it cannot return different types from a single function. One typical thing you can encounter is when you want a function to return either an object, or "Nothing" when it doesn't exists. Consider this Ruby function that is perfectly valid in Ruby:

def coolingCoil(terminal)
  # Assumes that the _coilNumber attribute stores a unique identifier...
   # And allCoils is an Array of all the coils in the model
  if terminal._coilNumber > 0: 
     return allCoils[terminal._coilNumber] 
  else:
     return nil # This returns nil, we could even have more return types given different conditions


  # Usage
  coil = coolingCoil(terminal)
  if not coil.nil?
      # We can assume that our "coil" variable is an Object of type "Coil" now
      coil.callAMethodOnCoilObjects()
  end

The above is not possible in boilerplate C++. Enters the concept of boost::optional (doc) which now made it into the Standard Template Library (STL) starting at C++ 17 as std::optional (doc).

The class template std::optional manages an optional contained value, i.e. a value that may or may not be present.

A common use case for optional is the return value of a function that may fail. As opposed to other approaches, such as std::pair<t,bool>, optional handles expensive-to-construct objects well and is more readable, as the intent is expressed explicitly.

So the above ruby example looks kinda like this in C++:

boost::optional<Coil> coolingCoil(Terminal& terminal) {
    boost::optional<Coil> result;
    if (terminal._coilNumber > 0) {
        result = allCoils[terminal._coilNumber];
    } // Else result is still uninitialized

    return result;
}

# Usage
boost::optional<Coil> _coil = coolingCoil(terminal);
if (!_coil.empty()) {
    Coil coil = _coil.get();
    coil.callAMethodOnCoilObjects();
    // (or just _coil->callAMethodOnCoilObjects()... but let's not complicate things)
}

Now OpenStudio's SDK is usually pretty darn logical with return types as a result. It's also pretty logical with the method names (Well...part is because of C++ strongly typed characteristics, but mostly it's because there was a lot of thoughts into it). If you know enough of the underlying object, you can infer the return type at pretty much all times. Here are a couple examples:

  • Cooling coil is optional in E+: terminal.coolingCoil() returns a boost::optional<XXX> (where XXX is HVACComponent or one of its children)
  • Cooling coil is ...
(more)
edit flag offensive delete link more

Comments

1

This is what I was looking for, thank you! Also, it is good to see your answers again!

Luis Lara's avatar Luis Lara  ( 2019-07-18 09:48:43 -0500 )edit
2

answered 2019-07-16 12:49:06 -0500

updated 2019-07-16 12:54:48 -0500

The coils are not required by EnergyPlus for cooling-only or heating-only mode. See the IDD too...

ZoneHVAC:TerminalUnit:VariableRefrigerantFlow,
...
  A11,  \field Cooling Coil Object Type
        \type choice
        \key Coil:Cooling:DX:VariableRefrigerantFlow
        \key Coil:Cooling:DX:VariableRefrigerantFlow:FluidTemperatureControl
        \note Cooling Coil Type must be Coil:Cooling:DX:VariableRefrigerantFlow
        \note if AirConditioner:VariableRefrigerantFlow is used
        \note to model VRF outdoor unit
        \note Cooling Coil Type must be
        \note Coil:Cooling:DX:VariableRefrigerantFlow:FluidTemperatureControl
        \note if AirConditioner:VariableRefrigerantFlow:FluidTemperatureControl or
        \note if AirConditioner:VariableRefrigerantFlow:FluidTemperatureControl:HR
        \note is used to model VRF outdoor unit
        \note This field may be left blank if heating-only mode is used
...
  A13,  \field Heating Coil Object Type
        \type choice
        \key Coil:Heating:DX:VariableRefrigerantFlow
        \key Coil:Heating:DX:VariableRefrigerantFlow:FluidTemperatureControl
        \note Heating Coil Type must be Coil:Heating:DX:VariableRefrigerantFlow
        \note if AirConditioner:VariableRefrigerantFlow is used
        \note to model VRF outdoor unit
        \note Heating Coil Type must be
        \note Coil:Heating:DX:VariableRefrigerantFlow:FluidTemperatureControl
        \note if AirConditioner:VariableRefrigerantFlow:FluidTemperatureControl or
        \note if AirConditioner:VariableRefrigerantFlow:FluidTemperatureControl:HR
        \note is used to model VRF outdoor unit
        \note This field may be left blank if cooling-only mode is used
...

PS - if you want to avoid casting optional objects to their specific class, you can use Julien's dirty method.

edit flag offensive delete link more
1

answered 2019-07-16 13:38:40 -0500

The general implementation is described here, see the section starting "Field Getters and Setters", particularly "Choice Fields".

Generally, whether an Optional is returned is based on the corresponding EnergyPlus idd field type.

edit flag offensive delete link more

Your Answer

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

Add Answer

Careers

Question Tools

1 follower

Stats

Asked: 2019-07-16 11:45:14 -0500

Seen: 278 times

Last updated: Jul 17 '19