Skip to content

Commit

Permalink
Finish air-cooled example generation.
Browse files Browse the repository at this point in the history
  • Loading branch information
nealkruis committed Dec 20, 2024
1 parent ed2ff0f commit fc4a933
Show file tree
Hide file tree
Showing 8 changed files with 533 additions and 335 deletions.
299 changes: 191 additions & 108 deletions chiller/chiller.py

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions chiller/conditions.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
from .fluid_properties import LiquidState
from .psychrometrics import PsychrometricState
from koozie import fr_u
from .fluid_properties import LiquidState, PsychrometricState


class OperatingConditions:
def __init__(
self,
condenser_inlet: LiquidState | PsychrometricState,
evaporator_outlet,
compressor_speed=0,
evaporator_outlet: LiquidState,
compressor_speed: int = 0,
):
self.condenser_inlet = condenser_inlet
self.evaporator_outlet = evaporator_outlet
Expand All @@ -25,6 +24,8 @@ def __init__(
evaporator_outlet=LiquidState(fr_u(44.0, "°F")),
)

AHRI_550_590_WATER_COOLED_CONDENSER_OUTLET = LiquidState(fr_u(94.3, "°F"))
AHRI_550_590_LIQUID_COOLED_CONDENSER_OUTLET = LiquidState(fr_u(94.3, "°F"))

AHRI_550_590_EVAPORATOR_INLET = LiquidState(fr_u(54.0, "°F"))
AHRI_550_590_EVAPORATOR_INLET = LiquidState(
fr_u(54.0, "°F")
) # TODO: This isn't treated as a constant
269 changes: 232 additions & 37 deletions chiller/fluid_properties.py
Original file line number Diff line number Diff line change
@@ -1,67 +1,262 @@
from re import S
import CoolProp.CoolProp as CP
from koozie import fr_u
import psychrolib

from koozie import fr_u, to_u

class LiquidState:
psychrolib.SetUnitSystem(psychrolib.SI)


class FluidState:
def __init__(
self,
temperature,
pressure=fr_u(1.0, "atm"),
temperature: float,
pressure: float = fr_u(1.0, "atm"),
volumetric_flow_rate=None,
mass_flow_rate=None,
fluid_name="Water",
):
self.fluid_name = fluid_name
self.T = temperature
self.p = pressure
self._rho = -999.0
self._cp = -999.0
self._V_dot = -999.0
self._m_dot = -999.0
self.rho_set = False
self.cp_set = False
self.V_dot_set = False
self._flow_rate_set = False
self.c = 0.0
if mass_flow_rate is not None and volumetric_flow_rate is not None:
raise Exception(
raise RuntimeError(
f"Cannot set both 'volumetric_flow_rate' and 'mass_flow_rate'."
)
if volumetric_flow_rate is None:
self.V_dot = None
else:
self.set_V_dot(volumetric_flow_rate)
if volumetric_flow_rate is not None:
self.V_dot = volumetric_flow_rate
return
if mass_flow_rate is None:
self.m_dot = None
else:
self.set_m_dot(mass_flow_rate)
if mass_flow_rate is not None:
self.m_dot = mass_flow_rate
return

def get_rho(self):
@property
def m_dot(self):
if self._flow_rate_set:
return self._m_dot
raise RuntimeError("m_dot not set")

@m_dot.setter
def m_dot(self, mass_flow_rate):
self._m_dot = mass_flow_rate
self._flow_rate_set = True
self._V_dot = self.m_dot / self.rho
self.c = self.m_dot * self.cp

@property
def V_dot(self):
if self._flow_rate_set:
return self._V_dot
raise RuntimeError("V_dot not set")

@V_dot.setter
def V_dot(self, volumetric_flow_rate):
self.m_dot = volumetric_flow_rate * self.rho

@property
def rho(self):
raise NotImplementedError()

@rho.setter
def rho(self, rho):
self._rho = rho
self.rho_set = True

@property
def cp(self):
raise NotImplementedError()

@cp.setter
def cp(self, cp):
self._cp = cp
self.cp_set = True

def get_heat(self, other_state: type["FluidState"]):
'''returns the amount of heat difference between this state and "other state"'''
return self.c * self.T - other_state.c * other_state.T


class LiquidState(FluidState):
def __init__(
self,
temperature,
pressure=fr_u(1.0, "atm"),
volumetric_flow_rate=None,
mass_flow_rate=None,
fluid_name="Water",
):
self.fluid_name = fluid_name
super().__init__(
temperature,
pressure,
volumetric_flow_rate,
mass_flow_rate,
)

@property
def rho(self):
if not self.rho_set:
self.rho = CP.PropsSI("D", "P", self.p, "T", self.T, self.fluid_name)
self.rho_set = True
return self.rho
return self._rho

def get_cp(self):
@rho.setter
def rho(self, rho):
self._rho = rho
self.rho_set = True

@property
def cp(self):
if not self.cp_set:
self.cp = CP.PropsSI("C", "P", self.p, "T", self.T, self.fluid_name)
self.cp_set = True
return self.cp

def set_V_dot(self, volumetric_flow_rate):
self.V_dot = volumetric_flow_rate
self.m_dot = self.V_dot * self.get_rho()
self.c = self.m_dot * self.get_cp()
self.V_dot_set = True
return self._cp

def set_m_dot(self, mass_flow_rate):
self.m_dot = mass_flow_rate
self.V_dot = self.m_dot / self.get_rho()
self.c = self.m_dot * self.get_cp()
self.V_dot_set = True
@cp.setter
def cp(self, cp):
self._cp = cp
self.cp_set = True

def add_heat(self, heat):
return LiquidState(
temperature=self.T + heat / self.c, mass_flow_rate=self.m_dot
)

def get_heat(self, other_state):
'''returns the amount of heat difference between this state and "other state"'''
return self.c * self.T - other_state.c * other_state.T

class PsychrometricState(FluidState):
def __init__(
self,
drybulb,
pressure=fr_u(1.0, "atm"),
volumetric_flow_rate=None,
mass_flow_rate=None,
**kwargs,
):
super().__init__(
drybulb,
pressure,
volumetric_flow_rate,
mass_flow_rate,
)
self.db_C = to_u(self.T, "°C")
self._wb = -999.0
self._h = -999.0
self._rh = -999.0
self._hr = -999.0
self.wb_set = False
self.rh_set = False
self.hr_set = False
self.dp_set = False
self.h_set = False
self.rho_set = False
if len(kwargs) > 1:
raise RuntimeError(
f"{PsychrometricState.__name__} can only be initialized with a single key word argument, but received {len(kwargs)}: {kwargs}"
)
if "wetbulb" in kwargs:
self.wb = kwargs["wetbulb"]
elif "humidity_ratio" in kwargs:
self.hr = kwargs["humidity_ratio"]
elif "relative_humidity" in kwargs:
self.rh = kwargs["relative_humidity"]
elif "enthalpy" in kwargs:
self.h = kwargs["enthalpy"]
else:
raise RuntimeError(
f"{PsychrometricState.__name__}: Unknown or missing key word argument {kwargs}."
)

@property
def cp(self):
if not self.cp_set:
self.cp = fr_u(1.006, "kJ/kg/K")
return self._cp

@cp.setter
def cp(self, cp):
self._cp = cp
self.cp_set = True

@property
def wb(self):
if self.wb_set:
return self._wb
raise RuntimeError("Wetbulb not set")

@wb.setter
def wb(self, wb):
self._wb = wb
self.wb_C = to_u(self._wb, "°C")
self.wb_set = True

def get_wb_C(self):
if self.wb_set:
return self.wb_C
else:
raise RuntimeError("Wetbulb not set")

@property
def hr(self):
if not self.hr_set:
self.hr = psychrolib.GetHumRatioFromTWetBulb(
self.db_C, self.get_wb_C(), self.p
)
return self._hr

@hr.setter
def hr(self, hr):
self._hr = hr
if not self.wb_set:
self.wb = fr_u(
psychrolib.GetTWetBulbFromHumRatio(self.db_C, self._hr, self.p),
"°C",
)

self.hr_set = True

@property
def rh(self):
if not self.rh_set:
self.rh = psychrolib.GetHumRatioFromTWetBulb(
self.db_C, self.get_wb_C(), self.p
)
return self._rh

@rh.setter
def rh(self, rh):
self._rh = rh
if not self.wb_set:
self.wb = fr_u(
psychrolib.GetTWetBulbFromRelHum(self.db_C, self._rh, self.p), "°C"
)
self.rh_set = True

@property
def h(self):
if not self.h_set:
self.h = psychrolib.GetMoistAirEnthalpy(self.db_C, self.hr)
return self._h

@h.setter
def h(self, h):
self._h = h
if not self.hr_set:
self.hr = psychrolib.GetHumRatioFromEnthalpyAndTDryBulb(self._h, self.db_C)
self.h_set = True

@property
def rho(self):
if not self.rho_set:
self.rho = psychrolib.GetMoistAirDensity(self.db_C, self.hr, self.p)
return self._rho

@rho.setter
def rho(self, rho):
self._rho = rho
self.rho_set = True


STANDARD_CONDITIONS = PsychrometricState(drybulb=fr_u(70.0, "°F"), humidity_ratio=0.0)
19 changes: 15 additions & 4 deletions chiller/models/ashrae_90_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from ..chiller import (
CompressorType,
CondenserType,
FloatRange
)


Expand Down Expand Up @@ -117,6 +118,11 @@ def __init__(
condenser_type: CondenserType,
compressor_type: CompressorType,
path_type,
cycling_degradation_coefficient=0.0,
standby_power=0.0,
space_gain_fraction=0.0,
oil_cooler_fraction=0.0,
auxiliary_fraction=0.0,
):

self.path_type = path_type
Expand Down Expand Up @@ -150,7 +156,7 @@ def __init__(
self.curve_set = matches_found[0]

# scaling
self.capacity_range = (
self.capacity_range = FloatRange(
self.curve_set.minimum_capacity,
self.curve_set.maximum_capacity,
)
Expand All @@ -164,6 +170,11 @@ def __init__(
capacity_temperature_coefficients=self.curve_set.capacity_temperature_coefficients,
minimum_part_load_ratio=0.25,
minimum_unloading_ratio=0.25,
cycling_degradation_coefficient=cycling_degradation_coefficient,
standby_power=standby_power,
space_gain_fraction=space_gain_fraction,
oil_cooler_fraction=oil_cooler_fraction,
auxiliary_fraction=auxiliary_fraction,
)

self.compressor_type = compressor_type
Expand All @@ -189,10 +200,10 @@ def generate_205_representation(self, capacity_range=None):
if compressor_text is not None:
type_text += f", {compressor_text} compressor"
type_text += f" chiller"
if self.capacity_range[1] == float("inf"):
size_description = f"{to_u(self.capacity_range[0],'ton_ref'):.1f}+"
if self.capacity_range.max == float("inf"):
size_description = f"{to_u(self.capacity_range.min,'ton_ref'):.1f}+"
else:
size_description = f"{to_u(self.capacity_range[0],'ton_ref'):.1f}-{to_u(self.capacity_range[1],'ton_ref'):.1f}"
size_description = f"{to_u(self.capacity_range.min,'ton_ref'):.1f}-{to_u(self.capacity_range.max,'ton_ref'):.1f}"
self.metadata.description = f"{standard_reference} '{self.curve_set.set_name}': {size_description} ton, {self.rated_cop:.2f} COP, {self.curve_set.iplv:.2f} IPLV {type_text}"
unique_characteristics = (
self.rated_net_evaporator_capacity,
Expand Down
Loading

0 comments on commit fc4a933

Please sign in to comment.