to expand on Dan's answer (code was roo long for comment) here is method I use to calculate perimeter for building. You could adjust this for multiple possible boundary conditions and also for an individual space or surface.

As is it has a single counter for all edges common to an exterior wall and a floor with ground boundary condition.

```
# currently takes in model and checks for edges shared by a ground exposed floor and exterior exposed wall. Later could be updated for a specific story independent of floor boundary condition.
def OsLib_Geometry.calculate_perimeter(model)
perimeter = 0
model.getSpaces.each do |space|
# counter to use later
edge_hash = {}
edge_counter = 0
space.surfaces.each do |surface|
# get vertices
vertex_hash = {}
vertex_counter = 0
surface.vertices.each do |vertex|
vertex_counter += 1
vertex_hash[vertex_counter] = [vertex.x,vertex.y,vertex.z]
end
# make edges
counter = 0
vertex_hash.each do |k,v|
edge_counter += 1
counter += 1
if vertex_hash.size != counter
edge_hash[edge_counter] = [v,vertex_hash[counter+1],surface,surface.outsideBoundaryCondition,surface.surfaceType]
else # different code for wrap around vertex
edge_hash[edge_counter] = [v,vertex_hash[1],surface,surface.outsideBoundaryCondition,surface.surfaceType]
end
end
end
# check edges for matches (need opposite vertices and proper boundary conditions)
edge_hash.each do |k1,v1|
next if v1[3] != "Ground" # skip if not ground exposed floor
next if v1[4] != "Floor"
edge_hash.each do |k2,v2|
next if v2[3] != "Outdoors" # skip if not exterior exposed wall (todo - update to handle basement)
next if v2[4] != "Wall"
# see if edges have same geometry
next if not v1[0] == v2[1] # next if not same geometry reversed
next if not v1[1] == v2[0]
point_one = OpenStudio::Point3d.new(v1[0][0],v1[0][1],v1[0][2])
point_two = OpenStudio::Point3d.new(v1[1][0],v1[1][1],v1[1][2])
length = OpenStudio::Vector3d.new(point_one - point_two).length
perimeter += length
end
end
end
return perimeter
end
```