Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 17 additions & 14 deletions src/osut/osut.py
Original file line number Diff line number Diff line change
Expand Up @@ -5243,15 +5243,12 @@ def spaceHeight(space=None) -> float:
(float): Full height of space (0.0 if invalid input).

"""
if not isinstance(space, openstudio.model.Space):
return 0
hght = 0
if not isinstance(space, openstudio.model.Space): return 0

hght = 0
minZ = 10000
maxZ = -10000

# The solution considers all surface types: "Floor", "Wall", "RoofCeiling".
# No presumption that floor are necessarily at ground level.
for surface in space.surfaces():
zs = [pt.z() for pt in surface.vertices()]
minZ = min(minZ, min(zs))
Expand Down Expand Up @@ -5294,21 +5291,19 @@ def spaceWidth(space=None) -> float:
# - retain only other floor surfaces sharing same 3D plane
# - recover potential union between floor surfaces
# - fall back to largest floor surface if invalid union
# - return width of largest bounded box
floors = sorted(floors, key=lambda fl: fl.grossArea(), reverse=True)
floor = floors[0]
plane = floor.plane()
t = openstudio.Transformation.alignFace(floor.vertices())
polyg = list(poly(floor, False, True, True, t, "ulc"))

if not polyg:
oslg.clean()
return 0
if not polyg: return 0

polyg.reverse()
polyg = p3Dv(polyg)
# polyg = p3Dv(polyg)

if len(floors) > 1:
floors = [flr for flr in floors if plane.equal(fl.plane(), 0.001)]
floors = [flr for flr in floors if plane.equal(flr.plane(), 0.001)]

if len(floors) > 1:
polygs = [poly(flr, False, True, True, t, "ulc") for flr in floors]
Expand All @@ -5321,12 +5316,20 @@ def spaceWidth(space=None) -> float:

union = openstudio.joinAll(polygs, 0.01)[0]
polyg = poly(union, False, True, True)
if not polyg: return 0

polyg = list(polyg)
polyg.reverse()
# box = boundedBox(polyg)

# A bounded box's 'height', at its narrowest, is its 'width'.
# return height(box)

box = boundedBox(polyg)
oslg.clean()
res = realignedFace(polyg)
if not res["box"]: return 0

# A bounded box's 'height', at its narrowest, is its 'width'.
return height(box)
return height(res["box"])
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing key piece.



def genAnchors(s=None, sset=[], tag="box") -> int:
Expand Down
87 changes: 86 additions & 1 deletion tests/test_osut.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def test03_dictionaries(self):
self.assertTrue("skylight" in osut.film())
self.assertTrue("skylight" in osut.uo())
self.assertEqual(osut.film().keys(), osut.uo().keys())

def test04_materials(self):
material = osut.mats()["material"]
sand = osut.mats()["sand"]
Expand Down Expand Up @@ -5890,5 +5890,90 @@ def test37_roller_shades(self):
del model
self.assertEqual(o.status(), 0)

def test37_roller_shades(self):
o = osut.oslg
self.assertEqual(o.status(), 0)
self.assertEqual(o.reset(DBG), DBG)
self.assertEqual(o.level(), DBG)
translator = openstudio.osversion.VersionTranslator()

path = openstudio.path("./tests/files/osms/in/warehouse.osm")
model = translator.loadModel(path)
self.assertTrue(model)
model = model.get()

fine = model.getSpaceByName("Zone2 Fine Storage")
self.assertTrue(fine)
fine = fine.get()

# The Fine Storage space has 2 floors, at different Z-axis levels:
# - main ground floor (slab on grade), Z=0.00m
# - mezzanine floor, adjacent to the office space ceiling below, Z=4.27m
self.assertTrue(len(osut.facets(fine, "all", "floor")), 2)
groundfloor = model.getSurfaceByName("Fine Storage Floor")
mezzanine = model.getSurfaceByName("Office Roof Reversed")
self.assertTrue(groundfloor)
self.assertTrue(mezzanine)
groundfloor = groundfloor.get()
mezzanine = mezzanine.get()

# The ground floor is L-shaped, floor surfaces have differenet Z=axis
# levels, etc. In the context of codes/standards like ASHRAE 90.1 or the
# Canadian NECB, determining what constitutes a space's 'height' and/or
# 'width' matters, namely with regards to geometry-based LPD rules
# (e.g. adjustments based on corridor 'width'). Not stating here what
# the definitive answers should be in all cases. There are however a few
# OSut functions that may be helpful.
#
# OSut's 'aligned' height and width functions were initially developed
# for non-flat surfaces, like walls and sloped roofs - particularly
# useful when such surfaces are rotated in 3D space. It's somewhat less
# intuitive when applied to horizontal surfaces like floors. In a
# nutshell, the functions lay out the surface in a 2D grid, aligning it
# along its 'bounded box'. It then determines a bounding box around the
# surface, once aligned:
# - 'aligned height' designates the narrowest edge of the bounding box
# - 'aligned width' designates the widest edge of the bounding box
#
# Useful? In some circumstances, maybe. One can argue that these may be
# of limited use for width-based LPD adjustment calculations.
self.assertAlmostEqual(osut.alignedHeight(groundfloor), 30.48, places=2)
self.assertAlmostEqual(osut.alignedWidth(groundfloor), 45.72, places=2)
self.assertAlmostEqual(osut.alignedHeight(mezzanine), 9.14, places=2)
self.assertAlmostEqual(osut.alignedWidth(mezzanine), 25.91, places=2)

# OSut's 'spaceHeight' and 'spaceWidth' are more suitable for height- or
# width-based LPD adjustement calculations. OSut sets a space's width as
# the length of the narrowest edge of the largest bounded box that fits
# within a collection of neighbouring floor surfaces. This is considered
# reasonable for a long corridor, with varying widths along its full
# length (e.g. occasional alcoves).
#
# Achtung! The function can be time consuming (multiple iterations) for
# very convoluted spaces (e.g. long corridors with multiple concavities).
self.assertAlmostEqual(osut.spaceHeight(fine), 8.53, places=2)
self.assertAlmostEqual(osut.spaceWidth(fine), 21.33, places=2)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warehouse

The value returned by spaceHeight (8.53 m) corresponds to the tallest floor-to-ceiling distance (along Z-axis).

The floor section circumscribed (and crisscrossed) with red dotted lines corresponds to the largest bounded box that fits in the (large) L-shaped floor surface. The length of the narrowest edge of this bounded box (21.33 m) is what spaceWidth returns.


# --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- #
path = openstudio.path("./tests/files/osms/out/seb_sky.osm")
model = translator.loadModel(path)
self.assertTrue(model)
model = model.get()

openarea = model.getSpaceByName("Open area 1")
self.assertTrue(openarea)
openarea = openarea.get()

floor = osut.facets(openarea, "all", "floor")
self.assertEqual(len(floor), 1)
floor = floor[0]

self.assertAlmostEqual(osut.alignedHeight(floor), 6.88, places=2)
self.assertAlmostEqual(osut.alignedWidth(floor), 8.22, places=2)
self.assertAlmostEqual(osut.spaceHeight(openarea), 3.96, places=2)
self.assertAlmostEqual(osut.spaceWidth(openarea), 3.77, places=2)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seb_sky
  • in red: full height of space, including skylight wells
  • in green: width of bounding box of space floor(s) ... outline is very approximate!
  • in blue: width of largest bounded box that can fit within grouped floor surfaces

The latter is retained here as the most adequate measure of what could be considered a room's width, e.g. for LPD adjustment calculations.


self.assertEqual(o.status(), 0)

if __name__ == "__main__":
unittest.main()