2

OpenStudio Optional Objects

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.

Luis Lara's avatar
2.1k
Luis Lara
asked 2019-07-16 11:45:14 -0500, updated 2019-07-16 11:47:39 -0500
edit flag offensive 0 remove flag close merge delete

Comments

add a comment see more comments

3 Answers

2

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)
Julien Marrec's avatar
29.7k
Julien Marrec
answered 2019-07-17 00:58:43 -0500
edit flag offensive 0 remove flag delete link

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
add a comment see more comments
2

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.

MatthewSteen's avatar
10.1k
MatthewSteen
answered 2019-07-16 12:49:06 -0500, updated 2019-07-16 12:54:48 -0500
edit flag offensive 0 remove flag delete link

Comments

add a comment see more comments
1

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.

ericringold's avatar
10.6k
ericringold
answered 2019-07-16 13:38:40 -0500
edit flag offensive 0 remove flag delete link

Comments

add a comment see more comments