Skip to content

Commit

Permalink
fix(model): Add a method to merge sensor grids together
Browse files Browse the repository at this point in the history
  • Loading branch information
chriswmackey committed Jan 10, 2025
1 parent 16fb78d commit 489a3a5
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 7 deletions.
27 changes: 27 additions & 0 deletions honeybee_radiance/properties/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,33 @@ def check_view_rooms_in_model(self, raise_exception=True, detailed=False):
return msg
return [] if detailed else ''

def merge_duplicate_identifier_grids(self):
"""Merge SensorGrids in the Model with the same identifier together.
This is one automated way of fixing check_duplicate_sensor_grid_identifiers
failures. In this case, it will be assumed that the user intended to
make the sensor grids with the same identifier apart of the same
SensorGrid object. The other possible way to address SensorGrids with
duplicate identifiers is to give each grid a unique identifier if the
truly are meant to be separate.
"""
# first group all grids with the same identifier
grid_dict = {}
for grid in self.sensor_grids:
try:
grid_dict[grid.identifier].append(grid)
except KeyError:
grid_dict[grid.identifier] = [grid]
# merge girds together if they have the same ID
merged_grids = []
for grids in grid_dict.values():
if len(grids) == 1:
merged_grids.append(grids[0])
else:
merged_grid = SensorGrid.from_merged_grids(grids)
merged_grids.append(merged_grid)
self.sensor_grids = merged_grids

def apply_properties_from_dict(self, data):
"""Apply the radiance properties of a dictionary to the host Model of this object.
Expand Down
38 changes: 37 additions & 1 deletion honeybee_radiance/sensorgrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ class SensorGrid(object):
* base_geometry
* group_identifier
* full_identifier
"""

__slots__ = ('_identifier', '_display_name', '_sensors', '_room_identifier',
Expand Down Expand Up @@ -313,6 +312,43 @@ def from_file(cls, file_path, start_line=None, end_line=None, identifier=None):

return cls(identifier, sensors)

@classmethod
def from_merged_grids(cls, grids):
"""Create a SensorGrid by merging together several SensorGrid objects.
The first SensorGrid in the input grids will be used to set global properties
of the result like identifiers and display_names.
Args:
grids: A list of SensorGrid objects to be merged together into a singe
resulting SensorGrid.
"""
# check the inputs
assert len(grids) != 0, 'At least one SensorGrid is needed to use ' \
'SensorGrid.from_merged_grids.'
for grid in grids:
assert isinstance(grid, SensorGrid), 'Expected SensorGrid for ' \
'from_merged_grids. Got {}.'.format(type(grid))
# merge the sensors, meshes, and base geometry together
sensors, meshes, base_geo = [], [], []
for grid in grids:
sensors.extend(grid.sensors)
if grid.mesh is not None:
meshes.extend(grid.mesh)
if grid.base_geometry is not None:
base_geo.extend(grid.base_geometry)
mesh = Mesh3D.join_meshes(meshes) if len(meshes) == len(grids) else None
base_geo = tuple(base_geo) if len(base_geo) != 0 else None
# create the new grid and set all properties based on the first one
new_grid = cls(grids[0].identifier, sensors)
new_grid.mesh = mesh
new_grid.base_geometry = base_geo
new_grid._display_name = grids[0]._display_name
new_grid._room_identifier = grids[0]._room_identifier
new_grid._group_identifier = grids[0]._group_identifier
new_grid._light_path = grids[0]._light_path
return new_grid

@property
def identifier(self):
"""Get or set text for a unique SensorGrid identifier."""
Expand Down
1 change: 1 addition & 0 deletions tests/assets/model/duplicate_sensor_grid_model.hbjson

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion tests/cli/edit_cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ def test_reset_resource_ids():
assert new_con_set.identifier != old_id



def test_add_room_sensors():
runner = CliRunner()
input_hb_model = './tests/assets/model/model_radiance_dynamic_states.hbjson'
Expand Down
23 changes: 18 additions & 5 deletions tests/model_extend_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def test_radiance_properties():
Point3D(2, 0, 4), Point3D(4, 0, 4))
mesh = Mesh3D(pts, [(0, 1, 2, 3), (2, 3, 4)])
awning_1 = ShadeMesh('Awning_1', mesh)

model = Model('TinyHouse', [room], shade_meshes=[awning_1])

assert hasattr(model.properties, 'radiance')
Expand Down Expand Up @@ -153,13 +153,13 @@ def check_sensor_grid_rooms_in_model():
face.apertures_by_ratio(0.2, 0.01)
Room.solve_adjacency([first_floor, second_floor], 0.01)
model = Model('Multi_Zone_Single_Family_House', [first_floor, second_floor])

model.properties.radiance.sensor_grids = \
[r.properties.radiance.generate_sensor_grid(1) for r in model.rooms]

assert model.properties.radiance.check_sensor_grid_rooms_in_model() == ''
assert model.properties.radiance.check_sensor_grid_rooms_in_model(False, True) == []

model.properties.radiance.sensor_grids[0].room_identifier = 'not_a_room'
assert model.properties.radiance.check_sensor_grid_rooms_in_model(False) != ''
assert model.properties.radiance.check_sensor_grid_rooms_in_model(False, True) != []
Expand All @@ -177,13 +177,13 @@ def check_view_rooms_in_model():
face.apertures_by_ratio(0.2, 0.01)
Room.solve_adjacency([first_floor, second_floor], 0.01)
model = Model('Multi_Zone_Single_Family_House', [first_floor, second_floor])

model.properties.radiance.views = \
[r.properties.radiance.generate_view((0, -1, 0)) for r in model.rooms]

assert model.properties.radiance.check_view_rooms_in_model() == ''
assert model.properties.radiance.check_view_rooms_in_model(False, True) == []

model.properties.radiance.views[0].room_identifier = 'not_a_room'
assert model.properties.radiance.check_view_rooms_in_model(False) != ''
assert model.properties.radiance.check_view_rooms_in_model(False, True) != []
Expand Down Expand Up @@ -913,3 +913,16 @@ def test_writer_to_rad_folder_sensor_grids_views():

# clean up the folder
nukedir(folder, rmdir=True)


def test_check_duplicate_sensor_grid_identifiers():
"""Test the check_duplicate_sensor_grid_identifiers method."""
input_hb_model = './tests/assets/model/duplicate_sensor_grid_model.hbjson'
model = Model.from_file(input_hb_model)

assert model.properties.radiance.check_duplicate_sensor_grid_identifiers(False) != ''
assert len(model.properties.radiance.sensor_grids) == 43

model.properties.radiance.merge_duplicate_identifier_grids()
assert model.properties.radiance.check_duplicate_sensor_grid_identifiers(False) == ''
assert len(model.properties.radiance.sensor_grids) == 1

0 comments on commit 489a3a5

Please sign in to comment.