IntroThis tutorial is intended as a walk-through for complete beginners to geomeppy. geomeppy is a python project built on top of eppy, and as the name implies, it adds much more geometry editing capabilities.At the end of the tutorial, you will have created and run an EnergyPlus model without once having to manually edit an […]

This post fixes a need to be able to see what I’m doing when generating EnergyPlus geometry files, without needing to fire up SketchUp.

The approach is pretty simple, combining `eppy`

with the `mplot3d`

library for `matplotlib`

. This is a much more lightweight solution than using something like `mayavi`

.

The goal here was to see whether my code had correctly intersected a pair of zones and set the adjoining surfaces as internal walls (done using Shapely, which may be the topic of a future post).

First the required imports (this is taken from a Jupyter notebook which you can download at the bottom of the post).

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
%matplotlib notebook import matplotlib.pyplot as plt from mpl_toolkits.mplot3d.art3d import Poly3DCollection from mpl_toolkits.mplot3d import Axes3D from eppy.modeleditor import IDF from eppy.function_helpers import getcoords # set the IDD for the version of EnergyPlus if IDF.getiddname() == None: IDF.setiddname("C:\EnergyPlusV8-4-0\Energy+.idd") # import the IDF test_idf = "../test_union.idf" idf = IDF(test_idf) |

Now a few useful functions…

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
def get_surfaces(): """Get the surfaces from the IDF. """ surface_types = ['BUILDINGSURFACE:DETAILED', 'FENESTRATIONSURFACE:DETAILED'] surfaces = [] for surface_type in surface_types: surfaces.extend(idf.idfobjects[surface_type]) return surfaces def get_collections(opacity=1): """Set up 3D collections for each surface type. """ surfaces = get_surfaces() # set up the collections walls = Poly3DCollection([getcoords(s) for s in surfaces if s.Surface_Type.lower() == 'wall'], alpha=opacity, facecolor='wheat', edgecolors='black' ) floors = Poly3DCollection([getcoords(s) for s in surfaces if s.Surface_Type.lower() == 'floor'], alpha=opacity, facecolor='dimgray', edgecolors='black' ) roofs = Poly3DCollection([getcoords(s) for s in surfaces if s.Surface_Type.lower() == 'roof'], alpha=opacity, facecolor='firebrick', edgecolors='black' ) windows = Poly3DCollection([getcoords(s) for s in surfaces if s.Surface_Type.lower() == 'window'], alpha=opacity, facecolor='cornflowerblue', edgecolors='black' ) return walls, roofs, floors, windows def get_limits(): """ Get limits for the x, y and z axes so the plot is fitted to the axes. """ surfaces = get_surfaces() x = [pt[0] for s in surfaces for pt in getcoords(s)] y = [pt[1] for s in surfaces for pt in getcoords(s)] z = [pt[2] for s in surfaces for pt in getcoords(s)] max_delta = max((max(x) - min(x)), (max(y) - min(y)), (max(z) - min(z))) return {'x': (min(x), min(x) + max_delta), 'y': (min(y), min(y) + max_delta), 'z': (min(z), min(y) + max_delta)} |

Next we’ll add windows to just the external walls so we can see that the internal walls have been created correctly. If it has worked, the internal walls won’t have any windows.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
def add_windows(idf, glazing_ratio=0.25): """Add windows to all outside walls. Parameters ---------- idf : object The eppy IDF object. glazing_ratio : float Value between 0 and 1. """ walls = [w for w in idf.idfobjects['BUILDINGSURFACE:DETAILED'] if w.Outside_Boundary_Condition.lower() == 'outdoors' and w.Surface_Type.lower() == 'wall'] for wall in walls: pts = window_vertices_given_wall_vertices(getcoords(wall), glazing_ratio) window = idf.newidfobject('FENESTRATIONSURFACE:DETAILED', Name = wall.Name + " WINDOW", Surface_Type = 'Window', Construction_Name = 'dummy_window', Building_Surface_Name = wall.Name, Number_of_Vertices = len(pts) ) for i, v in enumerate(pts, 1): window['Vertex_{0}_Xcoordinate'.format(i)] = v[0] window['Vertex_{0}_Ycoordinate'.format(i)] = v[1] window['Vertex_{0}_Zcoordinate'.format(i)] = v[2] def window_vertices_given_wall_vertices(vertices, glazing_ratio): """ Calculate window vertices given wall vertices and glazing ratio. For each axis: 1) Translate the axis points so that they are centred around zero 2) Either: a) Multiply the z dimension by the glazing ratio to shrink it vertically b) Multiply the x or y dimension by 0.995 to keep inside the surface 3) Translate the axis points back to their original positions Parameters ---------- vertices : list of lists List of [x, y, z] vertices, starting a the top left of a surface and working around it counterclockwise. We expect each surface to have four vertices. Returns ------- list Window vertices bounding a vertical strip midway up the surface. """ average_x = sum([x for x, _y, _z in vertices]) / len(vertices) average_y = sum([y for _x, y, _z in vertices]) / len(vertices) average_z = sum([z for _x, _y, z in vertices]) / len(vertices) # move windows in 0.5% from the edges so they can be drawn in SketchUp window_points = [[ ((x - average_x) * 0.995) + average_x, ((y - average_y) * 0.995) + average_y, ((z - average_z) * glazing_ratio) + average_z ] for x, y, z in vertices] return window_points add_windows(idf, glazing_ratio=0.5) |

And now we’re ready to show the chart. If you have this in a Jupyter notebook the chart will show up inline and will be interactive. You will be able to zoom in and out, as well as rotate the canvas.

1 2 3 4 5 6 7 8 9 10 11 12 13 |
# create the figure and add the surfaces fig = plt.figure() ax = plt.axes(projection='3d') collections = get_collections(opacity=0.5) map(ax.add_collection3d, collections) # calculate and set the axis limits limits = get_limits() ax.set_xlim(limits['x']) ax.set_ylim(limits['y']) ax.set_zlim(limits['z']) plt.show() |

And here’s the result.

So we can see that there is no window on the internal wall joining the two spaces, meaning that the generated IDF has the surface set as an internal wall. Perfect!

Download the Jupyter notebook and IDF to try this out for yourself.

## 1 Comment on “View EnergyPlus geometry in Python”

It’s surprising to find on oco-carbon.com a resource so precious about equations.

We will note your page as a benchmark for Implementing the Normal Equation in Excel VBA.

We also invite you to link and other web resources

for equations like http://equation-solver.org/ or https://en.wikipedia.org/wiki/Equation.

Thank you ang good luck!