diff --git a/dev-requirements.txt b/dev-requirements.txt index fea764c..9de5539 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,2 +1,3 @@ pytest -coverage \ No newline at end of file +coverage +black \ No newline at end of file diff --git a/hbph_installer.gh b/hbph_installer.gh index 10dbf1e..d867c4a 100644 Binary files a/hbph_installer.gh and b/hbph_installer.gh differ diff --git a/honeybee_grasshopper_ph/src/HBPH - Add Exhaust Ventilator.py b/honeybee_grasshopper_ph/src/HBPH - Add Exhaust Ventilator.py new file mode 100644 index 0000000..c9dff0e --- /dev/null +++ b/honeybee_grasshopper_ph/src/HBPH - Add Exhaust Ventilator.py @@ -0,0 +1,79 @@ +# +# Honeybee-PH: A Plugin for adding Passive-House data to LadybugTools Honeybee-Energy Models +# +# This component is part of the PH-Tools toolkit . +# +# Copyright (c) 2022, PH-Tools and bldgtyp, llc +# Honeybee-PH is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published +# by the Free Software Foundation; either version 3 of the License, +# or (at your option) any later version. +# +# Honeybee-PH is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# For a copy of the GNU General Public License +# see . +# +# @license GPL-3.0+ +# +""" +Add one or more Exhaust Ventilator devices to the Honeybee-Rooms. This is used +mainly for WUFI-Passive which has a separate input for dedicated exhaust equipment +such as that used for Kitchens, Dryers and other typical uses. +- +EM January 3, 2023 + Args: + _exhaust_vent_devices: (List[_ExhaustVentilatorBase]) A list of the Exhaust + Ventilator objects that you would like to add to the Honeybee-rooms. + + _hb_rooms: (List[room.Room]): A list of the rooms to add the Exhaust Ventilator + to. Note that each ventilator created will only show up once in the + WUFI-model. If you want more than one fan, you should create more than + one and add them to the approproate rooms. + + Returns: + hb_rooms_: The Honeybee rooms with the new Exhaust Ventilators added. +""" + +import scriptcontext as sc +import Rhino as rh +import rhinoscriptsyntax as rs +import ghpythonlib.components as ghc +import Grasshopper as gh + + +try: + from honeybee_ph_utils import preview +except ImportError as e: + raise ImportError('Failed to import honeybee_ph_utils:\t{}'.format(e)) + +try: + from honeybee_ph_rhino import gh_compo_io, gh_io +except ImportError as e: + raise ImportError('Failed to import honeybee_ph_rhino:\t{}'.format(e)) + + +# ------------------------------------------------------------------------------------- +import honeybee_ph_rhino._component_info_ +reload(honeybee_ph_rhino._component_info_) +ghenv.Component.Name = "HBPH - Add Exhaust Ventilator" +DEV = honeybee_ph_rhino._component_info_.set_component_params(ghenv, dev=False) +if DEV: + reload(gh_compo_io) + reload(gh_io) + from honeybee_ph_rhino.gh_compo_io import ghio_validators + reload(ghio_validators) + from honeybee_ph_rhino.gh_compo_io import mech_add_exhaust_vent as gh_compo_io + reload(gh_compo_io) + +# ------------------------------------------------------------------------------ +# -- GH Interface +IGH = gh_io.IGH( ghdoc, ghenv, sc, rh, rs, ghc, gh ) + + +# ------------------------------------------------------------------------------------- +gh_compo_interface = gh_compo_io.GHCompo_AddExhaustVent(IGH, _exhaust_vent_devices, _hb_rooms) +hb_rooms_ = gh_compo_interface.run() diff --git a/honeybee_grasshopper_ph/src/HBPH - Create Exhaust Ventilator.py b/honeybee_grasshopper_ph/src/HBPH - Create Exhaust Ventilator.py new file mode 100644 index 0000000..2acf185 --- /dev/null +++ b/honeybee_grasshopper_ph/src/HBPH - Create Exhaust Ventilator.py @@ -0,0 +1,92 @@ +# +# Honeybee-PH: A Plugin for adding Passive-House data to LadybugTools Honeybee-Energy Models +# +# This component is part of the PH-Tools toolkit . +# +# Copyright (c) 2022, PH-Tools and bldgtyp, llc +# Honeybee-PH is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published +# by the Free Software Foundation; either version 3 of the License, +# or (at your option) any later version. +# +# Honeybee-PH is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# For a copy of the GNU General Public License +# see . +# +# @license GPL-3.0+ +# +""" +Create a new HBPH Exhaust Ventilator for use in WUFI-Passive models. This device +can be added to one or more rooms using the "HBPH - Add Exhaust Ventilator" component +in order to simulate the effect of direct exhaust ventilation without heat-recovery. +This is useful for devices such as direct-vent dryers, kitchen hoods or other elements. +- +EM January 3, 2023 + Args: + _name: (str) Name for the new HBPH Exhaust Ventilator + + _type: (str) The type of device. Input either - + - "1-Dryer" + - "2-Kitchen Hood" + - "3-User Defined" + + _flow_rate_m3s: (float) The total m3/s of airflow from the exhaust + ventilation device. + + _annual_runtime_minutes: (float) Optional. If device type is 'User Defined' + input the annual runtime (minutes). + + Returns: + exhaust_vent_device_: An HBPH Exhaust Ventilator object which can be + added to one or more honeybee-rooms. +""" + +import scriptcontext as sc +import Rhino as rh +import rhinoscriptsyntax as rs +import ghpythonlib.components as ghc +import Grasshopper as gh + + +try: + from honeybee_ph_utils import preview +except ImportError as e: + raise ImportError('Failed to import honeybee_ph_utils:\t{}'.format(e)) + +try: + from honeybee_ph_rhino import gh_compo_io, gh_io +except ImportError as e: + raise ImportError('Failed to import honeybee_ph_rhino:\t{}'.format(e)) + + +# ------------------------------------------------------------------------------------- +import honeybee_ph_rhino._component_info_ +reload(honeybee_ph_rhino._component_info_) +ghenv.Component.Name = "HBPH - Create Exhaust Ventilator" +DEV = honeybee_ph_rhino._component_info_.set_component_params(ghenv, dev=False) +if DEV: + from honeybee_energy_ph.hvac import ventilation + reload(ventilation) + reload(gh_compo_io) + reload(gh_io) + from honeybee_ph_rhino.gh_compo_io import ghio_validators + reload(ghio_validators) + from honeybee_ph_rhino.gh_compo_io import mech_create_exhaust_vent as gh_compo_io + reload(gh_compo_io) + +# ------------------------------------------------------------------------------ +# -- GH Interface +IGH = gh_io.IGH( ghdoc, ghenv, sc, rh, rs, ghc, gh ) + + +# ------------------------------------------------------------------------------------- +gh_compo_interface = gh_compo_io.GHCompo_CreateExhaustVent( + IGH, _name, _type, _flow_rate_m3s, _annual_runtime_minutes) +exhaust_vent_device_ = gh_compo_interface.run() + +# ------------------------------------------------------------------------------------- +preview.object_preview(exhaust_vent_device_) \ No newline at end of file diff --git a/honeybee_grasshopper_ph/user_objects/HBPH - Add Exhaust Ventilator.ghuser b/honeybee_grasshopper_ph/user_objects/HBPH - Add Exhaust Ventilator.ghuser new file mode 100644 index 0000000..a6da57c Binary files /dev/null and b/honeybee_grasshopper_ph/user_objects/HBPH - Add Exhaust Ventilator.ghuser differ diff --git a/honeybee_grasshopper_ph/user_objects/HBPH - Create Exhaust Ventilator.ghuser b/honeybee_grasshopper_ph/user_objects/HBPH - Create Exhaust Ventilator.ghuser new file mode 100644 index 0000000..ea817a1 Binary files /dev/null and b/honeybee_grasshopper_ph/user_objects/HBPH - Create Exhaust Ventilator.ghuser differ diff --git a/honeybee_grasshopper_ph/user_objects/HBPH - Exhaust Vent. Types.ghuser b/honeybee_grasshopper_ph/user_objects/HBPH - Exhaust Vent. Types.ghuser new file mode 100644 index 0000000..88ace22 Binary files /dev/null and b/honeybee_grasshopper_ph/user_objects/HBPH - Exhaust Vent. Types.ghuser differ diff --git a/honeybee_ph_rhino/_component_info_.py b/honeybee_ph_rhino/_component_info_.py index 63f85b6..b1ad83d 100644 --- a/honeybee_ph_rhino/_component_info_.py +++ b/honeybee_ph_rhino/_component_info_.py @@ -5,7 +5,7 @@ These are called when the component is instantiated within the Grasshopper canvas. """ -RELEASE_VERSION = "Honeybee-PH v1.0" +RELEASE_VERSION = "Honeybee-PH v1.0.1" CATEGORY = "HB-PH" SUB_CATEGORIES = { 0: "00 | Utils", @@ -169,63 +169,77 @@ "Category": CATEGORY, "SubCategory": 1, }, - # -- - "HBPH - Create PH Equipment": { - "NickName": "Create PH Equipment", + # -- HVAC + "HBPH - Create Exhaust Ventilator": { + "NickName": "Create Exhaust Ventilator", "Message": RELEASE_VERSION, "Category": CATEGORY, "SubCategory": 1, }, - "HBPH - Add PH Equipment": { - "NickName": "Add PH Equipment", + "HBPH - Add Exhaust Ventilator": { + "NickName": "Create Exhaust Ventilator", "Message": RELEASE_VERSION, "Category": CATEGORY, "SubCategory": 1, }, - "HBPH - Set Res Occupancy": { - "NickName": "Set Res Occupancy", + "HBPH - Add Mech Systems": { + "NickName": "Add Mech", "Message": RELEASE_VERSION, "Category": CATEGORY, "SubCategory": 1, }, - "HBPH - Phius MF Res Calculator": { - "NickName": "Phius MF Res Calc", + "HBPH - Create Heating System": { + "NickName": "Create Heating", "Message": RELEASE_VERSION, "Category": CATEGORY, "SubCategory": 1, }, - "HBPH - Phius Program Finder": { - "NickName": "Phius Programs", + "HBPH - Create Cooling System": { + "NickName": "Create Cooling", "Message": RELEASE_VERSION, "Category": CATEGORY, "SubCategory": 1, }, - "HBPH - Create Conversion Factor": { - "NickName": "Factor", + "HBPH - Create Ventilation System": { + "NickName": "Create Vent", "Message": RELEASE_VERSION, "Category": CATEGORY, "SubCategory": 1, }, - "HBPH - Add Mech Systems": { - "NickName": "Add Mech", + # -- Elec Equipment + "HBPH - Create PH Equipment": { + "NickName": "Create PH Equipment", "Message": RELEASE_VERSION, "Category": CATEGORY, "SubCategory": 1, }, - "HBPH - Create Heating System": { - "NickName": "Create Heating", + "HBPH - Add PH Equipment": { + "NickName": "Add PH Equipment", "Message": RELEASE_VERSION, "Category": CATEGORY, "SubCategory": 1, }, - "HBPH - Create Cooling System": { - "NickName": "Create Cooling", + # -- + "HBPH - Set Res Occupancy": { + "NickName": "Set Res Occupancy", "Message": RELEASE_VERSION, "Category": CATEGORY, "SubCategory": 1, }, - "HBPH - Create Ventilation System": { - "NickName": "Create Vent", + "HBPH - Phius MF Res Calculator": { + "NickName": "Phius MF Res Calc", + "Message": RELEASE_VERSION, + "Category": CATEGORY, + "SubCategory": 1, + }, + "HBPH - Phius Program Finder": { + "NickName": "Phius Programs", + "Message": RELEASE_VERSION, + "Category": CATEGORY, + "SubCategory": 1, + }, + "HBPH - Create Conversion Factor": { + "NickName": "Factor", "Message": RELEASE_VERSION, "Category": CATEGORY, "SubCategory": 1, @@ -364,8 +378,7 @@ class ComponentNameError(Exception): def __init__(self, _name, error): - self.message = 'Error: Cannot get Component Params for: "{}"'.format( - _name) + self.message = 'Error: Cannot get Component Params for: "{}"'.format(_name) print(error) super(ComponentNameError, self).__init__(self.message) @@ -408,4 +421,4 @@ def set_component_params(ghenv, dev=False): ghenv.Component.SubCategory = sub_cat_name ghenv.Component.IconDisplayMode = ghenv.Component.IconDisplayMode.application - return dev \ No newline at end of file + return dev diff --git a/honeybee_ph_rhino/gh_compo_io/__init__.py b/honeybee_ph_rhino/gh_compo_io/__init__.py index 91207f6..f6f2b3c 100644 --- a/honeybee_ph_rhino/gh_compo_io/__init__.py +++ b/honeybee_ph_rhino/gh_compo_io/__init__.py @@ -28,6 +28,8 @@ from honeybee_ph_rhino.gh_compo_io.mech_create_heating_sys import GHCompo_CreateHeatingSystem from honeybee_ph_rhino.gh_compo_io.mech_create_cooling_sys import GHCompo_CreateCoolingSystem from honeybee_ph_rhino.gh_compo_io.mech_add_mech_systems import GHCompo_AddMechSystems +from honeybee_ph_rhino.gh_compo_io.mech_create_exhaust_vent import GHCompo_CreateExhaustVent +from honeybee_ph_rhino.gh_compo_io.mech_add_exhaust_vent import GHCompo_AddExhaustVent # -- Program [Schedule / Load] from honeybee_ph_rhino.gh_compo_io.prog_add_elec_equip import GHCompo_AddElecEquip from honeybee_ph_rhino.gh_compo_io.prog_create_elec_equip import GHCompo_CreateElecEquip diff --git a/honeybee_ph_rhino/gh_compo_io/ghio_validators.py b/honeybee_ph_rhino/gh_compo_io/ghio_validators.py index a5c811f..96f5fb7 100644 --- a/honeybee_ph_rhino/gh_compo_io/ghio_validators.py +++ b/honeybee_ph_rhino/gh_compo_io/ghio_validators.py @@ -24,11 +24,11 @@ class MyClassWithValidation(object): try: # import the core honeybee dependencies from honeybee.typing import clean_ep_string, clean_and_id_ep_string except ImportError as e: - raise ImportError('\nFailed to import honeybee:\n\t{}'.format(e)) + raise ImportError("\nFailed to import honeybee:\n\t{}".format(e)) try: from ph_units import parser, converter except ImportError as e: - raise ImportError('\nFailed to import ph-units:\n\t{}'.format(e)) + raise ImportError("\nFailed to import ph-units:\n\t{}".format(e)) class Validated(object): @@ -49,7 +49,7 @@ def __init__(self, storage_name, default=None, **kwargs): setattr(self, _k, _v) def __set__(self, instance, value): - """Set the value on the instance. Runs a .validate() method on self to + """Set the value on the instance. Runs a .validate() method on self to allow for subclasses to implement custom input validation. Arguments: @@ -60,7 +60,7 @@ def __set__(self, instance, value): value = self.validate( name=self.storage_name, new_value=value, - old_value=instance.__dict__.get(self.storage_name, None) + old_value=instance.__dict__.get(self.storage_name, None), ) instance.__dict__[self.storage_name] = value @@ -73,21 +73,24 @@ def __get__(self, instance, owner): """ return instance.__dict__[self.storage_name] - def validate(self, name, new_value, old_value): + def validate(self, name, new_value, old_value): """Return validated input value, or raise an error. Arguments: --------- * name: The Class-Attribute's name. Mostly used for Error messages. * new_value: The new value to validate. - * old_value: The original value of the descriptor, sometimes useful + * old_value: The original value of the descriptor, sometimes useful for returning if 'new_value' is None, or similar. """ raise NotImplementedError( - "Error: Must implement the .validated() method on subclass of Validated.") + "Error: Must implement the .validated() method on subclass of Validated." + ) def __str__(self): - return "Validated[{}](storage_name={})".format(self.__class__.__name__, self.storage_name) + return "Validated[{}](storage_name={})".format( + self.__class__.__name__, self.storage_name + ) class NotNone(Validated): @@ -138,13 +141,15 @@ def validate(self, name, new_value, old_value): try: new_value = int(new_value) except: - raise ValueError("Error: input {} of type: {} is not allowed." - "Supply positive integer only.".format( - new_value, type(new_value))) + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply positive integer only.".format(new_value, type(new_value)) + ) if new_value < 1: raise ValueError( - "Error: input for '{}' cannot be zero or negative.".format(name)) + "Error: input for '{}' cannot be zero or negative.".format(name) + ) return new_value @@ -163,15 +168,17 @@ def validate(self, name, new_value, old_value): try: new_value = float(new_value) except: - raise ValueError("Error: input {} of type: {} is not allowed." - "Supply float only.".format( - new_value, type(new_value))) + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply float only.".format(new_value, type(new_value)) + ) return new_value class FloatNonZero(Validated): """A Floating Point value which is not allowed to be zero.""" + tolerance = 0.0001 def validate(self, name, new_value, old_value): @@ -184,13 +191,13 @@ def validate(self, name, new_value, old_value): try: new_value = float(new_value) except: - raise ValueError("Error: input {} of type: {} is not allowed." - "Supply float values only.".format( - new_value, type(new_value))) + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply float values only.".format(new_value, type(new_value)) + ) if new_value - 0.0 < self.tolerance: - raise ValueError( - "Error: input for '{}' cannot be zero.".format(name)) + raise ValueError("Error: input for '{}' cannot be zero.".format(name)) return new_value @@ -208,13 +215,13 @@ def validate(self, name, new_value, old_value): try: new_value = float(new_value) except: - raise ValueError("Error: input {} of type: {} is not allowed." - "Supply float values only.".format( - new_value, type(new_value))) + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply float values only.".format(new_value, type(new_value)) + ) if new_value < 0: - raise ValueError( - "Error: input for '{}' cannot be negative.".format(name)) + raise ValueError("Error: input for '{}' cannot be negative.".format(name)) return new_value @@ -233,9 +240,10 @@ def validate(self, name, new_value, old_value): try: new_value = float(new_value) except: - raise ValueError("Error: input {} of type: {} is not allowed." - "Supply float values only.".format( - new_value, type(new_value))) + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply float values only.".format(new_value, type(new_value)) + ) # -- If the value is passed in as 0<->100, try and divide it if 1.0 < new_value < 100.0: @@ -243,13 +251,15 @@ def validate(self, name, new_value, old_value): if not 0.0 <= new_value <= 1.0: raise ValueError( - "Error: input for '{}' must be between 0.0 and 1.0".format(name)) + "Error: input for '{}' must be between 0.0 and 1.0".format(name) + ) return new_value class FloatMax24(Validated): """A Floating Point value which is less than 24.0""" + tolerance = -0.0001 def validate(self, name, new_value, old_value): @@ -263,13 +273,15 @@ def validate(self, name, new_value, old_value): try: new_value = float(new_value) except: - raise ValueError("Error: input {} of type: {} is not allowed." - "Supply float values only.".format( - new_value, type(new_value))) + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply float values only.".format(new_value, type(new_value)) + ) if 24.0 - new_value < self.tolerance: raise ValueError( - "Error: input for '{}' cannot be greater than 24.".format(name)) + "Error: input for '{}' cannot be greater than 24.".format(name) + ) return new_value @@ -294,14 +306,15 @@ def validate(self, name, new_value, old_value): try: input_value = float(input_value) except: - raise ValueError("Error: input {} of type: {} is not allowed." - "Supply float only.".format( - new_value, type(new_value))) + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply float only.".format(new_value, type(new_value)) + ) # -- Convert to Meters result = converter.convert(input_value, input_units or "M", "M") - print('Converting: {} -> {:.4f} M'.format(new_value, result)) + print("Converting: {} -> {:.4f} M".format(new_value, result)) return result @@ -323,14 +336,15 @@ def validate(self, name, new_value, old_value): try: input_value = float(input_value) except: - raise ValueError("Error: input {} of type: {} is not allowed." - "Supply float only.".format( - new_value, type(new_value))) + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply float only.".format(new_value, type(new_value)) + ) # -- Convert units result = converter.convert(input_value, input_units or "W/MK", "W/MK") - print('Converting: {} -> {:.4f} W/MK'.format(new_value, result)) + print("Converting: {} -> {:.4f} W/MK".format(new_value, result)) return result @@ -352,19 +366,19 @@ def validate(self, name, new_value, old_value): try: input_value = float(input_value) except: - raise ValueError("Error: input {} of type: {} is not allowed." - "Supply float only.".format( - new_value, type(new_value))) + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply float only.".format(new_value, type(new_value)) + ) # -- Convert units result = converter.convert(input_value, input_units or "W/M2K", "W/M2K") - print('Converting: {} -> {:.4f} W/M2K'.format(new_value, result)) + print("Converting: {} -> {:.4f} W/M2K".format(new_value, result)) # -- Make sure its positive if result and result < 0.0: - raise ValueError( - "Error: input for '{}' cannot be negative.".format(name)) + raise ValueError("Error: input for '{}' cannot be negative.".format(name)) return result @@ -386,19 +400,19 @@ def validate(self, name, new_value, old_value): try: input_value = float(input_value) except: - raise ValueError("Error: input {} of type: {} is not allowed." - "Supply float only.".format( - new_value, type(new_value))) + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply float only.".format(new_value, type(new_value)) + ) # -- Convert to Meters result = converter.convert(input_value, input_units or "W/K", "W/K") - print('Converting: {} -> {:.4f} W/K'.format(new_value, result)) + print("Converting: {} -> {:.4f} W/K".format(new_value, result)) # -- Make sure its positive if result and result < 0.0: - raise ValueError( - "Error: input for '{}' cannot be negative.".format(name)) + raise ValueError("Error: input for '{}' cannot be negative.".format(name)) return result @@ -420,14 +434,15 @@ def validate(self, name, new_value, old_value): try: input_value = float(input_value) except: - raise ValueError("Error: input {} of type: {} is not allowed." - "Supply float only.".format( - new_value, type(new_value))) + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply float only.".format(new_value, type(new_value)) + ) # -- Convert to Meters result = converter.convert(input_value, input_units or "DELTA-C", "DELTA-C") - print('Converting: {} -> {:.4f} Delta-C'.format(new_value, result)) + print("Converting: {} -> {:.4f} Delta-C".format(new_value, result)) return result @@ -449,14 +464,15 @@ def validate(self, name, new_value, old_value): try: input_value = float(input_value) except: - raise ValueError("Error: input {} of type: {} is not allowed." - "Supply float only.".format( - new_value, type(new_value))) + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply float only.".format(new_value, type(new_value)) + ) # -- Convert to Meters result = converter.convert(input_value, input_units or "C", "C") - print('Converting: {} -> {:.4f} C'.format(new_value, result)) + print("Converting: {} -> {:.4f} C".format(new_value, result)) return result @@ -478,14 +494,15 @@ def validate(self, name, new_value, old_value): try: input_value = float(input_value) except: - raise ValueError("Error: input {} of type: {} is not allowed." - "Supply float only.".format( - new_value, type(new_value))) + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply float only.".format(new_value, type(new_value)) + ) # -- Convert to Meters result = converter.convert(input_value, input_units or "M/S", "M/S") - print('Converting: {} -> {:.4f} meter/second'.format(new_value, result)) + print("Converting: {} -> {:.4f} meter/second".format(new_value, result)) return result @@ -507,23 +524,23 @@ def validate(self, name, new_value, old_value): try: input_value = float(input_value) except: - raise ValueError("Error: input {} of type: {} is not allowed." - "Supply float only.".format( - new_value, type(new_value))) + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply float only.".format(new_value, type(new_value)) + ) # -- Convert units result = converter.convert(input_value, input_units or "WH/M3", "WH/M3") - print('Converting: {} -> {:.4f} WH/M3'.format(new_value, result)) + print("Converting: {} -> {:.4f} WH/M3".format(new_value, result)) # -- Make sure its positive if result and result < 0.0: - raise ValueError( - "Error: input for '{}' cannot be negative.".format(name)) + raise ValueError("Error: input for '{}' cannot be negative.".format(name)) return result - + class UnitKWH_M2(Validated): """A kWH/M2 Energy Demand of any positive value.""" @@ -541,22 +558,22 @@ def validate(self, name, new_value, old_value): try: input_value = float(input_value) except: - raise ValueError("Error: input {} of type: {} is not allowed." - "Supply float only.".format( - new_value, type(new_value))) + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply float only.".format(new_value, type(new_value)) + ) # -- Convert units result = converter.convert(input_value, input_units or "KWH/M2", "KWH/M2") - print('Converting: {} -> {:.4f} KWH/M2'.format(new_value, result)) + print("Converting: {} -> {:.4f} KWH/M2".format(new_value, result)) # -- Make sure its positive if result and result < 0.0: - raise ValueError( - "Error: input for '{}' cannot be negative.".format(name)) + raise ValueError("Error: input for '{}' cannot be negative.".format(name)) return result - + class UnitW_M2(Validated): """A W/M2 Load of any positive value.""" @@ -575,22 +592,23 @@ def validate(self, name, new_value, old_value): try: input_value = float(input_value) except: - raise ValueError("Error: input {} of type: {} is not allowed." - "Supply float only.".format( - new_value, type(new_value))) + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply float only.".format(new_value, type(new_value)) + ) # -- Convert units result = converter.convert(input_value, input_units or "W/M2", "W/M2") - print('Converting: {} -> {:.4f} W/M2'.format(new_value, result)) + print("Converting: {} -> {:.4f} W/M2".format(new_value, result)) # -- Make sure its positive if result and result < 0.0: - raise ValueError( - "Error: input for '{}' cannot be negative.".format(name)) + raise ValueError("Error: input for '{}' cannot be negative.".format(name)) return result + class UnitM3_S(Validated): """A M3/Second Airflow of any positive value.""" @@ -608,19 +626,52 @@ def validate(self, name, new_value, old_value): try: input_value = float(input_value) except: - raise ValueError("Error: input {} of type: {} is not allowed." - "Supply float only.".format( - new_value, type(new_value))) + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply float only.".format(new_value, type(new_value)) + ) # -- Convert units result = converter.convert(input_value, input_units or "M3/S", "M3/S") - print('Converting: {} -> {:.4f} M3/S'.format(new_value, result)) + print("Converting: {} -> {:.4f} M3/S".format(new_value, result)) # -- Make sure its positive if result and result < 0.0: - raise ValueError( - "Error: input for '{}' cannot be negative.".format(name)) + raise ValueError("Error: input for '{}' cannot be negative.".format(name)) return result + +class UnitKW(Validated): + """A KW power of any positive value.""" + + def validate(self, name, new_value, old_value): + if new_value is None: + # If the user passed a 'default' attribute, try and use that + try: + return float(self.default) + except: + return old_value + + input_value, input_units = parser.parse_input(str(new_value)) + + # -- Make sure the value is a float + try: + input_value = float(input_value) + except: + raise ValueError( + "Error: input {} of type: {} is not allowed." + "Supply float only.".format(new_value, type(new_value)) + ) + + # -- Convert units + result = converter.convert(input_value, input_units or "KW", "KW") + + print("Converting: {} -> {:.4f} KW".format(new_value, result)) + + # -- Make sure its positive + if result and result < 0.0: + raise ValueError("Error: input for '{}' cannot be negative.".format(name)) + + return result diff --git a/honeybee_ph_rhino/gh_compo_io/mech_add_exhaust_vent.py b/honeybee_ph_rhino/gh_compo_io/mech_add_exhaust_vent.py new file mode 100644 index 0000000..c1eb191 --- /dev/null +++ b/honeybee_ph_rhino/gh_compo_io/mech_add_exhaust_vent.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# -*- Python Version: 2.7 -*- + +"""GHCompo Interface: HBPH - Add Exhaust Ventilators.""" + +from copy import copy + +try: + from typing import List +except ImportError: + pass # IronPython 2.7 + +try: + from honeybee import room +except ImportError as e: + raise ImportError("\nFailed to import honeybee:\n\t{}".format(e)) + +try: + from honeybee_ph_rhino import gh_io +except ImportError as e: + raise ImportError("\nFailed to import honeybee_ph_rhino:\n\t{}".format(e)) + +try: + from honeybee_energy_ph.hvac import ventilation +except ImportError as e: + raise ImportError("\nFailed to import honeybee_energy_ph:\n\t{}".format(e)) + + +class GHCompo_AddExhaustVent(object): + def __init__(self, IGH, _exhaust_vent_devices, _hb_rooms): + # type: (gh_io.IGH, List[ventilation._ExhaustVentilatorBase], List[room.Room]) -> None + self.IGH = IGH + self.exhaust_vent_devices = _exhaust_vent_devices + self.hb_rooms = _hb_rooms + + def run(self): + # type: () -> List[room.Room] + hb_rooms_ = [] + for hb_room in self.hb_rooms: + # -- Build up the new HB-HVAC + new_hvac = copy(hb_room.properties.energy.hvac.duplicate()) # type: ignore + + # -- Add the new Exhaust Devices to the room's vent system + for exhaust_device in self.exhaust_vent_devices: + new_hvac.properties.ph.exhaust_vent_devices.add(exhaust_device) + + # -- Output + new_room = hb_room.duplicate() + new_room.properties.energy.hvac = new_hvac # type: ignore + hb_rooms_.append(new_room) + + return hb_rooms_ diff --git a/honeybee_ph_rhino/gh_compo_io/mech_create_cooling_sys.py b/honeybee_ph_rhino/gh_compo_io/mech_create_cooling_sys.py index d31ca3b..5bb6b35 100644 --- a/honeybee_ph_rhino/gh_compo_io/mech_create_cooling_sys.py +++ b/honeybee_ph_rhino/gh_compo_io/mech_create_cooling_sys.py @@ -4,31 +4,33 @@ """GHCompo Interface: HBPH - Create Cooling System.""" from copy import copy + # Note: Use copy so that specific equipments can overwrite base with their own hints -from GhPython import Component # type: ignore -from Grasshopper.Kernel.Parameters import Hints # type: ignore +from GhPython import Component # type: ignore +from Grasshopper.Kernel.Parameters import Hints # type: ignore try: from typing import Optional, Dict, Any except ImportError: - pass # IronPython 2.7 + pass # IronPython 2.7 try: from honeybee_ph_rhino import gh_io + from honeybee_ph_rhino.gh_compo_io import ghio_validators from honeybee_ph_rhino.gh_io import input_to_int, ComponentInput except ImportError as e: - raise ImportError('\nFailed to import honeybee_ph_rhino:\n\t{}'.format(e)) + raise ImportError("\nFailed to import honeybee_ph_rhino:\n\t{}".format(e)) try: from honeybee_energy_ph.hvac import cooling except ImportError as e: - raise ImportError('\nFailed to import honeybee_energy_ph:\n\t{}'.format(e)) + raise ImportError("\nFailed to import honeybee_energy_ph:\n\t{}".format(e)) try: from honeybee_ph_utils import input_tools except ImportError as e: - raise ImportError('\nFailed to import honeybee_ph_utils:\n\t{}'.format(e)) + raise ImportError("\nFailed to import honeybee_ph_utils:\n\t{}".format(e)) class InputTypeNotFoundError(Exception): @@ -38,74 +40,114 @@ def __init__(self, _in): # ----------------------------------------------------------------------------- -# Setup the component input node groups +# Setup all the component input node groups + inputs_base = { - 1: ComponentInput(_name='display_name', - _description='(str) Optional display name for the cooling system.', - _type_hint=Component.NewStrHint()), - 2: ComponentInput(_name='percent_coverage', - _description='(float) default=1.0 The fraction of total cooling supplied by this system (0-1)', - _type_hint=Component.NewFloatHint()), + 1: ComponentInput( + _name="display_name", + _description="(str) Optional display name for the cooling system.", + _type_hint=Component.NewStrHint(), + ), + 2: ComponentInput( + _name="percent_coverage", + _description="(float) default=1.0 The fraction of total cooling supplied by this system (0-1)", + _type_hint=Component.NewFloatHint(), + ), } inputs_ventilation = copy(inputs_base) -inputs_ventilation.update({ - 3: ComponentInput(_name='annual_COP', - _description='(float) The Annual COP (W/W) of the equipment.', - _type_hint=Component.NewFloatHint()), - 4: ComponentInput(_name='single_speed', - _description='(bool) Cyclical operation works through an on/off regulation of the compressor. If this is set to False, then the assumption is that the unit has a VRF (variant refrigerant flow), which works by modulating the efficiency of the compressor.', - _type_hint=Hints.GH_BooleanHint_CS()), - 5: ComponentInput(_name='min_coil_temp', - _description='(float) Deg. C', - _type_hint=Component.NewFloatHint()), - 6: ComponentInput(_name='capacity', - _description='(float) Maximum kW output.', - _type_hint=Component.NewFloatHint()), -}) +inputs_ventilation.update( + { + 3: ComponentInput( + _name="annual_COP", + _description="(float) The Annual COP (W/W) of the equipment.", + _type_hint=Component.NewFloatHint(), + ), + 4: ComponentInput( + _name="single_speed", + _description="(bool) Cyclical operation works through an on/off regulation of the compressor. If this is set to False, then the assumption is that the unit has a VRF (variant refrigerant flow), which works by modulating the efficiency of the compressor.", + _type_hint=Hints.GH_BooleanHint_CS(), + ), + 5: ComponentInput( + _name="min_coil_temp", + _description="(float) Deg. C", + _type_hint=Component.NewStrHint(), + ), + 6: ComponentInput( + _name="capacity", + _description="(float) Maximum kW output.", + _type_hint=Component.NewStrHint(), + ), + } +) inputs_recirculation = copy(inputs_base) -inputs_recirculation.update({ - 3: ComponentInput(_name='annual_COP', - _description='(float) The Annual COP (W/W) of the equipment.', - _type_hint=Component.NewFloatHint()), - 4: ComponentInput(_name='single_speed', - _description='(bool) Cyclical operation works through an on/off regulation of the compressor. If this is set to False, then the assumption is that the unit has a VRF (variant refrigerant flow), which works by modulating the efficiency of the compressor.', - _type_hint=Hints.GH_BooleanHint_CS()), - 5: ComponentInput(_name='min_coil_temp', - _description='(float) Deg. C', - _type_hint=Component.NewFloatHint()), - 6: ComponentInput(_name='capacity', - _description='(float) Maximum kW output.', - _type_hint=Component.NewFloatHint()), - 7: ComponentInput(_name='flow_rate_m3_hr', - _description='(float) The maximum airflow rate in m3/hr', - _type_hint=Component.NewFloatHint()), - 8: ComponentInput(_name='flow_rate_variable', - _description='(bool) VAV system: The volume flow changes proportionally to the cooling capacity, thereby reducing the temperature remains constant (usually better dehumidification)', - _type_hint=Hints.GH_BooleanHint_CS()), -}) +inputs_recirculation.update( + { + 3: ComponentInput( + _name="annual_COP", + _description="(float) The Annual COP (W/W) of the equipment.", + _type_hint=Component.NewFloatHint(), + ), + 4: ComponentInput( + _name="single_speed", + _description="(bool) Cyclical operation works through an on/off regulation of the compressor. If this is set to False, then the assumption is that the unit has a VRF (variant refrigerant flow), which works by modulating the efficiency of the compressor.", + _type_hint=Hints.GH_BooleanHint_CS(), + ), + 5: ComponentInput( + _name="min_coil_temp", + _description="(float) Deg. C", + _type_hint=Component.NewStrHint(), + ), + 6: ComponentInput( + _name="capacity", + _description="(float) Maximum kW output.", + _type_hint=Component.NewStrHint(), + ), + 7: ComponentInput( + _name="flow_rate_m3_s", + _description="(float) The maximum airflow rate in m3/s", + _type_hint=Component.NewStrHint(), + ), + 8: ComponentInput( + _name="flow_rate_variable", + _description="(bool) VAV system: The volume flow changes proportionally to the cooling capacity, thereby reducing the temperature remains constant (usually better dehumidification)", + _type_hint=Hints.GH_BooleanHint_CS(), + ), + } +) inputs_dehumidification = copy(inputs_base) -inputs_dehumidification.update({ - 3: ComponentInput(_name='annual_COP', - _description='(float) The Annual COP (W/W) of the equipment.', - _type_hint=Component.NewFloatHint()), - 4: ComponentInput(_name='useful_heat_loss', - _description='If this is set to True, then the waste heat from the dehumidification unit will be considered as an internal heat gain. On the contrary, dehumidification has no influence on the thermal balance.', - _type_hint=Component.NewFloatHint()), -}) +inputs_dehumidification.update( + { + 3: ComponentInput( + _name="annual_COP", + _description="(float) The Annual COP (W/W) of the equipment.", + _type_hint=Component.NewFloatHint(), + ), + 4: ComponentInput( + _name="useful_heat_loss", + _description="If this is set to True, then the waste heat from the dehumidification unit will be considered as an internal heat gain. On the contrary, dehumidification has no influence on the thermal balance.", + _type_hint=Hints.GH_BooleanHint_CS(), + ), + } +) inputs_panel = copy(inputs_base) -inputs_panel.update({ - 3: ComponentInput(_name='annual_COP', - _description='(float) The Annual COP (W/W) of the equipment.', - _type_hint=Component.NewFloatHint()), -}) +inputs_panel.update( + { + 3: ComponentInput( + _name="annual_COP", + _description="(float) The Annual COP (W/W) of the equipment.", + _type_hint=Component.NewFloatHint(), + ), + } +) # ----------------------------------------------------------------------------- +# -- Configure the Grasshopper Component with the right inputs nodes for the type input_groups = { 1: inputs_ventilation, @@ -114,8 +156,6 @@ def __init__(self, _in): 4: inputs_panel, } -# ----------------------------------------------------------------------------- - def get_component_inputs(_cooling_type): # type: (str) -> dict @@ -133,16 +173,130 @@ def get_component_inputs(_cooling_type): except KeyError: raise InputTypeNotFoundError(input_type_id) + +# ----------------------------------------------------------------------------- +# -- Facades for System Constructors (So they can do input validation and unit conversion) + + +class FacadePhCoolingVentilation(object): + display_name = ghio_validators.HBName( + "display_name", default="_unnamed_ventilation_cooling_" + ) + percent_coverage = ghio_validators.FloatPercentage("percent_coverage", default=1.0) + min_coil_temp = ghio_validators.UnitDegreeC("min_coil_temp", default=12) + capacity = ghio_validators.UnitKW("capacity", default=10) + + def __init__(self, _input_dict): + # type: (Dict) -> None + self.display_name = _input_dict["display_name"] + self.percent_coverage = _input_dict["percent_coverage"] + self.single_speed = _input_dict["single_speed"] or False + self.min_coil_temp = _input_dict["min_coil_temp"] + self.capacity = _input_dict["capacity"] + self.annual_COP = _input_dict["annual_COP"] or 2.0 + + def build(self): + # type: () -> cooling.PhCoolingVentilation + obj = cooling.PhCoolingVentilation() + obj.display_name = self.display_name + obj.percent_coverage = self.percent_coverage + obj.single_speed = self.single_speed + obj.min_coil_temp = self.min_coil_temp + obj.capacity = self.capacity + obj.annual_COP = self.annual_COP + return obj + + +class FacadePhCoolingRecirculation(object): + display_name = ghio_validators.HBName( + "display_name", default="_unnamed_recirculation_cooling_" + ) + percent_coverage = ghio_validators.FloatPercentage("percent_coverage", default=1.0) + min_coil_temp = ghio_validators.UnitDegreeC("min_coil_temp", default=12) + flow_rate_m3_s = ghio_validators.UnitM3_S("flow_rate_m3_s", default=0.0278) + capacity = ghio_validators.UnitKW("capacity", default=10) + + def __init__(self, _input_dict): + # type: (Dict) -> None + self.display_name = _input_dict["display_name"] + self.percent_coverage = _input_dict["percent_coverage"] + self.single_speed = _input_dict["single_speed"] or False + self.min_coil_temp = _input_dict["min_coil_temp"] + self.flow_rate_m3_s = _input_dict["flow_rate_m3_s"] + self.flow_rate_variable = _input_dict["flow_rate_variable"] or True + self.capacity = _input_dict["capacity"] + self.annual_COP = _input_dict["annual_COP"] or 2.0 + + def build(self): + # type: () -> cooling.PhCoolingRecirculation + obj = cooling.PhCoolingRecirculation() + obj.display_name = self.display_name + obj.percent_coverage = self.percent_coverage + obj.single_speed = self.single_speed + obj.min_coil_temp = self.min_coil_temp + obj.flow_rate_m3_hr = self.flow_rate_m3_s * 60 * 60 + obj.flow_rate_variable = self.flow_rate_variable + obj.capacity = self.capacity + obj.annual_COP = self.annual_COP + return obj + + +class FacadePhCoolingDehumidification(object): + display_name = ghio_validators.HBName( + "display_name", default="_unnamed_dehumidification_cooling_" + ) + percent_coverage = ghio_validators.FloatPercentage("percent_coverage", default=1.0) + + def __init__(self, _input_dict): + # type: (Dict) -> None + self.display_name = _input_dict["display_name"] + self.percent_coverage = _input_dict["percent_coverage"] + self.useful_heat_loss = _input_dict["useful_heat_loss"] or False + self.annual_COP = _input_dict["annual_COP"] or 2.0 + + def build(self): + # type: () -> cooling.PhCoolingDehumidification + obj = cooling.PhCoolingDehumidification() + obj.display_name = self.display_name + obj.percent_coverage = self.percent_coverage + obj.useful_heat_loss = self.useful_heat_loss + obj.annual_COP = self.annual_COP + return obj + + +class FacadePhCoolingPanel(object): + display_name = ghio_validators.HBName( + "display_name", default="_unnamed_panel_cooling_" + ) + percent_coverage = ghio_validators.FloatPercentage("percent_coverage", default=1.0) + + def __init__(self, _input_dict): + # type: (Dict) -> None + self.display_name = _input_dict["display_name"] + self.percent_coverage = _input_dict["percent_coverage"] + self.annual_COP = _input_dict["annual_COP"] or 2.0 + + def build(self): + # type: () -> cooling.PhCoolingPanel + obj = cooling.PhCoolingPanel() + obj.display_name = self.display_name + obj.percent_coverage = self.percent_coverage + obj.annual_COP = self.annual_COP + return obj + + # ----------------------------------------------------------------------------- +# -- GH Component Interface + class GHCompo_CreateCoolingSystem(object): cooling_classes = { - 1: cooling.PhCoolingVentilation, - 2: cooling.PhCoolingRecirculation, - 3: cooling.PhCoolingDehumidification, - 4: cooling.PhCoolingPanel, - } # type: (Dict[int, type[cooling.PhCoolingSystem]]) - + 1: FacadePhCoolingVentilation, + 2: FacadePhCoolingRecirculation, + 3: FacadePhCoolingDehumidification, + 4: FacadePhCoolingPanel, + } + valid_cooling_types = [ "1-ventilation", "2-recirculation", @@ -167,6 +321,7 @@ def system_type(self, _in): def run(self): # type: () -> Optional[cooling.PhCoolingSystem] + """Find the right System Builder and create the new System from the user inputs.""" if not self.system_type: msg = "Set the '_system_type' to configure the user-inputs." @@ -178,19 +333,10 @@ def run(self): cooling_class = self.cooling_classes[self.system_type] except KeyError as e: raise Exception( - "Error: Input Cooling type: '{}' not supported by this GH-Component. Please only input: "\ - "{}".format(self.system_type, self.valid_cooling_types) + "Error: Input Cooling type: '{}' not supported by this GH-Component. " + "Please only input: {}".format(self.system_type, self.valid_cooling_types) ) - # --- Build the Cooling system - cooling_system_ = cooling_class() - for attr_name in dir(cooling_system_): - if attr_name.startswith('_'): - continue - - input_val = self.input_dict.get(attr_name) - if input_val: - setattr(cooling_system_, attr_name, input_val) - - return cooling_system_ - + # --- Build the Cooling system from the user_inputs + cooling_builder = cooling_class(self.input_dict) + return cooling_builder.build() diff --git a/honeybee_ph_rhino/gh_compo_io/mech_create_exhaust_vent.py b/honeybee_ph_rhino/gh_compo_io/mech_create_exhaust_vent.py new file mode 100644 index 0000000..15eb1b2 --- /dev/null +++ b/honeybee_ph_rhino/gh_compo_io/mech_create_exhaust_vent.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +# -*- Python Version: 2.7 -*- + +"""GHCompo Interface: HBPH - Create Exhaust Ventilator.""" + +try: + from typing import Dict, Optional +except ImportError: + pass # IronPython 2.7 + +try: + from honeybee_ph_rhino import gh_io + from honeybee_ph_rhino.gh_compo_io import ghio_validators +except ImportError as e: + raise ImportError('\nFailed to import honeybee_ph_rhino:\n\t{}'.format(e)) + +try: + from honeybee_ph_utils import input_tools +except ImportError as e: + raise ImportError('\nFailed to import honeybee_ph_utils:\n\t{}'.format(e)) + +try: + from honeybee_energy_ph.hvac import ventilation +except ImportError as e: + raise ImportError('\nFailed to import honeybee_energy_ph:\n\t{}'.format(e)) + + +class GHCompo_CreateExhaustVent(object): + device_classes = { + 1: ventilation.ExhaustVentDryer, + 2: ventilation.ExhaustVentKitchenHood, + 3: ventilation.ExhaustVentUserDefined, + } # type: (Dict[int, type[ventilation._ExhaustVentilatorBase]]) + + valid_device_types = [ + "1-Dryer", + "2-Kitchen Hood", + "3-User Defined", + ] + + flow_rate_m3s = ghio_validators.UnitM3_S("flow_rate_m3s", default=0.0) + + def __init__(self, _IGH, _name, _type, _flow_rate_m3s, _annual_runtime_minutes): + # type: (gh_io.IGH, str, str, str, float) -> None + self.IGH = _IGH + self.name = _name + self.system_type = _type + self.flow_rate_m3s = _flow_rate_m3s + self.annual_runtime_minutes = _annual_runtime_minutes or 0.0 + + @property + def system_type(self): + # type: () -> Optional[int] + return self._system_type + + @system_type.setter + def system_type(self, _in): + self._system_type = input_tools.input_to_int(_in) + + def run(self): + # type: () -> Optional[ventilation._ExhaustVentilatorBase] + + if not self.system_type: + msg = "Set the '_system_type' to continue" + self.IGH.warning(msg) + return None + + # -- Figure out which Vent Device type is being built + try: + device_class = self.device_classes[self.system_type] + except KeyError as e: + raise Exception( + "Error: Input Device type: '{}' not supported by this GH-Component. "\ + "Please only input:{}".format(self.system_type, self.valid_device_types) + ) + + # --- Build the Vent. Device + vent_device_ = device_class() + vent_device_.display_name = self.name + vent_device_.annual_runtime_minutes = self.annual_runtime_minutes + try: + vent_device_.exhaust_flow_rate_m3s = float(self.flow_rate_m3s) + except Exception as e: + raise e + + return vent_device_ \ No newline at end of file diff --git a/honeybee_ph_rhino/gh_compo_io/prog_add_elec_equip.py b/honeybee_ph_rhino/gh_compo_io/prog_add_elec_equip.py index a7e3b7c..7deeee3 100644 --- a/honeybee_ph_rhino/gh_compo_io/prog_add_elec_equip.py +++ b/honeybee_ph_rhino/gh_compo_io/prog_add_elec_equip.py @@ -6,38 +6,38 @@ try: from typing import Tuple, List except ImportError: - pass #IronPython 2.7 + pass # IronPython 2.7 try: from honeybee import room from honeybee.typing import clean_and_id_ep_string, clean_ep_string except ImportError as e: - raise ImportError('\nFailed to import honeybee:\n\t{}'.format(e)) + raise ImportError("\nFailed to import honeybee:\n\t{}".format(e)) try: from honeybee_energy.schedule.ruleset import ScheduleRuleset from honeybee_energy.load import equipment from honeybee_energy.lib.scheduletypelimits import schedule_type_limit_by_identifier except ImportError as e: - raise ImportError('\nFailed to import honeybee_energy:\n\t{}'.format(e)) + raise ImportError("\nFailed to import honeybee_energy:\n\t{}".format(e)) try: from honeybee_ph_standards.programtypes.default_elec_equip import ph_default_equip except ImportError as e: - raise ImportError('\nFailed to import honeybee_ph_standards:\n\t{}'.format(e)) + raise ImportError("\nFailed to import honeybee_ph_standards:\n\t{}".format(e)) try: from honeybee_energy_ph.load import ph_equipment except ImportError as e: - raise ImportError('\nFailed to import honeybee_energy_ph:\n\t{}'.format(e)) + raise ImportError("\nFailed to import honeybee_energy_ph:\n\t{}".format(e)) try: from honeybee_ph_rhino import gh_io except ImportError as e: - raise ImportError('\nFailed to import honeybee_ph_rhino:\n\t{}'.format(e)) + raise ImportError("\nFailed to import honeybee_ph_rhino:\n\t{}".format(e)) -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ # -- Equipment Builder Classes and Functions @@ -60,32 +60,34 @@ def set_hb_room_ee(self): new_rooms = [] for hb_room in self.hb_rooms: dup_room = hb_room.duplicate() - dup_room.properties.energy.electric_equipment = dup_hb_ee # type: ignore + dup_room.properties.energy.electric_equipment = dup_hb_ee # type: ignore new_rooms.append(dup_room) self.hb_rooms = new_rooms def add_hbph_equipment(self, ph_equip_item): # type: (ph_equipment.PhEquipment) -> None - self.hb_electric_equipment.properties.ph.equipment_collection.add_equipment(ph_equip_item) # type: ignore - + self.hb_electric_equipment.properties.ph.equipment_collection.add_equipment(ph_equip_item) # type: ignore + def set_hb_ee_wattage(self): # type: () -> None """Set the HBE-ElecEquip watts-per-area from the PH-Equip.""" dup_ee = self.hb_electric_equipment.duplicate() - ph_equip = dup_ee.properties.ph.equipment_collection # type: ignore - total_wattage = sum(ph_equip.total_collection_wattage(hb_room) - for hb_room in self.hb_rooms) + ph_equip = dup_ee.properties.ph.equipment_collection # type: ignore + total_wattage = sum( + ph_equip.total_collection_wattage(hb_room) for hb_room in self.hb_rooms + ) total_m2 = sum(rm.floor_area for rm in self.hb_rooms) avg_wattage_per_m2 = total_wattage / total_m2 - dup_ee.watts_per_area = avg_wattage_per_m2 # type: ignore + dup_ee.watts_per_area = avg_wattage_per_m2 # type: ignore self.hb_electric_equipment = dup_ee def __str__(self): - return '{}(hb_electric_equipment={}, hb_rooms={})'.format( - self.__class__.__name__, self.hb_electric_equipment, self.hb_rooms) + return "{}(hb_electric_equipment={}, hb_rooms={})".format( + self.__class__.__name__, self.hb_electric_equipment, self.hb_rooms + ) def __repr__(self): return str(self) @@ -103,12 +105,12 @@ def __init__(self): def add_to_collection(self, _hb_room): # type: (room.Room) -> None """Use the HB-Room's ElectricEquipment.identifier as the key to log the HBElecEquipWithRooms""" - ee_id = _hb_room.properties.energy.electric_equipment.identifier # type: ignore + ee_id = _hb_room.properties.energy.electric_equipment.identifier # type: ignore try: self._collection[ee_id].hb_rooms.append(_hb_room.duplicate()) except KeyError: new_ee_with_rooms = HBElecEquipWithRooms( - _hb_electric_equipment=_hb_room.properties.energy.electric_equipment # type: ignore + _hb_electric_equipment=_hb_room.properties.energy.electric_equipment # type: ignore ) new_ee_with_rooms.hb_rooms.append(_hb_room) self._collection[ee_id] = new_ee_with_rooms @@ -123,7 +125,9 @@ def items(self): return self._collection.items() def __str__(self): - return '{}({} items in the collection)'.format(self.__class__.__name__, len(self.keys())) + return "{}({} items in the collection)".format( + self.__class__.__name__, len(self.keys()) + ) def __repr__(self): return str(self) @@ -139,13 +143,13 @@ def _create_default_hb_elec_equip(_identifier): identifier=_identifier, watts_per_area=0.0, schedule=ScheduleRuleset.from_constant_value( - clean_ep_string(clean_and_id_ep_string('EEConstantSchedule')), + clean_ep_string(clean_and_id_ep_string("EEConstantSchedule")), 1.0, - schedule_type_limit_by_identifier('Fractional') + schedule_type_limit_by_identifier("Fractional"), ), radiant_fraction=0, latent_fraction=0, - lost_fraction=0 + lost_fraction=0, ) @@ -155,9 +159,10 @@ def _clean_rooms_elec_equip(_hb_rooms): hb_rooms_ = [] for hb_room in _hb_rooms: - if hb_room.properties.energy.electric_equipment is None: # type: ignore - hb_room.properties.energy.electric_equipment = _create_default_hb_elec_equip( # type: ignore - hb_room.display_name) + if hb_room.properties.energy.electric_equipment is None: # type: ignore + hb_room.properties.energy.electric_equipment = _create_default_hb_elec_equip( # type: ignore + hb_room.display_name + ) hb_rooms_.append(hb_room) return hb_rooms_ @@ -185,34 +190,63 @@ def _add_Phius_default_equipment_to_list(_equipment, _defaults=1): equipment_list_ = _equipment[:] if _defaults == 1: # Residential Single family: - equipment_list_.append(ph_equipment.PhDishwasher( - _defaults=ph_default_equip['PhDishwasher']['PHIUS'])) - equipment_list_.append(ph_equipment.PhClothesWasher( - _defaults=ph_default_equip['PhClothesWasher']['PHIUS'])) - equipment_list_.append(ph_equipment.PhClothesDryer( - _defaults=ph_default_equip['PhClothesDryer']['PHIUS'])) - equipment_list_.append(ph_equipment.PhFridgeFreezer( - _defaults=ph_default_equip['PhFridgeFreezer']['PHIUS'])) - equipment_list_.append(ph_equipment.PhCooktop( - _defaults=ph_default_equip['PhCooktop']['PHIUS'])) - equipment_list_.append(ph_equipment.PhPhiusMEL( - _defaults=ph_default_equip['PhPhiusMEL']['PHIUS'])) - equipment_list_.append(ph_equipment.PhPhiusLightingInterior( - _defaults=ph_default_equip['PhPhiusLightingInterior']['PHIUS'])) - equipment_list_.append(ph_equipment.PhPhiusLightingExterior( - _defaults=ph_default_equip['PhPhiusLightingExterior']['PHIUS'])) + equipment_list_.append( + ph_equipment.PhDishwasher(_defaults=ph_default_equip["PhDishwasher"]["PHIUS"]) + ) + equipment_list_.append( + ph_equipment.PhClothesWasher( + _defaults=ph_default_equip["PhClothesWasher"]["PHIUS"] + ) + ) + equipment_list_.append( + ph_equipment.PhClothesDryer( + _defaults=ph_default_equip["PhClothesDryer"]["PHIUS"] + ) + ) + equipment_list_.append( + ph_equipment.PhFridgeFreezer( + _defaults=ph_default_equip["PhFridgeFreezer"]["PHIUS"] + ) + ) + equipment_list_.append( + ph_equipment.PhCooktop(_defaults=ph_default_equip["PhCooktop"]["PHIUS"]) + ) + equipment_list_.append( + ph_equipment.PhPhiusMEL(_defaults=ph_default_equip["PhPhiusMEL"]["PHIUS"]) + ) + equipment_list_.append( + ph_equipment.PhPhiusLightingInterior( + _defaults=ph_default_equip["PhPhiusLightingInterior"]["PHIUS"] + ) + ) + equipment_list_.append( + ph_equipment.PhPhiusLightingExterior( + _defaults=ph_default_equip["PhPhiusLightingExterior"]["PHIUS"] + ) + ) if _defaults == 2: # Multifamily Residential - equipment_list_.append(ph_equipment.PhDishwasher( - _defaults=ph_default_equip['PhDishwasher']['PHIUS'])) - equipment_list_.append(ph_equipment.PhClothesWasher( - _defaults=ph_default_equip['PhClothesWasher']['PHIUS'])) - equipment_list_.append(ph_equipment.PhClothesDryer( - _defaults=ph_default_equip['PhClothesDryer']['PHIUS'])) - equipment_list_.append(ph_equipment.PhFridgeFreezer( - _defaults=ph_default_equip['PhFridgeFreezer']['PHIUS'])) - equipment_list_.append(ph_equipment.PhCooktop( - _defaults=ph_default_equip['PhCooktop']['PHIUS'])) + equipment_list_.append( + ph_equipment.PhDishwasher(_defaults=ph_default_equip["PhDishwasher"]["PHIUS"]) + ) + equipment_list_.append( + ph_equipment.PhClothesWasher( + _defaults=ph_default_equip["PhClothesWasher"]["PHIUS"] + ) + ) + equipment_list_.append( + ph_equipment.PhClothesDryer( + _defaults=ph_default_equip["PhClothesDryer"]["PHIUS"] + ) + ) + equipment_list_.append( + ph_equipment.PhFridgeFreezer( + _defaults=ph_default_equip["PhFridgeFreezer"]["PHIUS"] + ) + ) + equipment_list_.append( + ph_equipment.PhCooktop(_defaults=ph_default_equip["PhCooktop"]["PHIUS"]) + ) if _defaults == 3: # Multifamily NonResidential pass @@ -241,24 +275,34 @@ def _add_Phi_default_equipment_to_list(_equipment, _defaults=0): equipment_list_ = _equipment[:] if _defaults == 1: - equipment_list_.append(ph_equipment.PhDishwasher( - _defaults=ph_default_equip['PhDishwasher']['PHI'])) - equipment_list_.append(ph_equipment.PhClothesWasher( - _defaults=ph_default_equip['PhClothesWasher']['PHI'])) - equipment_list_.append(ph_equipment.PhClothesDryer( - _defaults=ph_default_equip['PhClothesDryer']['PHI'])) - equipment_list_.append(ph_equipment.PhFridgeFreezer( - _defaults=ph_default_equip['PhFridgeFreezer']['PHI'])) - equipment_list_.append(ph_equipment.PhCooktop( - _defaults=ph_default_equip['PhCooktop']['PHI'])) + equipment_list_.append( + ph_equipment.PhDishwasher(_defaults=ph_default_equip["PhDishwasher"]["PHI"]) + ) + equipment_list_.append( + ph_equipment.PhClothesWasher( + _defaults=ph_default_equip["PhClothesWasher"]["PHI"] + ) + ) + equipment_list_.append( + ph_equipment.PhClothesDryer( + _defaults=ph_default_equip["PhClothesDryer"]["PHI"] + ) + ) + equipment_list_.append( + ph_equipment.PhFridgeFreezer( + _defaults=ph_default_equip["PhFridgeFreezer"]["PHI"] + ) + ) + equipment_list_.append( + ph_equipment.PhCooktop(_defaults=ph_default_equip["PhCooktop"]["PHI"]) + ) return equipment_list_ -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ # -- Component Interface class GHCompo_AddElecEquip(object): - def __init__(self, _IGH, _phius_defaults, _phi_defaults, _equipment, _hb_rooms): # type: (gh_io.IGH, bool, bool, List[ph_equipment.PhEquipment], List[room.Room]) -> None self.IGH = _IGH @@ -275,31 +319,33 @@ def run(self): backwards upstream in the GH definition) and cause unwanted behavior. """ - #---------------------------------------------------------------------- + # ---------------------------------------------------------------------- # -- Create the HBPH-Equipment set if self.phius_defaults: - ph_equipment = _add_Phius_default_equipment_to_list(self.equipment, self.phius_defaults) + ph_equipment = _add_Phius_default_equipment_to_list( + self.equipment, self.phius_defaults + ) elif self.phi_defaults: - ph_equipment = _add_Phi_default_equipment_to_list(self.equipment, self.phi_defaults) + ph_equipment = _add_Phi_default_equipment_to_list( + self.equipment, self.phi_defaults + ) else: ph_equipment = self.equipment - #---------------------------------------------------------------------- + # ---------------------------------------------------------------------- # -- Find and collect all the unique HBE-ElectricEquipment objects on the HB-Rooms hb_ee_collection = HBElecEquipCollection() hb_rooms = _clean_rooms_elec_equip(self.hb_rooms) for hb_room in hb_rooms: hb_ee_collection.add_to_collection(hb_room) - - #---------------------------------------------------------------------- + # ---------------------------------------------------------------------- # --- Add the PH-equipment to each of the unique HBE-ElectricEquipment .properties.ph objects found for ph_equip_item in ph_equipment: for hb_ElecEquipWithRooms_obj in hb_ee_collection.values(): hb_ElecEquipWithRooms_obj.add_hbph_equipment(ph_equip_item) - - #---------------------------------------------------------------------- + # ---------------------------------------------------------------------- # --- Add the new modified Elec-Equipment with the new PH-Equip back onto the HB-Rooms hb_rooms_ = [] for obj in hb_ee_collection.values(): @@ -314,4 +360,4 @@ def run(self): obj.set_hb_room_ee() hb_rooms_ += obj.hb_rooms - return hb_rooms_, ph_equipment \ No newline at end of file + return hb_rooms_, ph_equipment diff --git a/honeybee_ph_rhino/gh_compo_io/space_add_spc.py b/honeybee_ph_rhino/gh_compo_io/space_add_spc.py index 36c8113..cdce178 100644 --- a/honeybee_ph_rhino/gh_compo_io/space_add_spc.py +++ b/honeybee_ph_rhino/gh_compo_io/space_add_spc.py @@ -4,7 +4,7 @@ """GHCompo Interface: HBPH - Add Spaces.""" try: - import Rhino.Geometry as rg + import Rhino.Geometry as rg # type: ignore except ImportError as e: raise ImportError('Failed to import Rhino Geometry.\n{}'.format(e)) @@ -23,10 +23,16 @@ except ImportError as e: raise ImportError('\nFailed to import honeybee:\n\t{}'.format(e)) +try: + from honeybee_ph import space +except ImportError as e: + raise ImportError('\nFailed to import honeybee_ph:\n\t{}'.format(e)) -from honeybee_ph_rhino.make_spaces import make_space -from honeybee_ph_rhino import gh_io -from honeybee_ph import space +try: + from honeybee_ph_rhino.make_spaces import make_space + from honeybee_ph_rhino import gh_io +except ImportError as e: + raise ImportError('\nFailed to import honeybee_ph_rhino:\n\t{}'.format(e)) class GHCompo_AddPHSpaces(object): diff --git a/honeybee_ph_rhino/gh_compo_io/space_create_spc.py b/honeybee_ph_rhino/gh_compo_io/space_create_spc.py index fcf78ea..add54ee 100644 --- a/honeybee_ph_rhino/gh_compo_io/space_create_spc.py +++ b/honeybee_ph_rhino/gh_compo_io/space_create_spc.py @@ -71,7 +71,7 @@ def __init__(self, self.vol_geom = _volume_geometry self.vol_heights = _volume_heights self.names = _space_names - self.number = _space_numbers + self.numbers = _space_numbers self.vent_rates = _space_ph_vent_rates @property @@ -228,7 +228,7 @@ def run(self): input_len = len(self.flr_geom.Branches) space_names = self._clean_input_tree(self.names, input_len, '_Unnamed_', String) - space_numbers = self._clean_input_tree(self.number, input_len, '000', String) + space_numbers = self._clean_input_tree(self.numbers, input_len, '000', String) weighting_factors = self._clean_input_tree( self.weighting_factors, input_len, 1.0, Double) volume_heights = self._clean_volume_heights_tree( diff --git a/honeybee_ph_rhino/make_spaces/make_space.py b/honeybee_ph_rhino/make_spaces/make_space.py index d06006d..5a6b3fc 100644 --- a/honeybee_ph_rhino/make_spaces/make_space.py +++ b/honeybee_ph_rhino/make_spaces/make_space.py @@ -5,11 +5,21 @@ from collections import namedtuple -from honeybee import room -from ladybug_rhino.fromgeometry import from_point3d -from ladybug_rhino.togeometry import to_point3d +try: + from honeybee import room +except ImportError as e: + raise ImportError('\nFailed to import honeybee:\n\t{}'.format(e)) -from honeybee_ph import space +try: + from ladybug_rhino.fromgeometry import from_point3d + from ladybug_rhino.togeometry import to_point3d +except ImportError as e: + raise ImportError('\nFailed to import ladybug_rhino:\n\t{}'.format(e)) + +try: + from honeybee_ph import space +except ImportError as e: + raise ImportError('\nFailed to import honeybee_ph:\n\t{}'.format(e)) SpaceData = namedtuple('SpaceData', ['space', 'reference_points']) @@ -69,44 +79,42 @@ def add_spaces_to_honeybee_rooms(_spaces, _hb_rooms, _inherit_names=False): for space in _spaces: spaces_dict[id(space)] = SpaceData(space, [pt for pt in space.reference_points]) - # -- Duplicate HB Rooms to ensure no conflicts - hb_rooms = [rm.duplicate() for rm in _hb_rooms] - # -- Add the spaces to the host-rooms new_rooms = [] - for room in hb_rooms: + for hb_room in _hb_rooms: + dup_room = hb_room.duplicate() # -- See if any of the Space Reference points are inside the Room Geometry for space_data_id, space_data in spaces_dict.items(): for pt in space_data.reference_points: - if not room.geometry.is_point_inside(pt): + if not dup_room.geometry.is_point_inside(pt): continue - sp = space_data.space - sp.host = room + sp = space_data.space.duplicate() + sp.host = dup_room # -- If 'inherit names', simplify the spaces so that # -- there is only a single space inside of the HB-Room # -- and it will inherit its name from the parent HB-Room. if _inherit_names: - sp.name = room.display_name - room.properties.ph.merge_new_space(sp) + sp.name = dup_room.display_name + dup_room.properties.ph.merge_new_space(sp) # type: ignore else: - room.properties.ph.add_new_space(sp) + dup_room.properties.ph.add_new_space(sp) # type: ignore # -- Add in any detailed PH-Style vent flow rates if they exist if sp.properties.ph.has_ventilation_flow_rates: sp_flow_rate = sp.properties.ph.honeybee_flow_rate - existing_room_flow = room.properties.energy.ventilation.flow_per_zone + existing_room_flow = dup_room.properties.energy.ventilation.flow_per_zone # type: ignore new_room_flow = sp_flow_rate + existing_room_flow - room.properties.energy.abolute_ventilation(new_room_flow) + dup_room.properties.energy.abolute_ventilation(new_room_flow) # type: ignore # -- to speed up further checks del spaces_dict[space_data_id] break - new_rooms.append(room) + new_rooms.append(dup_room) # -- There should not be any spaces left in the dict if all were # -- hosted properly. Raise warning error if any are un-hosted? diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b4e344b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,2 @@ +[tool.black] +line-length = 90 \ No newline at end of file