Skip to content

Commit

Permalink
fix(phius_mf): Refactor Phius MF calculator
Browse files Browse the repository at this point in the history
  • Loading branch information
ed-p-may committed Jun 5, 2023
1 parent fe0a261 commit 27906bc
Show file tree
Hide file tree
Showing 3 changed files with 253 additions and 50 deletions.
203 changes: 153 additions & 50 deletions honeybee_energy_ph/load/phius_mf.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# -*- Python Version: 2.7 -*-
# -*- coding: utf-8 -*-

"""Helper/Temp Classes for calculating Phius Multifamily Elec. Energy"""
"""Classes for calculating Phius Multifamily Elec. Energy as per Phius Multifamily Calculator (v4.2)"""

try:
from typing import ValuesView, List
from typing import ValuesView, List, Any
except ImportError:
pass # IronPython 2.7

Expand All @@ -17,14 +17,19 @@


class PhiusResidentialStory(object):
"""Represents one Residential Story of the Phius Multifamily Calculator."""
"""Represents one Residential Story of the Phius Multifamily Calculator (v4.2)"""

def __init__(self, hb_room_list):
# type: (List[room.Room]) -> None
self.phius_resnet_fraction = 0.8
self.lighting_int_HE_frac = 1.0
self.lighting_ext_HE_frac = 1.0
self.lighting_garage_HE_frac = 1.0

self.story_number = hb_room_list[0].story
try:
self.story_number = hb_room_list[0].story
except:
self.story_number = 1
self.total_floor_area_m2 = self.calc_story_floor_area(hb_room_list)
self.total_number_dwellings = self.calc_num_dwellings(hb_room_list)
self.total_number_bedrooms = self.calc_story_bedrooms(hb_room_list)
Expand All @@ -35,10 +40,28 @@ def __init__(self, hb_room_list):
self.lighting_ext = self.calc_lighting_ext()
self.lighting_garage = self.calc_lighting_garage()

@property
def story_number(self):
# type: () -> str
"""Return the story number."""
return str(self._story_number)

@story_number.setter
def story_number(self, value):
# type: (Any) -> None
"""Set the story number."""
# -- If the input is a single integer value, enforce padding
try:
self._story_number = "{:03d}".format(int(value))
except ValueError:
self._story_number = str(value)

@property
def total_floor_area_ft2(self):
# type () -> float
return self.total_floor_area_m2 * 10.7639
# type: () -> float
"""Return the total floor area of the story in ft2."""
FT2_PER_M2 = 10.7639
return self.total_floor_area_m2 * FT2_PER_M2

def calc_story_floor_area(self, _hb_rooms):
# type: (List[room.Room]) -> float
Expand All @@ -50,35 +73,98 @@ def calc_story_floor_area(self, _hb_rooms):

def calc_story_bedrooms(self, _hb_rooms):
# type: (List[room.Room]) -> int
return sum(rm.properties.energy.people.properties.ph.number_bedrooms for rm in _hb_rooms)
return sum(
rm.properties.energy.people.properties.ph.number_bedrooms for rm in _hb_rooms
)

def calc_design_occupancy(self, _hb_rooms):
# type: (List[room.Room]) -> float
return sum(rm.properties.energy.people.properties.ph.number_people for rm in _hb_rooms)
return sum(
rm.properties.energy.people.properties.ph.number_people for rm in _hb_rooms
)

def calc_num_dwellings(self, _hb_rooms):
# type: (List[room.Room]) -> int
return sum(rm.properties.energy.people.properties.ph.number_dwelling_units for rm in _hb_rooms)
return sum(
rm.properties.energy.people.properties.ph.number_dwelling_units
for rm in _hb_rooms
)

def calc_mel(self):
# type: () -> float
return (self.total_number_dwellings * 413 + 69 * self.total_number_bedrooms + 0.91 * self.total_floor_area_ft2) * 0.8
"""Calculate the Phius MF Misc. Electrical Loads (MEL) for the story.
Resnet 2014
https://codes.iccsafe.org/content/RESNET3012014P1/4-home-energy-rating-calculation-procedures-
"""
TV_A_KWH = 413
RESIDUAL_MELS_KWH_FT2 = 0.91
TV_C_KWH = 69

a = TV_A_KWH * self.total_number_dwellings
b = RESIDUAL_MELS_KWH_FT2 * self.total_floor_area_ft2
c = TV_C_KWH * self.total_number_bedrooms

return (a + b + c) * self.phius_resnet_fraction

def calc_lighting_int(self):
# type: () -> float
return (0.2+0.8*(4-3*self.lighting_int_HE_frac)/3.7)*(self.total_number_dwellings*455+0.8*self.total_floor_area_ft2)*0.8
"""Calculate the Phius MF Interior Lighting for the story.
Resnet 2014
https://codes.iccsafe.org/content/RESNET3012014P1/4-home-energy-rating-calculation-procedures-
"""
INT_LIGHTING_KHW = 455
INT_LIGHTING_KWH_FT2 = 0.8

a = 0.2 + 0.8 * (4 - 3 * self.lighting_int_HE_frac) / 3.7
b = INT_LIGHTING_KHW * self.total_number_dwellings
c = INT_LIGHTING_KWH_FT2 * self.total_floor_area_ft2

return a * (b + c) * self.phius_resnet_fraction

def calc_lighting_ext(self):
# type: () -> float
return (1-0.75*self.lighting_ext_HE_frac)*(self.total_number_dwellings*100+0.05*self.total_floor_area_ft2)*0.8
"""Calculate the Phius MF Exterior Lighting for the story.
Resnet 2014
https://codes.iccsafe.org/content/RESNET3012014P1/4-home-energy-rating-calculation-procedures-
"""
EXT_LIGHTING_KHW = 100
EXT_LIGHTING_KWH_FT2 = 0.05

a = EXT_LIGHTING_KHW * self.total_number_dwellings
b = EXT_LIGHTING_KWH_FT2 * self.total_floor_area_ft2
e = 1 - 0.75 * self.lighting_ext_HE_frac

return e * (a + b) * self.phius_resnet_fraction

def calc_lighting_garage(self):
# type: () -> float
return self.total_number_dwellings*(100*(1-self.lighting_garage_HE_frac)+25*self.lighting_garage_HE_frac)*0.8
"""Calculate the Phius MF Garage Lighting for the story."""
GARAGE_LIGHTING_KHW = 100
a = (
GARAGE_LIGHTING_KHW * (1 - self.lighting_garage_HE_frac)
+ 25 * self.lighting_garage_HE_frac
)

return self.total_number_dwellings * a * self.phius_resnet_fraction

def __lt__(self, other):
# type: (PhiusResidentialStory) -> bool
return self.story_number < other.story_number

def __eq__(self, other):
# type: (PhiusResidentialStory) -> bool
return self.story_number == other.story_number

def __str__(self):
return "{}(total_floor_area_m2={}, total_number_dwellings={}, total_number_bedrooms={})".format(
self.__class__.__name__, self.total_floor_area_m2, self.total_number_dwellings, self.total_number_bedrooms)
self.__class__.__name__,
self.total_floor_area_m2,
self.total_number_dwellings,
self.total_number_bedrooms,
)

def __repr__(self):
return str(self)
Expand Down Expand Up @@ -114,13 +200,15 @@ def mel_kWh_ft2_yr(self):
def to_phius_mf_workbook(self):
# type: () -> str
"""Returns a text block formatted to match the Phius MF Calculator."""
return ",".join([
str(self.name),
str(self.usage_days_yr),
str(self.operating_hours_day),
str(self.lighting_W_per_ft2),
str(self.mel_kWh_ft2_yr),
])
return ",".join(
[
str(self.name),
str(self.usage_days_yr),
str(self.operating_hours_day),
str(self.lighting_W_per_ft2),
str(self.mel_kWh_ft2_yr),
]
)

@classmethod
def from_hb_room(cls, _hb_room):
Expand All @@ -129,11 +217,14 @@ def from_hb_room(cls, _hb_room):
obj = cls()

# -- type Alias
rm_prop_energy = _hb_room.properties.energy # type: RoomEnergyProperties # type: ignore
rm_prop_energy = (
_hb_room.properties.energy
) # type: RoomEnergyProperties # type: ignore

obj.name = rm_prop_energy.program_type.display_name
obj.operating_hours_day = obj._operating_hours_day_from_hb_schedule(
rm_prop_energy.lighting.schedule)
rm_prop_energy.lighting.schedule
)
obj.lighting_W_per_m2 = rm_prop_energy.lighting.watts_per_area
obj.mel_kWh_m2_yr = obj._calc_mel_kWh_per_m2_from_hb_elec(
rm_prop_energy.electric_equipment
Expand All @@ -144,7 +235,7 @@ def from_hb_room(cls, _hb_room):

def _calc_mel_kWh_per_m2_from_hb_elec(self, _hb_elec_equip):
# type: (equipment.ElectricEquipment) -> float
"""Returns the total Elec. Equip kWh for the HBE-Electric-Equipment."""
"""Returns the total Elec. Equip kWh for an HBE-Electric-Equipment object."""

# -- Calc annual full-load hours
annual_full_load_hours = sum(_hb_elec_equip.schedule.values())
Expand All @@ -159,7 +250,7 @@ def _operating_hours_day_from_hb_schedule(self, _hb_lght_sched):
return operating_frac * 24

def __str__(self):
return '{}(name={})'.format(self.__class__.__name__, self.name)
return "{}(name={})".format(self.__class__.__name__, self.name)

def __repr__(self):
return str(self)
Expand Down Expand Up @@ -205,7 +296,7 @@ def __init__(self):
self.occupancy_sensor = "N"
self.name = "_unnamed_phius_nonres_room"
self.icfa_m2 = 0.0
self.misc_mel = 0.0 # kWh/yr
self.misc_mel = 0.0 # kWh/yr
self.program_type = PhiusNonResProgram()

@property
Expand All @@ -217,7 +308,7 @@ def icfa_ft2(self):
def mel_kWh_yr(self):
# type: () -> float
"""Total yearly MEL kWh EXCLUDING the Misc MEL"""
return (self.icfa_m2 * self.program_type.mel_kWh_m2_yr)
return self.icfa_m2 * self.program_type.mel_kWh_m2_yr

@property
def mel_w_m2(self):
Expand All @@ -233,32 +324,40 @@ def total_mel_kWh(self):
@property
def total_lighting_kWh(self):
# type: () -> float
return (self.program_type.usage_days_yr * self.program_type.operating_hours_day *
self.program_type.lighting_W_per_m2 * self.icfa_m2) / 1000
return (
self.program_type.usage_days_yr
* self.program_type.operating_hours_day
* self.program_type.lighting_W_per_m2
* self.icfa_m2
) / 1000

def to_phius_mf_workbook(self):
# type: () -> str
"""Returns a string representation that matches the Phius MF Calculator."""
return ",".join([
str(self.program_type.name),
"", # Blank
"", # Number
str(self.name),
str(self.occupancy_sensor),
str(self.multiplier),
str(self.icfa_ft2),
str(self.mel_w_m2),
])
return ",".join(
[
str(self.program_type.name),
"", # Blank
"", # Number
str(self.name),
str(self.occupancy_sensor),
str(self.multiplier),
str(self.icfa_ft2),
str(self.mel_w_m2),
]
)

def to_phius_mf_workbook_results(self):
return ",".join([
str(self.program_type.lighting_W_per_ft2),
str(self.program_type.usage_days_yr),
str(self.program_type.operating_hours_day),
str(self.program_type.mel_kWh_ft2_yr),
str(self.total_lighting_kWh),
str(self.mel_kWh_yr),
])
return ",".join(
[
str(self.program_type.lighting_W_per_ft2),
str(self.program_type.usage_days_yr),
str(self.program_type.operating_hours_day),
str(self.program_type.mel_kWh_ft2_yr),
str(self.total_lighting_kWh),
str(self.mel_kWh_yr),
]
)

@classmethod
def from_ph_space(cls, _ph_space):
Expand All @@ -273,9 +372,13 @@ def from_ph_space(cls, _ph_space):
return obj

def __str__(self):
return '{}(name={}, program_type={},'\
'misc_mel={}, icfa={})'.format(
self.__class__.__name__, self.name, self.program_type, self.misc_mel, self.icfa_m2)
return "{}(name={}, program_type={}," "misc_mel={}, icfa={})".format(
self.__class__.__name__,
self.name,
self.program_type,
self.misc_mel,
self.icfa_m2,
)

def __repr__(self):
return str(self)
Expand Down
Empty file.
Loading

0 comments on commit 27906bc

Please sign in to comment.