From d6789ba7ab3a643c1ef96ad4d5c91331df8f93f7 Mon Sep 17 00:00:00 2001 From: PH Tools Date: Thu, 2 May 2024 16:21:38 -0400 Subject: [PATCH] fix(pipe): Fix all pipe and duct transforms - Fix bug (degrees/radians) in pip and duct rotation transforms --- .../programtypes/PHIUS_programs.py | 6 +- honeybee_phhvac/ducting.py | 90 ++--- honeybee_phhvac/heat_pumps.py | 42 +- honeybee_phhvac/heating.py | 14 +- honeybee_phhvac/hot_water_devices.py | 9 + honeybee_phhvac/hot_water_piping.py | 358 +++++++++++------- honeybee_phhvac/hot_water_system.py | 82 ++-- honeybee_phhvac/properties/room.py | 156 +++++--- honeybee_phhvac/renewable_devices.py | 52 +-- honeybee_phhvac/supportive_device.py | 46 +-- honeybee_phhvac/ventilation.py | 114 +++--- .../test_hot_water_piping.py | 164 +++++++- .../test_systems/test_hot_water_system.py | 18 +- 13 files changed, 732 insertions(+), 419 deletions(-) diff --git a/honeybee_ph_standards/programtypes/PHIUS_programs.py b/honeybee_ph_standards/programtypes/PHIUS_programs.py index 404da32..4a13ac2 100644 --- a/honeybee_ph_standards/programtypes/PHIUS_programs.py +++ b/honeybee_ph_standards/programtypes/PHIUS_programs.py @@ -745,7 +745,7 @@ }, "2021::PHIUS_NR::Library_Storage": { "name": "Library Storage", - "hb_base_program": "", + "hb_base_program": "2019::SecondarySchool::Library", "protocol": "PHIUS_NonRes", "description": "Library magazine and stores", "source": ["PHIUS_Certification_Guidebook_v3.02_N10", ""], @@ -795,7 +795,7 @@ }, "2021::PHIUS_NR::Library_Stacks": { "name": "Library Stacks", - "hb_base_program": "", + "hb_base_program": "2019::SecondarySchool::Library", "protocol": "PHIUS_NonRes", "description": "Library-open stacks areas", "source": ["PHIUS_Certification_Guidebook_v3.02_N10", ""], @@ -1160,7 +1160,7 @@ }, "2021::PHIUS_NR::Office_Workspace_Closed": { "name": "Office Workspace Closed", - "hb_base_program": "", + "hb_base_program": "2019::MediumOffice::ClosedOffice", "protocol": "PHIUS_NonRes", "description": "Personal office (single occupant)", "source": ["PHIUS_Certification_Guidebook_v3.02_N10", ""], diff --git a/honeybee_phhvac/ducting.py b/honeybee_phhvac/ducting.py index 0dc5b56..f8daf74 100644 --- a/honeybee_phhvac/ducting.py +++ b/honeybee_phhvac/ducting.py @@ -3,6 +3,8 @@ """Honeybee-PH-HVAC-Equipment: Ducts.""" +from math import radians + try: from typing import Any, Dict, List, Optional, Union except ImportError: @@ -145,18 +147,18 @@ def __repr__(self): def ToString(self): return self.__repr__() - def move(self, moving_vec): + def move(self, moving_vec3D): # type: (Point3D) -> PhDuctSegment """Move the duct segment along a vector. Args: - moving_vec: A Vector3D with the direction and distance to move the ray. + moving_vec3D: A Vector3D with the direction and distance to move the ray. """ new_segment = self.duplicate() - new_segment.geometry = self.geometry.move(moving_vec) + new_segment.geometry = self.geometry.move(moving_vec3D) return new_segment - def rotate(self, axis, angle, origin): + def rotate(self, axis_vec3D, angle_degrees, origin_pt3D): # type: (Point3D, float, Point3D) -> PhDuctSegment """Rotate the duct segment by a certain angle around an axis and origin. @@ -165,54 +167,54 @@ def rotate(self, axis, angle, origin): If axis has a negative orientation, rotation will be counterclockwise. Args: - axis: A Vector3D axis representing the axis of rotation. - angle: An angle for rotation in radians. - origin: A Point3D for the origin around which the object will be rotated. + axis_vec3D: A Vector3D axis representing the axis of rotation. + angle_degrees: An angle for rotation in degrees. + origin_pt3D: A Point3D for the origin around which the object will be rotated. """ new_segment = self.duplicate() - new_segment.geometry = self.geometry.rotate(axis, angle, origin) + new_segment.geometry = self.geometry.rotate(axis_vec3D, radians(angle_degrees), origin_pt3D) return new_segment - def rotate_xy(self, angle, origin): + def rotate_xy(self, angle_degrees, origin_pt3D): # type: (float, Point3D) -> PhDuctSegment """Rotate the duct segment counterclockwise in the XY plane by a certain angle. Args: - angle: An angle in radians. - origin: A Point3D for the origin around which the object will be rotated. + angle_degrees: An angle in degrees. + origin_pt3D: A Point3D for the origin around which the object will be rotated. """ new_segment = self.duplicate() - new_segment.geometry = self.geometry.rotate_xy(angle, origin) + new_segment.geometry = self.geometry.rotate_xy(radians(angle_degrees), origin_pt3D) return new_segment - def reflect(self, normal, origin): + def reflect(self, normal_vec3D, origin_pt3D): # type: (Point3D, Point3D) -> PhDuctSegment """Reflected the duct segment across a plane with the input normal vector and origin. Args: - normal: A Vector3D representing the normal vector for the plane across + normal_vec3D: A Vector3D representing the normal vector for the plane across which the line segment will be reflected. THIS VECTOR MUST BE NORMALIZED. - origin: A Point3D representing the origin from which to reflect. + origin_pt3D: A Point3D representing the origin from which to reflect. """ new_segment = self.duplicate() - new_segment.geometry = self.geometry.reflect(normal, origin) + new_segment.geometry = self.geometry.reflect(normal_vec3D, origin_pt3D) return new_segment - def scale(self, factor, origin=None): + def scale(self, scale_factor, origin_pt3D=None): # type: (float, Optional[Point3D]) -> PhDuctSegment """Scale the duct segment by a factor from an origin point. Args: - factor: A number representing how much the line segment should be scaled. - origin: A Point3D representing the origin from which to scale. + scale_factor: A number representing how much the line segment should be scaled. + origin_pt3D: A Point3D representing the origin from which to scale. If None, it will be scaled from the World origin (0, 0, 0). """ new_segment = self.duplicate() - new_segment.geometry = self.geometry.scale(factor, origin) - new_segment.insulation_thickness = self.insulation_thickness * factor - new_segment.diameter = self.diameter * factor - new_segment.height = factor * self.height if self.height else None - new_segment.width = factor * self.width if self.width else None + new_segment.geometry = self.geometry.scale(scale_factor, origin_pt3D) + new_segment.insulation_thickness = self.insulation_thickness * scale_factor + new_segment.diameter = self.diameter * scale_factor + new_segment.height = scale_factor * self.height if self.height else None + new_segment.width = scale_factor * self.width if self.width else None return new_segment @@ -361,19 +363,19 @@ def __repr__(self): def ToString(self): return self.__repr__() - def move(self, moving_vec): + def move(self, moving_vec3D): """Move the duct element's segment along a vector. Args: - moving_vec: A Vector3D with the direction and distance to move the ray. + moving_vec3D: A Vector3D with the direction and distance to move the ray. """ new_element = self.duplicate() new_element.clear_segments() for segment in self.segments: - new_element.add_segment(segment.move(moving_vec)) + new_element.add_segment(segment.move(moving_vec3D)) return new_element - def rotate(self, axis, angle, origin): + def rotate(self, axis_vec3D, angle_degrees, origin_pt3D): """Rotate the duct element's segment by a certain angle around an axis and origin. Right hand rule applies: @@ -381,54 +383,54 @@ def rotate(self, axis, angle, origin): If axis has a negative orientation, rotation will be counterclockwise. Args: - axis: A Vector3D axis representing the axis of rotation. - angle: An angle for rotation in radians. - origin: A Point3D for the origin around which the object will be rotated. + axis_vec3D: A Vector3D axis representing the axis of rotation. + angle_degrees: An angle for rotation in degrees. + origin_pt3D: A Point3D for the origin around which the object will be rotated. """ new_element = self.duplicate() new_element.clear_segments() for segment in self.segments: - new_element.add_segment(segment.rotate(axis, angle, origin)) + new_element.add_segment(segment.rotate(axis_vec3D, angle_degrees, origin_pt3D)) return new_element - def rotate_xy(self, angle, origin): + def rotate_xy(self, angle_degrees, origin_pt3D): """Rotate the duct element's segment counterclockwise in the XY plane by a certain angle. Args: - angle: An angle in radians. - origin: A Point3D for the origin around which the object will be rotated. + angle_degree: An angle in degrees. + origin_pt3D: A Point3D for the origin around which the object will be rotated. """ new_element = self.duplicate() new_element.clear_segments() for segment in self.segments: - new_element.add_segment(segment.rotate_xy(angle, origin)) + new_element.add_segment(segment.rotate_xy(angle_degrees, origin_pt3D)) return new_element - def reflect(self, normal, origin): + def reflect(self, normal_vec3D, origin_pt3D): """Reflected the duct element's segment across a plane with the input normal vector and origin. Args: - normal: A Vector3D representing the normal vector for the plane across + normal_vec3D: A Vector3D representing the normal vector for the plane across which the line segment will be reflected. THIS VECTOR MUST BE NORMALIZED. - origin: A Point3D representing the origin from which to reflect. + origin_pt3D: A Point3D representing the origin from which to reflect. """ new_element = self.duplicate() new_element.clear_segments() for segment in self.segments: - new_element.add_segment(segment.reflect(normal, origin)) + new_element.add_segment(segment.reflect(normal_vec3D, origin_pt3D)) return new_element - def scale(self, factor, origin=None): + def scale(self, scale_factor, origin_pt3D=None): # type: (float, Optional[Point3D]) -> PhDuctElement """Scale the duct element's segments by a factor from an origin point. Args: - factor: A number representing how much the line segment should be scaled. - origin: A Point3D representing the origin from which to scale. + scale_factor: A number representing how much the line segment should be scaled. + origin_pt3D: A Point3D representing the origin from which to scale. If None, it will be scaled from the World origin (0, 0, 0). """ new_element = self.duplicate() new_element.clear_segments() for segment in self.segments: - new_element.add_segment(segment.scale(factor, origin)) + new_element.add_segment(segment.scale(scale_factor, origin_pt3D)) return new_element diff --git a/honeybee_phhvac/heat_pumps.py b/honeybee_phhvac/heat_pumps.py index 984a55d..bbbcf5a 100644 --- a/honeybee_phhvac/heat_pumps.py +++ b/honeybee_phhvac/heat_pumps.py @@ -72,54 +72,54 @@ def __lt__(self, other): # type: (PhHeatPumpSystem) -> bool return self.identifier < other.identifier - def move(self, moving_vec): + def move(self, moving_vec3D): """Move the System's elements along a vector. Args: - moving_vec: A Vector3D with the direction and distance to move the ray. + moving_vec3D: A Vector3D with the direction and distance to move the ray. """ pass - def rotate(self, axis, angle, origin): - """Rotate the System's elements by a certain angle around an axis and origin. + def rotate(self, axis_vec3D, angle_degrees, origin_pt3D): + """Rotate the System's elements by a certain angle around an axis_vec3D and origin_pt3D. Right hand rule applies: - If axis has a positive orientation, rotation will be clockwise. - If axis has a negative orientation, rotation will be counterclockwise. + If axis_vec3D has a positive orientation, rotation will be clockwise. + If axis_vec3D has a negative orientation, rotation will be counterclockwise. Args: - axis: A Vector3D axis representing the axis of rotation. - angle: An angle for rotation in radians. - origin: A Point3D for the origin around which the object will be rotated. + axis_vec3D: A Vector3D axis_vec3D representing the axis_vec3D of rotation. + angle_degrees: An angle for rotation in degrees. + origin_pt3D: A Point3D for the origin_pt3D around which the object will be rotated. """ pass - def rotate_xy(self, angle, origin): + def rotate_xy(self, angle_degrees, origin_pt3D): """Rotate the System's elements counterclockwise in the XY plane by a certain angle. Args: - angle: An angle in radians. - origin: A Point3D for the origin around which the object will be rotated. + angle_degrees: An angle in degrees. + origin_pt3D: A Point3D for the origin_pt3D around which the object will be rotated. """ pass - def reflect(self, normal, origin): - """Reflected the System's elements across a plane with the input normal vector and origin. + def reflect(self, normal_vec3D, origin_pt3D): + """Reflected the System's elements across a plane with the input normal vector and origin_pt3D. Args: - normal: A Vector3D representing the normal vector for the plane across + normal_vec3D: A Vector3D representing the normal vector for the plane across which the line segment will be reflected. THIS VECTOR MUST BE NORMALIZED. - origin: A Point3D representing the origin from which to reflect. + origin_pt3D: A Point3D representing the origin_pt3D from which to reflect. """ pass - def scale(self, factor, origin=None): - """Scale the System's elements by a factor from an origin point. + def scale(self, scale_factor, origin_pt3D=None): + """Scale the System's elements by a factor from an origin_pt3D point. Args: - factor: A number representing how much the line segment should be scaled. - origin: A Point3D representing the origin from which to scale. - If None, it will be scaled from the World origin (0, 0, 0). + scale_factor: A number representing how much the line segment should be scaled. + origin_pt3D: A Point3D representing the origin_pt3D from which to scale. + If None, it will be scaled from the World origin_pt3D (0, 0, 0). """ pass diff --git a/honeybee_phhvac/heating.py b/honeybee_phhvac/heating.py index b4bba00..970f018 100644 --- a/honeybee_phhvac/heating.py +++ b/honeybee_phhvac/heating.py @@ -67,7 +67,7 @@ def __lt__(self, other): # type: (PhHeatingSystem) -> bool return self.identifier < other.identifier - def move(self, moving_vec): + def move(self, moving_vec3D): """Move the System's elements along a vector. Args: @@ -75,7 +75,7 @@ def move(self, moving_vec): """ pass - def rotate(self, axis, angle, origin): + def rotate(self, axis_vec3D, angle_degree, origin_pt3D): """Rotate the System's elements by a certain angle around an axis and origin. Right hand rule applies: @@ -84,21 +84,21 @@ def rotate(self, axis, angle, origin): Args: axis: A Vector3D axis representing the axis of rotation. - angle: An angle for rotation in radians. + angle: An angle for rotation in degrees. origin: A Point3D for the origin around which the object will be rotated. """ pass - def rotate_xy(self, angle, origin): + def rotate_xy(self, angle_degree, origin_pt3D): """Rotate the System's elements counterclockwise in the XY plane by a certain angle. Args: - angle: An angle in radians. + angle: An angle in degrees. origin: A Point3D for the origin around which the object will be rotated. """ pass - def reflect(self, normal, origin): + def reflect(self, normal_vec3D, origin_pt3D): """Reflected the System's elements across a plane with the input normal vector and origin. Args: @@ -108,7 +108,7 @@ def reflect(self, normal, origin): """ pass - def scale(self, factor, origin=None): + def scale(self, scale_factor, origin=None): """Scale the System's elements by a factor from an origin point. Args: diff --git a/honeybee_phhvac/hot_water_devices.py b/honeybee_phhvac/hot_water_devices.py index 0cf1ad6..dba0962 100644 --- a/honeybee_phhvac/hot_water_devices.py +++ b/honeybee_phhvac/hot_water_devices.py @@ -151,6 +151,15 @@ def __init__(self): self.percent_coverage = 1.0 self.in_conditioned_space = True + def __copy__(self): + # type: () -> PhHvacHotWaterHeater + # TODO: Implement copy + return self + + def duplicate(self): + # type: () -> PhHvacHotWaterHeater + return self.__copy__() + def to_dict(self): # type: () -> dict d = super(PhHvacHotWaterHeater, self).to_dict() diff --git a/honeybee_phhvac/hot_water_piping.py b/honeybee_phhvac/hot_water_piping.py index bd573ae..6e3204f 100644 --- a/honeybee_phhvac/hot_water_piping.py +++ b/honeybee_phhvac/hot_water_piping.py @@ -4,6 +4,7 @@ """Honeybee-PH-HVAC: Hot Water Piping.""" from copy import copy +from math import radians try: from typing import Any, Dict, List, Optional, Union @@ -128,7 +129,7 @@ def length(self): def __copy__(self): # type: () -> PhHvacPipeSegment - new_obj = PhHvacPipeSegment(self.geometry) + new_obj = PhHvacPipeSegment(self.geometry.duplicate()) new_obj.diameter = PhPipeDiameter(self.diameter.value) new_obj.material = PhHvacPipeMaterial(self.material.value) @@ -142,7 +143,7 @@ def __copy__(self): new_obj.display_name = self.display_name new_obj.user_data = copy(self.user_data) - return self + return new_obj def duplicate(self): # type: () -> PhHvacPipeSegment @@ -189,64 +190,84 @@ def __repr__(self): def ToString(self): return self.__repr__() - def move(self, moving_vec): + def move(self, moving_vec3D): + # type: (Vector3D) -> PhHvacPipeSegment """Move the pipe's geometry along a vector. Args: - moving_vec: A Vector3D with the direction and distance to move the ray. + moving_vec3D: A Vector3D with the direction and distance to move the ray. + Returns: + A new PhHvacPipeSegment with the moved geometry. """ - self.geometry = self.geometry.move(moving_vec) + dup = self.duplicate() + dup.geometry = self.geometry.move(moving_vec3D) + return dup - def rotate(self, axis, angle, origin): - """Rotate the pipe's geometry by a certain angle around an axis and origin. + def rotate(self, axis_3D, angle_degrees, origin_pt3D): + # type: (Vector3D, float, Point3D) -> PhHvacPipeSegment + """Rotate the pipe's geometry by a certain angle_degrees around an axis_3D and origin_pt3D. Right hand rule applies: - If axis has a positive orientation, rotation will be clockwise. - If axis has a negative orientation, rotation will be counterclockwise. + If axis_3D has a positive orientation, rotation will be clockwise. + If axis_3D has a negative orientation, rotation will be counterclockwise. Args: - axis: A Vector3D axis representing the axis of rotation. - angle: An angle for rotation in radians. - origin: A Point3D for the origin around which the object will be rotated. + axis_3D: A Vector3D axis_3D representing the axis_3D of rotation. + angle_degrees: An angle_degrees for rotation in degrees. + origin_pt3D: A Point3D for the origin_pt3D around which the object will be rotated. + Returns: + A new PhHvacPipeSegment with the rotated geometry. """ - self.geometry = self.geometry.rotate(axis, angle, origin) + dup = self.duplicate() + dup.geometry = self.geometry.rotate(axis_3D, radians(angle_degrees), origin_pt3D) + return dup - def rotate_xy(self, angle, origin): - """Rotate the pipe's geometry counterclockwise in the XY plane by a certain angle. + def rotate_xy(self, angle_degrees, origin_pt3D): + # type: (float, Point3D) -> PhHvacPipeSegment + """Rotate the pipe's geometry counterclockwise in the XY plane by a certain angle_degrees. Args: - angle: An angle in radians. - origin: A Point3D for the origin around which the object will be rotated. + angle_degrees: An angle_degrees in degrees. + origin_pt3D: A Point3D for the origin_pt3D around which the object will be rotated. + Returns: + A new PhHvacPipeSegment with the rotated geometry. """ - self.geometry = self.geometry.rotate_xy(angle, origin) + dup = self.duplicate() + dup.geometry = self.geometry.rotate_xy(radians(angle_degrees), origin_pt3D) + return dup - def reflect(self, normal, origin): - """Reflected the pipe's geometry across a plane with the input normal vector and origin. + def reflect(self, normal_vec3D, origin_pt3D): + # type: (Vector3D, Point3D) -> PhHvacPipeSegment + """Reflected the pipe's geometry across a plane with the input normal vector and origin_pt3D. Args: - normal: A Vector3D representing the normal vector for the plane across + normal_vec3D: A Vector3D representing the normal vector for the plane across which the line segment will be reflected. THIS VECTOR MUST BE NORMALIZED. - origin: A Point3D representing the origin from which to reflect. + origin_pt3D: A Point3D representing the origin_pt3D from which to reflect. + Returns: + A new PhHvacPipeSegment with the reflected geometry. """ - self.geometry = self.geometry.reflect(normal, origin) + dup = self.duplicate() + dup.geometry = self.geometry.reflect(normal_vec3D, origin_pt3D) + return dup - def scale(self, factor, origin=None): + def scale(self, scale_factor, origin_pt3D=None): # type: (float, Union[None, Point3D]) -> PhHvacPipeSegment - """Scale the pipe's geometry by a factor from an origin point. + """Scale the pipe's geometry by a factor from an origin_pt3D point. Args: - factor: A number representing how much the line segment should be scaled. - origin: A Point3D representing the origin from which to scale. - If None, it will be scaled from the World origin (0, 0, 0). + scale_factor: A number representing how much the line segment should be scaled. + origin_pt3D: A Point3D representing the origin_pt3D from which to scale. + If None, it will be scaled from the World origin_pt3D (0, 0, 0). """ new_pipe_segment = self.duplicate() - new_pipe_segment.geometry = self.geometry.scale(factor, origin) - new_pipe_segment.insulation_thickness *= factor + new_pipe_segment.geometry = self.geometry.scale(scale_factor, origin_pt3D) + new_pipe_segment.insulation_thickness *= scale_factor return new_pipe_segment class PhHvacPipeElement(_base._PhHVACBase): - """A Pipe Element made up of one or more individual Pipe Segments.""" + """A Pipe Element (Fixture) made up of one or more individual Pipe Segments.""" def __init__(self): super(PhHvacPipeElement, self).__init__() @@ -255,6 +276,7 @@ def __init__(self): @property def segments(self): # type: () -> List[PhHvacPipeSegment] + """Return a list of a;; the Pipe-Segments in the Pipe-Element.""" return list(self._segments.values()) @property @@ -265,12 +287,14 @@ def length(self): @property def water_temp(self): - # Return the length-weighted average water temperature of all the pipe segments + # type: () -> float + """Return the length-weighted average water temperature of all the pipe segments""" return sum(s.length * s.water_temp for s in self.segments) / self.length @property def daily_period(self): - # Return the length-weighted average daily period of all the pipe segments + # type: () -> float + """Return the length-weighted average daily period of all the pipe segments""" return sum(s.length * s.daily_period for s in self.segments) / self.length @property @@ -282,6 +306,7 @@ def segment_names(self): @property def material_name(self): # type: () -> str + """Return the material name of the pipe element.""" materials = {s.material for s in self.segments} if len(materials) == 0: return PhHvacPipeMaterial.allowed[0] @@ -294,6 +319,7 @@ def material_name(self): @property def diameter_name(self): # type: () -> str + """Return the diameter name of the pipe element.""" diameters = {s.diameter for s in self.segments} if len(diameters) == 0: return PhPipeDiameter.allowed[0] @@ -305,10 +331,17 @@ def diameter_name(self): def add_segment(self, _segment): # type: (PhHvacPipeSegment) -> None + """Add a new Pipe Segment to the Pipe Element.""" self._segments[_segment.identifier] = _segment + def clear_segments(self): + # type: () -> None + """Clear all the segments from the pipe element.""" + self._segments = {} + def __copy__(self): # type: () -> PhHvacPipeElement + """Duplicate the pipe element.""" new_obj = PhHvacPipeElement() for segment in self.segments: @@ -360,99 +393,108 @@ def __repr__(self): def ToString(self): return self.__repr__() - def move(self, moving_vec): + def move(self, moving_vec3D): # type: (Vector3D) -> PhHvacPipeElement """Move the pipe's segments along a vector. Args: - moving_vec: A Vector3D with the direction and distance to move the ray. + moving_vec3D: A Vector3D with the direction and distance to move the ray. """ - new_segments = {} - for k, segment in self._segments.items(): - new_segments[k] = segment.move(moving_vec) + """Rotate the pipe's segments by a certain angle_degrees around an axis_3D and origin_pt3D. + + Right hand rule applies: + If axis_3D has a positive orientation, rotation will be clockwise. + If axis_3D has a negative orientation, rotation will be counterclockwise. + Args: + axis_3D: A Vector3D axis_3D representing the axis_3D of rotation. + angle_degrees: An angle_degrees for rotation in radians. + origin_pt3D: A Point3D for the origin_pt3D around which the object will be rotated. + Returns: + A new PhHvacPipeElement with the rotated segments. + """ new_pipe_element = self.duplicate() - new_pipe_element._segments = new_segments + new_pipe_element.clear_segments() + for segment in self.segments: + new_pipe_element.add_segment(segment.move(moving_vec3D)) return new_pipe_element - def rotate(self, axis, angle, origin): + def rotate(self, axis_3D, angle_degrees, origin_pt3D): # type: (Vector3D, float, Point3D) -> PhHvacPipeElement - """Rotate the pipe's segments by a certain angle around an axis and origin. + """Rotate the pipe's segments by a certain angle_degrees around an axis_3D and origin_pt3D. Right hand rule applies: - If axis has a positive orientation, rotation will be clockwise. - If axis has a negative orientation, rotation will be counterclockwise. + If axis_3D has a positive orientation, rotation will be clockwise. + If axis_3D has a negative orientation, rotation will be counterclockwise. Args: - axis: A Vector3D axis representing the axis of rotation. - angle: An angle for rotation in radians. - origin: A Point3D for the origin around which the object will be rotated. + axis_3D: A Vector3D axis_3D representing the axis_3D of rotation. + angle_degrees: An angle_degrees for rotation in degrees. + origin_pt3D: A Point3D for the origin_pt3D around which the object will be rotated. + Returns: + A new PhHvacPipeElement with the rotated segments. """ - new_segments = {} - for k, segment in self._segments.items(): - new_segments[k] = segment.rotate(axis, angle, origin) - new_pipe_element = self.duplicate() - new_pipe_element._segments = new_segments + new_pipe_element.clear_segments() + for segment in self.segments: + new_pipe_element.add_segment(segment.rotate(axis_3D, angle_degrees, origin_pt3D)) return new_pipe_element - def rotate_xy(self, angle, origin): + def rotate_xy(self, angle_degrees, origin_pt3D): # type: (float, Point3D) -> PhHvacPipeElement - """Rotate the pipe's segments counterclockwise in the XY plane by a certain angle. + """Rotate the pipe's segments counterclockwise in the XY plane by a certain angle_degrees. Args: - angle: An angle in radians. - origin: A Point3D for the origin around which the object will be rotated. + angle_degrees: An angle_degrees in degrees. + origin_pt3D: A Point3D for the origin_pt3D around which the object will be rotated. + Returns: + A new PhHvacPipeElement with the rotated segments. """ - new_segments = {} - for k, segment in self._segments.items(): - new_segments[k] = segment.rotate_xy(angle, origin) - new_pipe_element = self.duplicate() - new_pipe_element._segments = new_segments + new_pipe_element.clear_segments() + for segment in self.segments: + new_pipe_element.add_segment(segment.rotate_xy(angle_degrees, origin_pt3D)) return new_pipe_element - def reflect(self, normal, origin): + def reflect(self, normal_vec3D, origin_pt3D): # type: (Vector3D, Point3D) -> PhHvacPipeElement - """Reflected the pipe's segments across a plane with the input normal vector and origin. + """Reflected the pipe's segments across a plane with the input normal_vec3D vector and origin_pt3D. Args: - normal: A Vector3D representing the normal vector for the plane across + normal_vec3D: A Vector3D representing the normal_vec3D vector for the plane across which the line segment will be reflected. THIS VECTOR MUST BE NORMALIZED. - origin: A Point3D representing the origin from which to reflect. + origin_pt3D: A Point3D representing the origin_pt3D from which to reflect. + Returns: + A new PhHvacPipeElement with the reflected segments. """ - new_segments = {} - for k, segment in self._segments.items(): - new_segments[k] = segment.reflect(normal, origin) - new_pipe_element = self.duplicate() - new_pipe_element._segments = new_segments + new_pipe_element.clear_segments() + for segment in self.segments: + new_pipe_element.add_segment(segment.reflect(normal_vec3D, origin_pt3D)) return new_pipe_element - def scale(self, factor, origin=None): + def scale(self, factor, origin_pt3D=None): # type: (float, Optional[Point3D]) -> PhHvacPipeElement - """Scale the pipe's segments by a factor from an origin point. + """Scale the pipe's segments by a factor from an origin_pt3D point. Args: factor: A number representing how much the line segment should be scaled. - origin: A Point3D representing the origin from which to scale. - If None, it will be scaled from the World origin (0, 0, 0). + origin_pt3D: A Point3D representing the origin_pt3D from which to scale. + If None, it will be scaled from the World origin_pt3D (0, 0, 0). + Returns: + A new PhHvacPipeElement with the scaled segments. """ - new_segments = {} - for k, segment in self._segments.items(): - new_segments[k] = segment.scale(factor, origin) - new_pipe_element = self.duplicate() - new_pipe_element._segments = new_segments + new_pipe_element.clear_segments() + for segment in self.segments: + new_pipe_element.add_segment(segment.scale(factor, origin_pt3D)) return new_pipe_element class PhHvacPipeBranch(_base._PhHVACBase): """A 'Branch' Pipe which has geometry, and serves one or more 'Fixture' (Twig) pipe elements.""" - def __init__( - self, - ): + def __init__(self): # type: () -> None super(PhHvacPipeBranch, self).__init__() self.pipe_element = PhHvacPipeElement() @@ -479,6 +521,7 @@ def twigs(self): @property def segments(self): # type () -> List[PhPipeSegment] + """Return a list of all the Pipe-Segments in the Branch.""" return self.pipe_element.segments @property @@ -492,16 +535,19 @@ def length(self): @property def water_temp(self): # type: () -> float + """Return the length-weighted average water temperature of all the pipe segments.""" return self.pipe_element.water_temp @property def daily_period(self): # type: () -> float + """Return the length-weighted average daily period of all the pipe segments.""" return self.pipe_element.daily_period @property def num_fixtures(self): # type: () -> int + """Return the number of fixtures connected to the branch.""" return len(self.fixtures) @property @@ -587,75 +633,85 @@ def ToString(self): # type: () -> str return self.__repr__() - def move(self, moving_vec): + def move(self, moving_vec3D): # type: (Vector3D) -> PhHvacPipeBranch """Move the pipe's elements along a vector. Args: - moving_vec: A Vector3D with the direction and distance to move the ray. + moving_vec3D: A Vector3D with the direction and distance to move the ray. + Returns: + A new PhHvacPipeBranch with the moved elements. """ new_branch = self.duplicate() - new_branch.fixtures = [fixture.move(moving_vec) for fixture in self.fixtures] - new_branch.pipe_element = self.pipe_element.move(moving_vec) + new_branch.fixtures = [fixture.move(moving_vec3D) for fixture in self.fixtures] + new_branch.pipe_element = self.pipe_element.move(moving_vec3D) return new_branch - def rotate(self, axis, angle, origin): + def rotate(self, axis_3D, angle_degrees, origin_pt3D): # type: (Vector3D, float, Point3D) -> PhHvacPipeBranch - """Rotate the pipe's elements by a certain angle around an axis and origin. + """Rotate the pipe's elements by a certain angle_degrees around an axis_3D and origin_pt3D. Right hand rule applies: - If axis has a positive orientation, rotation will be clockwise. - If axis has a negative orientation, rotation will be counterclockwise. + If axis_3D has a positive orientation, rotation will be clockwise. + If axis_3D has a negative orientation, rotation will be counterclockwise. Args: - axis: A Vector3D axis representing the axis of rotation. - angle: An angle for rotation in radians. - origin: A Point3D for the origin around which the object will be rotated. + axis_3D: A Vector3D axis_3D representing the axis_3D of rotation. + angle_degrees: An angle_degrees for rotation in degrees. + origin_pt3D: A Point3D for the origin_pt3D around which the object will be rotated. + Returns: + A new PhHvacPipeBranch with the rotated elements. """ new_branch = self.duplicate() - new_branch.fixtures = [fixture.rotate(axis, angle, origin) for fixture in self.fixtures] - new_branch.pipe_element = self.pipe_element.rotate(axis, angle, origin) + new_branch.fixtures = [fixture.rotate(axis_3D, angle_degrees, origin_pt3D) for fixture in self.fixtures] + new_branch.pipe_element = self.pipe_element.rotate(axis_3D, angle_degrees, origin_pt3D) return new_branch - def rotate_xy(self, angle, origin): + def rotate_xy(self, angle_degrees, origin_pt3D): # type: (float, Point3D) -> PhHvacPipeBranch - """Rotate the pipe's elements counterclockwise in the XY plane by a certain angle. + """Rotate the pipe's elements counterclockwise in the XY plane by a certain angle_degrees. Args: - angle: An angle in radians. - origin: A Point3D for the origin around which the object will be rotated. + angle_degrees: An angle_degrees in degrees. + origin_pt3D: A Point3D for the origin_pt3D around which the object will be rotated. + Returns: + A new PhHvacPipeBranch with the rotated elements. """ new_branch = self.duplicate() - new_branch.fixtures = [fixture.rotate_xy(angle, origin) for fixture in self.fixtures] - new_branch.pipe_element = self.pipe_element.rotate_xy(angle, origin) + new_branch.fixtures = [fixture.rotate_xy(angle_degrees, origin_pt3D) for fixture in self.fixtures] + new_branch.pipe_element = self.pipe_element.rotate_xy(angle_degrees, origin_pt3D) return new_branch - def reflect(self, normal, origin): + def reflect(self, normal_vec3D, origin_pt3D): # type: (Vector3D, Point3D) -> PhHvacPipeBranch - """Reflected the pipe's elements across a plane with the input normal vector and origin. + """Reflected the pipe's elements across a plane with the input normal_vec3D vector and origin_pt3D. Args: - normal: A Vector3D representing the normal vector for the plane across + normal_vec3D: A Vector3D representing the normal_vec3D vector for the plane across which the line segment will be reflected. THIS VECTOR MUST BE NORMALIZED. - origin: A Point3D representing the origin from which to reflect. + origin_pt3D: A Point3D representing the origin_pt3D from which to reflect. + Returns: + A new PhHvacPipeBranch with the reflected elements. """ new_branch = self.duplicate() - new_branch.fixtures = [fixture.reflect(normal, origin) for fixture in self.fixtures] - new_branch.pipe_element = self.pipe_element.reflect(normal, origin) + new_branch.fixtures = [fixture.reflect(normal_vec3D, origin_pt3D) for fixture in self.fixtures] + new_branch.pipe_element = self.pipe_element.reflect(normal_vec3D, origin_pt3D) return new_branch - def scale(self, factor, origin=None): + def scale(self, factor, origin_pt3D=None): # type: (float, Optional[Point3D]) -> PhHvacPipeBranch - """Scale the pipe's elements by a factor from an origin point. + """Scale the pipe's elements by a factor from an origin_pt3D point. Args: factor: A number representing how much the line segment should be scaled. - origin: A Point3D representing the origin from which to scale. - If None, it will be scaled from the World origin (0, 0, 0). + origin_pt3D: A Point3D representing the origin_pt3D from which to scale. + If None, it will be scaled from the World origin_pt3D (0, 0, 0). + Returns: + A new PhHvacPipeBranch with the scaled elements. """ new_branch = self.duplicate() - new_branch.fixtures = [fixture.scale(factor, origin) for fixture in self.fixtures] - new_branch.pipe_element = self.pipe_element.scale(factor, origin) + new_branch.fixtures = [fixture.scale(factor, origin_pt3D) for fixture in self.fixtures] + new_branch.pipe_element = self.pipe_element.scale(factor, origin_pt3D) return new_branch @@ -672,16 +728,19 @@ def __init__(self): @property def material_name(self): # type: () -> str + """Return the material name of the pipe element.""" return self.pipe_element.material_name @property def diameter_name(self): # type: () -> str + """Return the diameter name of the pipe element.""" return self.pipe_element.diameter_name @property def segments(self): # type () -> List[PhPipeSegment] + """Return a list of all the Pipe-Segments in the Trunk.""" return self.pipe_element.segments @property @@ -693,16 +752,19 @@ def length(self): @property def water_temp(self): # type: () -> float + """Return the length-weighted average water temperature of all the pipe segments.""" return self.pipe_element.water_temp @property def daily_period(self): # type: () -> float + """Return the length-weighted average daily period of all the pipe segments.""" return self.pipe_element.daily_period @property def num_fixtures(self): # type: () -> int + """Return the number of fixtures connected to the trunk.""" return sum(branch.num_fixtures for branch in self.branches) @property @@ -792,73 +854,83 @@ def ToString(self): # type: () -> str return self.__repr__() - def move(self, moving_vec): + def move(self, moving_vec3D): # type: (Vector3D) -> PhHvacPipeTrunk """Move the pipe's elements along a vector. Args: - moving_vec: A Vector3D with the direction and distance to move the ray. + moving_vec3D: A Vector3D with the direction and distance to move the ray. + Returns: + A new PhHvacPipeTrunk with the moved elements. """ new_trunk = self.duplicate() - new_trunk.branches = [branch.move(moving_vec) for branch in self.branches] - new_trunk.pipe_element = self.pipe_element.move(moving_vec) + new_trunk.branches = [branch.move(moving_vec3D) for branch in self.branches] + new_trunk.pipe_element = self.pipe_element.move(moving_vec3D) return new_trunk - def rotate(self, axis, angle, origin): + def rotate(self, axis_3D, angle_degrees, origin_pt3D): # type: (Vector3D, float, Point3D) -> PhHvacPipeTrunk - """Rotate the pipe's elements by a certain angle around an axis and origin. + """Rotate the pipe's elements by a certain angle_degrees around an axis_3D and origin_pt3D. Right hand rule applies: - If axis has a positive orientation, rotation will be clockwise. - If axis has a negative orientation, rotation will be counterclockwise. + If axis_3D has a positive orientation, rotation will be clockwise. + If axis_3D has a negative orientation, rotation will be counterclockwise. Args: - axis: A Vector3D axis representing the axis of rotation. - angle: An angle for rotation in radians. - origin: A Point3D for the origin around which the object will be rotated. + axis_3D: A Vector3D axis_3D representing the axis_3D of rotation. + angle_degrees: An angle_degrees for rotation in degrees. + origin_pt3D: A Point3D for the origin_pt3D around which the object will be rotated. + Returns: + A new PhHvacPipeTrunk with the rotated elements. """ new_trunk = self.duplicate() - new_trunk.branches = [branch.rotate(axis, angle, origin) for branch in self.branches] - new_trunk.pipe_element = self.pipe_element.rotate(axis, angle, origin) + new_trunk.branches = [branch.rotate(axis_3D, angle_degrees, origin_pt3D) for branch in self.branches] + new_trunk.pipe_element = self.pipe_element.rotate(axis_3D, angle_degrees, origin_pt3D) return new_trunk - def rotate_xy(self, angle, origin): + def rotate_xy(self, angle_degrees, origin_pt3D): # type: (float, Point3D) -> PhHvacPipeTrunk - """Rotate the pipe's elements counterclockwise in the XY plane by a certain angle. + """Rotate the pipe's elements counterclockwise in the XY plane by a certain angle_degrees. Args: - angle: An angle in radians. - origin: A Point3D for the origin around which the object will be rotated. + angle_degrees: An angle_degrees in degrees. + origin_pt3D: A Point3D for the origin_pt3D around which the object will be rotated. + Returns: + A new PhHvacPipeTrunk with the rotated elements. """ new_trunk = self.duplicate() - new_trunk.branches = [branch.rotate_xy(angle, origin) for branch in self.branches] - new_trunk.pipe_element = self.pipe_element.rotate_xy(angle, origin) + new_trunk.branches = [branch.rotate_xy(angle_degrees, origin_pt3D) for branch in self.branches] + new_trunk.pipe_element = self.pipe_element.rotate_xy(angle_degrees, origin_pt3D) return new_trunk - def reflect(self, normal, origin): + def reflect(self, normal_vec3D, origin_pt3D): # type (Vector3D, Point3D) -> PhHvacPipeTrunk - """Reflected the pipe's elements across a plane with the input normal vector and origin. + """Reflected the pipe's elements across a plane with the input normal_vec3D vector and origin_pt3D. Args: - normal: A Vector3D representing the normal vector for the plane across + normal_vec3D: A Vector3D representing the normal_vec3D vector for the plane across which the line segment will be reflected. THIS VECTOR MUST BE NORMALIZED. - origin: A Point3D representing the origin from which to reflect. + origin_pt3D: A Point3D representing the origin_pt3D from which to reflect. + Returns: + A new PhHvacPipeTrunk with the reflected elements. """ new_trunk = self.duplicate() - new_trunk.branches = [branch.reflect(normal, origin) for branch in self.branches] - new_trunk.pipe_element = self.pipe_element.reflect(normal, origin) + new_trunk.branches = [branch.reflect(normal_vec3D, origin_pt3D) for branch in self.branches] + new_trunk.pipe_element = self.pipe_element.reflect(normal_vec3D, origin_pt3D) return new_trunk - def scale(self, factor, origin=None): + def scale(self, factor, origin_pt3D=None): # type: (float, Optional[Point3D]) -> PhHvacPipeTrunk - """Scale the pipe's elements by a factor from an origin point. + """Scale the pipe's elements by a factor from an origin_pt3D point. Args: factor: A number representing how much the line segment should be scaled. - origin: A Point3D representing the origin from which to scale. - If None, it will be scaled from the World origin (0, 0, 0). + origin_pt3D: A Point3D representing the origin_pt3D from which to scale. + If None, it will be scaled from the World origin_pt3D (0, 0, 0). + Returns: + A new PhHvacPipeTrunk with the scaled elements. """ new_trunk = self.duplicate() - new_trunk.branches = [branch.scale(factor, origin) for branch in self.branches] - new_trunk.pipe_element = self.pipe_element.scale(factor, origin) + new_trunk.branches = [branch.scale(factor, origin_pt3D) for branch in self.branches] + new_trunk.pipe_element = self.pipe_element.scale(factor, origin_pt3D) return new_trunk diff --git a/honeybee_phhvac/hot_water_system.py b/honeybee_phhvac/hot_water_system.py index 85f0a22..9cb0ba5 100644 --- a/honeybee_phhvac/hot_water_system.py +++ b/honeybee_phhvac/hot_water_system.py @@ -114,14 +114,14 @@ def heaters(self): def clear_heaters(self): self._heaters = {} - def add_heater(self, _h): + def add_heater(self, _heater): # type: (Optional[hwd.PhHvacHotWaterHeater]) -> None """Adds a new hot-water heater to the system.""" - if not _h: + if not _heater: return - assert hasattr(_h, "to_dict"), 'Error: HW-Heater "{}" is not serializable?'.format(_h) - self._heaters[_h.identifier] = _h + assert hasattr(_heater, "to_dict"), 'Error: HW-Heater "{}" is not serializable?'.format(_heater) + self._heaters[_heater.identifier] = _heater def add_distribution_piping(self, _distribution_piping, _key=None): # type: (Union[hwp.PhHvacPipeTrunk, hwp.PhHvacPipeBranch, hwp.PhHvacPipeElement], Optional[str]) -> None @@ -273,7 +273,7 @@ def __copy__(self): new_obj.tank_solar = self.tank_solar.duplicate() for k, v in self._heaters.items(): - new_obj.add_heater(v) + new_obj.add_heater(v.duplicate()) for k, v in self._distribution_piping.items(): new_obj.add_distribution_piping(v.duplicate(), _key=k) @@ -384,11 +384,14 @@ def __eq__(self, other): return True - def move(self, moving_vec): + def move(self, moving_vec3D): + # type: (Point3D) -> PhHotWaterSystem """Move the System's piping along a vector. Args: - moving_vec: A Vector3D with the direction and distance to move the ray. + moving_vec3D: A Vector3D with the direction and distance to move the ray. + Reflect: + A new PhHotWaterSystem object with the moved piping. """ new_system = self.duplicate() new_system.identifier = self.identifier @@ -396,12 +399,15 @@ def move(self, moving_vec): new_system.clear_recirc_piping() for k, pipe in self._distribution_piping.items(): - new_system.add_distribution_piping(pipe.move(moving_vec), _key=k) + new_system.add_distribution_piping(pipe.move(moving_vec3D), _key=k) for k, pipe in self._recirc_piping.items(): - new_system.add_recirc_piping(pipe.move(moving_vec), _key=k) + new_system.add_recirc_piping(pipe.move(moving_vec3D), _key=k) - def rotate(self, axis, angle, origin): + return new_system + + def rotate(self, axis_vec3D, angle_degrees, origin_pt3D): + # type: (Point3D, float, Point3D) -> PhHotWaterSystem """Rotate the System's piping by a certain angle around an axis and origin. Right hand rule applies: @@ -409,9 +415,11 @@ def rotate(self, axis, angle, origin): If axis has a negative orientation, rotation will be counterclockwise. Args: - axis: A Vector3D axis representing the axis of rotation. - angle: An angle for rotation in radians. - origin: A Point3D for the origin around which the object will be rotated. + axis_vec3D: A Vector3D axis representing the axis of rotation. + angle_degrees: An angle for rotation in degrees. + origin_pt3D: A Point3D for the origin around which the object will be rotated. + Returns: + A new PhHotWaterSystem object with the rotated piping. """ new_system = self.duplicate() new_system.identifier = self.identifier @@ -419,17 +427,22 @@ def rotate(self, axis, angle, origin): new_system.clear_recirc_piping() for k, pipe in self._distribution_piping.items(): - new_system.add_distribution_piping(pipe.rotate(axis, angle, origin), _key=k) + new_system.add_distribution_piping(pipe.rotate(axis_vec3D, angle_degrees, origin_pt3D), _key=k) for k, pipe in self._recirc_piping.items(): - new_system.add_recirc_piping(pipe.rotate(axis, angle, origin), _key=k) + new_system.add_recirc_piping(pipe.rotate(axis_vec3D, angle_degrees, origin_pt3D), _key=k) + + return new_system - def rotate_xy(self, angle, origin): + def rotate_xy(self, angle_degrees, origin_pt3D): + # type: (float, Point3D) -> PhHotWaterSystem """Rotate the System's piping counterclockwise in the XY plane by a certain angle. Args: - angle: An angle in radians. - origin: A Point3D for the origin around which the object will be rotated. + angle_degrees: An angle in degrees. + origin_pt3D: A Point3D for the origin around which the object will be rotated. + Returns: + A new PhHotWaterSystem object with the rotated piping. """ new_system = self.duplicate() new_system.identifier = self.identifier @@ -437,18 +450,23 @@ def rotate_xy(self, angle, origin): new_system.clear_recirc_piping() for k, pipe in self._distribution_piping.items(): - new_system.add_distribution_piping(pipe.rotate_xy(angle, origin), _key=k) + new_system.add_distribution_piping(pipe.rotate_xy(angle_degrees, origin_pt3D), _key=k) for k, pipe in self._recirc_piping.items(): - new_system.add_recirc_piping(pipe.rotate_xy(angle, origin), _key=k) + new_system.add_recirc_piping(pipe.rotate_xy(angle_degrees, origin_pt3D), _key=k) - def reflect(self, normal, origin): + return new_system + + def reflect(self, normal_vec3D, origin_pt3D): + # type: (Point3D, Point3D) -> PhHotWaterSystem """Reflected the System's piping across a plane with the input normal vector and origin. Args: - normal: A Vector3D representing the normal vector for the plane across + normal_vec3D: A Vector3D representing the normal vector for the plane across which the line segment will be reflected. THIS VECTOR MUST BE NORMALIZED. - origin: A Point3D representing the origin from which to reflect. + origin_pt3D: A Point3D representing the origin from which to reflect. + Returns: + A new PhHotWaterSystem object with the reflected piping. """ new_system = self.duplicate() new_system.identifier = self.identifier @@ -456,19 +474,23 @@ def reflect(self, normal, origin): new_system.clear_recirc_piping() for k, pipe in self._distribution_piping.items(): - new_system.add_distribution_piping(pipe.reflect(normal, origin), _key=k) + new_system.add_distribution_piping(pipe.reflect(normal_vec3D, origin_pt3D), _key=k) for k, pipe in self._recirc_piping.items(): - new_system.add_recirc_piping(pipe.reflect(normal, origin), _key=k) + new_system.add_recirc_piping(pipe.reflect(normal_vec3D, origin_pt3D), _key=k) + + return new_system - def scale(self, factor, origin=None): + def scale(self, scale_factor, origin_pt3D=None): # type: (float, Optional[Point3D]) -> PhHotWaterSystem """Scale the System's piping by a factor from an origin point. Args: - factor: A number representing how much the line segment should be scaled. - origin: A Point3D representing the origin from which to scale. + scale_factor: A number representing how much the line segment should be scaled. + origin_pt3D: A Point3D representing the origin from which to scale. If None, it will be scaled from the World origin (0, 0, 0). + Returns: + A new PhHotWaterSystem object with the scaled piping. """ new_system = self.duplicate() new_system.identifier = self.identifier @@ -476,9 +498,9 @@ def scale(self, factor, origin=None): new_system.clear_recirc_piping() for k, pipe in self._distribution_piping.items(): - new_system.add_distribution_piping(pipe.scale(factor, origin), _key=k) + new_system.add_distribution_piping(pipe.scale(scale_factor, origin_pt3D), _key=k) for k, pipe in self._recirc_piping.items(): - new_system.add_recirc_piping(pipe.scale(factor, origin), _key=k) + new_system.add_recirc_piping(pipe.scale(scale_factor, origin_pt3D), _key=k) return new_system diff --git a/honeybee_phhvac/properties/room.py b/honeybee_phhvac/properties/room.py index 4db8548..c220de6 100644 --- a/honeybee_phhvac/properties/room.py +++ b/honeybee_phhvac/properties/room.py @@ -134,6 +134,16 @@ def set_hot_water_system(self, _hot_water_system): """Set the Hot Water System serving the Room.""" self._hot_water_system = _hot_water_system + def clear_systems(self): + """Clear all the HVAC Systems from the Room.""" + self._ventilation_system = None + self._heating_systems.clear() + self._heat_pump_systems.clear() + self._exhaust_vent_devices.clear() + self._supportive_devices.clear() + self._renewable_devices.clear() + self._hot_water_system = None + def to_dict(self, abridged=False): # type: (bool) -> Dict[str, Any] d = {} @@ -256,7 +266,7 @@ def __copy__(self, new_host=None): new_obj = RoomPhHvacProperties(_host) new_obj.id_num = self.id_num - new_obj.set_ventilation_system(self.ventilation_system) + new_obj.set_ventilation_system(self.ventilation_system.duplicate() if self.ventilation_system else None) for htg_sys in self.heating_systems: new_obj.add_heating_system(htg_sys) @@ -273,155 +283,197 @@ def __copy__(self, new_host=None): for renewable_device in self.renewable_devices: new_obj.add_renewable_device(renewable_device) - new_obj.set_hot_water_system(self.hot_water_system) + new_obj.set_hot_water_system(self.hot_water_system.duplicate() if self.hot_water_system else None) return new_obj - def move(self, moving_vec): + def move(self, move_vec3D): """Move the Room's HVAC Systems along a vector. + When used in within Honeybee, this method will most often be triggered as part of a + larger model-transform. For instance, when triggered during a Room's + `honeybee.properties._Properties` transform. + + When this method is used on its own, it is best practice to duplicate the + object before applying this transform since it applies the transform to the object + directly, rather than returning a new object with the transform applied. + Args: - moving_vec: A Vector3D with the direction and distance to move the ray. + move_vec3D: A Vector3D with the direction and distance to move the ray. """ if self._ventilation_system: - self._ventilation_system.move(moving_vec) + self._ventilation_system.move(move_vec3D) for sys in self._heating_systems: - sys.move(moving_vec) + sys.move(move_vec3D) for sys in self._heat_pump_systems: - sys.move(moving_vec) + sys.move(move_vec3D) for sys in self._exhaust_vent_devices: - sys.move(moving_vec) + sys.move(move_vec3D) for sys in self._supportive_devices: - sys.move(moving_vec) + sys.move(move_vec3D) for sys in self._renewable_devices: - sys.move(moving_vec) + sys.move(move_vec3D) if self._hot_water_system: - self._hot_water_system.move(moving_vec) + self._hot_water_system.move(move_vec3D) - def rotate(self, axis, angle, origin): + def rotate(self, axis_vec3D, angle_degrees, origin_pt3D): """Rotate the Room's HVAC Systems by a certain angle around an axis and origin. + When used in within Honeybee, this method will most often be triggered as part of a + larger model-transform. For instance, when triggered during a Room's + `honeybee.properties._Properties` transform. + + When this method is used on its own, it is best practice to duplicate the + object before applying this transform since it applies the transform to the object + directly, rather than returning a new object with the transform applied. + Right hand rule applies: If axis has a positive orientation, rotation will be clockwise. If axis has a negative orientation, rotation will be counterclockwise. Args: - axis: A Vector3D axis representing the axis of rotation. - angle: An angle for rotation in radians. - origin: A Point3D for the origin around which the object will be rotated. + axis_vec3D: A Vector3D axis representing the axis of rotation. + angle_degrees: An angle for rotation in degrees. + origin_pt3D: A Point3D for the origin around which the object will be rotated. """ if self._ventilation_system: - self._ventilation_system.rotate(axis, angle, origin) + self._ventilation_system.rotate(axis_vec3D, angle_degrees, origin_pt3D) for sys in self._heating_systems: - sys.rotate(axis, angle, origin) + sys.rotate(axis_vec3D, angle_degrees, origin_pt3D) for sys in self._heat_pump_systems: - sys.rotate(axis, angle, origin) + sys.rotate(axis_vec3D, angle_degrees, origin_pt3D) for sys in self._exhaust_vent_devices: - sys.rotate(axis, angle, origin) + sys.rotate(axis_vec3D, angle_degrees, origin_pt3D) for sys in self._supportive_devices: - sys.rotate(axis, angle, origin) + sys.rotate(axis_vec3D, angle_degrees, origin_pt3D) for sys in self._renewable_devices: - sys.rotate(axis, angle, origin) + sys.rotate(axis_vec3D, angle_degrees, origin_pt3D) if self._hot_water_system: - self._hot_water_system.rotate(axis, angle, origin) + self._hot_water_system.rotate(axis_vec3D, angle_degrees, origin_pt3D) - def rotate_xy(self, angle, origin): + def rotate_xy(self, angle_degree, origin_pt3D): + # type: (float, geometry3d.Point3D) -> None """Rotate the Room's HVAC Systems counterclockwise in the XY plane by a certain angle. + When used in within Honeybee, this method will most often be triggered as part of a + larger model-transform. For instance, when triggered during a Room's + `honeybee.properties._Properties` transform. + + When this method is used on its own, it is best practice to duplicate the + object before applying this transform since it applies the transform to the object + directly, rather than returning a new object with the transform applied. + Args: - angle: An angle in radians. - origin: A Point3D for the origin around which the object will be rotated. + angle_degree: An angle in degrees. + origin_pt3D: A Point3D for the origin around which the object will be rotated. """ + if self._ventilation_system: - self._ventilation_system.rotate_xy(angle, origin) + self._ventilation_system = self._ventilation_system.rotate_xy(angle_degree, origin_pt3D) for sys in self._heating_systems: - sys.rotate_xy(angle, origin) + sys.rotate_xy(angle_degree, origin_pt3D) for sys in self._heat_pump_systems: - sys.rotate_xy(angle, origin) + sys.rotate_xy(angle_degree, origin_pt3D) for sys in self._exhaust_vent_devices: - sys.rotate_xy(angle, origin) + sys.rotate_xy(angle_degree, origin_pt3D) for sys in self._supportive_devices: - sys.rotate_xy(angle, origin) + sys.rotate_xy(angle_degree, origin_pt3D) for sys in self._renewable_devices: - sys.rotate_xy(angle, origin) + sys.rotate_xy(angle_degree, origin_pt3D) if self._hot_water_system: - self._hot_water_system.rotate_xy(angle, origin) + self._hot_water_system = self._hot_water_system.rotate_xy(angle_degree, origin_pt3D) - def reflect(self, normal, origin): + def reflect(self, normal_vec3D, origin_pt3D): """Reflected the Room's HVAC Systems across a plane with the input normal vector and origin. + When used in within Honeybee, this method will most often be triggered as part of a + larger model-transform. For instance, when triggered during a Room's + `honeybee.properties._Properties` transform. + + When this method is used on its own, it is best practice to duplicate the + object before applying this transform since it applies the transform to the object + directly, rather than returning a new object with the transform applied. + Args: - normal: A Vector3D representing the normal vector for the plane across + normal_vec3D: A Vector3D representing the normal vector for the plane across which the line segment will be reflected. THIS VECTOR MUST BE NORMALIZED. - origin: A Point3D representing the origin from which to reflect. + origin_pt3D: A Point3D representing the origin from which to reflect. """ if self._ventilation_system: - self._ventilation_system.reflect(normal, origin) + self._ventilation_system.reflect(normal_vec3D, origin_pt3D) for sys in self._heating_systems: - sys.reflect(normal, origin) + sys.reflect(normal_vec3D, origin_pt3D) for sys in self._heat_pump_systems: - sys.reflect(normal, origin) + sys.reflect(normal_vec3D, origin_pt3D) for sys in self._exhaust_vent_devices: - sys.reflect(normal, origin) + sys.reflect(normal_vec3D, origin_pt3D) for sys in self._supportive_devices: - sys.reflect(normal, origin) + sys.reflect(normal_vec3D, origin_pt3D) for sys in self._renewable_devices: - sys.reflect(normal, origin) + sys.reflect(normal_vec3D, origin_pt3D) if self._hot_water_system: - self._hot_water_system.reflect(normal, origin) + self._hot_water_system.reflect(normal_vec3D, origin_pt3D) - def scale(self, factor, origin=None): + def scale(self, scale_factor, origin_pt3D=None): """Scale the Room's HVAC Systems by a factor from an origin point. + When used in within Honeybee, this method will most often be triggered as part of a + larger model-transform. For instance, when triggered during a Room's + `honeybee.properties._Properties` transform. + + When this method is used on its own, it is best practice to duplicate the + object before applying this transform since it applies the transform to the object + directly, rather than returning a new object with the transform applied. + Args: - factor: A number representing how much the line segment should be scaled. - origin: A Point3D representing the origin from which to scale. + scale_factor: A number representing how much the line segment should be scaled. + origin_pt3D: A Point3D representing the origin from which to scale. If None, it will be scaled from the World origin (0, 0, 0). """ if self._ventilation_system: - self.set_ventilation_system(self._ventilation_system.scale(factor, origin)) + self.set_ventilation_system(self._ventilation_system.scale(scale_factor, origin_pt3D)) for sys in self._heating_systems: - sys.scale(factor, origin) + sys.scale(scale_factor, origin_pt3D) for sys in self._heat_pump_systems: - sys.scale(factor, origin) + sys.scale(scale_factor, origin_pt3D) for sys in self._exhaust_vent_devices: - sys.scale(factor, origin) + sys.scale(scale_factor, origin_pt3D) for sys in self._supportive_devices: - sys.scale(factor, origin) + sys.scale(scale_factor, origin_pt3D) for sys in self._renewable_devices: - sys.scale(factor, origin) + sys.scale(scale_factor, origin_pt3D) if self._hot_water_system: - self.set_hot_water_system(self._hot_water_system.scale(factor, origin)) + self.set_hot_water_system(self._hot_water_system.scale(scale_factor, origin_pt3D)) def __str__(self): # type: () -> str diff --git a/honeybee_phhvac/renewable_devices.py b/honeybee_phhvac/renewable_devices.py index c4f2ba2..79d3fe4 100644 --- a/honeybee_phhvac/renewable_devices.py +++ b/honeybee_phhvac/renewable_devices.py @@ -68,54 +68,54 @@ def __lt__(self, other): # type: (PhRenewableEnergyDevice) -> bool return self.identifier < other.identifier - def move(self, moving_vec): - """Move the device along a vector. + def move(self, moving_vec3D): + """Move the device's elements along a vector. Args: - moving_vec: A Vector3D with the direction and distance to move the ray. + moving_vec3D: A Vector3D with the direction and distance to move the ray. """ pass - def rotate(self, axis, angle, origin): - """Rotate the device by a certain angle around an axis and origin. + def rotate(self, axis_vec3D, angle_degrees, origin_pt3D): + """Rotate the device's elements by a certain angle around an axis_vec3D and origin_pt3D. Right hand rule applies: - If axis has a positive orientation, rotation will be clockwise. - If axis has a negative orientation, rotation will be counterclockwise. + If axis_vec3D has a positive orientation, rotation will be clockwise. + If axis_vec3D has a negative orientation, rotation will be counterclockwise. Args: - axis: A Vector3D axis representing the axis of rotation. - angle: An angle for rotation in radians. - origin: A Point3D for the origin around which the object will be rotated. + axis_vec3D: A Vector3D axis_vec3D representing the axis_vec3D of rotation. + angle_degrees: An angle for rotation in degrees. + origin_pt3D: A Point3D for the origin_pt3D around which the object will be rotated. """ pass - def rotate_xy(self, angle, origin): - """Rotate the device counterclockwise in the XY plane by a certain angle. + def rotate_xy(self, angle_degrees, origin_pt3D): + """Rotate the device's elements counterclockwise in the XY plane by a certain angle. Args: - angle: An angle in radians. - origin: A Point3D for the origin around which the object will be rotated. + angle_degrees: An angle in degrees. + origin_pt3D: A Point3D for the origin_pt3D around which the object will be rotated. """ pass - def reflect(self, normal, origin): - """Reflected the device across a plane with the input normal vector and origin. + def reflect(self, normal_vec3D, origin_pt3D): + """Reflected the device's elements across a plane with the input normal vector and origin_pt3D. Args: - normal: A Vector3D representing the normal vector for the plane across + normal_vec3D: A Vector3D representing the normal vector for the plane across which the line segment will be reflected. THIS VECTOR MUST BE NORMALIZED. - origin: A Point3D representing the origin from which to reflect. + origin_pt3D: A Point3D representing the origin_pt3D from which to reflect. """ pass - def scale(self, factor, origin=None): - """Scale the device by a factor from an origin point. + def scale(self, scale_factor, origin_pt3D=None): + """Scale the device's elements by a factor from an origin_pt3D point. Args: - factor: A number representing how much the line segment should be scaled. - origin: A Point3D representing the origin from which to scale. - If None, it will be scaled from the World origin (0, 0, 0). + scale_factor: A number representing how much the line segment should be scaled. + origin_pt3D: A Point3D representing the origin_pt3D from which to scale. + If None, it will be scaled from the World origin_pt3D (0, 0, 0). """ pass @@ -165,11 +165,11 @@ class PhRenewableEnergyDeviceBuilder(object): def from_dict(cls, _input_dict): # type: (dict[str, Any]) -> PhRenewableEnergyDevice """Find the right device constructor class from the module based on the device_typename.""" - valid_device_typenames = [nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")] + valid_device_type_names = [nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")] device_typename = _input_dict["device_typename"] - if device_typename not in valid_device_typenames: - raise UnknownPhRenewableEnergyTypeError(valid_device_typenames, device_typename) + if device_typename not in valid_device_type_names: + raise UnknownPhRenewableEnergyTypeError(valid_device_type_names, device_typename) device_class = getattr(sys.modules[__name__], device_typename) # type: PhRenewableEnergyDevice new_device = device_class.from_dict(_input_dict) return new_device diff --git a/honeybee_phhvac/supportive_device.py b/honeybee_phhvac/supportive_device.py index 54aca1f..20d463a 100644 --- a/honeybee_phhvac/supportive_device.py +++ b/honeybee_phhvac/supportive_device.py @@ -82,53 +82,53 @@ def __repr__(self): def ToString(self): return self.__repr__() - def move(self, moving_vec): - """Move the device along a vector. + def move(self, moving_vec3D): + """Move the device's elements along a vector. Args: - moving_vec: A Vector3D with the direction and distance to move the ray. + moving_vec3D: A Vector3D with the direction and distance to move the ray. """ pass - def rotate(self, axis, angle, origin): - """Rotate the device by a certain angle around an axis and origin. + def rotate(self, axis_vec3D, angle_degrees, origin_pt3D): + """Rotate the device's elements by a certain angle around an axis_vec3D and origin_pt3D. Right hand rule applies: - If axis has a positive orientation, rotation will be clockwise. - If axis has a negative orientation, rotation will be counterclockwise. + If axis_vec3D has a positive orientation, rotation will be clockwise. + If axis_vec3D has a negative orientation, rotation will be counterclockwise. Args: - axis: A Vector3D axis representing the axis of rotation. - angle: An angle for rotation in radians. - origin: A Point3D for the origin around which the object will be rotated. + axis_vec3D: A Vector3D axis_vec3D representing the axis_vec3D of rotation. + angle_degrees: An angle for rotation in degrees. + origin_pt3D: A Point3D for the origin_pt3D around which the object will be rotated. """ pass - def rotate_xy(self, angle, origin): - """Rotate the device counterclockwise in the XY plane by a certain angle. + def rotate_xy(self, angle_degrees, origin_pt3D): + """Rotate the device's elements counterclockwise in the XY plane by a certain angle. Args: - angle: An angle in radians. - origin: A Point3D for the origin around which the object will be rotated. + angle_degrees: An angle in degrees. + origin_pt3D: A Point3D for the origin_pt3D around which the object will be rotated. """ pass - def reflect(self, normal, origin): - """Reflected the device across a plane with the input normal vector and origin. + def reflect(self, normal_vec3D, origin_pt3D): + """Reflected the device's elements across a plane with the input normal vector and origin_pt3D. Args: - normal: A Vector3D representing the normal vector for the plane across + normal_vec3D: A Vector3D representing the normal vector for the plane across which the line segment will be reflected. THIS VECTOR MUST BE NORMALIZED. - origin: A Point3D representing the origin from which to reflect. + origin_pt3D: A Point3D representing the origin_pt3D from which to reflect. """ pass - def scale(self, factor, origin=None): - """Scale the device by a factor from an origin point. + def scale(self, scale_factor, origin_pt3D=None): + """Scale the device's elements by a factor from an origin_pt3D point. Args: - factor: A number representing how much the line segment should be scaled. - origin: A Point3D representing the origin from which to scale. - If None, it will be scaled from the World origin (0, 0, 0). + scale_factor: A number representing how much the line segment should be scaled. + origin_pt3D: A Point3D representing the origin_pt3D from which to scale. + If None, it will be scaled from the World origin_pt3D (0, 0, 0). """ pass diff --git a/honeybee_phhvac/ventilation.py b/honeybee_phhvac/ventilation.py index 31f8426..048fe65 100644 --- a/honeybee_phhvac/ventilation.py +++ b/honeybee_phhvac/ventilation.py @@ -6,8 +6,6 @@ import sys from copy import copy -from honeybee_phhvac._base import _PhHVACBase - try: from typing import Any, Dict, List, Optional except ImportError: @@ -251,19 +249,19 @@ def __repr__(self): def ToString(self): return self.__repr__() - def move(self, moving_vec): + def move(self, moving_vec3D): """Move the System's ducts along a vector. Args: - moving_vec: A Vector3D with the direction and distance to move the ray. + moving_vec3D: A Vector3D with the direction and distance to move the ray. """ new_system = self.duplicate() new_system.identifier = self.identifier - new_system.supply_ducting = [duct_element.move(moving_vec) for duct_element in self.supply_ducting] - new_system.exhaust_ducting = [duct_element.move(moving_vec) for duct_element in self.exhaust_ducting] + new_system.supply_ducting = [duct_element.move(moving_vec3D) for duct_element in self.supply_ducting] + new_system.exhaust_ducting = [duct_element.move(moving_vec3D) for duct_element in self.exhaust_ducting] return new_system - def rotate(self, axis, angle, origin): + def rotate(self, axis_vec3D, angle_degrees, origin_pt3D): """Rotate the System's ducts by a certain angle around an axis and origin. Right hand rule applies: @@ -271,56 +269,72 @@ def rotate(self, axis, angle, origin): If axis has a negative orientation, rotation will be counterclockwise. Args: - axis: A Vector3D axis representing the axis of rotation. - angle: An angle for rotation in radians. - origin: A Point3D for the origin around which the object will be rotated. + axis_vec3D: A Vector3D axis representing the axis of rotation. + angle_degrees: An angle for rotation in degrees. + origin_pt3D: A Point3D for the origin around which the object will be rotated. """ new_system = self.duplicate() new_system.identifier = self.identifier - new_system.supply_ducting = [duct_element.rotate(axis, angle, origin) for duct_element in self.supply_ducting] - new_system.exhaust_ducting = [duct_element.rotate(axis, angle, origin) for duct_element in self.exhaust_ducting] + new_system.supply_ducting = [ + duct_element.rotate(axis_vec3D, angle_degrees, origin_pt3D) for duct_element in self.supply_ducting + ] + new_system.exhaust_ducting = [ + duct_element.rotate(axis_vec3D, angle_degrees, origin_pt3D) for duct_element in self.exhaust_ducting + ] return new_system - def rotate_xy(self, angle, origin): + def rotate_xy(self, angle_degrees, origin_pt3D): """Rotate the System's ducts counterclockwise in the XY plane by a certain angle. Args: - angle: An angle in radians. - origin: A Point3D for the origin around which the object will be rotated. + angle_degrees: An angle in degrees. + origin_pt3D: A Point3D for the origin around which the object will be rotated. """ new_system = self.duplicate() new_system.identifier = self.identifier - new_system.supply_ducting = [duct_element.rotate_xy(angle, origin) for duct_element in self.supply_ducting] - new_system.exhaust_ducting = [duct_element.rotate_xy(angle, origin) for duct_element in self.exhaust_ducting] + new_system.supply_ducting = [ + duct_element.rotate_xy(angle_degrees, origin_pt3D) for duct_element in self.supply_ducting + ] + new_system.exhaust_ducting = [ + duct_element.rotate_xy(angle_degrees, origin_pt3D) for duct_element in self.exhaust_ducting + ] return new_system - def reflect(self, normal, origin): + def reflect(self, normal_vec3D, origin_pt3D): """Reflected the System's ducts across a plane with the input normal vector and origin. Args: - normal: A Vector3D representing the normal vector for the plane across + normal_vec3D: A Vector3D representing the normal vector for the plane across which the line segment will be reflected. THIS VECTOR MUST BE NORMALIZED. - origin: A Point3D representing the origin from which to reflect. + origin_pt3D: A Point3D representing the origin from which to reflect. """ new_system = self.duplicate() new_system.identifier = self.identifier - new_system.supply_ducting = [duct_element.reflect(normal, origin) for duct_element in self.supply_ducting] - new_system.exhaust_ducting = [duct_element.reflect(normal, origin) for duct_element in self.exhaust_ducting] + new_system.supply_ducting = [ + duct_element.reflect(normal_vec3D, origin_pt3D) for duct_element in self.supply_ducting + ] + new_system.exhaust_ducting = [ + duct_element.reflect(normal_vec3D, origin_pt3D) for duct_element in self.exhaust_ducting + ] return new_system - def scale(self, factor, origin=None): + def scale(self, scale_factor, origin_pt3D=None): # type: (float, Optional[Point3D]) -> PhVentilationSystem """Scale the System's ducts by a factor from an origin point. Args: - factor: A number representing how much the line segment should be scaled. - origin: A Point3D representing the origin from which to scale. + scale_factor: A number representing how much the line segment should be scaled. + origin_pt3D: A Point3D representing the origin from which to scale. If None, it will be scaled from the World origin (0, 0, 0). """ new_system = self.duplicate() new_system.identifier = self.identifier - new_system.supply_ducting = [duct_element.scale(factor, origin) for duct_element in self.supply_ducting] - new_system.exhaust_ducting = [duct_element.scale(factor, origin) for duct_element in self.exhaust_ducting] + new_system.supply_ducting = [ + duct_element.scale(scale_factor, origin_pt3D) for duct_element in self.supply_ducting + ] + new_system.exhaust_ducting = [ + duct_element.scale(scale_factor, origin_pt3D) for duct_element in self.exhaust_ducting + ] return new_system @@ -375,54 +389,54 @@ def __repr__(self): def ToString(self): return self.__repr__() - def move(self, moving_vec): - """Move the device along a vector. + def move(self, moving_vec3D): + """Move the device's elements along a vector. Args: - moving_vec: A Vector3D with the direction and distance to move the ray. + moving_vec3D: A Vector3D with the direction and distance to move the ray. """ pass - def rotate(self, axis, angle, origin): - """Rotate the device by a certain angle around an axis and origin. + def rotate(self, axis_vec3D, angle_degrees, origin_pt3D): + """Rotate the device's elements by a certain angle around an axis_vec3D and origin_pt3D. Right hand rule applies: - If axis has a positive orientation, rotation will be clockwise. - If axis has a negative orientation, rotation will be counterclockwise. + If axis_vec3D has a positive orientation, rotation will be clockwise. + If axis_vec3D has a negative orientation, rotation will be counterclockwise. Args: - axis: A Vector3D axis representing the axis of rotation. - angle: An angle for rotation in radians. - origin: A Point3D for the origin around which the object will be rotated. + axis_vec3D: A Vector3D axis_vec3D representing the axis_vec3D of rotation. + angle_degrees: An angle for rotation in degrees. + origin_pt3D: A Point3D for the origin_pt3D around which the object will be rotated. """ pass - def rotate_xy(self, angle, origin): - """Rotate the device counterclockwise in the XY plane by a certain angle. + def rotate_xy(self, angle_degrees, origin_pt3D): + """Rotate the device's elements counterclockwise in the XY plane by a certain angle. Args: - angle: An angle in radians. - origin: A Point3D for the origin around which the object will be rotated. + angle_degrees: An angle in degrees. + origin_pt3D: A Point3D for the origin_pt3D around which the object will be rotated. """ pass - def reflect(self, normal, origin): - """Reflected the device across a plane with the input normal vector and origin. + def reflect(self, normal_vec3D, origin_pt3D): + """Reflected the device's elements across a plane with the input normal vector and origin_pt3D. Args: - normal: A Vector3D representing the normal vector for the plane across + normal_vec3D: A Vector3D representing the normal vector for the plane across which the line segment will be reflected. THIS VECTOR MUST BE NORMALIZED. - origin: A Point3D representing the origin from which to reflect. + origin_pt3D: A Point3D representing the origin_pt3D from which to reflect. """ pass - def scale(self, factor, origin=None): - """Scale the device by a factor from an origin point. + def scale(self, scale_factor, origin_pt3D=None): + """Scale the device's elements by a factor from an origin_pt3D point. Args: - factor: A number representing how much the line segment should be scaled. - origin: A Point3D representing the origin from which to scale. - If None, it will be scaled from the World origin (0, 0, 0). + scale_factor: A number representing how much the line segment should be scaled. + origin_pt3D: A Point3D representing the origin_pt3D from which to scale. + If None, it will be scaled from the World origin_pt3D (0, 0, 0). """ pass diff --git a/tests/test_honeybee_phhvac/test_distribution/test_hot_water_piping.py b/tests/test_honeybee_phhvac/test_distribution/test_hot_water_piping.py index 40f7424..82e720f 100644 --- a/tests/test_honeybee_phhvac/test_distribution/test_hot_water_piping.py +++ b/tests/test_honeybee_phhvac/test_distribution/test_hot_water_piping.py @@ -1,6 +1,8 @@ import pytest +from math import radians + from ladybug_geometry.geometry3d.line import LineSegment3D -from ladybug_geometry.geometry3d.pointvector import Point3D +from ladybug_geometry.geometry3d.pointvector import Point3D, Vector3D from honeybee_phhvac import hot_water_piping @@ -24,16 +26,19 @@ def test_PhPipeSegment_dict_round_trip(): def test_scale_PhPipeSegment(): - p1, p2 = Point3D(0, 0, 0), Point3D(0, 0, 10) + p1, p2 = Point3D(0, 0, 0), Vector3D(0, 0, 10) geom = LineSegment3D(p1, p2) pipe1 = hot_water_piping.PhHvacPipeSegment(geom, _insul_thickness=1.0) assert pipe1.length == 10 assert pipe1.insulation_thickness == 1.0 - pipe1.scale(2.0) + pipe2 = pipe1.scale(2.0) + + assert pipe1.length == 10 + assert pipe1.insulation_thickness == 1.0 - assert pipe1.length == 20 - assert pipe1.insulation_thickness == 2.0 + assert pipe2.length == 20 + assert pipe2.insulation_thickness == 2.0 # -- Element @@ -150,7 +155,7 @@ def test_PhPipeElement_with_two_different_segments_diameter_name(): def test_scale_PhPipeElement_with_multiple_segments(): - p1, p2 = Point3D(0, 0, 0), Point3D(0, 0, 10) + p1, p2 = Point3D(0, 0, 0), Vector3D(0, 0, 10) geom = LineSegment3D(p1, p2) seg1 = hot_water_piping.PhHvacPipeSegment(geom, _insul_thickness=1.0) seg2 = hot_water_piping.PhHvacPipeSegment(geom, _insul_thickness=2.0) @@ -160,9 +165,50 @@ def test_scale_PhPipeElement_with_multiple_segments(): assert ele1.length == 20 - ele1.scale(2.0) + ele2 = ele1.scale(2.0) + + assert ele1.length == 20 + assert ele2.length == 40 + + +def test_rotate_xy_PhPipeElement_with_multiple_segments(): + p1, p2 = Point3D(0, 0, 0), Vector3D(10, 0, 0) + geom = LineSegment3D(p1, p2) + seg1 = hot_water_piping.PhHvacPipeSegment(geom, _insul_thickness=1.0) + seg2 = hot_water_piping.PhHvacPipeSegment(geom, _insul_thickness=2.0) + ele1 = hot_water_piping.PhHvacPipeElement() + ele1.add_segment(seg1) + ele1.add_segment(seg2) + + assert ele1.length == 20.0 + assert ele1.segments[0].geometry.p2.x == pytest.approx(10.0) + + # +90 means a 90 degree COUNTER-clockwise rotation + ele2 = ele1.rotate_xy(90, Point3D(0, 0, 0)) + + assert ele1.length == pytest.approx(20.0) + assert ele2.length == pytest.approx(20.0) + assert ele2.segments[0].geometry.p2.x == pytest.approx(0.0) + assert ele2.segments[0].geometry.p2.y == pytest.approx(10.0) + assert ele2.segments[0].geometry.p2.z == pytest.approx(0.0) - assert ele1.length == 40 + # +180 means a 189 degree COUNTER-clockwise rotation + ele3 = ele1.rotate_xy(180, Point3D(0, 0, 0)) + + assert ele1.length == pytest.approx(20.0) + assert ele3.length == pytest.approx(20.0) + assert ele3.segments[0].geometry.p2.x == pytest.approx(-10.0) + assert ele3.segments[0].geometry.p2.y == pytest.approx(0.0) + assert ele3.segments[0].geometry.p2.z == pytest.approx(0.0) + + # +180 means a 189 degree COUNTER-clockwise rotation + ele4 = ele1.rotate_xy(270, Point3D(0, 0, 0)) + + assert ele1.length == pytest.approx(20.0) + assert ele4.length == pytest.approx(20.0) + assert ele4.segments[0].geometry.p2.x == pytest.approx(0.0) + assert ele4.segments[0].geometry.p2.y == pytest.approx(-10.0) + assert ele4.segments[0].geometry.p2.z == pytest.approx(0.0) # --- Branch @@ -194,9 +240,9 @@ def test_PhPipeBranch_dict_round_trip(): assert branch2.to_dict() != branch1.to_dict() -def test_PhPipeBranch_with_element_but_no_fixtures(): +def test_scale_PhPipeBranch_with_element_but_no_fixtures(): # -- Build the geometry - p1, p2 = Point3D(0, 0, 0), Point3D(0, 0, 10) + p1, p2 = Point3D(0, 0, 0), Vector3D(0, 0, 10) geom = LineSegment3D(p1, p2) ## -- Set the Branch's Geometry @@ -219,9 +265,9 @@ def test_PhPipeBranch_with_element_but_no_fixtures(): assert branch2.total_length == 20 -def test_PhPipeBranch_with_multiple_fixtures(): +def test_scale_PhPipeBranch_with_multiple_fixtures(): # -- Build the geometry - p1, p2 = Point3D(0, 0, 0), Point3D(0, 0, 10) + p1, p2 = Point3D(0, 0, 0), Vector3D(0, 0, 10) geom = LineSegment3D(p1, p2) ## -- Set the Branch's Geometry @@ -253,6 +299,52 @@ def test_PhPipeBranch_with_multiple_fixtures(): assert branch2.total_length == 60 +def test_rotate_xy_PhPipeBranch_with_multiple_fixtures(): + # -- Build the geometry + p1, p2 = Point3D(0, 0, 0), Vector3D(10, 0, 0) + geom = LineSegment3D(p1, p2) + + ## -- Set the Branch's Geometry + seg1 = hot_water_piping.PhHvacPipeSegment(geom) + ele1 = hot_water_piping.PhHvacPipeElement() + ele1.add_segment(seg1) + + # -- Build the Branch + branch1 = hot_water_piping.PhHvacPipeBranch() + branch1.pipe_element = ele1 + + # -- Build and add fixtures + seg2 = hot_water_piping.PhHvacPipeSegment(geom) + ele2 = hot_water_piping.PhHvacPipeElement() + ele2.add_segment(seg2) + branch1.add_fixture(ele2) + + seg3 = hot_water_piping.PhHvacPipeSegment(geom) + ele3 = hot_water_piping.PhHvacPipeElement() + ele3.add_segment(seg3) + branch1.add_fixture(ele3) + + assert len(branch1.fixtures) == 2 + assert branch1.total_length == 30 + + # +90 means a 90 degree COUNTER-clockwise rotation + branch2 = branch1.rotate_xy(90, Point3D(0, 0, 0)) + + assert len(branch2.fixtures) == 2 + assert branch2.total_length == pytest.approx(30.0) + assert branch2.fixtures[0].segments[0].geometry.p2.x == pytest.approx(0.0) + assert branch2.fixtures[0].segments[0].geometry.p2.y == pytest.approx(10.0) + assert branch2.fixtures[0].segments[0].geometry.p2.z == pytest.approx(0.0) + + # +180 means a 189 degree COUNTER-clockwise rotation + branch3 = branch1.rotate_xy(180, Point3D(0, 0, 0)) + + assert len(branch3.fixtures) == 2 + assert branch3.total_length == pytest.approx(30.0) + assert branch3.fixtures[0].segments[0].geometry.p2.x == pytest.approx(-10.0) + assert branch3.fixtures[0].segments[0].geometry.p + + # -- Trunk @@ -282,3 +374,51 @@ def test_PhPipeTrunk_dict_round_trip(): assert "test_key" in trunk2.user_data assert "test_key" not in trunk1.user_data assert trunk2.to_dict() != trunk1.to_dict() + + +def test_rotate_xy_PhPipeTrunk_with_multiple_branches(): + # -- Build the geometry + p1, p2 = Point3D(0, 0, 0), Vector3D(10, 0, 0) + geom = LineSegment3D(p1, p2) + + ## -- Set the Branch's Geometry + seg1 = hot_water_piping.PhHvacPipeSegment(geom) + ele1 = hot_water_piping.PhHvacPipeElement() + ele1.add_segment(seg1) + + # -- Build the Branch + branch1 = hot_water_piping.PhHvacPipeBranch() + branch1.pipe_element = ele1 + + # -- Build and add fixtures + seg2 = hot_water_piping.PhHvacPipeSegment(geom) + ele2 = hot_water_piping.PhHvacPipeElement() + ele2.add_segment(seg2) + branch1.add_fixture(ele2) + + seg3 = hot_water_piping.PhHvacPipeSegment(geom) + ele3 = hot_water_piping.PhHvacPipeElement() + ele3.add_segment(seg3) + branch1.add_fixture(ele3) + + # -- Build the Trunk + trunk1 = hot_water_piping.PhHvacPipeTrunk() + trunk1.pipe_element = ele1 + trunk1.add_branch(branch1) + + assert len(trunk1.branches) == 1 + assert trunk1.total_length == 40 + + # +90 means a 90 degree COUNTER-clockwise rotation + trunk2 = trunk1.rotate_xy(90, Point3D(0, 0, 0)) + + assert len(trunk2.branches) == 1 + assert trunk2.total_length == pytest.approx(40.0) + assert trunk2.branches[0].fixtures[0].segments[0].geometry.p2.x == pytest.approx(0.0) + assert trunk2.branches[0].fixtures[0].segments[0].geometry.p2.y == pytest.approx(10.0) + assert trunk2.branches[0].fixtures[0].segments[0].geometry.p2.z == pytest.approx(0.0) + + # +180 means a 189 degree COUNTER-clockwise rotation + trunk3 = trunk1.rotate_xy(180, Point3D(0, 0, 0)) + + assert len(trunk3.branches) == 1 diff --git a/tests/test_honeybee_phhvac/test_systems/test_hot_water_system.py b/tests/test_honeybee_phhvac/test_systems/test_hot_water_system.py index 0c48995..0619e32 100644 --- a/tests/test_honeybee_phhvac/test_systems/test_hot_water_system.py +++ b/tests/test_honeybee_phhvac/test_systems/test_hot_water_system.py @@ -266,7 +266,7 @@ def test_scale_system_with_no_piping(): def test_scale_system_with_single_trunk(): - system = hws.PhHotWaterSystem() + system1 = hws.PhHotWaterSystem() pt1 = Point3D(0, 0, 0) vec1 = Vector3D(0, 0, 1) pipe_segment_1 = hwp.PhHvacPipeSegment(LineSegment3D.from_sdl(pt1, vec1, 144.0)) @@ -274,20 +274,22 @@ def test_scale_system_with_single_trunk(): pipe_element_1.add_segment(pipe_segment_1) trunk1 = hwp.PhHvacPipeTrunk() trunk1.pipe_element = pipe_element_1 - system.add_distribution_piping(trunk1) + system1.add_distribution_piping(trunk1) - system.scale(0.0254) - assert system.total_distribution_pipe_length == 3.6576 + system2 = system1.scale(0.0254) + assert system1.total_distribution_pipe_length == pytest.approx(144.0) + assert system2.total_distribution_pipe_length == pytest.approx(3.6576) def test_scale_system_with_single_recirc_element(): - system = hws.PhHotWaterSystem() + system1 = hws.PhHotWaterSystem() pt1 = Point3D(0, 0, 0) vec1 = Vector3D(0, 0, 1) pipe_segment_1 = hwp.PhHvacPipeSegment(LineSegment3D.from_sdl(pt1, vec1, 144.0)) pipe_element_1 = hwp.PhHvacPipeElement() pipe_element_1.add_segment(pipe_segment_1) - system.add_recirc_piping(pipe_element_1) + system1.add_recirc_piping(pipe_element_1) - system.scale(0.0254) - assert system.total_recirc_pipe_length == 3.6576 + system2 = system1.scale(0.0254) + assert system1.total_recirc_pipe_length == pytest.approx(144.0) + assert system2.total_recirc_pipe_length == pytest.approx(3.6576)