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

Revision history [back]

... Python OpenStudio bindings: surface.vertices() -> tuple?

To OpenStudio SDK devs working with Python bindings ...

As a Python-SDK training exercise, I'm in the process of converting existing Ruby gems to Python packages/modules. It's going fairly smoothly (e.g. Ruby vs Python unit test results are lining up). Yet I'm hitting something weird when I try the following (here, using the interactive Python interpreter):

vtx = openstudio.Point3dVector()
vtx.append(openstudio.Point3d( 0, 0,10))
vtx.append(openstudio.Point3d( 0, 0, 0))
vtx.append(openstudio.Point3d(10, 0, 0))
vtx.append(openstudio.Point3d(10, 0,10))

model = openstudio.model.Model()
surface = openstudio.model.Surface(vtx, model)
print(surface.vertices().__class__.__name__)

>>> tuple

Tuple? Shouldn't it be an openstudio.model.Point3dVector? I can work around this, e.g. converting to a list, or generating a new Point3dVector (point by point). But it would be better to stick to SDK documented return types. This is the first snag of this nature I've come across.

Is this a bug, or expected behaviour? Am I missing something really obvious (documented somewhere)? I haven't found anything on UMH or the OpenStudio GitHub repo. Working with SDK v3.9.0 & Python 3.10, BTW.

Thanks in advance,


... Python OpenStudio bindings: surface.vertices() -> tuple?

To OpenStudio SDK devs working with Python bindings ...

As a Python-SDK training exercise, I'm in the process of converting existing Ruby gems to Python packages/modules. It's going fairly smoothly (e.g. Ruby vs Python unit test results are lining up). Yet I'm hitting something weird when I try the following (here, using the interactive Python interpreter):

vtx = openstudio.Point3dVector()
vtx.append(openstudio.Point3d( 0, 0,10))
vtx.append(openstudio.Point3d( 0, 0, 0))
vtx.append(openstudio.Point3d(10, 0, 0))
vtx.append(openstudio.Point3d(10, 0,10))

model = openstudio.model.Model()
surface = openstudio.model.Surface(vtx, model)
print(surface.vertices().__class__.__name__)

>>> tuple

Tuple? Shouldn't it be an openstudio.model.Point3dVector? I can work around this, e.g. converting to a list, or generating a new Point3dVector (point by point). But it would be better to stick to SDK documented return types. This is the first snag of this nature I've come across.

Is this a bug, or expected behaviour? Am I missing something really obvious (documented somewhere)? I haven't found anything on UMH or the OpenStudio GitHub repo. Working with SDK v3.9.0 & Python 3.10, BTW.

Thanks in advance,


... Python OpenStudio bindings: surface.vertices() -> tuple?

To OpenStudio SDK devs working with Python bindings ...

As a Python-SDK training exercise, I'm in the process of converting existing Ruby gems to Python packages/modules. It's going fairly smoothly (e.g. Ruby vs Python unit test results are lining up). Yet I'm hitting something weird when I try the following (here, using the interactive Python interpreter):

vtx = openstudio.Point3dVector()
vtx.append(openstudio.Point3d( 0, 0,10))
vtx.append(openstudio.Point3d( 0, 0, 0))
vtx.append(openstudio.Point3d(10, 0, 0))
vtx.append(openstudio.Point3d(10, 0,10))

model = openstudio.model.Model()
surface = openstudio.model.Surface(vtx, model)
print(surface.vertices().__class__.__name__)

>>> tuple

Tuple? Shouldn't it be an openstudio.model.Point3dVector? I can work around this, e.g. converting to a list, or generating a new Point3dVector (point by point). But it would be better to stick to SDK documented return types. This is the first snag of this nature I've come across.

Is this a bug, or expected behaviour? Am I missing something really obvious (documented somewhere)? I haven't found anything on UMH or the OpenStudio GitHub repo. Working with SDK v3.9.0 & Python 3.10, BTW.

Thanks in advance,

EDIT:

The following isn't really answering "why?" or "where can one find documentation on this?". If someone does provide a suitable answer, I'll gladly accept it, and edit the original question with my comments below.


In a nutshell, an OpenStudio getter method returns a copy of an OpenStudio model object. Lower-level objects, like 3D points or materials, are returned as other instances of the same class. 1D collections of objects (e.g. floor.vertices(), model.getSpaces()) are also returned as copies. Yet often as simpler, built-in class instances, like Python tuples or Ruby Arrays. I've only come across this with 1D collections, when using either Python or Ruby. There's nothing inherently wrong with this, but it isn't initially obvious. I'd suggest relying on the online OpenStudio SDK documentation with a pinch of caution, and test return variables of interest (particularly 1D collections) when coding (via interactive shells, unit tests, etc.). More of a head's up for beginners ...


... a few observations:

#1. This is not unique to Python. The Ruby bindings offer similar behaviour. I just hadn't paid attention to this until recently - my bad. Revisiting the example in the question:

Python:

import openstudio

vx1 = openstudio.Point3dVector()
vx1.append(openstudio.Point3d(10, 0, 0))
vx1.append(openstudio.Point3d(10,10, 0))
vx1.append(openstudio.Point3d( 0,10, 0))
vx1.append(openstudio.Point3d( 0, 0, 0))

vx2 = openstudio.Point3dVector()
vx2.append(openstudio.Point3d(20, 0, 0))
vx2.append(openstudio.Point3d(20,10, 0))
vx2.append(openstudio.Point3d(10,10, 0))
vx2.append(openstudio.Point3d(10, 0, 0))

vtx = openstudio.Point3dVectorVector()
vtx.append(vx1)
vtx.append(vx2)

print(len(vtx))
> 2

print(vtx.__class__.__name__)
> Point3dVectorVector

print(vtx[0].__class__.__name__)
> tuple

print(vtx[0][0].__class__.__name__)
> Point3d

Ruby:

require 'openstudio'

vx1  = OpenStudio::Point3dVector.new
vx1 << OpenStudio::Point3d.new(10, 0, 0)
vx1 << OpenStudio::Point3d.new(10,10, 0)
vx1 << OpenStudio::Point3d.new( 0,10, 0)
vx1 << OpenStudio::Point3d.new( 0, 0, 0)

vx2  = OpenStudio::Point3dVector.new
vx2 << OpenStudio::Point3d.new(20, 0, 0)
vx2 << OpenStudio::Point3d.new(20,10, 0)
vx2 << OpenStudio::Point3d.new(10,10, 0)
vx2 << OpenStudio::Point3d.new(10, 0, 0)

vtx  = OpenStudio::Point3dVectorVector.new
vtx << vx1
vtx << vx2

puts vtx.size
> 2

puts vtx.class
> OpenStudio::Point3dVectorVector

puts vtx[0].class
> Array 

puts vtx[0][0].class
> OpenStudio::Point3d

Slight differences of course: With a call to surface.vertices(), Python returns a tuple while Ruby returns an Array. Otherwise, very similar.


#2. This is not limited to 3D points, 3D point vectors, etc. Other collections behave similarly:

Python:

model  = openstudio.model.Model()
c      = openstudio.model.Construction(model)
layers = openstudio.model.MaterialVector()

print(layers.__class__.__name__)
> MaterialVector

exterior   = openstudio.model.MasslessOpaqueMaterial(model)
insulation = openstudio.model.MasslessOpaqueMaterial(model)
interior   = openstudio.model.MasslessOpaqueMaterial(model)
layers.append(exterior)
layers.append(insulation)
layers.append(interior)
c.setLayers(layers)

print(c.layers().__class__.__name__)
> tuple

print(c.layers()[0].__class__.__name__)
> Material

Ruby:

model  = OpenStudio::Model::Model.new
c      = OpenStudio::Model::Construction.new(model)
layers = OpenStudio::Model::MaterialVector.new

puts layers.class
> OpenStudio::Model::MaterialVector

exterior   = OpenStudio::Model::MasslessOpaqueMaterial.new(model)
insulation = OpenStudio::Model::MasslessOpaqueMaterial.new(model)
exterior   = OpenStudio::Model::MasslessOpaqueMaterial.new(model)
layers << exterior
layers << insulation
layers << interior
c.setLayers(layers)

puts c.layers.class
> Array

puts c.layers[0].class
> OpenStudio::Model::Material