The documentation, examples and tutorials should be understandable and the code bug-free.
+As all user’s have different backgrounds, you may not understand everything or encounter bugs.
+In that case, PLEASE raise an issue here.
+
Consider labeling the issue using the flag bug or documentation / question.
If you instead want to contribute new features or fix bugs yourself, we are more than happy.
+
Please also raise an issue and create a new branch labeled issueXY_some_name.
+Here, XY is the number of your issue and some_name is a meaingful description.
+
Once you’re feature is ready, create a pull request and check if the pipeline succeeds.
+Assign a reviewer before merging. Once review is finished, you can merge.
+
Before implementing or modifying modules, classes or functions, please read the following page.
We use PEP8 as a styleguide. Some IDEs (like PyCharm) automatically show you code that is not in PEP8. If you don’t have such an IDE, please read this page to get a better understanding of it.
All created or modified function should be documented properly. Try to follow the structure already present. If possible, write a little doctest example into the docstring to make clear to user’s what the desired output of your function is. All non self-explanatory lines of code should include a comment. We use the pycharm-style for docstrings, e.g.
+
deffoo(dummy,dummy2):
+"""
+ Describe what the function does in here.
+ The blank line below is necessary for the doc to render nicely.
+
+ Args:
+ dummy (str):
+ Any parameter description
+ dummy2 (int,float):
+ A variable that may have two types
+ """
+
Espacially when creating new functions or classes, you have to add a unit-test function.
+Open the test_module.py file in the \tests-directory and add a function to the class TestModulewith a name like test_my_new_function. If you create a new module, you have to create a new test_my_new_module.py file and follow the existing structure of the
+other test-files.
+
If you are not familiar with unit-tests, here is a quick summary:
+
+
Test as many things as possible. Even seemingly silly tests like correct input-format help prevent future problems for new users
+
use the self.assertSOMETHING functions provided by unittest. This way a test failure is presented correctly An error inside your test function will not be handeled as a failure but an error.
+
If the success of your test depends on the used device, you can use decorators like skip(), skipif(numpy.__version__<(1,0),"notsupportedwithyournumpyversion"), etc.
+
setUp() and tearDown() are called before and after each test. Use this functions to define parameters used in every test, or to close applications like Dymola once a test is completed.
[docs]classBaseComponent(ABC):
+"""
+ Abstract base class for defining interfaces of components in the vapor compression cycle.
+
+ Methods:
+ start_secondary_med_prop():
+ To use multiprocessing, MedProp can't start in the main thread, as the object can't be pickled.
+ This function starts possible secondary MedProp classes, for instance in heat exchangers.
+ The default component does not have to override this function.
+
+ Properties:
+ state_inlet (ThermodynamicState):
+ Property for accessing and setting the inlet state of the component.
+ state_outlet (ThermodynamicState):
+ Property for accessing and setting the outlet state of the component.
+ m_flow (float):
+ Property for accessing and setting the mass flow rate through the component.
+ med_prop (MedProp):
+ Property for accessing and setting the property wrapper for the working fluid.
+ """
+
+ def__init__(self):
+"""
+ Initialize the BaseComponent.
+ """
+ self._state_inlet:ThermodynamicState=None
+ self._state_outlet:ThermodynamicState=None
+ self._m_flow:float=None
+ self._med_prop:MedProp=None
+
+
[docs]defstart_secondary_med_prop(self):
+"""
+ Start secondary MedProp classes for multiprocessing.
+
+ To use multiprocessing, MedProp can't start in the main thread, as the object can't be pickled.
+ This function starts possible secondary MedProp classes, for instance in heat exchangers.
+ The default component does not have to override this function.
+ """
+ pass
+
+
[docs]defterminate_secondary_med_prop(self):
+"""
+ To use multi-processing, MedProp can't start
+ in the main thread, as the object can't be pickled.
+
+ This function terminates possible secondary med-prop
+ classes, for instance in heat exchangers.
+ The default component does not have to override
+ this function.
+ """
+ pass
+
+ @property
+ defstate_inlet(self)->ThermodynamicState:
+"""
+ Get or set the inlet state of the component.
+
+ Returns:
+ ThermodynamicState: Inlet state of the component.
+ """
+ returnself._state_inlet
+
+ @state_inlet.setter
+ defstate_inlet(self,state_inlet:ThermodynamicState):
+"""
+ Set the inlet state of the component.
+
+ Args:
+ state_inlet (ThermodynamicState): Inlet state to set.
+ """
+ self._state_inlet=state_inlet
+
+ @property
+ defstate_outlet(self)->ThermodynamicState:
+"""
+ Get or set the outlet state of the component.
+
+ Returns:
+ ThermodynamicState: Outlet state of the component.
+ """
+ returnself._state_outlet
+
+ @state_outlet.setter
+ defstate_outlet(self,state_outlet:ThermodynamicState):
+"""
+ Set the outlet state of the component.
+
+ Args:
+ state_outlet (ThermodynamicState): Outlet state to set.
+ """
+ self._state_outlet=state_outlet
+
+ @property
+ defm_flow(self)->float:
+"""
+ Get or set the mass flow rate through the component.
+
+ Returns:
+ float: Mass flow rate through the component.
+ """
+ returnself._m_flow
+
+ @m_flow.setter
+ defm_flow(self,m_flow:float):
+"""
+ Set the mass flow rate through the component.
+
+ Args:
+ m_flow (float): Mass flow rate to set.
+ """
+ self._m_flow=m_flow
+
+ @property
+ defmed_prop(self)->MedProp:
+"""
+ Get or set the property wrapper for the working fluid.
+
+ Returns:
+ MedProp: Property wrapper for the working fluid.
+ """
+ returnself._med_prop
+
+ @med_prop.setter
+ defmed_prop(self,med_prop:MedProp):
+"""
+ Set the property wrapper for the working fluid.
+
+ Args:
+ med_prop (MedProp): Property wrapper to set.
+ """
+ self._med_prop=med_prop
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/components/compressors/compressor.html b/docs/0.1.1/docs/_modules/vclibpy/components/compressors/compressor.html
new file mode 100644
index 0000000..edcc6f6
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/components/compressors/compressor.html
@@ -0,0 +1,277 @@
+
+
+
+
+
+
+
+ vclibpy.components.compressors.compressor — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Source code for vclibpy.components.compressors.compressor
+"""
+Module for different compressor models
+"""
+
+fromvclibpy.components.componentimportBaseComponent
+fromvclibpy.datamodelsimportInputs,FlowsheetState
+
+
+
[docs]classCompressor(BaseComponent):
+"""
+ Base compressor class to be extended for specific compressor models.
+
+ Args:
+ N_max (float): Maximal rotations per second of the compressor.
+ V_h (float): Volume of the compressor in m^3.
+
+ Methods:
+ get_lambda_h(inputs: Inputs) -> float:
+ Get the volumetric efficiency.
+
+ get_eta_isentropic(p_outlet: float, inputs: Inputs) -> float:
+ Get the isentropic efficiency.
+
+ get_eta_mech(inputs: Inputs) -> float:
+ Get the mechanical efficiency.
+
+ get_p_outlet() -> float:
+ Get the outlet pressure.
+
+ get_n_absolute(n: float) -> float:
+ Return the absolute compressor frequency based on the relative speed.
+
+ calc_state_outlet(p_outlet: float, inputs: Inputs, fs_state: FlowsheetState):
+ Calculate the outlet state based on the high pressure level and provided inputs.
+
+ calc_m_flow(inputs: Inputs, fs_state: FlowsheetState) -> float:
+ Calculate the refrigerant mass flow rate.
+
+ calc_electrical_power(inputs: Inputs, fs_state: FlowsheetState) -> float:
+ Calculate the electrical power consumed by the compressor based on an adiabatic energy balance.
+ """
+
+ def__init__(self,N_max:float,V_h:float):
+"""
+ Initialize the compressor.
+
+ Args:
+ N_max (float): Maximal rotations per second of the compressor.
+ V_h (float): Volume of the compressor in m^3.
+ """
+ super().__init__()
+ self.N_max=N_max
+ self.V_h=V_h
+
+
[docs]defget_lambda_h(self,inputs:Inputs)->float:
+"""
+ Get the volumetric efficiency.
+
+ Args:
+ inputs (Inputs): Inputs for the calculation.
+
+ Returns:
+ float: Volumetric efficiency.
+ """
+ raiseNotImplementedError("Re-implement this function to use it")
+
+
[docs]defget_eta_isentropic(self,p_outlet:float,inputs:Inputs)->float:
+"""
+ Get the isentropic efficiency.
+
+ Args:
+ p_outlet (float): High pressure value.
+ inputs (Inputs): Inputs for the calculation.
+
+ Returns:
+ float: Isentropic efficiency.
+ """
+ raiseNotImplementedError("Re-implement this function to use it")
+
+
[docs]defget_eta_mech(self,inputs:Inputs)->float:
+"""
+ Get the mechanical efficiency including motor and inverter efficiencies.
+
+ Args:
+ inputs (Inputs): Inputs for the calculation.
+
+ Returns:
+ float: Mechanical efficiency including motor and inverter efficiencies.
+ """
+ raiseNotImplementedError("Re-implement this function to use it")
+
+
[docs]defget_p_outlet(self)->float:
+"""
+ Get the outlet pressure.
+
+ Returns:
+ float: Outlet pressure.
+ """
+ assertself.state_outletisnotNone,"You have to calculate the outlet state first."
+ returnself.state_outlet.p
+
+
[docs]defget_n_absolute(self,n:float)->float:
+"""
+ Return given relative n as absolute rounds/sec based on self.N_max.
+
+ Args:
+ n (float): Relative compressor speed between 0 and 1.
+
+ Returns:
+ float: Absolute compressor frequency in rounds/sec.
+ """
+ returnself.N_max*n
+
+
[docs]defcalc_state_outlet(self,p_outlet:float,inputs:Inputs,fs_state:FlowsheetState):
+"""
+ Calculate the output state based on the high pressure level and the provided inputs.
+ The state is automatically set as the outlet state of this component.
+
+ Args:
+ p_outlet (float): High pressure value.
+ inputs (Inputs): Inputs for calculation.
+ fs_state (FlowsheetState): Flowsheet state.
+ """
+ state_outlet_isentropic=self.med_prop.calc_state("PS",p_outlet,self.state_inlet.s)
+ eta_is=self.get_eta_isentropic(p_outlet=p_outlet,inputs=inputs)
+ h_outlet=(
+ self.state_inlet.h+(state_outlet_isentropic.h-self.state_inlet.h)/
+ eta_is
+ )
+ fs_state.set(name="eta_is",value=eta_is,unit="%",description="Isentropic efficiency")
+ self.state_outlet=self.med_prop.calc_state("PH",p_outlet,h_outlet)
[docs]defcalc_electrical_power(self,inputs:Inputs,fs_state:FlowsheetState)->float:
+"""
+ Calculate the electrical power consumed by the compressor based on an adiabatic energy balance.
+
+ Args:
+ inputs (Inputs): Inputs for the calculation.
+ fs_state (FlowsheetState): Flowsheet state.
+
+ Returns:
+ float: Electrical power consumed.
+ """
+ # Heat flow in the compressor
+ P_t=self.m_flow*(self.state_outlet.h-self.state_inlet.h)
+ # Electrical power consumed
+ eta_mech=self.get_eta_mech(inputs=inputs)
+ P_el=P_t/eta_mech
+ fs_state.set(name="eta_mech",value=eta_mech,unit="-",description="Mechanical efficiency")
+ returnP_el
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/components/compressors/constant_effectivness.html b/docs/0.1.1/docs/_modules/vclibpy/components/compressors/constant_effectivness.html
new file mode 100644
index 0000000..b249cb1
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/components/compressors/constant_effectivness.html
@@ -0,0 +1,202 @@
+
+
+
+
+
+
+
+ vclibpy.components.compressors.constant_effectivness — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
[docs]classConstantEffectivenessCompressor(Compressor):
+"""
+ Compressor model with constant efficiencies.
+
+ Inherits from the Compressor class, which defines the basic properties and behavior of a compressor in a vapor
+ compression cycle.
+
+ Parameters:
+ N_max (float): Maximal rotations per second of the compressor.
+ V_h (float): Volume of the compressor in m^3.
+ eta_isentropic (float): Constant isentropic efficiency of the compressor.
+ eta_mech (float): Constant mechanical efficiency of the compressor.
+ lambda_h (float): Constant volumetric efficiency.
+
+ Args:
+ N_max (float): Maximal rotations per second of the compressor.
+ V_h (float): Volume of the compressor in m^3.
+ eta_isentropic (float): Constant isentropic efficiency of the compressor.
+ eta_inverter (float): Constant inverter efficiency of the compressor.
+ eta_motor (float): Constant motor efficiency of the compressor.
+ eta_mech (float): Constant mechanical efficiency of the compressor including motor and inverter efficiencies.
+ lambda_h (float): Constant volumetric efficiency.
+
+ Methods:
+ get_lambda_h(inputs: Inputs) -> float:
+ Returns the constant volumetric efficiency of the compressor.
+
+ get_eta_isentropic(p_outlet: float, inputs: Inputs) -> float:
+ Returns the constant isentropic efficiency of the compressor.
+
+ get_eta_mech(inputs: Inputs) -> float:
+ Returns the constant mechanical efficiency including motor and inverter efficiencies.
+
+ """
+
+ def__init__(self,
+ N_max:float,V_h:float,
+ eta_isentropic:float,
+ eta_mech:float,
+ lambda_h:float):
+"""
+ Initialize the ConstantEffectivenessCompressor.
+
+ Args:
+ N_max (float): Maximal rotations per second of the compressor.
+ V_h (float): Volume of the compressor in m^3.
+ eta_isentropic (float): Constant isentropic efficiency of the compressor.
+ eta_inverter (float): Constant inverter efficiency of the compressor.
+ eta_motor (float): Constant motor efficiency of the compressor.
+ eta_mech (float): Constant mechanical efficiency of the compressor.
+ lambda_h (float): Constant volumetric efficiency.
+ """
+ super().__init__(N_max=N_max,V_h=V_h)
+ self.eta_isentropic=eta_isentropic
+ self.eta_mech=eta_mech
+ self.lambda_h=lambda_h
+
+
[docs]defget_lambda_h(self,inputs:Inputs)->float:
+"""
+ Returns the constant volumetric efficiency of the compressor.
+
+ Args:
+ inputs (Inputs): Input parameters for the calculation.
+
+ Returns:
+ float: Constant volumetric efficiency.
+ """
+ returnself.lambda_h
+
+
[docs]defget_eta_isentropic(self,p_outlet:float,inputs:Inputs)->float:
+"""
+ Returns the constant isentropic efficiency of the compressor.
+
+ Args:
+ p_outlet (float): High pressure value.
+ inputs (Inputs): Input parameters for the calculation.
+
+ Returns:
+ float: Constant isentropic efficiency.
+ """
+ returnself.eta_isentropic
+
+
[docs]defget_eta_mech(self,inputs:Inputs)->float:
+"""
+ Returns the product of the constant mechanical, motor, and inverter efficiencies
+ as the effective mechanical efficiency of the compressor.
+
+ Args:
+ inputs (Inputs): Input parameters for the calculation.
+
+ Returns:
+ float: Effective mechanical efficiency.
+ """
+ returnself.eta_mech
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/components/compressors/rotary.html b/docs/0.1.1/docs/_modules/vclibpy/components/compressors/rotary.html
new file mode 100644
index 0000000..0d8e802
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/components/compressors/rotary.html
@@ -0,0 +1,231 @@
+
+
+
+
+
+
+
+ vclibpy.components.compressors.rotary — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
[docs]classRotaryCompressor(Compressor):
+"""
+ Compressor model based on the thesis of Mirko Engelpracht.
+
+ This compressor is characterized by using regressions provided by Mirko Engelpracht for a family of rotary
+ compressors. The coefficients used in the regressions are sourced from his Master's thesis.
+
+ Parameters:
+ N_max (float): Maximal rotations per second of the compressor.
+ V_h (float): Volume of the compressor in m^3.
+
+ Methods:
+ get_lambda_h(inputs: Inputs) -> float:
+ Returns the volumetric efficiency based on the regressions of Mirko Engelpracht.
+
+ get_eta_isentropic(p_outlet: float, inputs: Inputs) -> float:
+ Returns the isentropic efficiency based on the regressions of Mirko Engelpracht.
+
+ get_eta_mech(inputs: Inputs) -> float:
+ Returns the mechanical efficiency based on the regressions of Mirko Engelpracht.
+
+ """
+
+
[docs]defget_lambda_h(self,inputs:Inputs)->float:
+"""
+ Returns the volumetric efficiency based on the regressions of Mirko Engelpracht.
+
+ Args:
+ inputs (Inputs): Input parameters for the calculation.
+
+ Returns:
+ float: Volumetric efficiency.
+ """
+ p_outlet=self.get_p_outlet()
+ # If not constant value is given, eta_is is calculated based on the regression of Mirko Engelpracht
+ n=self.get_n_absolute(inputs.n)
+ T_1=self.state_inlet.T
+
+ a_1=0.80179
+ a_2=-0.05210
+ sigma_pi=1.63495
+ pi_ave=4.54069
+ a_3=3.21616e-4
+ sigma_T_1=8.43797
+ T_1_ave=263.86428
+ a_4=-0.00494
+ a_5=0.04981
+ sigma_n=20.81378
+ n_ave=64.41071
+ a_6=-0.02190
+
+ pi=p_outlet/self.state_inlet.p
+ return(
+ a_1+
+ a_2*(pi-pi_ave)/sigma_pi+
+ a_3*(T_1-T_1_ave)/sigma_T_1*(pi-pi_ave)/sigma_pi+
+ a_4*(T_1-T_1_ave)/sigma_T_1+
+ a_5*(n-n_ave)/sigma_n+
+ a_6*((n-n_ave)/sigma_n)**2
+ )
+
+
[docs]defget_eta_isentropic(self,p_outlet:float,inputs:Inputs)->float:
+"""
+ Returns the isentropic efficiency based on the regressions of Mirko Engelpracht.
+
+ Args:
+ p_outlet (float): High pressure value.
+ inputs (Inputs): Input parameters for the calculation.
+
+ Returns:
+ float: Isentropic efficiency.
+ """
+ # If not constant value is given, eta_is is calculated based on the regression of Mirko Engelpracht
+ n=self.get_n_absolute(inputs.n)
+
+ a_1=0.5816
+ a_2=0.002604
+ a_3=-1.515e-7
+ a_4=-0.00473
+ pi=p_outlet/self.state_inlet.p
+ eta=(
+ a_1+
+ a_2*n+
+ a_3*n**3+
+ a_4*pi**2
+ )
+ ifeta<=0:
+ raiseValueError("Efficiency is lower or equal to 0")
+ returneta
+
+
[docs]defget_eta_mech(self,inputs:Inputs)->float:
+"""
+ Returns the mechanical efficiency based on the regressions of Mirko Engelpracht.
+
+ Args:
+ inputs (Inputs): Input parameters for the calculation.
+
+ Returns:
+ float: Mechanical efficiency.
+ """
+ p_outlet=self.get_p_outlet()
+ n=self.get_n_absolute(inputs.n)
+ # If not constant value is given, eta_is is calculated based on the regression of Mirko Engelpracht
+ a_00=0.2199
+ a_10=-0.0193
+ a_01=0.02503
+ a_11=8.817e-5
+ a_20=-0.001345
+ a_02=-0.0003382
+ a_21=1.584e-5
+ a_12=-1.083e-6
+ a_22=-5.321e-8
+ a_03=1.976e-6
+ a_13=4.053e-9
+ a_04=-4.292e-9
+ pi=p_outlet/self.state_inlet.p
+ return(
+ a_00+
+ a_10*pi+a_01*n+a_11*pi*n+
+ a_20*pi**2+a_02*n**2+a_21*pi**2*n+a_12*pi*n**2+a_22*pi**2*n**2+
+ a_03*n**3+a_13*pi*n**3+
+ a_04*n**4
+ )
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/components/compressors/ten_coefficient.html b/docs/0.1.1/docs/_modules/vclibpy/components/compressors/ten_coefficient.html
new file mode 100644
index 0000000..360d607
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/components/compressors/ten_coefficient.html
@@ -0,0 +1,471 @@
+
+
+
+
+
+
+
+ vclibpy.components.compressors.ten_coefficient — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
[docs]defcalc_ten_coefficients(T_eva,T_con,coef_list):
+"""
+ Calculate the result using the ten-coefficient method.
+
+ Args:
+ T_eva (float): Evaporator temperature in Celsius.
+ T_con (float): Condenser temperature in Celsius.
+ coef_list (list): List of coefficients.
+
+ Returns:
+ float: Result of the calculation.
+ """
+ # Formula for the ten-coefficient method according to the datasheet
+ z=coef_list[0]+coef_list[1]*T_eva+coef_list[2]*T_con+coef_list[3]*T_eva**2+ \
+ coef_list[4]*T_eva*T_con+coef_list[5]*T_con**2+coef_list[6]*T_eva**3+ \
+ coef_list[7]*T_eva**2*T_con+coef_list[8]*T_con**2*T_eva+coef_list[9]*T_con**3
+ returnz
+
+
+
[docs]classBaseTenCoefficientCompressor(Compressor,ABC):
+"""
+ Base class for compressors using the ten-coefficient method.
+
+ Used table has to be in this format
+ (order of the columns is not important).
+ The values must be the same as in the example tabel.
+ The column names can be different but must
+ then be matched with argument parameter_names.
+ (All typed in numbers are fictional placeholders)
+
+ Capacity(W) Input Power(W) Flow Rate(kg/h) Capacity(W) ... Flow Rate(kg/h)
+ n n1 n1 n1 n2 ... n_last
+ P1 42 12 243 32 ... 412
+ ... ... ... ... ... ... ...
+ P10 10 23 21 41 ... 2434
+
+ Args:
+ N_max (float): Maximal rotations per second of the compressor.
+ V_h (float): Volume of the compressor in m^3.
+ datasheet (str): Path of the datasheet file.
+ **kwargs:
+ parameter_names (dict, optional):
+ Dictionary to match internal parameter names (keys) to the names used in the table values.
+ Default
+ {
+ "m_flow": "Flow Rate(kg/h)",
+ "capacity": "Capacity(W)",
+ "input_power": "Input Power(W)",
+ "eta_s": "Isentropic Efficiency(-)",
+ "lambda_h": "Volumentric Efficiency(-)",
+ "eta_mech": "Mechanical Efficiency(-)"
+ }
+ sheet_name (str, optional): Name of the sheet in the datasheet. Defaults to None.
+ """
+
+ def__init__(self,N_max,V_h,datasheet,**kwargs):
+"""
+ Initialize the BaseTenCoefficientCompressor.
+
+ Args:
+ N_max (float): Maximal rotations per second of the compressor.
+ V_h (float): Volume of the compressor in m^3.
+ datasheet (str): Path of the datasheet file.
+ parameter_names (dict, optional): Dictionary of parameter names. Defaults to None.
+ sheet_name (str, optional): Name of the sheet in the datasheet. Defaults to None.
+ """
+
+ super().__init__(N_max,V_h)
+ sheet_name=kwargs.get('sheet_name',None)
+ self.md=pd.read_excel(datasheet,sheet_name=sheet_name)
+ parameter_names=kwargs.get('parameter_names',None)
+ ifparameter_namesisNone:
+ self.parameter_names={
+ "m_flow":"Flow Rate(kg/h)",
+ "capacity":"Capacity(W)",
+ "input_power":"Input Power(W)",
+ "eta_s":"Isentropic Efficiency(-)",
+ "lambda_h":"Volumentric Efficiency(-)",
+ "eta_mech":"Mechanical Efficiency(-)"
+ }
+ else:
+ self.parameter_names=parameter_names
+
+
[docs]defget_parameter(self,T_eva,T_con,n,type_):
+"""
+ Get a parameter based on temperatures, rotations, and parameter type from the datasheet.
+
+ Args:
+ T_eva (float): Evaporator temperature in Celsius.
+ T_con (float): Condenser temperature in Celsius.
+ n (float): Rotations per second.
+ type_ (str): Parameter type in parameter_names.
+
+ Returns:
+ float: Interpolated parameter value.
+ """
+ param_list=[]
+ n_list=[]
+
+ sampling_points=sum(
+ self.parameter_names[type_]insforsinlist(self.md.columns.values))# counts number of sampling points
+
+ foriinrange(sampling_points):
+ ifi==0:
+ coefficients=self.md[self.parameter_names[type_]].tolist()
+ else:
+ coefficients=self.md[str(self.parameter_names[type_]+"."+str(i))].tolist()
+ n_list.append(coefficients.pop(0))
+ param_list.append(calc_ten_coefficients(T_eva,T_con,coefficients))
+
+ returnnp.interp(self.get_n_absolute(n),n_list,param_list)# linear interpolation
+
+
+
[docs]classTenCoefficientCompressor(BaseTenCoefficientCompressor):
+"""
+ Compressor based on the ten coefficient method.
+
+ Used table has to be in this format
+ (order of the columns is not important).
+ The values must be the same as in the example tabel.
+ The column names can be different but must
+ then be matched with the keyword argument parameter_names.
+ (All typed in numbers are fictional placeholders)
+
+ Capacity(W) Input Power(W) Flow Rate(kg/h) Capacity(W) ... Flow Rate(kg/h)
+ n n1 n1 n1 n2 ... n_last
+ P1 42 12 243 32 ... 412
+ ... ... ... ... ... ... ...
+ P10 10 23 21 41 ... 2434
+
+ T_sh and T_sc have to be set according to the data sheet of your compressor. capacity_definition defines the
+ parameter "capacity" in the datasheet. If capacity is the specific cooling capacity (h1-h4), set it on "cooling".
+ If capacity is the specific heating capacity (h2-h3), set it on "heating".
+ In the case of cooling capacity, the mechanical efficiency of the compressor has to be assumed (assumed_eta_mech)
+ as h2 can't be calculated otherwise. Summary:
+ - For the case heating capacity: h2 = h3 + capacity / m_flow
+ - For the case cooling capacity: h2 = h3 + (capacity + p_el * assumed_eta_mech) / m_flow
+
+ Args:
+ N_max (float): Maximal rotations per second of the compressor.
+ V_h (float): Volume of the compressor in m^3.
+ T_sc (float): Subcooling according to datasheet in K.
+ T_sh (float): Superheating according to datasheet in K.
+ capacity_definition (str): Definition of "capacity" in the datasheet. "cooling" or "heating".
+ assumed_eta_mech (float): Assumed mechanical efficiency of the compressor (only needed if cooling).
+ datasheet (str): Path of the modified datasheet.
+ **kwargs:
+ parameter_names (dict, optional):
+ Dictionary to match internal parameter names (keys) to the names used in the table values.
+ Default
+ {
+ "m_flow": "Flow Rate(kg/h)",
+ "capacity": "Capacity(W)",
+ "input_power": "Input Power(W)"
+ }
+ sheet_name (str, optional): Name of the sheet in the datasheet. Defaults to None.
+ """
+
+ def__init__(self,N_max,V_h,T_sc,T_sh,capacity_definition,assumed_eta_mech,datasheet,**kwargs):
+ super().__init__(N_max=N_max,V_h=V_h,datasheet=datasheet,**kwargs)
+ self.T_sc=T_sc
+ self.T_sh=T_sh
+ ifcapacity_definitionnotin["cooling","heating"]:
+ raiseValueError("capacity_definition has to be either 'heating' or 'cooling'")
+ self._capacity_definition=capacity_definition
+ self.assumed_eta_mech=assumed_eta_mech
+ self.datasheet=datasheet
+
+
[docs]defget_lambda_h(self,inputs:Inputs):
+"""
+ Get the volumetric efficiency.
+
+ Args:
+ inputs (Inputs): Input parameters.
+
+ Returns:
+ float: Volumetric efficiency.
+ """
+ p_outlet=self.get_p_outlet()
+
+ n_abs=self.get_n_absolute(inputs.n)
+ T_eva=self.med_prop.calc_state("PQ",self.state_inlet.p,1).T-273.15# [°C]
+ T_con=self.med_prop.calc_state("PQ",p_outlet,0).T-273.15# [°C]
+
+ ifround((self.state_inlet.T-T_eva-273.15),2)!=round(self.T_sh,2):
+ warnings.warn("The superheating of the given state is not "
+ "equal to the superheating of the datasheet. "
+ "State1.T_sh= "+str(round((self.state_inlet.T-T_eva-273.15),2))+
+ ". Datasheet.T_sh = "+str(self.T_sh))
+ # The datasheet has a given superheating temperature which can
+ # vary from the superheating of the real state 1
+ # which is given by the user.
+ # Thus a new self.state_inlet_datasheet has to
+ # be defined for all further calculations
+ state_inlet_datasheet=self.med_prop.calc_state("PT",self.state_inlet.p,T_eva+273.15+self.T_sh)
+
+ m_flow=self.get_parameter(T_eva,T_con,inputs.n,"m_flow")/3600# [kg/s]
+
+ lambda_h=m_flow/(n_abs*state_inlet_datasheet.d*self.V_h)
+ returnlambda_h
[docs]classDataSheetCompressor(BaseTenCoefficientCompressor):
+"""
+ Compressor based on the ten coefficient method.
+
+ Used table has to be in this format
+ (order of the columns is not important).
+ The values must be the same as in the example tabel.
+ The column names can be different but must
+ then be matched with the keyword argument parameter_names.
+ (All typed in numbers are fictional placeholders)
+
+ Isentropic Volumetric Mechanical Isentropic Mechanical
+ Efficiency(-) Efficiency(-) Efficiency(-) Efficiency(-) ... Efficiency(-)
+ n n1 n1 n1 n2 ... n_last
+ P1 42 12 243 32 ... 412
+ ... ... ... ... ... ... ...
+ P10 10 23 21 41 ... 2434
+
+ Args:
+ N_max (float): Maximal rotations per second of the compressor.
+ V_h (float): Volume of the compressor in m^3.
+ datasheet (str): Path of the datasheet file.
+ **kwargs:
+ parameter_names (dict, optional):
+ Dictionary to match internal parameter names (keys) to the names used in the table values.
+ Default
+ {
+ "eta_s": "Isentropic Efficiency(-)",
+ "lambda_h": "Volumetric Efficiency(-)",
+ "eta_mech": "Mechanical Efficiency(-)"
+ }
+ sheet_name (str, optional): Name of the sheet in the datasheet. Defaults to None.
+ """
+
+ def__init__(self,N_max,V_h,datasheet,**kwargs):
+ super().__init__(N_max=N_max,V_h=V_h,datasheet=datasheet,**kwargs)
+
+
Source code for vclibpy.components.expansion_valves.expansion_valve
+"""
+Module with classes for EV models.
+They are not used for the calculation.
+They may be used to see if the output is correct.
+"""
+importabc
+
+fromvclibpy.components.componentimportBaseComponent
+
+
+
[docs]classExpansionValve(BaseComponent,abc.ABC):
+"""Base class for an expansion valve.
+
+ Args:
+ A (float): Cross-sectional area of the expansion valve.
+ """
+
+ def__init__(self,A):
+ super().__init__()
+ self.A=A# Cross-sectional area of the expansion valve
+
+
[docs]@abc.abstractmethod
+ defcalc_m_flow_at_opening(self,opening)->float:
+"""
+ Calculate the mass flow rate for the given opening.
+
+ Args:
+ opening (float): Opening of valve between 0 and 1
+
+ Returns:
+ float: Mass flow rate in kg/s
+ """
+ raiseNotImplementedError
+
+
[docs]@abc.abstractmethod
+ defcalc_opening_at_m_flow(self,m_flow,**kwargs)->float:
+"""
+ Calculate the opening for the given mass flow rate
+
+ Args:
+ m_flow (float): Mass flow rate in kg/s
+ **kwargs: Possible keyword arguments for child classes
+
+ Returns:
+ float: Opening
+ """
+ raiseNotImplementedError
[docs]classVaporInjectionEconomizerNTU(BasicNTU):
+"""
+ Economizer heat exchanger which is NTU based.
+ Used only for vapor injection cycles, as
+ calculations are purely based for two-phase
+ and liquid estimations.
+
+ See parent class for more arguments.
+
+ Assumptions:
+
+ - Default `flow_type` is counter_flow.
+ - Default `ratio_outer_to_inner_area` is 1, as
+ - pipes are nearly same diameter and length
+ - Secondary heat transfer for alpha is disabled; gas,
+ liquid and two-phase models are used for both sides.
+ """
+
+ def__init__(self,**kwargs):
+ kwargs.pop("secondary_heat_transfer",None)
+ kwargs.pop("secondary_medium",None)
+ self._state_two_phase_outlet=None
+ self._state_two_phase_inlet=None
+ super().__init__(
+ flow_type=kwargs.pop("flow_type","counter"),
+ secondary_heat_transfer="None",
+ secondary_medium="None",
+ ratio_outer_to_inner_area=kwargs.pop("ratio_outer_to_inner_area",1),
+ **kwargs)
+
+ @property
+ defstate_two_phase_inlet(self)->ThermodynamicState:
+ returnself._state_two_phase_inlet
+
+ @state_two_phase_inlet.setter
+ defstate_two_phase_inlet(self,state_inlet:ThermodynamicState):
+ self._state_two_phase_inlet=state_inlet
+
+ @property
+ defstate_two_phase_outlet(self)->ThermodynamicState:
+ returnself._state_two_phase_outlet
+
+ @state_two_phase_outlet.setter
+ defstate_two_phase_outlet(self,state_outlet:ThermodynamicState):
+ self._state_two_phase_outlet=state_outlet
+
+
[docs]defcalc(self,inputs,fs_state)->(float,float):
+ raiseNotImplementedError("Could be moved from VaporInjectionEconomizer")
[docs]classHeatExchanger(BaseComponent,abc.ABC):
+"""
+ Class for a heat exchanger.
+
+ Args:
+ A (float):
+ Area of HE in m^2 for NTU calculation
+ secondary_medium (str):
+ Name for secondary medium, e.g. `water` or `air`
+ wall_heat_transfer (HeatTransfer):
+ Model for heat transfer inside wall
+ secondary_heat_transfer (HeatTransfer):
+ Model for heat transfer from secondary medium to wall
+ gas_heat_transfer (HeatTransfer):
+ Model for heat transfer from refrigerant gas to wall
+ liquid_heat_transfer (HeatTransfer):
+ Model for heat transfer from refrigerant liquid to wall
+ two_phase_heat_transfer (TwoPhaseHeatTransfer):
+ Model for heat transfer from refrigerant two phase to wall
+ """
+
+ def__init__(
+ self,
+ A:float,
+ wall_heat_transfer:HeatTransfer,
+ secondary_heat_transfer:HeatTransfer,
+ gas_heat_transfer:HeatTransfer,
+ liquid_heat_transfer:HeatTransfer,
+ two_phase_heat_transfer:TwoPhaseHeatTransfer,
+ secondary_medium:str
+ ):
+ super().__init__()
+ self.A=A
+ self.secondary_medium=secondary_medium.lower()
+
+ self._wall_heat_transfer=wall_heat_transfer
+ self._secondary_heat_transfer=secondary_heat_transfer
+ self._gas_heat_transfer=gas_heat_transfer
+ self._liquid_heat_transfer=liquid_heat_transfer
+ self._two_phase_heat_transfer=two_phase_heat_transfer
+
+ self.med_prop_sec=None# Later start in start_secondary_med_prop
+ self._m_flow_secondary=None
+ self._secondary_cp=0# Allow initial calculation of _m_flow_secondary_cp if cp is not set
+ self._m_flow_secondary_cp=0
+
+
[docs]defstart_secondary_med_prop(self):
+"""
+ Set up the wrapper for the secondary medium's media properties.
+ """
+ # Set up the secondary_medium wrapper:
+ med_prop_class,med_prop_kwargs=media.get_global_med_prop_and_kwargs()
+ ifself.secondary_medium=="air"andmed_prop_class==media.RefProp:
+ fluid_name="AIR.PPF"
+ else:
+ fluid_name=self.secondary_medium
+ ifself.med_prop_secisnotNone:
+ ifself.med_prop_sec.fluid_name==fluid_name:
+ return
+ self.med_prop_sec.terminate()
+ self.med_prop_sec=med_prop_class(fluid_name=self.secondary_medium,**med_prop_kwargs)
[docs]@abc.abstractmethod
+ defcalc(self,inputs:Inputs,fs_state:FlowsheetState)->Tuple[float,float]:
+"""
+ Calculate the heat exchanger based on the given inputs.
+
+ The flowsheet state can be used to save important variables
+ during calculation for later analysis.
+
+ Both return values are used to check if the heat transfer is valid or not.
+
+ Args:
+ inputs (Inputs): The inputs for the calculation.
+ fs_state (FlowsheetState): The flowsheet state to save important variables.
+
+ Returns:
+ Tuple[float, float]:
+ error: Error in percentage between the required and calculated heat flow rates.
+ dT_min: Minimal temperature difference (can be negative).
+ """
+ raiseNotImplementedError
+
+
[docs]defcalc_alpha_two_phase(self,state_q0,state_q1,inputs:Inputs,fs_state:FlowsheetState)->float:
+"""
+ Calculate the two-phase heat transfer coefficient.
+
+ Args:
+ state_q0: State at vapor quality 0.
+ state_q1: State at vapor quality 1.
+ inputs (Inputs): The inputs for the calculation.
+ fs_state (FlowsheetState): The flowsheet state to save important variables.
+
+ Returns:
+ float: The two-phase heat transfer coefficient.
+ """
+ returnself._two_phase_heat_transfer.calc(
+ state_q0=state_q0,
+ state_q1=state_q1,
+ inputs=inputs,
+ fs_state=fs_state,
+ m_flow=self.m_flow,
+ med_prop=self.med_prop,
+ state_inlet=self.state_inlet,
+ state_outlet=self.state_outlet
+ )
+
+
[docs]defcalc_alpha_liquid(self,transport_properties)->float:
+"""
+ Calculate the liquid-phase heat transfer coefficient.
+
+ Args:
+ transport_properties: Transport properties for the liquid phase.
+
+ Returns:
+ float: The liquid-phase heat transfer coefficient.
+ """
+ returnself._liquid_heat_transfer.calc(
+ transport_properties=transport_properties,
+ m_flow=self.m_flow
+ )
+
+
[docs]defcalc_alpha_gas(self,transport_properties)->float:
+"""
+ Calculate the gas-phase heat transfer coefficient.
+
+ Args:
+ transport_properties: Transport properties for the gas phase.
+
+ Returns:
+ float: The gas-phase heat transfer coefficient.
+ """
+ returnself._gas_heat_transfer.calc(
+ transport_properties=transport_properties,
+ m_flow=self.m_flow
+ )
+
+
[docs]defcalc_alpha_secondary(self,transport_properties)->float:
+"""
+ Calculate the secondary-medium heat transfer coefficient.
+
+ Args:
+ transport_properties: Transport properties for the secondary medium.
+
+ Returns:
+ float: The secondary-medium heat transfer coefficient.
+ """
+ returnself._secondary_heat_transfer.calc(
+ transport_properties=transport_properties,
+ m_flow=self.m_flow_secondary
+ )
+
+
[docs]defcalc_wall_heat_transfer(self)->float:
+"""
+ Calculate the heat transfer coefficient inside the wall.
+
+ Returns:
+ float: The wall heat transfer coefficient.
+ """
+ # Arguments are not required
+ returnself._wall_heat_transfer.calc(
+ transport_properties=media.TransportProperties(),
+ m_flow=0
+ )
[docs]defcalc_secondary_cp(self,T:float,p=None):
+"""
+ Calculate and set the heat capacity rate m_flow_cp of the secondary medium.
+
+ Args:
+ T (float): Temperature of the secondary medium.
+ p (float, optional): Pressure of the secondary medium. Defaults to None.
+ """
+ self._secondary_cp=self.calc_transport_properties_secondary_medium(T=T,p=p).cp
+ self._m_flow_secondary_cp=self.m_flow_secondary*self._secondary_cp
[docs]defcalc_Q_flow(self)->float:
+"""
+ Calculate the total heat flow rate.
+
+ Returns:
+ float: The total heat flow rate.
+ """
+ returnself.m_flow*abs(self.state_inlet.h-self.state_outlet.h)
+
+
[docs]defcalc_transport_properties_secondary_medium(self,T,p=None)->media.TransportProperties:
+"""
+ Calculate the transport properties for the selected secondary_medium.
+
+ Args:
+ T (float): Temperature in K.
+ p (float, optional): Pressure to use. Defaults to None.
+
+ Returns:
+ media.TransportProperties: The calculated transport properties.
+ """
+ ifpisNone:
+ ifself.secondary_medium=="water":
+ p=2e5# 2 bar (default hydraulic pressure)
+ elifself.secondary_medium=="air":
+ p=101325# 1 atm
+ else:
+ raiseNotImplementedError(
+ "Default pressures for secondary_mediums aside from water and air are not supported yet."
+ )
+ # Calc state
+ state=self.med_prop_sec.calc_state("PT",p,T)
+ # Return properties
+ returnself.med_prop_sec.calc_transport_properties(state)
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/heat_transfer/air_to_wall.html b/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/heat_transfer/air_to_wall.html
new file mode 100644
index 0000000..d25eb33
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/heat_transfer/air_to_wall.html
@@ -0,0 +1,215 @@
+
+
+
+
+
+
+
+ vclibpy.components.heat_exchangers.heat_transfer.air_to_wall — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
[docs]classAirToWallTransfer(HeatTransfer,abc.ABC):
+"""
+ Heat transfer model for air to wall.
+
+ Args:
+ A_cross (float):
+ Cross-section area in m2.
+ characteristic_length (float):
+ Length in m to calculate the similitude approach for the
+ heat transfer from secondary_medium -> wall.
+ """
+
+ def__init__(self,A_cross:float,characteristic_length:float):
+ self.A_cross=A_cross
+ self.characteristic_length=characteristic_length
+
+
[docs]defcalc(self,transport_properties:TransportProperties,m_flow:float)->float:
+"""
+ Heat transfer coefficient from air to the wall of the heat exchanger.
+ The flow is assumed to be always laminar.
+
+ Returns:
+ float: Heat transfer coefficient in W/(m^2*K).
+ """
+ Re=self.calc_reynolds(dynamic_viscosity=transport_properties.dyn_vis,m_flow=m_flow)
+ alpha_sec=self.calc_laminar_area_nusselt(Re,transport_properties.Pr,lambda_=transport_properties.lam)
+ returnalpha_sec
+
+
[docs]@abc.abstractmethod
+ defcalc_reynolds(self,dynamic_viscosity:float,m_flow:float):
+"""
+ Calculate the reynolds number of the given flow rate.
+
+ Args:
+ dynamic_viscosity (float): Dynamic viscosity.
+ m_flow (float): Mass flow rate.
+
+ Returns:
+ float: Reynolds number
+ """
+ raiseNotImplementedError
+
+
[docs]@abc.abstractmethod
+ defcalc_laminar_area_nusselt(self,Re,Pr,lambda_):
+"""
+ Calculate the Nusselt number for laminar heat transfer
+ on an area (Used for Air->Wall in the evaporator).
+
+ Args:
+ Re (float): Reynolds number
+ Pr (float): Prandlt number
+ lambda_ (float): Lambda of air
+
+ Returns:
+ float: Nusselt number
+ """
+ raiseNotImplementedError
+
+
+
[docs]classWSUAirToWall(AirToWallTransfer):
+"""
+ Class to implement the heat transfer calculations
+ based on the WSÜ-Script at the RWTH.
+ """
+
+
[docs]defcalc_reynolds(self,dynamic_viscosity:float,m_flow:float)->float:
+"""
+ Calculate the Reynolds number on air side.
+
+ Args:
+ dynamic_viscosity (float): Dynamic viscosity of air.
+ m_flow (float): Mass flow rate of air.
+
+ Returns:
+ float: Reynolds number.
+ """
+ velocity_times_dens=m_flow/self.A_cross
+ return(velocity_times_dens*self.characteristic_length)/dynamic_viscosity
+
+
[docs]defcalc_laminar_area_nusselt(self,Re,Pr,lambda_)->float:
+"""
+ Calculate the Nusselt number for laminar heat transfer
+ on an area (Used for Air->Wall in the evaporator).
+
+ Args:
+ Re (float): Reynolds number of air.
+ Pr (float): Prandtl number of air.
+ lambda_ (float): Lambda of air.
+
+ Returns:
+ float: Nusselt number of air.
+ """
+ const_fac=0.664
+ exp_rey=0.5
+ exp_pra=1/3
+ ifRe>2e5:
+ raiseValueError(f"Given Re {Re} is outside of allowed bounds for WSÜ-Script")
+ ifPr>10orPr<0.6:
+ raiseValueError(f"Given Pr {Pr} is outside of allowed bounds for WSÜ-Script")
+ Nu=const_fac*Re**exp_rey*Pr**exp_pra
+ returnNu*lambda_/self.characteristic_length
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/heat_transfer/constant.html b/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/heat_transfer/constant.html
new file mode 100644
index 0000000..8afc932
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/heat_transfer/constant.html
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+ vclibpy.components.heat_exchangers.heat_transfer.constant — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Source code for vclibpy.components.heat_exchangers.heat_transfer.constant
+"""
+Module with constant heat transfer assumptions
+"""
+importabc
+
+fromvclibpy.mediaimportTransportProperties
+
+
+
[docs]classConstantHeatTransfer(abc.ABC):
+"""
+ Constant heat transfer assumption
+
+ Args:
+ alpha (float):
+ Constant heat transfer coefficient in W/(m2*K)
+ """
+
+ def__init__(self,alpha:float):
+ self.alpha=alpha
+
+
[docs]defcalc(self,transport_properties:TransportProperties,m_flow:float)->float:
+"""
+ Calculate constant heat transfer coefficient.
+
+ Args:
+ transport_properties (TransportProperties): Transport properties of the medium (not used).
+ m_flow (float): Mass flow rate (not used).
+
+ Returns:
+ float: Constant heat transfer coefficient in W/(m2*K).
+
+ """
+ returnself.alpha
+
+
+
[docs]classConstantTwoPhaseHeatTransfer(abc.ABC):
+"""
+ Constant heat transfer assumption.
+
+ Args:
+ alpha (float):
+ Constant heat transfer coefficient in W/(m2*K).
+ """
+
+ def__init__(self,alpha:float):
+ self.alpha=alpha
+
+
[docs]defcalc(self,**kwargs)->float:
+"""
+ Calculate constant two-phase heat transfer coefficient.
+
+ Args:
+ **kwargs: Allows to set arguments for different heat transfer classes which are not used here.
+
+ Returns:
+ float: Constant two-phase heat transfer coefficient in W/(m2*K).
+
+ """
+ returnself.alpha
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/heat_transfer/heat_transfer.html b/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/heat_transfer/heat_transfer.html
new file mode 100644
index 0000000..6203caf
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/heat_transfer/heat_transfer.html
@@ -0,0 +1,196 @@
+
+
+
+
+
+
+
+ vclibpy.components.heat_exchangers.heat_transfer.heat_transfer — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Source code for vclibpy.components.heat_exchangers.heat_transfer.heat_transfer
+"""
+Module with basic functions to calculate heat transfer coefficients.
+"""
+importabc
+
+importnumpyasnp
+
+fromvclibpy.mediaimportTransportProperties,ThermodynamicState,MedProp
+fromvclibpy.datamodelsimportFlowsheetState,Inputs
+
+
+
[docs]classHeatTransfer(abc.ABC):
+"""
+ Base class to implement possible heat transfer models.
+
+ Methods:
+ calc(transport_properties: TransportProperties, m_flow: float) -> float:
+ Abstract method to calculate heat transfer.
+
+ """
+
+
[docs]@abc.abstractmethod
+ defcalc(self,transport_properties:TransportProperties,m_flow:float)->float:
+"""
+ Calculate heat transfer.
+
+ Args:
+ transport_properties (TransportProperties): Transport properties of the medium.
+ m_flow (float): Mass flow rate.
+
+ Returns:
+ float: Calculated heat transfer coefficient.
+
+ Raises:
+ NotImplementedError: If the method is not implemented in the subclass.
+ """
+ raiseNotImplementedError
+
+
+
[docs]classTwoPhaseHeatTransfer(abc.ABC):
+"""
+ Base class to implement possible heat transfer models
+ """
+
+
[docs]@abc.abstractmethod
+ defcalc(
+ self,
+ state_q0:ThermodynamicState,
+ state_q1:ThermodynamicState,
+ state_inlet:ThermodynamicState,
+ state_outlet:ThermodynamicState,
+ med_prop:MedProp,
+ inputs:Inputs,
+ fs_state:FlowsheetState,
+ m_flow:float
+ )->float:
+"""
+ Calculate two-phase heat transfer.
+
+ Args:
+ state_q0 (ThermodynamicState): Thermodynamic state at the beginning of the two-phase region.
+ state_q1 (ThermodynamicState): Thermodynamic state at the end of the two-phase region.
+ state_inlet (ThermodynamicState): Inlet thermodynamic state.
+ state_outlet (ThermodynamicState): Outlet thermodynamic state.
+ med_prop (MedProp): Medium properties class.
+ inputs (Inputs): Input parameters.
+ fs_state (FlowsheetState): Flowsheet state.
+ m_flow (float): Mass flow rate.
+
+ Returns:
+ float: Calculated two-phase heat transfer coefficient.
+
+ Raises:
+ NotImplementedError: If the method is not implemented in the subclass.
+ """
+ raiseNotImplementedError
+
+
+
[docs]defcalc_reynolds_pipe(dynamic_viscosity:float,m_flow:float,characteristic_length:float)->float:
+"""
+ Calculate Reynolds number for flow inside a pipe.
+
+ Args:
+ dynamic_viscosity (float): Dynamic viscosity of the fluid.
+ m_flow (float): Mass flow rate.
+ characteristic_length (float): Characteristic length (e.g., diameter) of the pipe.
+
+ Returns:
+ float: Reynolds number.
+
+ """
+ return4*m_flow/(np.pi*characteristic_length*dynamic_viscosity)
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/heat_transfer/pipe_to_wall.html b/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/heat_transfer/pipe_to_wall.html
new file mode 100644
index 0000000..acc909f
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/heat_transfer/pipe_to_wall.html
@@ -0,0 +1,203 @@
+
+
+
+
+
+
+
+ vclibpy.components.heat_exchangers.heat_transfer.pipe_to_wall — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Source code for vclibpy.components.heat_exchangers.heat_transfer.pipe_to_wall
+"""
+Module with models for pipe-to-wall heat transfer.
+"""
+
+from.heat_transferimportHeatTransfer,calc_reynolds_pipe
+fromvclibpy.mediaimportTransportProperties
+
+
+
[docs]classTurbulentFluidInPipeToWallTransfer(HeatTransfer):
+"""
+ Class to model turbulent heat exchange in a pipe.
+
+ Args:
+ method (str):
+ Equation to calc the nusselt number of turbulent flow for
+ a given Re and Pr number.
+ Note: Just for one-phase heat transfer!!
+ Implemented Options are:
+
+ - Taler2016
+ - Domanski1989_sp_smooth
+ - Amalfi2016
+ - ScriptWSÜ. For turbulent regimes, eta_by_eta_w is assumed to be one.
+
+ Refer to the paper / documents or the function in this class for more
+ info on numbers and assumptions
+ characteristic_length (float):
+ Length to calculate the similitude approach for the
+ heat transfer from ref -> wall. For heat pumps this is
+ always the Diameter of the HE in m
+ """
+
+ def__init__(self,method:str,characteristic_length:float):
+ self.method=method
+ self.characteristic_length=characteristic_length
+
+
[docs]defcalc(self,transport_properties:TransportProperties,m_flow:float)->float:
+"""
+ Calculate heat transfer coefficient from refrigerant to the wall of the heat exchanger.
+
+ The flow is assumed to be always turbulent and is based on a calibrated
+ Nusselt correlation.
+
+ Args:
+ transport_properties (TransportProperties): Transport properties of the fluid.
+ m_flow (float): Mass flow rate of the fluid.
+
+ Returns:
+ float: Heat transfer coefficient from refrigerant to HE in W/(m^2*K).
+ """
+ Re=calc_reynolds_pipe(
+ characteristic_length=self.characteristic_length,
+ dynamic_viscosity=transport_properties.dyn_vis,
+ m_flow=m_flow
+ )
+ Nu=self.calc_turbulent_tube_nusselt(Re,transport_properties.Pr)
+ returnNu*transport_properties.lam/self.characteristic_length
+
+
[docs]defcalc_turbulent_tube_nusselt(self,Re,Pr)->float:
+"""
+ Calculate the Nuseelt number for turbulent heat transfer
+ in a tube (used for ref/water->Wall in the evaporator and condernser).
+
+ Args:
+ Re (float): Reynolds number.
+ Pr (float): Prandtl number.
+
+ Returns:
+ float: Nusselt number.
+ """
+ ifself.method=="Taler2016":
+ ifRe<3e3orRe>1e6:
+ raiseValueError(f"Given Re {Re} is outside of allowed bounds for method {self.method}")
+ if0.1<=Pr<=1:
+ return0.02155*Re**0.8018*Pr**0.7095
+ elif1<Pr<=3:
+ return0.02155*Re**0.8018*Pr**0.7095
+ elif3<Pr<=1000:
+ return0.02155*Re**0.8018*Pr**0.7095
+ else:
+ raiseValueError(f"Given Pr {Pr} is outside of allowed bounds for method {self.method}")
+ elifself.method=="ScriptWSÜ":
+ ifRe<3000orRe>1e5:
+ raiseValueError(f"Given Re {Re} is outside of allowed bounds for method {self.method}")
+ eta_by_eta_w=1
+ return0.027*Re**0.8**Pr**(1/3)*eta_by_eta_w**0.14
+ elifself.method=="Amalfi2016":
+ ifRe<=700:
+ Nu=(0.0295*Pr-0.115)*Re**0.954
+ else:
+ Nu=(1.760*Pr-5.391)*Re**0.262
+ ifNu<0:
+ raiseValueError(f"Given Pr {Pr} is outside of allowed bounds for method {self.method}")
+ returnNu
+ elifself.method=="Domanski1989_sp_smooth":
+ # According to Domanski 1989 for singular phase and smooth surfaces
+ return0.023*Re**0.8*Pr**0.4
+ else:
+ raiseTypeError(f"Method {self.method} not supported!")
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/heat_transfer/vdi_atlas_air_to_wall.html b/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/heat_transfer/vdi_atlas_air_to_wall.html
new file mode 100644
index 0000000..5bc668b
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/heat_transfer/vdi_atlas_air_to_wall.html
@@ -0,0 +1,272 @@
+
+
+
+
+
+
+
+ vclibpy.components.heat_exchangers.heat_transfer.vdi_atlas_air_to_wall — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
[docs]@dataclass
+classAirSourceHeatExchangerGeometry:
+"""
+ Geometry of a fin and tube heat exchanger with two rows of pipes in a shifted arrangement.
+
+ Source: VDI-Wärmeatlas, Berechnungsblätter für den Wärmeübergang, 11. Auflage, S.1461
+
+ As the source is in German, the names are kept in German as well.
+ """
+ t_l:float# Achsabstand der Rohre in Luftrichtung in m
+ t_q:float# Achsabstand der Rohre quer zur Luftrichtung in m
+ tiefe:float# Tiefe der Rippe gesamt
+ d_a:float# Äußerer Rohrdurchmesser
+ d_i:float# Innener Rohrdurchmesser
+ lambda_R:float# Wärmeleitfähigkeit Material der Wand
+ n_Rohre:int=50
+ n_Rippen:int=500
+ a:float=1.95e-3
+ dicke_rippe:float=0.05e-3
+ laenge:float=1.040
+ hoehe:float=0.64
+
+ @property
+ defchar_length(self)->float:
+"""Return the characteristic length d_a."""
+ returnself.d_a
+
+ @property
+ defA_Rippen(self)->float:
+"""Return the total surface area of the fins."""
+ return2*self.n_Rippen*(self.tiefe*self.hoehe-self.n_Rohre*np.pi*0.25*self.d_a**2)
+
+ @property
+ defA(self)->float:
+"""Total air side heat transfer area."""
+ returnself.A_Rippen+self.A_FreieOberflaecheRohr
+
+ @property
+ defA_FreieOberflaecheRohr(self)->float:
+"""Free air side area of the tubes."""
+ returnself.n_Rippen*np.pi*self.d_a*self.a*self.n_Rohre
+
+ @property
+ defA_RohrInnen(self)->float:
+"""Total refrigerant heat transfer area."""
+ returnself.laenge*self.d_i*np.pi*self.n_Rohre
+
+ @property
+ defA_RohrUnberippt(self)->float:
+"""Total outside area of the tubes without fins"""
+ returnself.laenge*self.d_a*np.pi*self.n_Rohre
+
+ @property
+ defverjuengungsfaktor(self)->float:
+"""Reduction factor A_cross_free/A_cross_smallest"""
+ return((self.hoehe*self.laenge)/
+ (self.hoehe*self.laenge-
+ self.hoehe*self.n_Rippen*self.dicke_rippe-
+ self.d_a*self.n_Rohre/2*(self.laenge-self.n_Rippen*self.dicke_rippe)))
+
+ @property
+ defphi(self)->float:
+"""Auxiliary variable for fin efficiency"""
+ ifself.t_l>=0.5*self.t_q:
+ b_r=self.t_q
+ else:
+ b_r=2*self.t_l
+ l_r=np.sqrt(self.t_l**2+self.t_q**2/4)
+ phi_strich=1.27*b_r/self.d_a*np.sqrt(l_r/b_r-0.3)
+ return(phi_strich-1)*(1+0.35*np.log(phi_strich))
+
+
[docs]defeta_R(self,alpha_R)->float:
+"""
+ Calculate fin efficiency.
+
+ Args:
+ alpha_R (float): Average heat transfer coefficient for tube and fin.
+
+ Returns:
+ float: Fin efficiency.
+ """
+ X=self.phi*self.d_a/2*np.sqrt(2*alpha_R/(self.lambda_R*self.dicke_rippe))
+ return1/X*(np.exp(X)-np.exp(-X))/(np.exp(X)+np.exp(-X))
+
+
[docs]defalpha_S(self,alpha_R)->float:
+"""
+ Calculate apparent heat transfer coefficient.
+
+ Args:
+ alpha_R (float): Average heat transfer coefficient for tube and fin.
+
+ Returns:
+ float: Apparent heat transfer coefficient.
+ """
+ A_R_to_A=self.A_Rippen/self.A
+ returnalpha_R*(1-(1-self.eta_R(alpha_R))*A_R_to_A)
+
+
+
[docs]classVDIAtlasAirToWallTransfer(AirToWallTransfer):
+"""
+ VDI-Atlas based heat transfer estimation.
+ Check `AirToWallTransfer` for further argument options.
+
+ This class assumes two pipes with a shifted arrangement.
+
+ Args:
+ A_cross (float): Free cross-sectional area.
+ characteristic_length (float): Characteristic length d_a.
+ geometry_parameters (AirSourceHeatExchangerGeometry):
+ Geometry parameters for heat exchanger according to VDI-Atlas
+ """
+
+ def__init__(self,
+ A_cross:float,characteristic_length:float,
+ geometry_parameters:AirSourceHeatExchangerGeometry):
+ super().__init__(A_cross=A_cross,characteristic_length=characteristic_length)
+ self.geometry_parameters=geometry_parameters
+
+
[docs]classMovingBoundaryNTU(BasicNTU,abc.ABC):
+"""
+ Moving boundary NTU based heat exchanger.
+
+ See parent classe for arguments.
+ """
+
+
[docs]defseparate_phases(self,state_max:ThermodynamicState,state_min:ThermodynamicState,p:float):
+"""
+ Separates a flow with possible phase changes into three parts:
+ subcooling (sc), latent phase change (lat), and superheating (sh)
+ at the given pressure.
+
+ Args:
+ state_max (ThermodynamicState): State with higher enthalpy.
+ state_min (ThermodynamicState): State with lower enthalpy.
+ p (float): Pressure of phase change.
+
+ Returns:
+ Tuple[float, float, float, ThermodynamicState, ThermodynamicState]:
+ Q_sc: Heat for subcooling.
+ Q_lat: Heat for latent phase change.
+ Q_sh: Heat for superheating.
+ state_q0: State at vapor quality 0 and the given pressure.
+ state_q1: State at vapor quality 1 and the given pressure.
+ """
+ # Get relevant states:
+ state_q0=self.med_prop.calc_state("PQ",p,0)
+ state_q1=self.med_prop.calc_state("PQ",p,1)
+ Q_sc=max(0.0,
+ min((state_q0.h-state_min.h),
+ (state_max.h-state_min.h)))*self.m_flow
+ Q_lat=max(0.0,
+ (min(state_max.h,state_q1.h)-
+ max(state_min.h,state_q0.h)))*self.m_flow
+ Q_sh=max(0.0,
+ min((state_max.h-state_q1.h),
+ (state_max.h-state_min.h)))*self.m_flow
+ returnQ_sc,Q_lat,Q_sh,state_q0,state_q1
+
+
[docs]defiterate_area(self,dT_max,alpha_pri,alpha_sec,Q)->float:
+"""
+ Iteratively calculates the required area for the heat exchange.
+
+ Args:
+ dT_max (float): Maximum temperature differential.
+ alpha_pri (float): Heat transfer coefficient for the primary medium.
+ alpha_sec (float): Heat transfer coefficient for the secondary medium.
+ Q (float): Heat flow rate.
+
+ Returns:
+ float: Required area for heat exchange.
+ """
+ _accuracy=1e-6# square mm
+ _step=1.0
+ R=self.calc_R()
+ k=self.calc_k(alpha_pri,alpha_sec)
+ m_flow_cp_min=self.calc_m_flow_cp_min()
+ # First check if point is feasible at all
+ ifdT_max<=0:
+ returnself.A
+ eps_necessary=Q/(m_flow_cp_min*dT_max)
+
+ # Special cases:
+ # ---------------
+ # eps is equal or higher than 1, an infinite amount of area would be necessary.
+ ifeps_necessary>=1:
+ returnself.A
+ # eps is lower or equal to zero: No Area required (Q<=0)
+ ifeps_necessary<=0:
+ return0
+
+ area=0.0
+ whileTrue:
+ NTU=self.calc_NTU(area,k,m_flow_cp_min)
+ eps=self.calc_eps(R,NTU)
+ ifeps>=eps_necessary:
+ if_step<=_accuracy:
+ break
+ else:
+ # Go back
+ area-=_step
+ _step/=10
+ continue
+ if_step<_accuracyandarea>self.A:
+ break
+ area+=_step
+
+ returnmin(area,self.A)
+
+
+
[docs]classMovingBoundaryNTUCondenser(MovingBoundaryNTU):
+"""
+ Condenser class which implements the actual `calc` method.
+
+ Assumptions:
+ - No phase changes in secondary medium
+ - cp of secondary medium is constant over heat-exchanger
+
+ See parent classes for arguments.
+ """
+
+
[docs]defcalc(self,inputs:Inputs,fs_state:FlowsheetState)->(float,float):
+"""
+ Calculate the heat exchanger with the NTU-Method based on the given inputs.
+
+ The flowsheet state can be used to save important variables
+ during calculation for later analysis.
+
+ Both return values are used to check if the heat transfer is valid or not.
+
+ Args:
+ inputs (Inputs): The inputs for the calculation.
+ fs_state (FlowsheetState): The flowsheet state to save important variables.
+
+ Returns:
+ Tuple[float, float]:
+ error: Error in percentage between the required and calculated heat flow rates.
+ dT_min: Minimal temperature difference (can be negative).
+ """
+ self.m_flow_secondary=inputs.m_flow_con# [kg/s]
+ self.calc_secondary_cp(T=inputs.T_con_in)
+
+ # First we separate the flow:
+ Q_sc,Q_lat,Q_sh,state_q0,state_q1=self.separate_phases(
+ self.state_inlet,
+ self.state_outlet,
+ self.state_inlet.p
+ )
+ Q=Q_sc+Q_lat+Q_sh
+
+ # Note: As Q_con_ntu has to converge to Q_con (m_ref*delta_h), we can safely
+ # calculate the output temperature.
+
+ T_mean=inputs.T_con_in+self.calc_secondary_Q_flow(Q)/(self.m_flow_secondary_cp*2)
+ tra_prop_med=self.calc_transport_properties_secondary_medium(T_mean)
+ alpha_med_wall=self.calc_alpha_secondary(tra_prop_med)
+
+ # Calculate secondary_medium side temperatures:
+ # Assumption loss is the same correlation for each regime
+ T_sc=inputs.T_con_in+self.calc_secondary_Q_flow(Q_sc)/self.m_flow_secondary_cp
+ T_sh=T_sc+self.calc_secondary_Q_flow(Q_lat)/self.m_flow_secondary_cp
+ T_out=T_sh+self.calc_secondary_Q_flow(Q_sh)/self.m_flow_secondary_cp
+
+ # 1. Regime: Subcooling
+ Q_sc_ntu,A_sc=0,0
+ ifQ_sc>0and(state_q0.T!=self.state_outlet.T):
+ self.set_primary_cp((state_q0.h-self.state_outlet.h)/(state_q0.T-self.state_outlet.T))
+ # Get transport properties:
+ tra_prop_ref_con=self.med_prop.calc_mean_transport_properties(state_q0,self.state_outlet)
+ alpha_ref_wall=self.calc_alpha_liquid(tra_prop_ref_con)
+
+ # Only use still available area:
+ A_sc=self.iterate_area(dT_max=(state_q0.T-inputs.T_con_in),
+ alpha_pri=alpha_ref_wall,
+ alpha_sec=alpha_med_wall,
+ Q=Q_sc)
+ A_sc=min(self.A,A_sc)
+
+ Q_sc_ntu,k_sc=self.calc_Q_ntu(dT_max=(state_q0.T-inputs.T_con_in),
+ alpha_pri=alpha_ref_wall,
+ alpha_sec=alpha_med_wall,
+ A=A_sc)
+
+ # 2. Regime: Latent heat exchange
+ Q_lat_ntu,A_lat=0,0
+ ifQ_lat>0:
+ self.set_primary_cp(np.inf)
+ # Get transport properties:
+ alpha_ref_wall=self.calc_alpha_two_phase(
+ state_q0=state_q0,
+ state_q1=state_q1,
+ fs_state=fs_state,
+ inputs=inputs
+ )
+
+ A_lat=self.iterate_area(dT_max=(state_q1.T-T_sc),
+ alpha_pri=alpha_ref_wall,
+ alpha_sec=alpha_med_wall,
+ Q=Q_lat)
+ # Only use still available area:
+ A_lat=min(self.A-A_sc,A_lat)
+
+ Q_lat_ntu,k_lat=self.calc_Q_ntu(dT_max=(state_q1.T-T_sc),
+ alpha_pri=alpha_ref_wall,
+ alpha_sec=alpha_med_wall,
+ A=A_lat)
+ logger.debug(f"con_lat: pri: {round(alpha_ref_wall,2)} sec: {round(alpha_med_wall,2)}")
+
+ # 3. Regime: Superheat heat exchange
+ Q_sh_ntu,A_sh=0,0
+ ifQ_shand(self.state_inlet.T!=state_q1.T):
+ self.set_primary_cp((self.state_inlet.h-state_q1.h)/(self.state_inlet.T-state_q1.T))
+ # Get transport properties:
+ tra_prop_ref_con=self.med_prop.calc_mean_transport_properties(self.state_inlet,state_q1)
+ alpha_ref_wall=self.calc_alpha_gas(tra_prop_ref_con)
+
+ # Only use still available area:
+ A_sh=self.A-A_sc-A_lat
+
+ Q_sh_ntu,k_sh=self.calc_Q_ntu(dT_max=(self.state_inlet.T-T_sh),
+ alpha_pri=alpha_ref_wall,
+ alpha_sec=alpha_med_wall,
+ A=A_sh)
+ logger.debug(f"con_sh: pri: {round(alpha_ref_wall,2)} sec: {round(alpha_med_wall,2)}")
+
+ Q_ntu=Q_sh_ntu+Q_sc_ntu+Q_lat_ntu
+ error=(Q_ntu/Q-1)*100
+ # Get possible dT_min:
+ dT_min_in=self.state_outlet.T-inputs.T_con_in
+ dT_min_out=self.state_inlet.T-T_out
+ dT_min_LatSH=state_q1.T-T_sh
+
+ fs_state.set(name="A_con_sh",value=A_sh,unit="m2",description="Area for superheat heat exchange in condenser")
+ fs_state.set(name="A_con_lat",value=A_lat,unit="m2",description="Area for latent heat exchange in condenser")
+ fs_state.set(name="A_con_sc",value=A_sc,unit="m2",description="Area for subcooling heat exchange in condenser")
+
+ returnerror,min(dT_min_in,
+ dT_min_LatSH,
+ dT_min_out)
+
+
+
[docs]classMovingBoundaryNTUEvaporator(MovingBoundaryNTU):
+"""
+ Evaporator class which implements the actual `calc` method.
+
+ Assumptions:
+ - No phase changes in secondary medium
+ - cp of secondary medium is constant over heat-exchanger
+
+ See parent classes for arguments.
+ """
+
+
[docs]defcalc(self,inputs:Inputs,fs_state:FlowsheetState)->(float,float):
+"""
+ Calculate the heat exchanger with the NTU-Method based on the given inputs.
+
+ The flowsheet state can be used to save important variables
+ during calculation for later analysis.
+
+ Both return values are used to check if the heat transfer is valid or not.
+
+ Args:
+ inputs (Inputs): The inputs for the calculation.
+ fs_state (FlowsheetState): The flowsheet state to save important variables.
+
+ Returns:
+ Tuple[float, float]:
+ error: Error in percentage between the required and calculated heat flow rates.
+ dT_min: Minimal temperature difference (can be negative).
+ """
+ self.m_flow_secondary=inputs.m_flow_eva# [kg/s]
+ self.calc_secondary_cp(T=inputs.T_eva_in)
+
+ # First we separate the flow:
+ Q_sc,Q_lat,Q_sh,state_q0,state_q1=self.separate_phases(
+ self.state_outlet,
+ self.state_inlet,
+ self.state_inlet.p
+ )
+
+ Q=Q_sc+Q_lat+Q_sh
+
+ # Note: As Q_eva_ntu has to converge to Q_eva (m_ref*delta_h), we can safely
+ # calculate the output temperature.
+ T_mean=inputs.T_eva_in-Q/(self.m_flow_secondary_cp*2)
+ tra_prop_med=self.calc_transport_properties_secondary_medium(T_mean)
+ alpha_med_wall=self.calc_alpha_secondary(tra_prop_med)
+
+ # Calculate secondary_medium side temperatures:
+ T_sh=inputs.T_eva_in-Q_sh/self.m_flow_secondary_cp
+ T_sc=T_sh-Q_lat/self.m_flow_secondary_cp
+ T_out=T_sc-Q_sc/self.m_flow_secondary_cp
+
+ # 1. Regime: Superheating
+ Q_sh_ntu,A_sh=0,0
+ ifQ_shand(self.state_outlet.T!=state_q1.T):
+ self.set_primary_cp((self.state_outlet.h-state_q1.h)/(self.state_outlet.T-state_q1.T))
+ # Get transport properties:
+ tra_prop_ref_eva=self.med_prop.calc_mean_transport_properties(self.state_outlet,state_q1)
+ alpha_ref_wall=self.calc_alpha_gas(tra_prop_ref_eva)
+
+ ifQ_lat>0:
+ A_sh=self.iterate_area(dT_max=(inputs.T_eva_in-state_q1.T),
+ alpha_pri=alpha_ref_wall,
+ alpha_sec=alpha_med_wall,
+ Q=Q_sh)
+ else:
+ # if only sh is present --> full area:
+ A_sh=self.A
+
+ # Only use still available area
+ A_sh=min(self.A,A_sh)
+
+ Q_sh_ntu,k_sh=self.calc_Q_ntu(dT_max=(inputs.T_eva_in-state_q1.T),
+ alpha_pri=alpha_ref_wall,
+ alpha_sec=alpha_med_wall,
+ A=A_sh)
+
+ logger.debug(f"eva_sh: pri: {round(alpha_ref_wall,2)} sec: {round(alpha_med_wall,2)}")
+
+ # 2. Regime: Latent heat exchange
+ Q_lat_ntu,A_lat=0,0
+ ifQ_lat>0:
+ self.set_primary_cp(np.inf)
+
+ alpha_ref_wall=self.calc_alpha_two_phase(
+ state_q0=state_q0,
+ state_q1=state_q1,
+ fs_state=fs_state,
+ inputs=inputs
+ )
+
+ ifQ_sc>0:
+ A_lat=self.iterate_area(dT_max=(T_sh-self.state_inlet.T),
+ alpha_pri=alpha_ref_wall,
+ alpha_sec=alpha_med_wall,
+ Q=Q_lat)
+ else:
+ A_lat=self.A-A_sh
+
+ # Only use still available area:
+ A_lat=min(self.A-A_sh,A_lat)
+ Q_lat_ntu,k_lat=self.calc_Q_ntu(dT_max=(T_sh-self.state_inlet.T),
+ alpha_pri=alpha_ref_wall,
+ alpha_sec=alpha_med_wall,
+ A=A_lat)
+ logger.debug(f"eva_lat: pri: {round(alpha_ref_wall,2)} sec: {round(alpha_med_wall,2)}")
+
+ # 3. Regime: Subcooling
+ Q_sc_ntu,A_sc=0,0
+ ifQ_sc>0and(state_q0.T!=self.state_inlet.T):
+ self.set_primary_cp((state_q0.h-self.state_inlet.h)/(state_q0.T-self.state_inlet.T))
+ # Get transport properties:
+ tra_prop_ref_eva=self.med_prop.calc_mean_transport_properties(state_q0,self.state_inlet)
+ alpha_ref_wall=self.calc_alpha_liquid(tra_prop_ref_eva)
+
+ # Only use still available area:
+ A_sc=self.A-A_sh-A_lat
+
+ Q_sc_ntu,k_sc=self.calc_Q_ntu(dT_max=(T_sc-self.state_inlet.T),
+ alpha_pri=alpha_ref_wall,
+ alpha_sec=alpha_med_wall,
+ A=A_sc)
+
+ Q_ntu=Q_sh_ntu+Q_sc_ntu+Q_lat_ntu
+ error=(Q_ntu/Q-1)*100
+ # Get dT_min
+ dT_min_in=inputs.T_eva_in-self.state_outlet.T
+ dT_min_out=T_out-self.state_inlet.T
+
+ fs_state.set(name="A_eva_sh",value=A_sh,unit="m2",description="Area for superheat heat exchange in evaporator")
+ fs_state.set(name="A_eva_lat",value=A_lat,unit="m2",description="Area for latent heat exchange in evaporator")
+
+ returnerror,min(dT_min_out,dT_min_in)
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/ntu.html b/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/ntu.html
new file mode 100644
index 0000000..3996938
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/components/heat_exchangers/ntu.html
@@ -0,0 +1,277 @@
+
+
+
+
+
+
+
+ vclibpy.components.heat_exchangers.ntu — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
[docs]classBasicNTU(HeatExchanger,abc.ABC):
+"""
+ Moving boundary NTU based heat exchanger.
+
+ See parent class for more arguments.
+
+ Args:
+ flow_type (str):
+ Counter, Cross or parallel flow
+ ratio_outer_to_inner_area (float):
+ The NTU method uses the overall heat transfer coefficient `k`
+ and multiplies it with the outer area `A` (area of the secondary side).
+ However, depending on the heat exchanger type, the areas may
+ differ drastically. For instance in an air-to-water heat exchanger.
+ The VDI-Atlas proposes the ratio of outer area to inner pipe area
+ to account for this mismatch in sizes.
+ The calculation follows the code in the function `calc_k`.
+ Typical values are around 20-30.
+ """
+
+ def__init__(self,flow_type:str,ratio_outer_to_inner_area:float,**kwargs):
+"""
+ Initializes BasicNTU.
+
+ Args:
+ flow_type (str): Type of flow: Counter, Cross, or Parallel.
+ ratio_outer_to_inner_area (float):
+ The NTU method uses the overall heat transfer coefficient `k`
+ and multiplies it with the overall area `A`.
+ However, depending on the heat exchanger type, the areas may
+ differ drastically. For instance in an air-to-water heat exchanger.
+ The VDI-Atlas proposes the ratio of outer area to inner pipe area
+ to account for this mismatch in sizes.
+ The calculation follows the code in the function `calc_k`.
+ **kwargs: Additional keyword arguments passed to the parent class.
+ """
+ super().__init__(**kwargs)
+ self.ratio_outer_to_inner_area=ratio_outer_to_inner_area
+
+ # Set primary cp:
+ self._primary_cp=None
+
+ # Type of HE:
+ self.flow_type=flow_type.lower()
+ ifself.flow_typenotin["counter","cross","parallel"]:
+ raiseTypeError("Given flow_type is not supported")
+
+
[docs]defset_primary_cp(self,cp:float):
+"""
+ Set the specific heat (cp) for the primary medium.
+
+ Args:
+ cp (float): Specific heat for the primary medium.
+ """
+ self._primary_cp=cp
+
+
[docs]defcalc_eps(self,R:float,NTU:float)->float:
+"""
+ Calculate the effectiveness (eps) of the heat exchanger based on NTU.
+
+ Source of implementation: EBC Lecture SimModelle.
+
+ Args:
+ R (float): Ratio of heat capacity rates (m_flow*cp) of the primary to secondary medium.
+ NTU (float): Number of Transfer Units.
+
+ Returns:
+ float: Effectiveness (eps) of the heat exchanger.
+ """
+ ifRin(0,1):
+ returnNTU/(NTU+1)
+ ifself.flow_type=="counter":
+ return(1-np.exp(-NTU*(1-R)))/(1-R*np.exp(-NTU*(1-R)))
+ ifself.flow_type=="cross":
+ ifNTU==0:
+ return0
+ eta=NTU**-0.22
+ return1-np.exp((np.exp(-NTU*R*eta)-1)/(R*eta))
+ ifself.flow_type=="parallel":
+ return(1-np.exp(-NTU*(1+R)))/(1+R)
+ raiseTypeError(f"Flow type {self.flow_type} not supported")
+
+
[docs]defcalc_R(self)->float:
+"""
+ Calculate the R value, which is the ratio of heat capacity rates (m_flow*cp) of the primary to secondary medium.
+
+ Returns:
+ float: R value.
+ """
+ m_flow_pri_cp=self.m_flow*self._primary_cp
+ ifm_flow_pri_cp>self.m_flow_secondary_cp:
+ returnself.m_flow_secondary_cp/m_flow_pri_cp
+ returnm_flow_pri_cp/self.m_flow_secondary_cp
+
+
[docs]defcalc_k(self,alpha_pri:float,alpha_sec:float)->float:
+"""
+ Calculate the overall heat transfer coefficient (k) of the heat exchanger.
+
+ Args:
+ alpha_pri (float): Heat transfer coefficient for the primary medium.
+ alpha_sec (float): Heat transfer coefficient for the secondary medium.
+
+ Returns:
+ float: Overall heat transfer coefficient (k).
+ """
+ k_wall=self.calc_wall_heat_transfer()
+ k=(1/(
+ (1/alpha_pri)*self.ratio_outer_to_inner_area+
+ (1/k_wall)*self.ratio_outer_to_inner_area+
+ (1/alpha_sec)
+ )
+ )
+ returnk
+
+
[docs]@staticmethod
+ defcalc_NTU(A:float,k:float,m_flow_cp:float)->float:
+"""
+ Calculate the Number of Transfer Units (NTU) for the heat exchanger.
+
+ Args:
+ A (float): Area of the heat exchanger.
+ k (float): Overall heat transfer coefficient.
+ m_flow_cp (float): Minimal heat capacity rates (m_flow*cp) between primary and secondary side.
+
+ Returns:
+ float: Number of Transfer Units (NTU).
+ """
+ returnk*A/m_flow_cp
+
+
[docs]defcalc_m_flow_cp_min(self)->float:
+"""
+ Calculate the minimum value between the heat capacity rates (m_flow*cp) for the primary and secondary mediums.
+
+ Returns:
+ float: Minimum value.
+ """
+ returnmin(
+ self.m_flow*self._primary_cp,
+ self.m_flow_secondary_cp
+ )
+
+
[docs]defcalc_Q_ntu(self,dT_max:float,alpha_pri:float,alpha_sec:float,A:float)->(float,float):
+"""
+ Calculate the heat transfer and overall heat transfer coefficient for the heat exchanger based on NTU.
+
+ Args:
+ dT_max (float): Maximum temperature differential.
+ alpha_pri (float): Heat transfer coefficient for the primary medium.
+ alpha_sec (float): Heat transfer coefficient for the secondary medium.
+ A (float): Area of the heat exchanger.
+
+ Returns:
+ Tuple[float, float]: Heat transfer and overall heat transfer coefficient.
+ """
+ R=self.calc_R()
+ k=self.calc_k(alpha_pri,alpha_sec)
+ m_flow_cp_min=self.calc_m_flow_cp_min()
+ NTU=self.calc_NTU(A,k,m_flow_cp_min)
+ eps=self.calc_eps(R,NTU)
+
+ # Get the maximal allowed heat flow
+ Q_max=m_flow_cp_min*dT_max
+ returnQ_max*eps,k
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/components/phase_separator.html b/docs/0.1.1/docs/_modules/vclibpy/components/phase_separator.html
new file mode 100644
index 0000000..d2c17e4
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/components/phase_separator.html
@@ -0,0 +1,183 @@
+
+
+
+
+
+
+
+ vclibpy.components.phase_separator — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Source code for vclibpy.components.phase_separator
+"""
+Module with a simple phase separator model.
+"""
+
+fromvclibpy.mediaimportThermodynamicState
+fromvclibpy.components.componentimportBaseComponent
+
+
+
[docs]classPhaseSeparator(BaseComponent):
+"""
+ A simple phase separator model.
+ """
+
+ def__init__(self):
+ super().__init__()
+ self._state_outlet_liquid:ThermodynamicState=None
+ self._state_outlet_vapor:ThermodynamicState=None
+
+ @BaseComponent.state_inlet.setter
+ defstate_inlet(self,state_inlet:ThermodynamicState):
+"""
+ Set the state of the inlet and calculate the outlet states for liquid and vapor phases.
+
+ Args:
+ state_inlet (ThermodynamicState): Inlet state.
+ """
+ self._state_inlet=state_inlet
+ self.state_outlet_vapor=self.med_prop.calc_state("PQ",self.state_inlet.p,1)
+ self.state_outlet_liquid=self.med_prop.calc_state("PQ",self.state_inlet.p,0)
+
+ @BaseComponent.state_outlet.setter
+ defstate_outlet(self,state:ThermodynamicState):
+"""
+ This outlet is disabled for this component.
+
+ Args:
+ state (ThermodynamicState): Outlet state.
+ """
+ raiseNotImplementedError("This outlet is disabled for this component")
+
+ @property
+ defstate_outlet_vapor(self)->ThermodynamicState:
+"""
+ Getter for the outlet state of the vapor phase.
+
+ Returns:
+ ThermodynamicState: Outlet state for the vapor phase.
+ """
+ returnself._state_outlet_vapor
+
+ @state_outlet_vapor.setter
+ defstate_outlet_vapor(self,state:ThermodynamicState):
+"""
+ Setter for the outlet state of the vapor phase.
+
+ Args:
+ state (ThermodynamicState): Outlet state for the vapor phase.
+ """
+ self._state_outlet_vapor=state
+
+ @property
+ defstate_outlet_liquid(self)->ThermodynamicState:
+"""
+ Getter for the outlet state of the liquid phase.
+
+ Returns:
+ ThermodynamicState: Outlet state for the liquid phase.
+ """
+ returnself._state_outlet_liquid
+
+ @state_outlet_liquid.setter
+ defstate_outlet_liquid(self,state:ThermodynamicState):
+"""
+ Setter for the outlet state of the liquid phase.
+
+ Args:
+ state (ThermodynamicState): Outlet state for the liquid phase.
+ """
+ self._state_outlet_liquid=state
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/datamodels.html b/docs/0.1.1/docs/_modules/vclibpy/datamodels.html
new file mode 100644
index 0000000..8b4544b
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/datamodels.html
@@ -0,0 +1,345 @@
+
+
+
+
+
+
+
+ vclibpy.datamodels — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"""
+Module which contains datamodels which are used in this library.
+"""
+
+fromdataclassesimportdataclass
+fromtypingimportDict,Any
+fromcopyimportdeepcopy
+
+
+
[docs]@dataclass
+classVariable:
+"""
+ Class for a variable used in analysis.
+
+ Args:
+ name (str): The name of the variable.
+ value (float): The numerical value of the variable.
+ unit (str): The unit of measurement for the variable (optional).
+ description (str): A description of the variable (optional).
+ """
+ name:str
+ value:float
+ unit:str=None
+ description:str=None
+
+
+
[docs]classVariableContainer:
+"""
+ Class which holds Variables to be used anywhere in the models.
+
+ This class enables dynamic addition of Variables.
+ """
+ def__init__(self):
+ self._variables:Dict[str,Variable]={}
+
+ def__str__(self):
+ returnf"{self.__class__.__name__}:\n"+"\n".join(
+ [f"{var.name}={var.value}{var.unit} ({var.description})"
+ forvarinself._variables.values()]
+ )
+
+
[docs]defset(self,name:str,value:float,unit:str=None,description:str=None):
+"""
+ Add or update a Variable in the container.
+
+ Args:
+ name (str): The name of the variable.
+ value (float): The numerical value of the variable.
+ unit (str): The unit of measurement for the variable (optional).
+ description (str): A description of the variable (optional).
+ """
+ ifnameinself._variables:
+ self._variables[name].value=value
+ else:
+ self._variables[name]=Variable(
+ name=name,value=value,unit=unit,description=description
+ )
+
+ def__getattr__(self,item):
+"""
+ Overwrite the dunder method to enable usage of e.g.
+ fs_state.COP
+ """
+ ifitemin{'__getstate__','__setstate__'}:
+ returnsuper().__getattr__(self,item)
+ ifiteminself._variables:
+ returnself._variables.get(item).value
+ raiseAttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'")
+
+
[docs]defget_variable_names(self)->list:
+"""
+ Get the names of all variables in the container.
+
+ Returns:
+ list: A list of variable names.
+ """
+ returnlist(self._variables.keys())
+
+
[docs]defget_variables(self):
+"""
+ Get all variables in the container.
+
+ Returns:
+ Dict[str, Variable]: A dictionary of variable names and Variable objects.
+ """
+ returnself._variables
+
+
[docs]defitems(self):
+"""
+ Get items from the container.
+
+ Returns:
+ Dict[str, Variable]: A dictionary of variable names and Variable objects.
+ """
+ returnself._variables.items()
+
+
[docs]defget(self,name:str,default:Any=None):
+"""
+ Get the Variable with the specified name.
+
+ Args:
+ name (str): The name of the variable.
+ default (Any): Default value to return if the variable is not found.
+
+ Returns:
+ Variable: The Variable object.
+
+ """
+ returnself._variables.get(name,default)
+
+
[docs]defcopy(self):
+"""
+ Return a deepcopy of the instance as the variable dict is mutable.
+
+ Returns:
+ VariableContainer: A deepcopy of the VariableContainer instance.
+ """
+ returndeepcopy(self)
+
+
[docs]defconvert_to_str_value_format(self,with_unit_and_description:bool)->Dict[str,float]:
+"""
+ Returns a dictionary with a str: float format which is handy when storing results
+ in files like .csv or .xlsx.
+
+ Args:
+ with_unit_and_description (bool): When False, only the name is in the string.
+
+ Returns:
+ dict: Containing all variables and values.
+ """
+ ifwith_unit_and_description:
+ return{f"{k} in {v.unit} ({v.description})":v.valuefork,vinself.items()}
+ return{k:v.valuefork,vinself.items()}
+
+
+
[docs]classFlowsheetState(VariableContainer):
+"""
+ This class is used to define the unique states of the flowsheet
+ in the heat pump.
+
+ The class is dynamic in the sense that attributes may be
+ added during calculation of new flowsheet. This enables
+ the easy adding of custom values to analyze the whole flowsheet
+ and not restrict to a certain naming convention.
+ """
+
+
+
[docs]classInputs(VariableContainer):
+"""
+ Class defining inputs to calculate the FlowsheetState.
+
+ While the inputs are pre-defined, you may add further ones
+ using the `set` method.
+
+ Args:
+ n (float): Relative compressor speed between 0 and 1.
+ T_eva_in (float): Secondary side evaporator inlet temperature.
+ T_con_in (float): Secondary side condenser inlet temperature.
+ m_flow_eva (float): Secondary side evaporator mass flow rate.
+ m_flow_con (float): Secondary side condenser mass flow rate.
+ dT_eva_superheating (float): Super-heating after evaporator.
+ dT_con_subcooling (float): Subcooling after condenser.
+ T_ambient (float): Ambient temperature of the machine.
+ """
+
+ def__init__(
+ self,
+ n:float=None,
+ T_eva_in:float=None,
+ T_con_in:float=None,
+ m_flow_eva:float=None,
+ m_flow_con:float=None,
+ dT_eva_superheating:float=None,
+ dT_con_subcooling:float=None,
+ T_ambient:float=None
+ ):
+"""
+ Initializes an Inputs object with parameters representing external conditions
+ for the vapor compression cycle.
+
+ Args:
+ n (float): Relative compressor speed between 0 and 1 (unit: -).
+ T_eva_in (float): Secondary side evaporator inlet temperature (unit: K).
+ T_con_in (float): Secondary side condenser inlet temperature (unit: K).
+ m_flow_eva (float): Secondary side evaporator mass flow rate (unit: kg/s).
+ m_flow_con (float): Secondary side condenser mass flow rate (unit: kg/s).
+ dT_eva_superheating (float): Super-heating after evaporator (unit: K).
+ dT_con_subcooling (float): Subcooling after condenser (unit: K).
+ T_ambient (float): Ambient temperature of the machine (unit: K).
+ """
+ super().__init__()
+ self.set(
+ name="n",
+ value=n,
+ unit="-",
+ description="Relative compressor speed"
+ )
+ self.set(
+ name="T_eva_in",
+ value=T_eva_in,
+ unit="K",
+ description="Secondary side evaporator inlet temperature"
+ )
+ self.set(
+ name="T_con_in",
+ value=T_con_in,
+ unit="K",
+ description="Secondary side condenser inlet temperature"
+ )
+ self.set(
+ name="m_flow_con",
+ value=m_flow_con,
+ unit="kg/s",
+ description="Secondary side condenser mass flow rate"
+ )
+ self.set(
+ name="m_flow_eva",
+ value=m_flow_eva,
+ unit="kg/s",
+ description="Secondary side evaporator mass flow rate"
+ )
+ self.set(
+ name="dT_eva_superheating",
+ value=dT_eva_superheating,
+ unit="K",
+ description="Super-heating after evaporator"
+ )
+ self.set(
+ name="dT_con_subcooling",
+ value=dT_con_subcooling,
+ unit="K",
+ description="Subcooling after condenser"
+ )
+ ifT_ambientisNone:
+ T_ambient=T_eva_in
+ self.set(
+ name="T_ambient",
+ value=T_ambient,
+ unit="K",
+ description="Ambient temperature of machine"
+ )
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/media/cool_prop.html b/docs/0.1.1/docs/_modules/vclibpy/media/cool_prop.html
new file mode 100644
index 0000000..e8672a0
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/media/cool_prop.html
@@ -0,0 +1,246 @@
+
+
+
+
+
+
+
+ vclibpy.media.cool_prop — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
[docs]classCoolProp(MedProp):
+"""
+ Class using the open-source CoolProp package
+ to access the properties.
+
+ Args:
+ use_high_level_api (bool):
+ True to use the high-level api, which is much slower,
+ but you can use all modes in calc_state.
+ Default is False.
+ """
+
+ _mode_map={
+ "PT":(CoolPropInternal.PT_INPUTS,True),
+ "TQ":(CoolPropInternal.QT_INPUTS,False),
+ "PS":(CoolPropInternal.PSmass_INPUTS,True),
+ "PH":(CoolPropInternal.HmassP_INPUTS,False),
+ "PQ":(CoolPropInternal.PQ_INPUTS,True)
+ }
+
+ def__init__(self,fluid_name,use_high_level_api:bool=False):
+ super().__init__(fluid_name=fluid_name)
+ # Set molar mass and trigger a possible fluid-name error
+ # if the fluid is not supported.
+ self._helmholtz_equation_of_state=CoolPropInternal.AbstractState("HEOS",self.fluid_name)
+ self.M=self._helmholtz_equation_of_state.molar_mass()
+ self.use_high_level_api=use_high_level_api
+
+
[docs]defcalc_transport_properties(self,state:ThermodynamicState):
+ if0<=state.q<=1:
+ mode="PQ"
+ var1,var2=state.p,state.q
+ else:
+ # Get using p and T
+ mode="PT"
+ var1,var2=state.p,state.T
+
+ ifself.use_high_level_api:
+ args=[mode[0],var1,mode[1],var2,self.fluid_name]
+ # CoolProp returns -
+ pr=CoolPropInternal.PropsSI('PRANDTL',*args)
+ # CoolProp returns J/kg/K
+ cp=CoolPropInternal.PropsSI('C',*args)
+ # CoolProp returns J/kg/K
+ cv=CoolPropInternal.PropsSI('CVMASS',*args)
+ # CoolProp returns W/m/K
+ lam=CoolPropInternal.PropsSI('CONDUCTIVITY',*args)
+ # CoolProp returns Pa*s
+ dyn_vis=CoolPropInternal.PropsSI('VISCOSITY',*args)
+ # Internal calculation as kinematic vis is ration of dyn_vis to density
+ # In m^2/s
+ kin_vis=dyn_vis/state.d
+
+ # Create transport properties instance
+ returnTransportProperties(lam=lam,dyn_vis=dyn_vis,kin_vis=kin_vis,
+ pr=pr,cp=cp,cv=cv,state=state)
+ # Low-level API
+ self._update_coolprop_heos(mode=mode,var1=var1,var2=var2)
+ # Create transport properties instance
+ returnTransportProperties(
+ lam=self._helmholtz_equation_of_state.conductivity(),
+ dyn_vis=self._helmholtz_equation_of_state.viscosity(),
+ kin_vis=self._helmholtz_equation_of_state.viscosity()/state.d,
+ pr=self._helmholtz_equation_of_state.Prandtl(),
+ cp=self._helmholtz_equation_of_state.cpmass(),
+ cv=self._helmholtz_equation_of_state.cvmass(),
+ state=state
+ )
+
+ def_update_coolprop_heos(self,mode,var1,var2):
+ ifmodenotinself._mode_map:
+ raiseKeyError(
+ f"Given mode '{mode}' is currently not supported with the "
+ f"faster low-level API to cool-prop. "
+ f"Either use the high-level API or raise an issue. "
+ f"Supported modes: {', '.join(self._mode_map.keys())}"
+ )
+ i_input,not_reverse_variables=self._mode_map[mode]
+ ifnot_reverse_variables:
+ self._helmholtz_equation_of_state.update(i_input,var1,var2)
+ else:
+ self._helmholtz_equation_of_state.update(i_input,var2,var1)
+
+
+"""Module with wrappers to access and handle media property databases.
+
+This module provides interfaces to load media properties using various wrappers and
+handle calculations related to media properties.
+
+Classes:
+ MedProp: Base class for all media property interfaces.
+
+Functions:
+ get_two_phase_limits: Return the states of the boundaries of the two-phase section for a given fluid.
+
+"""
+importabc
+importlogging
+importwarnings
+fromtypingimportList
+importnumpyasnp
+
+fromvclibpy.mediaimportThermodynamicState,TransportProperties
+
+
+logger=logging.getLogger(__name__)
+
+
+
[docs]classMedProp(abc.ABC):
+"""Base class for all media property interfaces.
+
+ This class serves as the base for defining interfaces to access and compute media properties.
+
+ Methods:
+ calc_state: Calculate the thermodynamic state based on mode and state variables.
+ calc_transport_properties: Calculate transport properties for a given state.
+ get_critical_point: Retrieve critical point information.
+ get_molar_mass: Retrieve molar mass information.
+ get_two_phase_limits: Retrieve the two-phase limits for plotting.
+ calc_mean_transport_properties: Calculate the average transport properties for given states.
+ """
+ _fluid_mapper={}
+
+ def__init__(self,fluid_name):
+"""Initialize the MedProp class instance.
+
+ Args:
+ fluid_name (str): The name of the fluid.
+ """
+ # Check if better internal names exist (e.g. air is modelled as air.ppf)
+ self.fluid_name=self._fluid_mapper.get(fluid_name,fluid_name)
+ self._two_phase_limits:dict=None
+
+
[docs]defcalc_state(self,mode:str,var1:float,var2:float):
+"""Calculate the thermodynamic state based on the specified mode and state variables.
+
+ This function calculates the thermodynamic state based on the chosen mode and provided state variables.
+ The input state variables need to be in SI units.
+
+ Notes:
+ - PT does not work when the state might fall within the two-phase region.
+ - Only functions for density are implemented. In cases where you know the specific volume, use the density
+ functions with the inverse value.
+ - Quality (q) may have values outside the 'physical' scope:
+ - q = -998: Subcooled liquid
+ - q = 998: Superheated vapor
+ - q = 999: Supercritical state
+
+ Possible modes include:
+ - "PD": Pressure, Density
+ - "PH": Pressure, Enthalpy
+ - "PQ": Pressure, Quality
+ - "PS": Pressure, Entropy
+ - "PT": Pressure, Temperature
+ - "PU": Pressure, Internal Energy
+ - "TD": Temperature, Density
+ - "TH": Temperature, Enthalpy
+ - "TQ": Temperature, Quality
+ - "TS": Temperature, Entropy
+ - "TU": Temperature, Internal Energy
+ - "DH": Density, Enthalpy
+ - "DS": Density, Entropy
+ - "DU": Density, Internal Energy
+
+ Args:
+ mode (str): Defines the given input state variables (see possible modes above).
+ var1 (float): Value of the first state variable (as specified in the mode) in SI units.
+ var2 (float): Value of the second state variable (as specified in the mode) in SI units.
+
+ Returns:
+ ThermodynamicState: A ThermodynamicState instance with state variables.
+
+ Raises:
+ AssertionError: If the given mode is not within the available options.
+ """
+ available_options=['PD','PH','PQ','PS','PT',
+ 'PU','TD','TH','TQ','TS',
+ 'TU','DH','DS','DU',]
+ assertmodeinavailable_options,f'Given mode {mode} is not in available options'
+
+
[docs]defterminate(self):
+"""
+ Terminate the class.
+ Default behaviour does nothing.
+ """
+ pass
+
+
[docs]@abc.abstractmethod
+ defcalc_transport_properties(self,state:ThermodynamicState):
+"""Calculate the transport properties for the given state.
+
+ Args:
+ state (ThermodynamicState): The current thermodynamic state.
+
+ Returns:
+ TransportProperties: An instance of TransportProperties.
+ """
+ pass
+
+
[docs]@abc.abstractmethod
+ defget_critical_point(self):
+"""Retrieve critical point information for the fluid.
+
+ Returns:
+ Tuple[float, float, float]: A tuple containing critical point information
+ (Temperature Tc [K], Pressure pc [Pa], Density dc [kg/m^3]).
+ """
+ pass
+
+
[docs]@abc.abstractmethod
+ defget_molar_mass(self):
+"""Retrieve the molar mass of the current fluid.
+
+ Returns:
+ float: The molar mass M of the current fluid in kg/mol.
+ """
+ pass
+
+
[docs]defget_two_phase_limits(self,quantity:str,p_min:int=100000,p_step:int=5000):
+"""
+ Retrieve the two-phase limits for plotting a specified quantity.
+
+ This method returns the two-phase limits for a specified quantity (T, h, s, or p) in an array used for
+ plotting purposes. It calculates the limits within the pressure range from p_min and quality (q) 0 to the
+ critical pressure (pc), and then from the critical pressure to the pressure p_min and quality 1.
+
+ Args:
+ quantity (str): The specified quantity (T, h, s, or p).
+ p_min (int, optional): The minimum pressure value to start iteration. Default is 100000 Pa.
+ p_step (int, optional): The step size for pressure variation. Default is 5000 Pa.
+
+ Returns:
+ numpy.ndarray: An array containing the two-phase limits for the specified quantity.
+
+ Raises:
+ ValueError: If the given quantity is not supported (T, h, s, or p).
+ """
+ ifself._two_phase_limitsisnotNone:
+ # Check existing two-phase limits
+ p_min_old=self._two_phase_limits['p'][0]
+ p_step_old=self._two_phase_limits['p'][1]-p_min_old
+ ifnotnp.isclose(p_min_old,p_min,0,10)ornotnp.isclose(p_step_old,p_step,0,10):
+ warnings.warn(f"Overwriting previously calculated two-phase limits with "
+ f"p_min={p_min_old} and p_step={p_step_old}. This might take a few seconds.\n"
+ f"The quantity might not match with the previously calculated quantities.")
+ self._two_phase_limits=None
+
+ ifself._two_phase_limitsisNone:
+ # Calculate new two-phase limits for plotting
+ _two_phase_limits=get_two_phase_limits(self,p_step=p_step,p_min=p_min)
+ self._two_phase_limits={
+ "T":np.array([state.Tforstatein_two_phase_limits]),
+ "h":np.array([state.hforstatein_two_phase_limits]),
+ "s":np.array([state.sforstatein_two_phase_limits]),
+ "p":np.array([state.pforstatein_two_phase_limits]),
+ }
+
+ ifquantitynotinself._two_phase_limits:
+ raiseValueError("The given quantity is not supported. T, h, s, or p are supported.")
+ returnself._two_phase_limits[quantity]
+
+
[docs]defcalc_mean_transport_properties(self,state_in,state_out):
+"""
+ Calculate the average transport properties for the given states.
+
+ Args:
+ state_in (ThermodynamicState): First state
+ state_out (ThermodynamicState): Second state
+
+ Returns:
+ TransportProperties: Average transport properties
+
+ Notes:
+ The TransportProperties does not contain a state, as an average
+ state is not possible to calculate.
+ """
+ tr_pr_in=self.calc_transport_properties(state_in)
+ tr_pr_out=self.calc_transport_properties(state_out)
+
+ returnTransportProperties(
+ lam=0.5*(tr_pr_in.lam+tr_pr_out.lam),
+ dyn_vis=0.5*(tr_pr_in.dyn_vis+tr_pr_out.dyn_vis),
+ kin_vis=0.5*(tr_pr_in.kin_vis+tr_pr_out.kin_vis),
+ pr=0.5*(tr_pr_in.Pr+tr_pr_out.Pr),
+ cp=0.5*(tr_pr_in.cp+tr_pr_out.cp),
+ cv=0.5*(tr_pr_in.cv+tr_pr_out.cv),
+ state=None)
+
+
+
[docs]defget_two_phase_limits(med_prop:MedProp,p_step:int=1000,p_min:int=int(1e3))->List[ThermodynamicState]:
+"""
+ Return the states representing the boundaries of the two-phase section for the given fluid.
+
+ This function is primarily used for visualizing the two-phase section and validating the accuracy of calculations.
+
+ Args:
+ med_prop (MedProp): An instance of a valid MedProp-Class.
+ p_step (int): The step size for pressure variation in Pa. Default is 1000 Pa.
+ p_min (int): The minimum pressure in Pa from where to start calculation. Default is 1000 Pa.
+
+ Returns:
+ List[ThermodynamicState]: A list of ThermodynamicState instances representing the two-phase limits.
+
+ Notes:
+ The two-phase limits are computed by iterating over a range of pressures from the minimum pressure up to the
+ critical point pressure (exclusive) with a specified step size. States at quality 0 (saturated liquid)
+ and quality 1 (saturated vapor) are appended to form the two-phase boundary. The list is reversed to
+ maintain the correct order for visualization purposes.
+ """
+ _,_p_max,_=med_prop.get_critical_point()
+ q0,q1=[],[]
+ for_pinrange(p_min,int(_p_max),p_step):
+ try:
+ q0.append(med_prop.calc_state("PQ",_p,0))
+ q1.append(med_prop.calc_state("PQ",_p,1))
+ exceptValueErroraserr:
+ logger.info("Could not calculate two-phase limits for p=%s: %s",
+ _p,err)
+ # Reverse list for correct order
+ returnq0+q1[::-1]
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/media/ref_prop.html b/docs/0.1.1/docs/_modules/vclibpy/media/ref_prop.html
new file mode 100644
index 0000000..ec6fb0c
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/media/ref_prop.html
@@ -0,0 +1,1335 @@
+
+
+
+
+
+
+
+ vclibpy.media.ref_prop — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# -*- coding: utf-8 -*-
+"""
+Created on 21.04.2020
+
+@author: Christoph Hoeges, Fabian Wuellhorst, Jona Brach
+
+To test:
+- Transport properties are not fully tested (Status: 11.06.2020)
+- Error raising is not implemented at all refprop calls
+ - Additional change might be that not all errors and warning are excluded when 'use_error_check' is set to false but
+ only warnings or errors?
+"""
+importlogging
+importos
+importwarnings
+importshutil
+importatexit
+
+fromctREFPROP.ctREFPROPimportREFPROPFunctionLibrary
+
+fromvclibpy.mediaimportThermodynamicState,TransportProperties,MedProp
+
+
+logger=logging.getLogger(__name__)
+
+
+
[docs]classRefProp(MedProp):
+"""
+ Class to connect to refProp package.
+
+ Args:
+ :param string fluid_name:
+ Fluid name for RefProp use
+ :param list or None z:
+ Fluid composition. Should only be used, when a self-design mixture shall be used. Further information
+ see notes.
+ When you want to use a self-design mixture to as follows:
+ - Fluid-name needs to contain all component names within mixture: "R32.FLD|R125.FLD"
+ - z needs to be a list with molar fractions: [0.697, 0.303]
+ - Used example would be similar to R410A
+
+ :param string dll_path:
+ Specifier for the dll path used for RefProp,
+ e.g. dll_path='C:\\path_to_dll\\RefProp64.dll'.
+ If None, the `ref_prop_path` and function `get_dll_path` are
+ used to determine the dll path
+ :param boolean use_error_check:
+ Specifier whether errors and warnings shall be checked when calling RefProp or not
+ :param boolean use_warnings:
+ Specifier whether warnings shall be used
+ :param str ref_prop_path:
+ Path to RefProp package. Default is the ENV variable `RPPREFIX`.
+ :param bool copy_dll:
+ If True (not the default), a copy of the dll is created to enable simultaneous use of
+ multiple fluids in multiprocessing.
+ :param str copy_dll_directory:
+ If `copy_dll` is True, the DLL is copied to this directory.
+ If None (default), the current working directory is used.
+
+ Note:
+ - You need to install package ctREFPROP Package
+ https://github.com/usnistgov/REFPROP-wrappers/tree/master/wrappers/python
+ - In case you use a self-defined mixture: Entropy reference state might deviate from GUI!!
+
+ Functionality:
+ - It does not work to have multiple instances simultaneously. When calculating values the last fluid name
+ somehow will be used even though instance includes "new" name. Need to be fixed at some point.
+
+
+ How to use RefProp-Wrapper:
+ ---------------------------
+ 1.) Create RefProp instance: rp = RefProp("R32")
+ 2.) In case you want to calculate fluid properties (state variables) for a specific state: Use calc_state() function
+ Multiple inputs can be given (but you need to now two in order to define the values). For further
+ information see function header.
+ 3.) Further get-Functions implemented
+ - get_gwp(): Global warming potential
+ - get_odp(): Ozone depletion potential
+ - get_safety(): Safety class
+ - get_mass_fraction(): Mass fractions of pure substances in fluid
+ - get_molar_mass(): Molar mass of fluid
+ - get_mol_fraction(): Mol fractions of pure substances in fluid
+ - get_comp_names(): Pure substances names in fluid
+ - get_longname(): Long name of fluid within RefProp
+ - get_critical_point(): Crit. Temperature and Pressure
+ - get_version(): Version of wrapper and of RefProp dll
+
+
+ Version notes:
+ --------------
+ 0.1.0 (21.04.2020, Christoph Hoeges):
+ First implementation
+ - Contains multiple functions to call RefProp instance
+ - Can calculate state, crit, GWP, ODP, Safety class, molar mass etc. of fluid
+ - Mixtures might work but it wasn't fully testet - R455A e.g. still deviates slightly
+
+ 0.1.1 (25.04.2020, Christoph Hoeges):
+ Multiple changes, added functionality. Commands are still the same though
+ - Self designed mixtures are possible now as well (see instructions in init
+ - Added composition input to __init__ function
+ - Added modes in calc_state function and removed bug in PH calculation
+ - Additional protected functions due to different input
+ - Adjusted _call_refprop function (z is not mass frac but mol fraction)
+ - Added documentation / instruction
+
+ 0.1.2 (08.05.2020, Christoph Hoeges):
+ Multiple adjustments
+ - Added function to call ABFLSHdll function in refprop (high level function)
+ - Changed function calls in calc_state function to ABFLSH and adjusted conversion
+ - Change init-function so user can choose which dll shall be used
+ - Add function to get version of this current wrapper as well as RefProp-dll version
+
+ 0.1.3 (12.05.2020, Christoph Hoeges):
+ Multiple changes
+ - Added function to get all files in MIXTURE and FLUIDS directory to check available fluids
+ - Added error function in order to return errors when RefProp-Functions are called.
+ NOTE: Not all instances where refprop is called are checked for errors. Currently, it is only used in
+ init and calc_state
+
+ 0.1.4 (19.05.2020, Christoph Hoeges):
+ Multiple changes:
+ - Debugged fluid properties calculation for predefined mixtures
+ - Fixed option of self-defined mixtures
+
+ 0.1.5 (22.05.2020, Christoph Hoeges):
+ Include transport properties calculation into wrapper
+
+ 0.1.6 (10.06.2020, Fabian Wuellhorst):
+ Add option to use a custom dll path. This necessary to use multiple instances with possible
+ different fluids at the same time.
+ """
+ # General information
+ #
+ __version__="0.1.6"
+ __author__="Christoph Hoeges"
+
+ _fluid_mapper={'air':'air.ppf'}
+
+ def__init__(self,
+ fluid_name,
+ z=None,
+ dll_path:str=None,
+ use_error_check:bool=True,
+ use_warnings:bool=True,
+ ref_prop_path:str=None,
+ copy_dll:bool=True,
+ copy_dll_directory:str=None
+ ):
+ ifref_prop_pathisNone:
+ # Get environment variable for path to dll
+ ref_prop_path=os.environ["RPPREFIX"]
+ ifdll_pathisNone:
+ path_to_dll=self.get_dll_path(ref_prop_path)
+ else:
+ path_to_dll=dll_path
+
+ ifcopy_dll:
+ ifcopy_dll_directoryisNone:
+ copy_dll_directory=os.getcwd()
+ try:
+ self._delete_dll_path=os.path.join(
+ copy_dll_directory,
+ f"med_prop_{fluid_name}_REFPRP64.dll"
+ )
+ shutil.copyfile(path_to_dll,self._delete_dll_path)
+ atexit.register(self.terminate)
+ path_to_dll=self._delete_dll_path
+ except(PermissionError,FileExistsError)aserr:
+ logger.error("Can't copy file to new path: %s",err)
+ else:
+ self._delete_dll_path=None
+ logger.info("Using dll: %s",path_to_dll)
+
+ super().__init__(fluid_name=fluid_name)
+
+ self._flag_check_errors=use_error_check
+ self._flag_warnings=use_warnings
+ # Set path to RefProp package
+ self._ref_prop_path=ref_prop_path
+ self.rp=REFPROPFunctionLibrary(path_to_dll)
+ self.rp.SETPATHdll(ref_prop_path)
+ self.molar_base_si=self.rp.GETENUMdll(0,"MOLAR BASE SI").iEnum
+ # Set fluid name
+ self.fluid_name=fluid_name
+ # Get mass and mol fraction and number of components
+ self._get_comp_frac(z)
+ # Get component names
+ self._comp_names=self._get_comp_names()
+ # Mixture flag
+ ifself._n_comp>1:
+ self._mix_flag=True
+ else:
+ self._mix_flag=False
+
+ # Setup
+ self._setup_rp()
+ # Calculate molar mass in kg/mol
+ # self.M = self._call_refprop_allprop("M").Output[0]
+ self.M=self._call_refprop(inp_name="",out_name="M").Output[0]# kg/mol
+
+ self._nbp=None
+
+
+
+ def_delete_dll(self):
+ try:
+ # Taken from here: https://stackoverflow.com/questions/21770419/free-the-opened-ctypes-library-in-python
+ import_ctypes
+ importsys
+ _handle=self.rp.dll._handle
+ ifsys.platform.startswith('win'):
+ _ctypes.FreeLibrary(_handle)
+ else:
+ _ctypes.dlclose(_handle)
+ os.remove(self._delete_dll_path)
+ self._delete_dll_path=None
+ except(FileNotFoundError,PermissionError)aserr:
+ logger.error(
+ "Could not automatically delete the copied RefProp dll at %s. "
+ "Delete it yourself! Error message: %s",self._delete_dll_path,err
+ )
+
+ def_call_refprop_abflsh(self,
+ inp_name,
+ value_a,
+ value_b,
+ i_flag=1):
+""" Call RefProp via ABFLSHdll method
+ You can define multiple inputs but only "specific ones" where no input values are needed for
+ e.g. M, Tcrit, pcrit
+
+ Parameters:
+ -----------
+ :param string inp_name:
+ Input commands: "PQ"
+ :param float value_a:
+ Value of parameter b defined in inp_name. In case of None 0 will be used.
+ :param float value_b:
+ Value of parameter b defined in inp_name. In case of None 0 will be used.
+ :param int i_flag:
+ Flag
+ Return:
+ -------
+ :return ABFLSHdlloutput tmp:
+ Returns ABFLSH output
+
+ """
+ # TODO
+ tmp=self.rp.ABFLSHdll(inp_name,value_a,value_b,self._mol_frac,i_flag)
+
+ returntmp
+
+ def_call_refprop_allprop(self,
+ out_name,
+ T_val=None,
+ d_val=None,
+ i_mass=0,
+ i_flag=1):
+""" Call RefProp via ALLPROPSdll-method
+
+ Parameters:
+ -----------
+ :param string out_name:
+ Variables you want to calculate. Multiple outputs are possible:
+ - Single: "M"
+ - Multiple: "M,TC,PC"
+ :param float T_val:
+ Temperature in current state in K.
+ Note: In case you want to get general fluid parameters such as M, Tcrit, .. Stick to default value!
+ :param float d_val:
+ Density in current state (unit depending on i_mass flag - either mol/m^3 or kg/m^3)
+ Note: In case you want to get general fluid parameters such as M, Tcrit, .. Stick to default value!
+ :param int i_mass:
+ Specifies which units the inputs are given in.
+ - 0: Molar based
+ - 1: Mass based
+ Note: In current version (10.0.0.72) Ian Bell says in multiple Git-Issues that you should stick to molar
+ base!
+ :param int i_flag:
+ In current version (10.0.0.72) i_flag is used to define whether a string containing the units is written in
+ 'hUnits'.
+ - 0: Deactivated (increases the calculation speed)
+ - 1: activated
+ Return:
+ -------
+ :return ALLPROPSdlloutput result:
+ List with values for parameters
+
+ """
+ # Check values of T and d
+ ifT_valisNone:
+ T_val=0
+ ifd_valisNone:
+ d_val=0
+ # Define fraction used depending on i_mass flag
+ ifi_mass==0:
+ frac=self._mol_frac
+ elifi_mass==1:
+ frac=self._mass_frac
+ else:
+ raiseValueError("Chosen i_mass flag '{}' is not possible in ALLPROPSdll function!".format(i_mass))
+
+ # Call RefProp
+ res=self.rp.ALLPROPSdll(out_name,self.molar_base_si,i_mass,i_flag,T_val,d_val,frac)
+
+ returnres
+
+ def_call_refprop(self,
+ inp_name,
+ out_name,
+ value_a=None,
+ value_b=None,
+ i_mass=0,
+ i_flag=1,
+ frac=None,
+ fluid=None):
+""" Call general refProp function and calculate values
+
+ Parameters:
+ -----------
+ :param string fluid:
+ Fluid name - in case default value None is used, stored fluid name will be used for command
+ :param string inp_name:
+ Input parameter specification
+ :param string out_name:
+ Output string name
+ :param float value_a:
+ Value of parameter b defined in inp_name. In case of None 0 will be used.
+ :param float value_b:
+ Value of parameter b defined in inp_name. In case of None 0 will be used.
+ :param integer i_flag:
+ Defines further settings (see documentation)
+ :param int i_mass:
+ Specifies which units the inputs are given in. # TODO: WRONG! iMass determines composition, iUnits determines properties (except q)
+ - 0: Molar based
+ - 1: Mass based
+ Note: In current version (10.0.0.72) Ian Bell says in multiple Git-Issues that you should stick to molar
+ base!
+ :param list frac:
+ List with either mol or mass fraction of pure substances in current fluid
+ (in case of single pure substance: [1]).
+
+ Return:
+ -------
+ :return REFPROPdlloutput output:
+ Command of refprop
+ """
+ # Check inputs
+ ifvalue_aisNone:
+ value_a=0
+ ifvalue_bisNone:
+ value_b=0
+
+ iffluidisNone:
+ ifself._predefined:
+ fluid=self.fluid_name# TODO: in general not necessary, decreases performance
+ else:
+ fluid=""
+ iffracisNone:
+ ifself._predefined:
+ frac=""
+ else:
+ ifi_mass==0:
+ frac=self._mol_frac
+ elifi_mass==1:
+ frac=self._mass_frac
+ else:
+ raiseValueError("Variable i_mass has invalid input '{}'".format(i_mass))
+
+ # Call refprop function
+ tmp=self.rp.REFPROPdll(fluid,
+ inp_name,
+ out_name,
+ self.molar_base_si,
+ i_mass,
+ i_flag,
+ value_a,
+ value_b,
+ frac)
+
+ returntmp
+
+ def_check_error(self,
+ err_num,
+ err_msg,
+ func_name=""):
+""" Check error code and raise error in case it is critical
+
+ Parameters:
+ -----------
+ :param integer err_num:
+ Error return code
+ :param string err_msg:
+ Error message given in RefProp call
+ :param string func_name:
+ Name of function error needs to be checked in
+ """
+ # All fine in case error number is 0
+ # Throw warning in case error number different than 0 and smaller than 100 is given
+ # Throw error in case error number higher than 100 is given
+ iferr_num:
+ iferr_num<100:
+ ifself._flag_warnings:
+ warnings.warn("[WARNING] Error number {} was given in function '{}'. No critical error but "
+ "something went wrong maybe. \n Error message is: '{}'".format(str(err_num),
+ func_name,err_msg))
+ else:
+ ifself._flag_check_errors:
+ raiseTypeError("[ERROR] When calling RefProp in function '{}' error number {} was "
+ "returned. \n Error message is: '{}'".format(func_name,str(err_num),err_msg))
+
+ def_get_comp_names(self):
+""" Get component names. In case current fluid is mixture, only component names will be returned.
+ In case fluid is a pure substance, substance name is returned.
+
+ Return:
+ -------
+ :return list comp_names:
+ List with pure substances in current refrigerant
+ """
+ comp_names=[]
+ ifself._predefined:
+ # Note: While trying it was possible to get fluid name as well, therefore n_comp+1 is used.
+ ifself._n_comp>1:
+ foriinrange(1,self._n_comp+3):
+ test=self.rp.NAMEdll(i)
+ tmp=test.hn80.replace(".FLD","")
+ ifnottmp=="":
+ comp_names.append(tmp)
+ else:
+ foriinrange(self._n_comp+3):
+ tmp=self.rp.NAMEdll(i).hnam
+ ifnottmp=="":
+ comp_names.append(tmp)
+
+ else:
+ # Self-defined
+ tmp_str=self.fluid_name.split("|")
+ foriintmp_str:
+ i=i.replace(".FLD","")
+ i=i.replace(".MIX","")
+ iflen(i)<2:
+ continue
+ else:
+ comp_names.append(i)
+
+ # Replace Carbon Dioxide for CO2
+ fori,tmpinenumerate(comp_names):
+ if"Carbon dio"intmp:
+ comp_names[i]="CO2"
+
+ returncomp_names
+
+ def_get_comp_frac(self,z):
+""" Get mass/mol fraction and number of components of current fluid
+
+ Parameters:
+ -----------
+ :param list z:
+ Contains predefined molar fractions in case one is given. Otherwise, z will be None
+ """
+ # Check if predefined or not
+ # Predefined
+ ifzisNone:
+ self._predefined=True
+ # Pure substance
+ eliflen(z)==1:
+ self._predefined=True
+ # Self-designed mixture
+ else:
+ self._predefined=False
+
+ # In case predefined mixture or pure substance is used
+ ifself._predefined:
+ # Dummy function to get values for z in order to specify number of components
+ tmp_mol=self.rp.REFPROPdll(self.fluid_name,"PQ","H",self.molar_base_si,0,0,101325,0,[1])
+ tmp_mass=self.rp.REFPROPdll(self.fluid_name,"PQ","H",self.molar_base_si,1,0,101325,0,[])
+ # Check for errors
+ self._check_error(tmp_mol.ierr,tmp_mol.herr,self._get_comp_frac.__name__)
+ self._check_error(tmp_mass.ierr,tmp_mass.herr,self._get_comp_frac.__name__)
+ # Mass and molar fractions of components
+ self._mol_frac=[ziforziintmp_mol.zifzi>0]
+ self._mass_frac=[ziforziintmp_mass.zifzi>0]
+ # Get number of components
+ self._n_comp=len(self._mol_frac)
+ # Check, whether error occurred
+ ifself._n_comp<1:
+ # It might be possible that z value bugs when calling RefProp. In case of a pure substance this does not
+ # matter so an additional filter is included
+ iflen(self._mass_frac)==1:
+ self._n_comp=1
+ self._mol_frac=[1]
+ else:
+ raiseValueError("Number of components for current fluid '{}' is less than "
+ "one!".format(self.fluid_name))
+
+ # Get mol fraction
+ # self._mol_frac = self._transform_to_molfraction(self._mass_frac)
+ else:
+ # Mol fraction
+ self._mol_frac=z
+ self._mass_frac=[]
+ # Get number of components
+ self._n_comp=len(self._mol_frac)
+
+ def_setup_rp(self):
+""" Setup for RefProp """
+ # Errors can occur in case REFPROP is initalizated multiple time with same fluid - thus a pre setup is used here
+ self.rp.SETUPdll(1,"N2","HMX.BNC","DEF")
+ # In case of pure substance
+ ifself._n_comp==1:
+ self.rp.SETUPdll(self._n_comp,self.fluid_name,"HMX.BNC","DEF")
+ # In case of mixtures
+ else:
+ # Check if mixture is predefined
+ ifself._predefined:
+ # Pre defined mixture - different baseline operating point is used
+ mode=2
+ mixture="|".join([f+".FLD"forfinself._comp_names])
+ n_comp=self._n_comp
+ else:
+ # Self defined mixture
+ mode=1
+ n_comp=self._n_comp
+ # Define mixtures name
+ # TODO: Ending is not necessary to create mixtures....
+ mixture="|".join([f+".FLD"forfinself._comp_names])
+
+ # Setup for mixture
+ setup=self.rp.SETUPdll(n_comp,mixture,'HMX.BNC','DEF')
+ setref=self.rp.SETREFdll("DEF",mode,self._mol_frac,0,0,0,0)
+ # z = self._mol_frac
+ # Get mass fraction
+ self._mass_frac=self._transform_to_massfraction(self._mol_frac)
+
+ # Check whether mixing rules are available
+ ifsetup.ierr==117:
+ ifself._flag_check_errors:
+ raiseValueError(
+ "[MIXING ERROR] Mixing rules for mixture '{}' do not exist!".format(self._comp_names))
+ else:
+ print(
+ "[MIXING ERROR] Mixing rules for mixture '{}' do not exist!".format(self._comp_names))
+ elifsetup.ierr==-117:
+ ifself._flag_warnings:
+ warnings.warn(
+ "[MIXING ERROR] Mixing rules for mixture '{}' are estimated!".format(self._comp_names))
+ else:
+ print(
+ "[MIXING WARNING] Mixing rules for mixture '{}' are estimated!".format(self._comp_names))
+
+ def_transform_to_massfraction(self,
+ mol_frac):
+""" Transforms mol fraction to mass fraction
+
+ Parameters:
+ -----------
+ :param list mol_frac:
+ List containing floats for mol fraction
+
+ Return:
+ -------
+ :return list mass_frac:
+ List containing floats for mass fraction
+ """
+ tmp=self.rp.XMASSdll(mol_frac)
+ mass_frac=[yiforyiintmp.xkgifyi>0]
+ returnmass_frac
+
+ def_transform_to_molfraction(self,
+ mass_frac):
+""" Transforms mass fraction to mol fraction
+
+ Parameters:
+ -----------
+ :param list mass_frac:
+ List containing floats for mass fraction
+
+ Return:
+ -------
+ :return list frac:
+ List containing floats for mol fraction
+ """
+ tmp=self.rp.XMOLEdll(mass_frac)
+ mol_frac=[xiforxiintmp.xmolifxi>0]
+ returnmol_frac
+
+
[docs]defcalc_state(self,mode:str,var1:float,var2:float,kr=1):
+""" Calculate state. Depending on mode, different function will be chosen. Input state variables need to be in
+ SI units!
+
+ Notes:
+ ------
+ 1.) PT does not work when state might be within the two-phase region!
+ 2.) Only functions for density are implemented. In case you know the specific volume instead use density
+ functions with inverse value!
+ 3.) Quality can have values outside of 'physical' scope:
+ q = -998: Subcooled liquid
+ q = 998: Superheated vapor
+ q = 999: Supercritical state
+
+ Possible modes are currently:
+ - "PD": Pressure, Density
+ - "PH": Pressure, Enthalpy
+ - "PQ": Pressure, Quality
+ - "PS": Pressure, Entropy
+ - "PT": Pressure, Temperature
+ - "PU": Pressure, Internal Energy
+ - "TD": Temperature, Density
+ - "TH": Temperature, Enthalpy
+ - "TQ": Temperature, Quality
+ - "TS": Temperature, Entropy
+ - "TU": Temperature, Internal Energy
+ - "DH": Density, Enthalpy
+ - "DS": Density, Entropy
+ - "DU": Density, Internal Energy
+ - "HS": Enthalpy, Entropy
+
+ Parameters:
+ -----------
+ :param string mode:
+ Defines which input state variables are given (see possible modes above)
+ :param float var1:
+ Value of state variable 1 (first one in name) - use SI units!
+ :param float var2:
+ Value of state variable 2 (second one in name) - use SI units!
+ :param int kr:
+ phase flag (kr=1: lower density, kr=2: higher density)
+ relevant for "TH", "TS", "TU"
+
+ Return:
+ -------
+ :return ThermodynamicState state:
+ Thermodynamic state with state variables
+ """
+ # Multiplier for pressure since kPa is used in RefProp
+ p_multi=1e-3
+ # Multiplier for energy
+ e_multi=self.M
+ # Multiplier for density
+ d_multi=1/self.M/1000
+
+ # Init all parameters
+ p=None
+ d=None
+ T=None
+ u=None
+ h=None
+ s=None
+ q=None
+
+ # Check modi
+
+ # Pressure and density
+ ifmode=="PD":
+ p=var1
+ d=var2
+ var1=var1*p_multi
+ var2=var2*d_multi
+ tmp=self.rp.PDFLSHdll(var1,var2,self._mol_frac)
+ # Pressure and enthalpy
+ elifmode=="PH":
+ p=var1
+ var1=var1*p_multi
+ h=var2
+ var2=var2*e_multi
+ tmp=self.rp.PHFLSHdll(var1,var2,self._mol_frac)
+ # Pressure and quality
+ elifmode=="PQ":
+ p=var1
+ var1=var1*p_multi
+ q=var2
+ # In case current fluid is mixture you need to transform Q to molar base for RefProp-function
+ ifself._mix_flag:
+ var2=self._call_refprop("PQMASS","QMOLE",p,q,i_mass=1).Output[0]
+ tmp=self.rp.PQFLSHdll(var1,var2,self._mol_frac,0)
+ # Pressure and entropy
+ elifmode=="PS":
+ p=var1
+ var1=var1*p_multi
+ s=var2
+ var2=var2*e_multi
+ tmp=self.rp.PSFLSHdll(var1,var2,self._mol_frac)
+ # Pressure and Temperature
+ elifmode=="PT":
+ p=var1
+ var1=var1*p_multi
+ T=var2
+ tmp=self.rp.TPFLSHdll(var2,var1,self._mol_frac)
+ # Pressure and internal energy
+ elifmode=="PU":
+ p=var1
+ var1=var1*p_multi
+ u=var2
+ var2=var2*e_multi
+ # mode = "PE"
+ tmp=self.rp.PEFLSHdll(var1,var2,self._mol_frac)
+ # Temperature and density
+ elifmode=="TD":
+ T=var1
+ d=var2
+ var2=var2*d_multi
+ tmp=self.rp.TDFLSHdll(var1,var2,self._mol_frac)
+ # Temperature and enthalpy
+ elifmode=="TH":
+ T=var1
+ h=var2
+ var2=var2*e_multi
+ tmp=self.rp.THFLSHdll(T,var2,self._mol_frac,kr)
+ # Temperature and quality
+ elifmode=="TQ":
+ T=var1
+ q=var2
+ # In case current fluid is mixture you need to transform Q to molar base for RefProp-function
+ ifself._mix_flag:
+ var2=self._call_refprop("TQMASS","QMOLE",T,q,i_mass=1).Output[0]
+ tmp=self.rp.TQFLSHdll(T,var2,self._mol_frac,1)
+ # Temperature and entropy
+ elifmode=="TS":
+ T=var1
+ s=var2
+ var2=var2*e_multi
+ tmp=self.rp.TSFLSHdll(T,var2,self._mol_frac,kr)
+ # Temperature and internal energy
+ elifmode=="TU":
+ T=var1
+ u=var2
+ var2=var2*e_multi
+ # mode = "TE"
+ tmp=self.rp.TEFLSHdll(T,var2,self._mol_frac,kr)
+ # Density and enthalpy
+ elifmode=="DH":
+ d=var1
+ var1=var1*d_multi
+ h=var2
+ var2=var2*e_multi
+ tmp=self.rp.DHFLSHdll(var1,var2,self._mol_frac)
+ # Density and entropy
+ elifmode=="DS":
+ d=var1
+ var1=var1*d_multi
+ s=var2
+ var2=var2*e_multi
+ tmp=self.rp.DSFLSHdll(var1,var2,self._mol_frac)
+ # Density and inner energy
+ elifmode=="DU":
+ d=var1
+ var1=var1*d_multi
+ u=var2
+ var2=var2*e_multi
+ # mode = "DE"
+ tmp=self.rp.DEFLSHdll(var1,var2,self._mol_frac)
+ elifmode=="HS":
+ h=var1
+ var1=var1*e_multi
+ s=var2
+ var2=var2*e_multi
+ tmp=self.rp.HSFLSHdll(var1,var2,self._mol_frac)
+ else:
+ raiseValueError("Chosen mode is not available in refprop calc_state function!")
+
+ # Check for errors
+ self._check_error(tmp.ierr,tmp.herr,self.calc_state.__name__)
+
+ # Get all state variables
+ ifpisNone:
+ p=tmp.P/p_multi
+ ifTisNone:
+ T=tmp.T
+ ifuisNone:
+ u=tmp.e/e_multi
+ ifhisNone:
+ h=tmp.h/e_multi
+ ifsisNone:
+ s=tmp.s/e_multi
+ ifdisNone:
+ d=tmp.D/d_multi
+ ifqisNone:
+ ifself._mix_flag:
+ # Transform current q (molar) to mass based quality
+ tmp2=self._call_refprop("PH","QMASS",p,h*e_multi,i_mass=1)
+ iftmp2.Output[0]<0:
+ q_mass=tmp2.ierr
+ else:
+ q_mass=tmp2.Output[0]
+ q=q_mass
+ else:
+ q=tmp.q
+
+ # # In case not in two phase region reset q to -1
+ # if q > 1 or q < 0:
+ # q = -1
+
+ # Define state
+ state=ThermodynamicState(p=p,T=T,u=u,h=h,s=s,d=d,q=q)
+ returnstate
[docs]defcalc_transport_properties(self,state:ThermodynamicState):
+""" Calculate transport properties of RefProp fluid at given state
+
+ Parameters:
+ -----------
+ :param ThermodynamicState state:
+ Current thermodynamic state
+ Return:
+ -------
+ :return TransportProperties props:
+ Instance of TransportProperties
+ """
+ # Get properties
+ tmp=self._call_refprop_allprop("PRANDTL,VIS,TCX,KV,CV,CP,BETA,STN,ACF",state.T,state.d/self.M,i_mass=0)
+ # Create transport properties instance
+ props=TransportProperties(lam=tmp.Output[2],
+ dyn_vis=tmp.Output[1],
+ kin_vis=tmp.Output[3],
+ pr=tmp.Output[0],
+ cp=tmp.Output[5]/self.M,
+ cv=tmp.Output[4]/self.M,
+ beta=tmp.Output[6],
+ sur_ten=tmp.Output[7],
+ ace_fac=tmp.Output[8],
+ state=state)
+ # Return props
+ returnprops
+
+
[docs]defget_available_substances(self,
+ mode="all",
+ include_ending=False,
+ save_txt=False):
+""" Get all available RefProp fluids (mixtures and / or pure substances depending on mode)
+
+ Parameters:
+ -----------
+ :param string mode:
+ Mode defining which kind of fluids you want to have: 'pure': pure substances, 'mix': mixtures, 'all': all
+ :param boolean include_ending:
+ Defines, whether file ending shall be returned as well or not
+ :param boolean save_txt:
+ Defines, whether a text file with names shall be created in current working directory
+ Return:
+ -------
+ :return list names:
+ String list containing names of available fluids (depending on defined mode)
+ """
+ # Possible endings
+ _endings=["MIX","FLD","PPF"]
+
+ # Folders where fluid data is located
+ folders=[r"FLUIDS",r"MIXTURES"]
+
+ # Define paths by mode
+ ifmode=="pure":
+ paths=[os.path.join(self._ref_prop_path,folders[0])]
+ elifmode=="mix":
+ paths=[os.path.join(self._ref_prop_path,folders[1])]
+ elifmode=="all":
+ paths=[os.path.join(self._ref_prop_path,folders[0]),
+ os.path.join(self._ref_prop_path,folders[1])]
+ else:
+ raiseValueError("Chosen mode '{}' is not possible!".format(mode))
+
+ # Get files in folders, remove ending and append to names
+ names=[]
+ forpinpaths:
+ # Get all files in directory
+ files=[fforfinos.listdir(p)ifos.path.isfile(os.path.join(p,f))]
+ # Remove ending
+ ifinclude_ending:
+ tmp=[pathforpathinfilesifpath.split(".")[1]in_endings]
+ else:
+ tmp=[path.split(".")[0]forpathinfilesifpath.split(".")[1]in_endings]
+ # Append names to names list
+ names.extend(tmp)
+
+ # In case names shall be stored
+ ifsave_txt:
+ withopen("available_fluids.txt","w")asoutput:
+ foriinnames:
+ output.write("{}\n".format(i))
+
+ ifnotnames:
+ raiseValueError("No fluids are in current RefProp directory. Check path '{}'!".format(self._ref_prop_path))
+
+ # Return names
+ returnnames
+
+
[docs]defget_comp_names(self):
+""" Get name of components within current fluid"
+
+ Return:
+ -------
+ :return list comp_names:
+ String names of components within current fluid
+ """
+ returnself._comp_names
+
+
[docs]defget_nbp(self):
+""" Get normal boiling point (T @ q=0 and 1 bar)
+
+ Return:
+ -------
+ :return float nbp:
+ Normal boiling point of refrigerant in °C
+ """
+ ifnotself._nbp:
+ self._nbp=self.calc_state("PQ",1e5,0.0).T-273.15
+
+ returnself._nbp
+
+
[docs]defget_molar_composition(self,state:ThermodynamicState,z_molar=None):
+""" Get composition on molar base. Liquid phase, vapor phase and total.
+
+ :param ThermodynamicState state: the state whose compositions will be returned
+ :param list z_molar: molar composition of fluid. In case of None, default value _mol_frac is used
+ :return:
+ - list x:
+ composition of liquid phase
+ - list y:
+ composition of vapor phase
+ - list z:
+ composition in total
+ """
+ ifz_molarisNone:
+ z=self._mol_frac
+ M=self.M
+ else:
+ z=z_molar
+ M=self.rp.REFPROPdll(hFld="",
+ hIn="",
+ hOut="M",
+ iUnits=self.molar_base_si,
+ iMass=0,
+ iFlag=1,
+ a=0,
+ b=0,
+ z=z_molar).Output[0]
+ num_components=len(z)
+
+ tmp=self.rp.TDFLSHdll(T=state.T,
+ D=state.d/M/1000,
+ z=z)
+ # TDFLSHdll is deprecated, use the following in future:
+ # tmp = self.rp.ABFLSHdll(ab="TD",
+ # a=state.T,
+ # b=state.d / self.M / 1000,
+ # z=self._mol_frac,
+ # iFlag=0) # molar units
+
+ x=list(tmp.x[:num_components])
+ y=list(tmp.y[:num_components])
+
+ returnx,y,z
+
+
[docs]defget_critical_point(self):
+""" Get T and p of critical point
+
+ Return:
+ -------
+ :return float Tc:
+ Temperature at critical point in K
+ :return float pc:
+ Pressure at critical point in Pa
+ :return float dc:
+ Density at critical point in kg/m^3
+ """
+ mode=2
+ ifmode==1:
+ tmp=self._call_refprop("CRIT","T,P,D",i_mass=1)
+ Tc=tmp.Output[0]
+ pc=tmp.Output[1]
+ dc=tmp.Output[2]*self.M
+ else:
+ res=self._call_refprop_allprop("TC,PC,DC")
+ Tc=res.Output[0]
+ pc=res.Output[1]
+ dc=res.Output[2]*self.M
+ returnTc,pc,dc
+
+ #
+ #
+
[docs]defget_def_limits(self):
+""" Get limits of current ref prop fluid
+ Limits contain Tmin, Tmax, Dmax and Pmax (Temperatures, density, pressure)
+
+ :return dict limits:
+ Dictionary with definition limits in RefProp. Contains min/max temperature, max density, max pressure.
+ """
+ tmp=self._call_refprop_allprop("TMIN,TMAX,DMAX,PMAX")
+ limits={"Tmin":tmp.Output[0],
+ "Tmax":tmp.Output[1],
+ "Dmax":tmp.Output[2]*self.M,
+ "Pmax":tmp.Output[3]}
+ returnlimits
+
+
[docs]@staticmethod
+ defget_dll_path(ref_prop_path:str):
+"""
+ Return the location of the dll
+
+ Return:
+ :return: string path_to_dll:
+ Path of a valid dll
+ """
+ path_to_dll=os.path.join(ref_prop_path,r"REFPRP64.DLL")
+
+ # Check if dll actually exists:
+ ifnotos.path.exists(path_to_dll):
+ raiseFileNotFoundError("Selected dll not found automatically. "
+ "Please alter the local attribute or search for yourself.")
+
+ returnpath_to_dll
[docs]defget_gwp(self):
+""" Get gwp of current fluid from refProp
+
+ Return:
+ -------
+ :return float gwp:
+ Global warming potential of fluid. In case calculation failed, None will be returned.
+ """
+ # Call refProp
+ tmp=self._call_refprop("",
+ "GWP",
+ i_mass=1)
+ # Calculate gwp
+ gwp=round(sum(max(tmp.Output[i],0)*self._mass_frac[i]foriinrange(self._n_comp)),2)
+ # In case GWP cannot be calculated
+ ifgwp<0:
+ gwp=0
+ returngwp
+
+
[docs]defget_longname(self):
+""" Get longname of fluid
+
+ Return:
+ -------
+ :return string longname:
+ Name of current fluid in refprop - provides mass fractions and components as well (in case of mixture)
+ """
+ longname=self._call_refprop("","LONGNAME(0)").hUnits
+ returnlongname
+
+
[docs]defget_mass_fraction(self,use_round=True):
+""" Get mass fractions of pure substances in current fluid
+
+ Parameters:
+ :param boolean use_round:
+ Flag to define, whether the exact values or rounded values (by the fourth number) shall be used
+ Return:
+ -------
+ :return list mass_frac:
+ List of component mass fractions
+ """
+ ifuse_round:
+ mass_frac=[round(i,4)foriinself._mass_frac]
+ else:
+ mass_frac=self._mass_frac
+ returnmass_frac
+
+
[docs]defget_molar_mass(self):
+""" Get molar mass of current fluid
+
+ Return:
+ -------
+ :return float M:
+ Molar mass of current fluid in kg/mol
+ """
+ returnself.M
+
+
[docs]defget_mol_fraction(self,use_round=True):
+""" Get mol fractions of pure substances in current fluid
+
+ Parameters:
+ :param boolean use_round:
+ Flag to define, whether the exact values or rounded values (by the fourth number) shall be used
+ Return:
+ -------
+ :return list frac:
+ List of component mol fractions
+ """
+ ifuse_round:
+ mol_frac=[round(i,4)foriinself._mol_frac]
+ else:
+ mol_frac=self._mol_frac
+ returnmol_frac
+
+
[docs]defget_odp(self):
+""" Calculate ozone depletion potential
+ In case of mixtures: Maximum value of pure substances will be used
+
+ Return:
+ -------
+ :return float odp:
+ ODP of fluid. In case calculation failed, None will be returned.
+ """
+ # Call refProp
+ tmp=self._call_refprop("",
+ "ODP",
+ i_mass=1)
+ # Calculate odp
+ odp=max(max(tmp.Output),0)
+ # In case some error occured
+ ifodp<0:
+ odp=0
+ returnodp
+
+
[docs]defget_safety(self):
+""" Calculate safety class of refrigerant
+
+ Return:
+ -------
+ :return string safety:
+ Safety class of fluid.
+ """
+ # Call refProp
+ tmp=self._call_refprop("",
+ "SAFETY",
+ i_mass=1)
+ # Get safety class
+ safety=tmp.hUnits
+ # Return safety
+ returnsafety
+
+
[docs]defget_sat_vap_pressure(self,T_sat):
+""" Get pressure of saturated vapor for defined temperature
+
+ Note:
+ - works for vapor, liquid and solid
+ - returns equilibrium pressure at defined line (q=1)
+
+ Parameters:
+ :param float T_sat:
+ Temperature in K
+ Return:
+ :return float p_sat:
+ Vapor pressure in Pa
+ """
+ trip=self.get_triple_point()
+ iftrip[0]<=T_sat:
+ p_sat=self.calc_state("TQ",T_sat,1.0).p
+ else:
+ tmp=self.rp.REFPROPdll("","TSUBL","P",self.molar_base_si,0,0,T_sat,0.0,self._mol_frac)
+ p_sat=tmp.Output[0]
+ returnp_sat
+
+
[docs]defget_triple_point(self):
+""" Get temperature and pressure at triple point of current fluid
+
+ Note: Works fine for pure substances, mixtures might not work properly
+
+ Return:
+ :return float T_tpl:
+ Temperature at triple point in K
+ :return float p_tpl:
+ Pressure at triple point in Pa
+ """
+ # Call Refprop
+ tmp=self._call_refprop("TRIP","T;P")
+ returntmp.Output[0],tmp.Output[1]
+
+
[docs]defget_version(self):
+""" Get version of wrapper and used RefProp dll
+
+ Return:
+ :return string wrapper_version:
+ Refprop wrapper version
+ :return string refprop_version:
+ Version of used RefProp dll
+ """
+ returnself.__version__,self.rp.RPVersion()
+
+
[docs]defis_mixture(self):
+""" Find out if fluid is mixture or not.
+ In case current fluid is mixture, true is returned.
+ In case current fluid is pure substance, false is returned.
+
+ Return:
+ -------
+ :return boolean _mix_flag:
+ Boolean for mixture (True), pure substance (False)
+ """
+ returnself._mix_flag
+
+
[docs]defset_error_flag(self,flag):
+""" Set error flag
+
+ Parameters:
+ :param boolean flag:
+ New error flag
+ Return:
+ :return int err:
+ Notifier for error code - in case everything went fine, 0 is returned
+ """
+ self._flag_check_errors=flag
+ return0
+
+
[docs]defset_warning_flag(self,flag):
+""" Set warning flag
+
+ Parameters:
+ :param boolean flag:
+ New warning flag
+ Return:
+ :return int err:
+ Notifier for error code - in case everything went fine, 0 is returned
+ """
+ self._flag_warnings=flag
+ return0
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/media/states.html b/docs/0.1.1/docs/_modules/vclibpy/media/states.html
new file mode 100644
index 0000000..83bae8b
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/media/states.html
@@ -0,0 +1,286 @@
+
+
+
+
+
+
+
+ vclibpy.media.states — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"""
+Module containing classes for thermodynamic state and transport properties.
+"""
+fromvclibpy.datamodelsimportVariableContainer
+
+
+__all__=[
+ 'ThermodynamicState',
+ 'TransportProperties',
+]
+
+
+
[docs]classThermodynamicState:
+"""
+ Represents a thermodynamic state within a cycle.
+
+ Notes:
+ Does not necessarily need to have all state variables defined!
+
+ Args:
+ p (float): Pressure at the state in Pa.
+ T (float): Temperature at the state in K.
+ u (float): Inner energy at the state in J/kg.
+ h (float): Enthalpy at the state in J/kg.
+ s (float): Entropy at the state in J/(kg * K).
+ v (float): Specific volume at the state in m^3/kg.
+ q (float): Quality at the state (between 0 and 1).
+ d (float): Density at the state in kg/m^3.
+
+ Methods:
+ __init__: Initializes the state class.
+ __str__: Provides a string representation of the state.
+ get_pretty_print: Formats the state with names, units, and descriptions.
+ """
+
+ def__init__(self,
+ p=None,
+ T=None,
+ u=None,
+ h=None,
+ s=None,
+ v=None,
+ q=None,
+ d=None):
+"""
+ Initializes a thermodynamic state.
+
+ Args:
+ p (float): Pressure at the state in Pa.
+ T (float): Temperature at the state in K.
+ u (float): Inner energy at the state in J/kg.
+ h (float): Enthalpy at the state in J/kg.
+ s (float): Entropy at the state in J/(kg * K).
+ v (float): Specific volume at the state in m^3/kg.
+ q (float): Quality at the state (between 0 and 1).
+ d (float): Density at the state in kg/m^3.
+
+ Notes:
+ If only v or d is provided, the other attribute will be calculated. If both are given and they are similar,
+ an error will be raised.
+ """
+ self.p=p
+ self.T=T
+ self.u=u
+ self.h=h
+ self.s=s
+ self.v=v
+ self.q=q
+ self.d=d
+ # Define density
+ ifvandd:
+ ifnotround(1/v,4)==round(d,4):
+ raiseValueError("At current state d and v do not match",d,v)
+ elifv:
+ self.d=1/v
+ elifd:
+ self.v=1/d
+
+ def__str__(self):
+"""
+ Returns a string representation of the state.
+ """
+ return";".join([f"{k}={v}"fork,vinself.__dict__.items()])
+
+
[docs]defget_pretty_print(self):
+"""
+ Provides a formatted representation of the state with names, units, and descriptions.
+ """
+ _container=VariableContainer()
+ _container.__class__.__name__=self.__class__.__name__
+ _container.set(name="p",value=self.p,unit="Pa",description="Pressure")
+ _container.set(name="T",value=self.T,unit="K",description="Temperature")
+ _container.set(name="u",value=self.u,unit="J/kg",description="Inner energy")
+ _container.set(name="h",value=self.h,unit="J/kg",description="Enthalpy")
+ _container.set(name="s",value=self.s,unit="J/(kg*K)",description="Entropy")
+ _container.set(name="v",value=self.v,unit="m^3/kg",description="Specific volume")
+ _container.set(name="q",value=self.q,unit="-",description="Quality")
+ _container.set(name="d",value=self.d,unit="kg/m^3",description="Density")
+ returnstr(_container)
+
+
+
[docs]classTransportProperties:
+"""
+ Represents transport properties at a specific thermodynamic state.
+
+ Args:
+ lam (float): Thermal conductivity in W/(m*K).
+ dyn_vis (float): Dynamic viscosity in Pa*s.
+ kin_vis (float): Kinematic viscosity in m^2/s.
+ Pr (float): Prandtl number.
+ cp (float): Isobaric specific heat capacity in J/(kg*K).
+ cv (float): Isochoric specific heat capacity in J/(kg*K).
+ beta (float): Thermal expansion coefficient in 1/K.
+ sur_ten (float): Surface tension in N/m.
+ ace_fac (float): Acentric factor.
+ state (ThermodynamicState): The state the transport properties belong to.
+
+ Methods:
+ __init__: Initializes the transport properties class.
+ __str__: Provides a string representation of the transport properties.
+ get_pretty_print: Formats the properties with names, units, and descriptions.
+ """
+
+ def__init__(self,
+ lam=None,
+ dyn_vis=None,
+ kin_vis=None,
+ pr=None,
+ cp=None,
+ cv=None,
+ beta=None,
+ sur_ten=None,
+ ace_fac=None,
+ state=None):
+"""
+ Initializes transport properties.
+
+ Args:
+ lam (float): Thermal conductivity in W/(m*K).
+ dyn_vis (float): Dynamic viscosity in Pa*s.
+ kin_vis (float): Kinematic viscosity in m^2/s.
+ pr (float): Prandtl number.
+ cp (float): Isobaric specific heat capacity in J/(kg*K).
+ cv (float): Isochoric specific heat capacity in J/(kg*K).
+ beta (float): Thermal expansion coefficient in 1/K.
+ sur_ten (float): Surface tension in N/m.
+ ace_fac (float): Acentric factor.
+ state (ThermodynamicState): The state the transport properties belong to.
+ """
+ self.lam=lam
+ self.dyn_vis=dyn_vis
+ self.kin_vis=kin_vis
+ self.Pr=pr
+ self.cp=cp
+ self.cv=cv
+ self.beta=beta
+ self.sur_ten=sur_ten
+ self.ace_fac=ace_fac
+ self.state=state
+
+ def__str__(self):
+"""
+ Returns a string representation of the transport properties.
+ """
+ return";".join([f"{k}={v}"fork,vinself.__dict__.items()])
+
+
[docs]defget_pretty_print(self):
+"""
+ Provides a formatted representation of the properties with names, units, and descriptions.
+ """
+ _container=VariableContainer()
+ _container.__class__.__name__=self.__class__.__name__
+ _container.set(name="lam",value=self.lam,unit="W/(m*K)",description="Thermal conductivity")
+ _container.set(name="dyn_vis",value=self.dyn_vis,unit="Pa*s",description="Dynamic viscosity")
+ _container.set(name="kin_vis",value=self.kin_vis,unit="m^2/s",description="Kinematic viscosity")
+ _container.set(name="pr",value=self.Pr,unit="-",description="Prandtl number")
+ _container.set(name="cp",value=self.cp,unit="J/(kg*K)",description="Isobaric specific heat capacity")
+ _container.set(name="cv",value=self.cv,unit="J/(kg*K)",description="Isochoric specific heat capacity")
+ _container.set(name="beta",value=self.beta,unit="1/K",description="Thermal expansion coefficient")
+ _container.set(name="sur_ten",value=self.sur_ten,unit="N/m",description="Surface tension")
+ _container.set(name="ace_fac",value=self.ace_fac,unit="-",description="Acentric factor")
+ returnstr(_container)
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/utils/automation.html b/docs/0.1.1/docs/_modules/vclibpy/utils/automation.html
new file mode 100644
index 0000000..0b5848d
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/utils/automation.html
@@ -0,0 +1,324 @@
+
+
+
+
+
+
+
+ vclibpy.utils.automation — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
[docs]defcalc_multiple_states(
+ save_path:pathlib.Path,
+ heat_pump:BaseCycle,
+ inputs:List[Inputs],
+ **kwargs):
+"""
+ Function to calculate the flowsheet states for all given inputs.
+ All results are stored as a .xlsx file in the given save-path
+
+ Args:
+ save_path (pathlib.Path): Location where to save the results as xlsx.
+ heat_pump (BaseCycle): A valid flowsheet
+ inputs (List[Inputs]): A list with all inputs to simulate
+ **kwargs: Solver settings for the flowsheet
+ """
+ rel_infos=[]
+ fori,single_inputsinenumerate(inputs):
+ fs_state=None
+ logger.info(f"Running combination {i+1}/{len(inputs)}.")
+ try:
+ fs_state=heat_pump.calc_steady_state(inputs=single_inputs,
+ **kwargs)
+ exceptExceptionase:
+ # Avoid loss of data if un-excepted errors occur.
+ logger.error(f"An error occurred: {e}")
+ iffs_stateisNone:
+ fs_state=FlowsheetState()
+ hp_state_dic={
+ **single_inputs.convert_to_str_value_format(with_unit_and_description=True),
+ **fs_state.convert_to_str_value_format(with_unit_and_description=True)
+ }
+ rel_infos.append(hp_state_dic)
+ df=pd.DataFrame(rel_infos)
+ df.index.name="State Number"
+ df.to_excel(save_path.joinpath(f"{heat_pump}_{heat_pump.fluid}.xlsx"),sheet_name="HP_states",float_format="%.5f")
+
+
+
[docs]deffull_factorial_map_generation(
+ heat_pump:BaseCycle,
+ T_eva_in_ar:Union[list,np.ndarray],
+ T_con_in_ar:Union[list,np.ndarray],
+ n_ar:Union[list,np.ndarray],
+ m_flow_con:float,
+ m_flow_eva:float,
+ save_path:Union[pathlib.Path,str],
+ dT_eva_superheating=5,
+ dT_con_subcooling=0,
+ use_multiprocessing:bool=False,
+ save_plots:bool=False,
+ **kwargs
+)->(pathlib.Path,pathlib.Path):
+"""
+ Run a full-factorial simulation to create performance maps
+ used in other simulation tools like Modelica or to analyze
+ the off-design of the flowsheet.
+ The results are stored and returned as .sdf and .csv files.
+ Currently, only varying T_eva_in, T_con_in, and n is implemented.
+ However, changing this to more dimensions or other variables
+ is not much work. In this case, please raise an issue.
+
+ Args:
+ heat_pump (BaseCycle): The flowsheet to use
+ T_eva_in_ar: Array with inputs for T_eva_in
+ T_con_in_ar: Array with inputs for T_con_in
+ n_ar: Array with inputs for n_ar
+ m_flow_con: Condenser mass flow rate
+ m_flow_eva: Evaporator mass flow rate
+ save_path: Where to save all results.
+ dT_eva_superheating: Evaporator superheating
+ dT_con_subcooling: Condenser subcooling
+ use_multiprocessing:
+ True to use multiprocessing. May speed up the calculation. Default is False
+ save_plots:
+ True to save plots of each steady state point. Default is False
+ **kwargs: Solver settings for the flowsheet
+
+ Returns:
+ tuple (pathlib.Path, pathlib.Path):
+ Path to the created .sdf file and to the .csv file
+ """
+ ifisinstance(save_path,str):
+ save_path=pathlib.Path(save_path)
+ ifsave_plots:
+ kwargs["save_path_plots"]=pathlib.Path(save_path).joinpath(f"plots_{heat_pump.flowsheet_name}_{heat_pump.fluid}")
+ os.makedirs(kwargs["save_path_plots"],exist_ok=True)
+
+ list_mp_inputs=[]
+ list_inputs=[]
+ idx_for_access_later=[]
+ fori_T_eva_in,T_eva_ininenumerate(T_eva_in_ar):
+ fori_n,ninenumerate(n_ar):
+ fori_T_con_in,T_con_ininenumerate(T_con_in_ar):
+ idx_for_access_later.append([i_n,i_T_con_in,i_T_eva_in])
+ inputs=Inputs(n=n,
+ T_eva_in=T_eva_in,
+ T_con_in=T_con_in,
+ m_flow_eva=m_flow_eva,
+ m_flow_con=m_flow_con,
+ dT_eva_superheating=dT_eva_superheating,
+ dT_con_subcooling=dT_con_subcooling)
+ list_mp_inputs.append([heat_pump,inputs,kwargs])
+ list_inputs.append(inputs)
+ fs_states=[]
+ i=0
+ ifuse_multiprocessing:
+ pool=multiprocessing.Pool(processes=multiprocessing.cpu_count())
+ forfs_stateinpool.imap(_calc_single_hp_state,list_mp_inputs):
+ fs_states.append(fs_state)
+ i+=1
+ logger.info(f"Ran {i} of {len(list_mp_inputs)} points")
+ else:
+ forinputsinlist_inputs:
+ fs_state=_calc_single_hp_state([heat_pump,inputs,kwargs])
+ fs_states.append(fs_state)
+ i+=1
+ logger.info(f"Ran {i} of {len(list_mp_inputs)} points")
+
+ # Save to sdf
+ result_shape=(len(n_ar),len(T_con_in_ar),len(T_eva_in_ar))
+ _dummy=np.zeros(result_shape)# Use a copy to avoid overwriting of values of any sort.
+ _dummy[:]=np.nan
+ # Get all possible values:
+ all_variables={}
+ all_variables_info={}
+ variables_to_excel=[]
+ forfs_state,inputsinzip(fs_states,list_inputs):
+ all_variables.update({var:_dummy.copy()forvarinfs_state.get_variable_names()})
+ all_variables_info.update({var:variableforvar,variableinfs_state.get_variables().items()})
+ variables_to_excel.append({
+ **inputs.convert_to_str_value_format(with_unit_and_description=True),
+ **fs_state.convert_to_str_value_format(with_unit_and_description=True),
+ })
+
+ # Save to excel
+ save_path_sdf=save_path.joinpath(f"{heat_pump.flowsheet_name}_{heat_pump.fluid}.sdf")
+ save_path_csv=save_path.joinpath(f"{heat_pump.flowsheet_name}_{heat_pump.fluid}.csv")
+ pd.DataFrame(variables_to_excel).to_csv(
+ save_path_csv
+ )
+
+ forfs_state,idx_tripleinzip(fs_states,idx_for_access_later):
+ i_n,i_T_con_in,i_T_eva_in=idx_triple
+ forvariable_name,variableinfs_state.get_variables().items():
+ all_variables[variable_name][i_n][i_T_con_in][i_T_eva_in]=variable.value
+
+ _nd_data={}
+ forvariable,nd_datainall_variables.items():
+ _nd_data.update({
+ variable:{
+ "data":nd_data,
+ "unit":all_variables_info[variable].unit,
+ "comment":all_variables_info[variable].description}
+ })
+
+ _scale_values={
+ "n":n_ar,
+ "T_con_in":T_con_in_ar,
+ "T_eva_in":T_eva_in_ar
+ }
+ inputs:Inputs=list_inputs[0]
+ _parameters={}
+ forname,variableininputs.items():
+ ifnamenotin_scale_values:
+ _parameters[name]={
+ "data":variable.value,
+ "unit":variable.unit,
+ "comment":variable.description
+ }
+ _scales={}
+ forname,datain_scale_values.items():
+ _scales[name]={
+ "data":data,
+ "unit":inputs.get(name).unit,
+ "comment":inputs.get(name).description
+ }
+
+ sdf_data={
+ heat_pump.flowsheet_name:
+ {
+ heat_pump.fluid:(_scales,_nd_data,_parameters)
+ }
+ }
+ utils.save_to_sdf(data=sdf_data,save_path=save_path_sdf)
+
+ # Terminate heat pump med-props:
+ heat_pump.terminate()
+
+ returnsave_path_sdf,save_path_csv
+
+
+def_calc_single_hp_state(data):
+"""Helper function for a single state to enable multiprocessing"""
+ heat_pump,inputs,kwargs=data
+ fs_state=None
+ try:
+ fs_state=heat_pump.calc_steady_state(inputs=inputs,
+ **kwargs)
+ exceptExceptionase:
+ logger.error(f"An error occurred for input: {inputs.__dict__}: {e}")
+ iffs_stateisNone:
+ fs_state=FlowsheetState()
+ # Append the data to the dataframe
+ returnfs_state
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/utils/nominal_design.html b/docs/0.1.1/docs/_modules/vclibpy/utils/nominal_design.html
new file mode 100644
index 0000000..4989c26
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/utils/nominal_design.html
@@ -0,0 +1,190 @@
+
+
+
+
+
+
+
+ vclibpy.utils.nominal_design — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
[docs]defnominal_hp_design(
+ heat_pump:BaseCycle,
+ inputs:Inputs,
+ fluid:str,
+ dT_con:float=None,
+ dT_eva:float=None,
+ **kwargs)->dict:
+"""
+ Function to calculate the heat pump design
+ at a given nominal point.
+ Args:
+ heat_pump (BaseCycle): A supported flowsheet
+ inputs (Inputs):
+ The input values at the nominal point.
+ If the mass flow rates are not given, you
+ can use dT_con and dT_eva to iteratively calculate
+ the mass flow rates in order to achieve the required
+ temperature differences at the nominal point.
+ dT_con (float):
+ Condenser temperature difference to calculate mass flow rate
+ dT_eva (float):
+ Evaporator temperature difference to calculate mass flow rate
+ fluid (str): Fluid to be used.
+ **kwargs:
+ m_flow_eva_start: Guess start-value for iteration. Default 0.2
+ m_flow_con_start: Guess start-value for iteration. Default 1
+ accuracy: Minimal accuracy for mass flow rate iteration (in kg/s).
+ Default 0.001 kg/s
+
+ Returns:
+ dict: A dictionary with all flowsheet states and inputs containing
+ information about the nominal design.
+ """
+ t0=time.time()
+ # Define nominal values:
+ m_flow_con_start=kwargs.get("m_flow_con_start",0.2)
+ m_flow_eva_start=kwargs.get("m_flow_eva_start",1)
+ accuracy=kwargs.get("accuracy",0.001)
+ calculate_m_flow=dT_evaisnotNoneanddT_conisnotNone
+ ifcalculate_m_flow:
+ # Get nominal value:
+ fs_state=heat_pump.calc_steady_state(fluid=fluid,inputs=inputs)
+ iffs_stateisNone:
+ raiseValueError("Given configuration is infeasible at nominal point.")
+
+ else:
+ # We have to iterate to match the m_flows to the Q_cons:
+ m_flow_eva_next=m_flow_eva_start
+ m_flow_con_next=m_flow_con_start
+ whileTrue:
+ # Set values
+ m_flow_eva=m_flow_eva_next
+ m_flow_con=m_flow_con_next
+ inputs.set("m_flow_con",m_flow_con)
+ inputs.set("m_flow_eva",m_flow_eva)
+ # Get nominal value:
+ fs_state=heat_pump.calc_steady_state(fluid=fluid,inputs=inputs)
+ iffs_stateisNone:
+ raiseValueError("Given configuration is infeasible at nominal point.")
+ cp_eva=heat_pump.evaporator._secondary_cp
+ cp_con=heat_pump.condenser._secondary_cp
+ m_flow_con_next=fs_state.get("Q_con")/(dT_con*cp_con)
+ m_flow_eva_next=(fs_state.get("Q_con")*(1-1/fs_state.get("COP")))/(dT_eva*cp_eva)
+ # Check convergence:
+ ifabs(m_flow_eva_next-m_flow_eva)<accuracyandabs(m_flow_con-m_flow_con_next)<accuracy:
+ break
+
+ nominal_design_info={
+ **inputs.convert_to_str_value_format(with_unit_and_description=False),
+ **fs_state.convert_to_str_value_format(with_unit_and_description=False),
+ "dT_con":dT_con,
+ "dT_eva":dT_eva
+ }
+ logger.info("Auto-generation of nominal values took %s seconds",time.time()-t0)
+ logger.info('Nominal values: %s',nominal_design_info)
+
+ returnnominal_design_info
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/utils/plotting.html b/docs/0.1.1/docs/_modules/vclibpy/utils/plotting.html
new file mode 100644
index 0000000..8bd0f58
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/utils/plotting.html
@@ -0,0 +1,403 @@
+
+
+
+
+
+
+
+ vclibpy.utils.plotting — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
[docs]defplot_cycle(
+ med_prop:MedProp,
+ states:List[ThermodynamicState],
+ save_path:pathlib.Path=None,
+ show:bool=False
+):
+"""
+ Creates T-h and p-h diagrams for a thermodynamic cycle.
+
+ Args:
+ med_prop (MedProp):
+ Object containing the medium properties and two-phase limits.
+ states (List[ThermodynamicState]):
+ List of thermodynamic states defining the cycle points. Each state
+ should contain T, p, and h properties.
+ save_path (pathlib.Path, optional):
+ Path where the plot should be saved. If None, returns the figure
+ and axes objects instead. Defaults to None.
+ show (bool):
+ If True, plots are displayed. Default is False.
+
+ Returns:
+ tuple(matplotlib.figure.Figure, numpy.ndarray) or None:
+ If save_path is provided, saves the plot and returns None.
+ If show is True, shows the plot.
+ """
+ states.append(states[0])# Plot full cycle
+ # Unpack state var:
+ h_T=np.array([state.hforstateinstates])/1000
+ T=[state.T-273.15forstateinstates]
+ p=np.array([state.p/1e5forstateinstates])
+ h_p=h_T
+
+ fig,ax=plt.subplots(2,1,sharex=True)
+ ax[0].set_ylabel("$T$ in °C")
+ ax[1].set_xlabel("$h$ in kJ/kgK")
+ # Two phase limits
+ ax[0].plot(
+ med_prop.get_two_phase_limits("h")/1000,
+ med_prop.get_two_phase_limits("T")-273.15,color="black"
+ )
+
+ ax[0].plot(h_T,T,color="r",marker="s")
+ ax[1].set_yscale('log')# Set y-axis to logarithmic scale
+ ax[1].plot(h_p,p,marker="s",color="r")
+ # Two phase limits
+ ax[1].plot(
+ med_prop.get_two_phase_limits("h")/1000,
+ med_prop.get_two_phase_limits("p")/1e5,
+ color="black"
+ )
+ ax[1].set_ylabel("$log(p)$ in bar")
+ ax[1].set_ylim([np.min(p)*0.9,np.max(p)*1.1])
+ ax[0].set_ylim([np.min(T)-5,np.max(T)+5])
+ ax[1].set_xlim([np.min(h_T)*0.9,np.max(h_T)*1.1])
+ ax[0].set_xlim([np.min(h_T)*0.9,np.max(h_T)*1.1])
+ ifshow:
+ plt.show()
+ ifsave_pathisnotNone:
+ fig.tight_layout()
+ fig.savefig(save_path)
+ plt.close(fig)
+ return
+ returnfig,ax
+
+
+
[docs]defplot_sdf_map(
+ filepath_sdf:pathlib.Path,
+ nd_data:str,
+ first_dimension:str,
+ second_dimension:str,
+ fluids:List[str]=None,
+ flowsheets:List[str]=None,
+ violin_plot_variable:str=None,
+ third_dimension:str=None
+):
+"""
+ Generate and display visualizations based on data from an SDF (Structured Data File) dataset.
+ This function generates various types of visualizations based on the provided parameters,
+ including 3D scatter plots, 3D surface plots, and violin plots, and displays them using Matplotlib.
+
+ Args:
+ filepath_sdf (pathlib.Path):
+ The path to the SDF dataset file.
+ nd_data (str):
+ The name of the primary data to be plotted.
+ first_dimension (str):
+ The name of the first dimension for the visualization.
+ second_dimension (str):
+ The name of the second dimension for the visualization.
+ fluids (List[str], optional):
+ List of specific fluids to include in the visualization.
+ Default is None, which includes all fluids.
+ flowsheets (List[str], optional):
+ List of specific flowsheets to include in the visualization.
+ Default is None, which includes all flowsheets.
+ violin_plot_variable (str, optional):
+ The variable to be used for creating violin plots.
+ Default is None, which disables violin plots.
+ third_dimension (str, optional):
+ The name of the third dimension for 4D visualizations.
+ Default is None, which disables 4D plotting.
+
+ Raises:
+ KeyError: If the specified data or dimensions are not found in the dataset.
+
+ Examples:
+ >>> FILEPATH_SDF = r"HeatPumpMaps.sdf"
+ >>> plot_sdf_map(
+ >>> filepath_sdf=FILEPATH_SDF,
+ >>> nd_data="COP",
+ >>> first_dimension="T_eva_in",
+ >>> second_dimension="n",
+ >>> fluids=["R410A"],
+ >>> flowsheets=["OptiHorn"],
+ >>> )
+
+ """
+ iffluidsisNone:
+ fluids=[]
+ ifflowsheetsisNone:
+ flowsheets=[]
+ if"T_"insecond_dimension:
+ offset_sec=-273.15
+ else:
+ offset_sec=0
+ if"T_"infirst_dimension:
+ offset_pri=-273.15
+ else:
+ offset_pri=0
+ offset_thi=0
+ plot_4D=False
+ ifthird_dimensionisnotNone:
+ plot_4D=True
+ if"T_"inthird_dimension:
+ offset_thi=-273.15
+
+ dataset=sdf.load(str(filepath_sdf))
+ plot_violin=True
+ ifviolin_plot_variableisNone:
+ plot_violin=False
+ violin_plot_variable=""
+ ifplot_violin:
+ ifflowsheets:
+ n_rows=len(flowsheets)
+ else:
+ n_rows=len(dataset.groups)
+ fig_v,ax_v=plt.subplots(nrows=n_rows,ncols=1,sharex=True,
+ squeeze=False)
+ fig_v.suptitle(violin_plot_variable)
+ i_fs=0
+ nd_str_plot=nd_data
+ fac=1
+ ifnd_data=="dT_eva_min":
+ nd_data="T_1"
+ sub_str="T_eva_in"
+ fac=-1
+ elifnd_data=="dT_con":
+ nd_data="T_3"
+ sub_str="T_con_in"
+ elifnd_data=="dT_sh":
+ nd_data="T_1"
+ sub_str="T_4"
+ else:
+ sub_str=""
+
+ ifnd_str_plot.startswith("T_"):
+ offset_nd=-273.15
+ else:
+ offset_nd=0
+
+ forflowsheetindataset.groups:
+ violin_data={}
+ ifflowsheet.namenotinflowsheetsandlen(flowsheets)>0:
+ continue
+ forfluidinflowsheet.groups:
+ iffluid.namenotinfluidsandlen(fluids)>0:
+ continue
+ nd,fd,sd,sub_data,td=None,None,None,None,None
+ _other_scale={}
+ fordsinfluid.datasets:
+ ifds.name==nd_data:
+ nd=ds
+ elifds.name==first_dimension:
+ fd=ds.data
+ elifds.name==second_dimension:
+ sd=ds.data
+ ifds.name==sub_str:
+ sub_data=ds.data
+ ifds.name==violin_plot_variable:
+ data=ds.data.flatten()
+ violin_data[fluid.name]=data[~np.isnan(data)]
+ ifplot_4Dandds.name==third_dimension:
+ td=ds.data
+
+ ifndisNone:
+ raiseKeyError("nd-String not found in dataset")
+
+ ifsub_dataisNone:
+ sub_data=np.zeros(nd.data.shape)
+
+ # Check other scales:
+ fori,scaleinenumerate(nd.scales):
+ ifscale.namenotin[first_dimension,second_dimension]:
+ _other_scale[i]=scale
+
+ iffdisNoneorsdisNoneornot_other_scaleor(plot_4DandtdisNone):
+ raiseKeyError("One of the given strings was not found in dataset")
+
+ ifplot_4D:
+ fig=plt.figure()
+ figtitle=f"{flowsheet.name}_{fluid.name}_{nd_str_plot}"
+ fig.suptitle(figtitle)
+ ax=fig.add_subplot(111,projection='3d')
+ ax.set_xlabel(first_dimension)
+ ax.set_ylabel(second_dimension)
+ ax.set_zlabel(third_dimension)
+ fourth_dim=(nd.data-sub_data)*fac+offset_nd
+ # Scale values for better sizes of circles:
+ bounds=[fourth_dim.min(),fourth_dim.max()]
+ _max_circle_size=30
+ fourth_dim_scaled=(fourth_dim-bounds[0])/(bounds[1]-bounds[0])*_max_circle_size
+ inp=[fd+offset_pri,sd+offset_sec,td+offset_thi]
+ importitertools
+ scattergrid=np.array([cforcinitertools.product(*inp)])
+ ax.scatter(scattergrid[:,0],
+ scattergrid[:,1],
+ scattergrid[:,2],
+ c=fourth_dim_scaled,
+ s=fourth_dim_scaled)
+ else:
+ forindex,scalein_other_scale.items():
+ foridx_data,valueinenumerate(scale.data):
+ ifindex==0:
+ Z=nd.data[idx_data,:,:]
+ ifsub_strin["T_4",""]:
+ sub_data_use=sub_data[idx_data,:,:]
+ else:
+ sub_data_use=sub_data
+ elifindex==1:
+ Z=nd.data[:,idx_data,:]
+ ifsub_strin["T_4",""]:
+ sub_data_use=sub_data[:,idx_data,:]
+ else:
+ sub_data_use=sub_data
+ else:
+ Z=nd.data[:,:,idx_data]
+ ifsub_strin["T_4",""]:
+ sub_data_use=sub_data[:,:,idx_data]
+ else:
+ sub_data_use=sub_data
+ ifnotplot_violin:
+ fig=plt.figure()
+ figtitle=f"{flowsheet.name}_{fluid.name}_{nd_str_plot}_{scale.name}={round(value,3)}"
+ fig.suptitle(figtitle)
+ ax=fig.add_subplot(111,projection='3d')
+ ax.set_xlabel(first_dimension)
+ ax.set_ylabel(second_dimension)
+ X,Y=np.meshgrid(fd,sd)
+ ax.plot_surface(X+offset_pri,Y+offset_sec,(Z-sub_data_use)*fac+offset_nd)
+
+ ifplot_violin:
+ forkey,valueinviolin_data.items():
+ print(f"{violin_plot_variable}: {flowsheet.name}_{key}")
+ print(f"Min: {np.min(value)}")
+ print(f"Max: {np.max(value)}")
+ print(f"Mean: {np.mean(value)}")
+ print(f"Median: {np.median(value)}\n")
+ ax_v[i_fs][0].violinplot(
+ list(violin_data.values()),
+ showextrema=True,
+ showmeans=True,
+ showmedians=True
+ )
+ set_axis_style(ax_v[i_fs][0],list(violin_data.keys()))
+ ax_v[i_fs][0].set_ylabel(flowsheet.name.replace("Flowsheet",""))
+ i_fs+=1
+ plt.show()
[docs]defprint_states(**kwargs):
+"""
+ Transforms given states to DataFrame and prints table layout.
+ Declaration of state must start with 'state'
+
+ Returns:
+ :return pandas.DataFrame df_states:
+ DataFrame with states
+ """
+ # Remove keys in given data that are not label as "state"
+ forkeyinkwargs.keys():
+ ifnotkey.startswith("state"):
+ print(
+ f"Given value {key} is removed from data transformation! "
+ f"Value must be declared as 'state'"
+ )
+ kwargs.pop(key,None)
+
+ # Extract data from ThermodynamicStates
+ data_states={key:{"state":key[5:],"T":data.T,"p":data.p,"h":data.h,"s":data.s,"d":data.d}
+ forkey,datainkwargs.items()}
+ df_states=pd.DataFrame.from_dict(data=data_states)
+
+ # Similar to previous results - now with rounded data
+ data_states_rounded={key:{"State":key[5:],
+ "T in °C":round(data.T-273.15,2),
+ "p in bar":round(data.p*1e-5,3),
+ "h in kJ/kg":round(data.h*1e-3,3),
+ "s in kJ/kg":round(data.s*1e-3,3),
+ "d in kg/m3":round(data.d,3)}
+ forkey,datainkwargs.items()}
+ df_states_rounded=pd.DataFrame.from_dict(data=data_states_rounded)
+ print(df_states_rounded.T)
+
+ returndf_states
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/_modules/vclibpy/utils/sdf_.html b/docs/0.1.1/docs/_modules/vclibpy/utils/sdf_.html
new file mode 100644
index 0000000..618197c
--- /dev/null
+++ b/docs/0.1.1/docs/_modules/vclibpy/utils/sdf_.html
@@ -0,0 +1,267 @@
+
+
+
+
+
+
+
+ vclibpy.utils.sdf_ — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
[docs]defsave_to_sdf(data:dict,save_path:pathlib.Path):
+"""
+ Save given input dictionary to a sdf file in the given save_path
+
+ Args:
+ data (dict):
+ A dictionary with the following structure:
+ Keys: Flowsheet_name
+ Values: A dictionary with the following structure:
+
+ - Keys: Fluids
+ - Values: Data dictionaries with the following structure:
+ A tuple with three values, in that order:
+
+ - scales: Of the given nd_data, e.g. T_Amb, n
+ - nd_data: More-dimensional data, e.g. COP
+ - parameters: Scalar values, like m_flow_con or similar
+
+ save_path (pathlib.Path): Where to store the data
+ flowsheet_name (str): Name of the flowsheet. This is the top level group
+ :return:
+ """
+ ifisinstance(save_path,str):
+ save_path=pathlib.Path(save_path)
+ _all_groups=[]
+
+ forflowsheet_name,fluid_dictindata.items():
+ _all_fluids=[]
+ forfluid,fluid_datainfluid_dict.items():
+ # First write scales
+ _scales=[]
+ forscale_name,scale_valuesinfluid_data[0].items():
+ _scales.append(sdf.Dataset(scale_name,
+ data=scale_values["data"],
+ unit=scale_values["unit"],
+ is_scale=True,
+ display_name=scale_name,
+ comment=scale_values.get("comment","")))
+ # Now the ND-Data:
+ _nd_data=[]
+ fordata_name,data_valuesinfluid_data[1].items():
+ _nd_data.append(sdf.Dataset(data_name,
+ data=data_values["data"],
+ unit=data_values["unit"],
+ scales=_scales,
+ comment=data_values.get("comment","")))
+ # Now the constant parameters
+ _paras=[]
+ forpara_name,para_valueinfluid_data[2].items():
+ _paras.append(sdf.Dataset(para_name,
+ data=para_value["data"],
+ unit=para_value["unit"],
+ comment=para_value.get("comment","")))
+
+ # Save everything
+ fluid_group=sdf.Group(fluid,comment="Values for fluid",datasets=_scales+_nd_data+_paras)
+ _all_fluids.append(fluid_group)
+
+ flowsheet_group=sdf.Group(flowsheet_name,
+ comment="Multiple fluids for the flowsheet",
+ groups=_all_fluids)
+ _all_groups.append(flowsheet_group)
+
+ parent_group=sdf.Group("/",comment="Generated with VCLibPy",groups=_all_groups)
+ sdf.save(save_path,group=parent_group)
+ returnsave_path
+
+
+
[docs]defmerge_sdfs(filepaths,save_path):
+"""
+ Merge given files and return a merged file.
+ Be careful if both files contain the same combination.
+ Then, the latter element of the list will overwrite the first one.
+
+ Args:
+ filepaths (list): List with paths to the files
+ save_path (str): Save path for the new file
+ """
+ _all_flowsheets={}
+ # Merge to structure
+ forfpathinfilepaths:
+ dataset=sdf.load(fpath)
+ forflowsheetindataset.groups:
+ fluid_data={fluid.name:fluidforfluidinflowsheet.groups}
+ ifflowsheet.namenotin_all_flowsheets:
+ _all_flowsheets.update({flowsheet.name:fluid_data})
+ else:
+ _all_flowsheets[flowsheet.name].update(fluid_data)
+
+ # Write structure
+ _all_groups=[]
+ forflowsheet_name,fluid_dictin_all_flowsheets.items():
+ _all_fluids=[]
+ forfluid,datainfluid_dict.items():
+ _all_fluids.append(data)
+ flowsheet_group=sdf.Group(flowsheet_name,
+ comment="Multiple fluids for the flowsheet",
+ groups=_all_fluids)
+ _all_groups.append(flowsheet_group)
+
+ parent_group=sdf.Group("/",comment="Generated with python script",groups=_all_groups)
+ sdf.save(save_path,group=parent_group)
+ returnsave_path
+
+
+
[docs]defsdf_to_csv(filepath:pathlib.Path,save_path:pathlib.Path):
+"""
+ Convert a given .sdf file to multiple excel files,
+ for each combination of flowsheet and refrigerant one file.
+
+ Args:
+ filepath (pathlib.Path): sdf file
+ save_path (pathlib.Path): Directory where to store the csv files.
+ """
+ dataset=sdf.load(str(filepath))
+ forflowsheetindataset.groups:
+ forfluidinflowsheet.groups:
+ dfs=[]
+ fordatainfluid.datasets:
+ if_is_nd(data):
+ dfs.append(_unpack_nd_data(data))
+ df=pd.concat(dfs,axis=1)
+ df=df.loc[:,~df.columns.duplicated()]
+ df.to_csv(save_path.joinpath(f"{flowsheet.name}_{fluid.name}.csv"))
Calculate the output state based on the high pressure level and the provided inputs.
+The state is automatically set as the outlet state of this component.
+
+
Args:
p_outlet (float): High pressure value.
+inputs (Inputs): Inputs for calculation.
+fs_state (FlowsheetState): Flowsheet state.
Inherits from the Compressor class, which defines the basic properties and behavior of a compressor in a vapor
+compression cycle.
+
+
Parameters:
N_max (float): Maximal rotations per second of the compressor.
+V_h (float): Volume of the compressor in m^3.
+eta_isentropic (float): Constant isentropic efficiency of the compressor.
+eta_mech (float): Constant mechanical efficiency of the compressor.
+lambda_h (float): Constant volumetric efficiency.
+
+
Args:
N_max (float): Maximal rotations per second of the compressor.
+V_h (float): Volume of the compressor in m^3.
+eta_isentropic (float): Constant isentropic efficiency of the compressor.
+eta_inverter (float): Constant inverter efficiency of the compressor.
+eta_motor (float): Constant motor efficiency of the compressor.
+eta_mech (float): Constant mechanical efficiency of the compressor including motor and inverter efficiencies.
+lambda_h (float): Constant volumetric efficiency.
+
+
Methods:
+
get_lambda_h(inputs: Inputs) -> float:
Returns the constant volumetric efficiency of the compressor.
Compressor model based on the thesis of Mirko Engelpracht.
+
This compressor is characterized by using regressions provided by Mirko Engelpracht for a family of rotary
+compressors. The coefficients used in the regressions are sourced from his Master’s thesis.
+
+
Parameters:
N_max (float): Maximal rotations per second of the compressor.
+V_h (float): Volume of the compressor in m^3.
+
+
Methods:
+
get_lambda_h(inputs: Inputs) -> float:
Returns the volumetric efficiency based on the regressions of Mirko Engelpracht.
Base class for compressors using the ten-coefficient method.
+
Used table has to be in this format
+(order of the columns is not important).
+The values must be the same as in the example tabel.
+The column names can be different but must
+then be matched with argument parameter_names.
+(All typed in numbers are fictional placeholders)
N_max (float): Maximal rotations per second of the compressor.
+V_h (float): Volume of the compressor in m^3.
+datasheet (str): Path of the datasheet file.
+**kwargs:
+
+
+
parameter_names (dict, optional):
Dictionary to match internal parameter names (keys) to the names used in the table values.
+Default
+{
Get a parameter based on temperatures, rotations, and parameter type from the datasheet.
+
+
Args:
T_eva (float): Evaporator temperature in Celsius.
+T_con (float): Condenser temperature in Celsius.
+n (float): Rotations per second.
+type_ (str): Parameter type in parameter_names.
Used table has to be in this format
+(order of the columns is not important).
+The values must be the same as in the example tabel.
+The column names can be different but must
+then be matched with the keyword argument parameter_names.
+(All typed in numbers are fictional placeholders)
N_max (float): Maximal rotations per second of the compressor.
+V_h (float): Volume of the compressor in m^3.
+datasheet (str): Path of the datasheet file.
+**kwargs:
+
+
+
parameter_names (dict, optional):
Dictionary to match internal parameter names (keys) to the names used in the table values.
+Default
+{
Used table has to be in this format
+(order of the columns is not important).
+The values must be the same as in the example tabel.
+The column names can be different but must
+then be matched with the keyword argument parameter_names.
+(All typed in numbers are fictional placeholders)
T_sh and T_sc have to be set according to the data sheet of your compressor. capacity_definition defines the
+parameter “capacity” in the datasheet. If capacity is the specific cooling capacity (h1-h4), set it on “cooling”.
+If capacity is the specific heating capacity (h2-h3), set it on “heating”.
+In the case of cooling capacity, the mechanical efficiency of the compressor has to be assumed (assumed_eta_mech)
+as h2 can’t be calculated otherwise. Summary:
+
+
+
For the case heating capacity: h2 = h3 + capacity / m_flow
+
For the case cooling capacity: h2 = h3 + (capacity + p_el * assumed_eta_mech) / m_flow
+
+
+
+
Args:
N_max (float): Maximal rotations per second of the compressor.
+V_h (float): Volume of the compressor in m^3.
+T_sc (float): Subcooling according to datasheet in K.
+T_sh (float): Superheating according to datasheet in K.
+capacity_definition (str): Definition of “capacity” in the datasheet. “cooling” or “heating”.
+assumed_eta_mech (float): Assumed mechanical efficiency of the compressor (only needed if cooling).
+datasheet (str): Path of the modified datasheet.
+**kwargs:
+
+
+
parameter_names (dict, optional):
Dictionary to match internal parameter names (keys) to the names used in the table values.
+Default
+{
state_q0 (ThermodynamicState): Thermodynamic state at the beginning of the two-phase region.
+state_q1 (ThermodynamicState): Thermodynamic state at the end of the two-phase region.
+state_inlet (ThermodynamicState): Inlet thermodynamic state.
+state_outlet (ThermodynamicState): Outlet thermodynamic state.
+med_prop (MedProp): Medium properties class.
+inputs (Inputs): Input parameters.
+fs_state (FlowsheetState): Flowsheet state.
+m_flow (float): Mass flow rate.
+
+
Returns:
float: Calculated two-phase heat transfer coefficient.
+
+
Raises:
NotImplementedError: If the method is not implemented in the subclass.
dynamic_viscosity (float): Dynamic viscosity of the fluid.
+m_flow (float): Mass flow rate.
+characteristic_length (float): Characteristic length (e.g., diameter) of the pipe.
Equation to calc the nusselt number of turbulent flow for
+a given Re and Pr number.
+Note: Just for one-phase heat transfer!!
+Implemented Options are:
+
+
Taler2016
+
Domanski1989_sp_smooth
+
Amalfi2016
+
ScriptWSÜ. For turbulent regimes, eta_by_eta_w is assumed to be one.
+
+
Refer to the paper / documents or the function in this class for more
+info on numbers and assumptions
+
+
characteristic_length (float):
Length to calculate the similitude approach for the
+heat transfer from ref -> wall. For heat pumps this is
+always the Diameter of the HE in m
Economizer heat exchanger which is NTU based.
+Used only for vapor injection cycles, as
+calculations are purely based for two-phase
+and liquid estimations.
+
See parent class for more arguments.
+
Assumptions:
+
+
Default flow_type is counter_flow.
+
Default ratio_outer_to_inner_area is 1, as
+
pipes are nearly same diameter and length
+
Secondary heat transfer for alpha is disabled; gas,
+
+
liquid and two-phase models are used for both sides.
To use multi-processing, MedProp can’t start
+in the main thread, as the object can’t be pickled.
+
This function terminates possible secondary med-prop
+classes, for instance in heat exchangers.
+The default component does not have to override
+this function.
Calculate the two-phase heat transfer coefficient.
+
+
Args:
state_q0: State at vapor quality 0.
+state_q1: State at vapor quality 1.
+inputs (Inputs): The inputs for the calculation.
+fs_state (FlowsheetState): The flowsheet state to save important variables.
To use multi-processing, MedProp can’t start
+in the main thread, as the object can’t be pickled.
+
This function terminates possible secondary med-prop
+classes, for instance in heat exchangers.
+The default component does not have to override
+this function.
Iteratively calculates the required area for the heat exchange.
+
+
Args:
dT_max (float): Maximum temperature differential.
+alpha_pri (float): Heat transfer coefficient for the primary medium.
+alpha_sec (float): Heat transfer coefficient for the secondary medium.
+Q (float): Heat flow rate.
Separates a flow with possible phase changes into three parts:
+subcooling (sc), latent phase change (lat), and superheating (sh)
+at the given pressure.
+
+
Args:
state_max (ThermodynamicState): State with higher enthalpy.
+state_min (ThermodynamicState): State with lower enthalpy.
+p (float): Pressure of phase change.
Q_sc: Heat for subcooling.
+Q_lat: Heat for latent phase change.
+Q_sh: Heat for superheating.
+state_q0: State at vapor quality 0 and the given pressure.
+state_q1: State at vapor quality 1 and the given pressure.
The NTU method uses the overall heat transfer coefficient k
+and multiplies it with the outer area A (area of the secondary side).
+However, depending on the heat exchanger type, the areas may
+differ drastically. For instance in an air-to-water heat exchanger.
+The VDI-Atlas proposes the ratio of outer area to inner pipe area
+to account for this mismatch in sizes.
+The calculation follows the code in the function calc_k.
+Typical values are around 20-30.
Calculate the Number of Transfer Units (NTU) for the heat exchanger.
+
+
Args:
A (float): Area of the heat exchanger.
+k (float): Overall heat transfer coefficient.
+m_flow_cp (float): Minimal heat capacity rates (m_flow*cp) between primary and secondary side.
Calculate the heat transfer and overall heat transfer coefficient for the heat exchanger based on NTU.
+
+
Args:
dT_max (float): Maximum temperature differential.
+alpha_pri (float): Heat transfer coefficient for the primary medium.
+alpha_sec (float): Heat transfer coefficient for the secondary medium.
+A (float): Area of the heat exchanger.
+
+
Returns:
Tuple[float, float]: Heat transfer and overall heat transfer coefficient.
Abstract base class for defining interfaces of components in the vapor compression cycle.
+
+
Methods:
+
start_secondary_med_prop():
To use multiprocessing, MedProp can’t start in the main thread, as the object can’t be pickled.
+This function starts possible secondary MedProp classes, for instance in heat exchangers.
+The default component does not have to override this function.
+
+
+
+
Properties:
+
state_inlet (ThermodynamicState):
Property for accessing and setting the inlet state of the component.
+
+
state_outlet (ThermodynamicState):
Property for accessing and setting the outlet state of the component.
+
+
m_flow (float):
Property for accessing and setting the mass flow rate through the component.
+
+
med_prop (MedProp):
Property for accessing and setting the property wrapper for the working fluid.
Start secondary MedProp classes for multiprocessing.
+
To use multiprocessing, MedProp can’t start in the main thread, as the object can’t be pickled.
+This function starts possible secondary MedProp classes, for instance in heat exchangers.
+The default component does not have to override this function.
To use multi-processing, MedProp can’t start
+in the main thread, as the object can’t be pickled.
+
This function terminates possible secondary med-prop
+classes, for instance in heat exchangers.
+The default component does not have to override
+this function.
This class is used to define the unique states of the flowsheet
+in the heat pump.
+
The class is dynamic in the sense that attributes may be
+added during calculation of new flowsheet. This enables
+the easy adding of custom values to analyze the whole flowsheet
+and not restrict to a certain naming convention.
Class defining inputs to calculate the FlowsheetState.
+
While the inputs are pre-defined, you may add further ones
+using the set method.
+
+
Args:
n (float): Relative compressor speed between 0 and 1.
+T_eva_in (float): Secondary side evaporator inlet temperature.
+T_con_in (float): Secondary side condenser inlet temperature.
+m_flow_eva (float): Secondary side evaporator mass flow rate.
+m_flow_con (float): Secondary side condenser mass flow rate.
+dT_eva_superheating (float): Super-heating after evaporator.
+dT_con_subcooling (float): Subcooling after condenser.
+T_ambient (float): Ambient temperature of the machine.
name (str): The name of the variable.
+value (float): The numerical value of the variable.
+unit (str): The unit of measurement for the variable (optional).
+description (str): A description of the variable (optional).
name (str): The name of the variable.
+value (float): The numerical value of the variable.
+unit (str): The unit of measurement for the variable (optional).
+description (str): A description of the variable (optional).
Calculate the thermodynamic state based on the specified mode and state variables.
+
This function calculates the thermodynamic state based on the chosen mode and provided state variables.
+The input state variables need to be in SI units.
+
+
Notes:
+
PT does not work when the state might fall within the two-phase region.
+
Only functions for density are implemented. In cases where you know the specific volume, use the density
+functions with the inverse value.
+
+
Quality (q) may have values outside the ‘physical’ scope:
+
q = -998: Subcooled liquid
+
q = 998: Superheated vapor
+
q = 999: Supercritical state
+
+
+
+
+
+
+
Possible modes include:
+
“PD”: Pressure, Density
+
“PH”: Pressure, Enthalpy
+
“PQ”: Pressure, Quality
+
“PS”: Pressure, Entropy
+
“PT”: Pressure, Temperature
+
“PU”: Pressure, Internal Energy
+
“TD”: Temperature, Density
+
“TH”: Temperature, Enthalpy
+
“TQ”: Temperature, Quality
+
“TS”: Temperature, Entropy
+
“TU”: Temperature, Internal Energy
+
“DH”: Density, Enthalpy
+
“DS”: Density, Entropy
+
“DU”: Density, Internal Energy
+
+
+
Args:
mode (str): Defines the given input state variables (see possible modes above).
+var1 (float): Value of the first state variable (as specified in the mode) in SI units.
+var2 (float): Value of the second state variable (as specified in the mode) in SI units.
+
+
Returns:
ThermodynamicState: A ThermodynamicState instance with state variables.
+
+
Raises:
AssertionError: If the given mode is not within the available options.
This class serves as the base for defining interfaces to access and compute media properties.
+
+
Methods:
calc_state: Calculate the thermodynamic state based on mode and state variables.
+calc_transport_properties: Calculate transport properties for a given state.
+get_critical_point: Retrieve critical point information.
+get_molar_mass: Retrieve molar mass information.
+get_two_phase_limits: Retrieve the two-phase limits for plotting.
+calc_mean_transport_properties: Calculate the average transport properties for given states.
Calculate the thermodynamic state based on the specified mode and state variables.
+
This function calculates the thermodynamic state based on the chosen mode and provided state variables.
+The input state variables need to be in SI units.
+
+
Notes:
+
PT does not work when the state might fall within the two-phase region.
+
Only functions for density are implemented. In cases where you know the specific volume, use the density
+functions with the inverse value.
+
+
Quality (q) may have values outside the ‘physical’ scope:
+
q = -998: Subcooled liquid
+
q = 998: Superheated vapor
+
q = 999: Supercritical state
+
+
+
+
+
+
+
Possible modes include:
+
“PD”: Pressure, Density
+
“PH”: Pressure, Enthalpy
+
“PQ”: Pressure, Quality
+
“PS”: Pressure, Entropy
+
“PT”: Pressure, Temperature
+
“PU”: Pressure, Internal Energy
+
“TD”: Temperature, Density
+
“TH”: Temperature, Enthalpy
+
“TQ”: Temperature, Quality
+
“TS”: Temperature, Entropy
+
“TU”: Temperature, Internal Energy
+
“DH”: Density, Enthalpy
+
“DS”: Density, Entropy
+
“DU”: Density, Internal Energy
+
+
+
Args:
mode (str): Defines the given input state variables (see possible modes above).
+var1 (float): Value of the first state variable (as specified in the mode) in SI units.
+var2 (float): Value of the second state variable (as specified in the mode) in SI units.
+
+
Returns:
ThermodynamicState: A ThermodynamicState instance with state variables.
+
+
Raises:
AssertionError: If the given mode is not within the available options.
Retrieve the two-phase limits for plotting a specified quantity.
+
This method returns the two-phase limits for a specified quantity (T, h, s, or p) in an array used for
+plotting purposes. It calculates the limits within the pressure range from p_min and quality (q) 0 to the
+critical pressure (pc), and then from the critical pressure to the pressure p_min and quality 1.
+
+
Args:
quantity (str): The specified quantity (T, h, s, or p).
+p_min (int, optional): The minimum pressure value to start iteration. Default is 100000 Pa.
+p_step (int, optional): The step size for pressure variation. Default is 5000 Pa.
+
+
Returns:
numpy.ndarray: An array containing the two-phase limits for the specified quantity.
+
+
Raises:
ValueError: If the given quantity is not supported (T, h, s, or p).
z (list or None) – Fluid composition. Should only be used, when a self-design mixture shall be used. Further information
+see notes.
+When you want to use a self-design mixture to as follows:
+- Fluid-name needs to contain all component names within mixture: “R32.FLD|R125.FLD”
+- z needs to be a list with molar fractions: [0.697, 0.303]
+- Used example would be similar to R410A
+
dll_path (string) – Specifier for the dll path used for RefProp,
+e.g. dll_path=’C:path_to_dllRefProp64.dll’.
+If None, the ref_prop_path and function get_dll_path are
+used to determine the dll path
+
use_error_check (boolean) – Specifier whether errors and warnings shall be checked when calling RefProp or not
+
use_warnings (boolean) – Specifier whether warnings shall be used
+
ref_prop_path (str) – Path to RefProp package. Default is the ENV variable RPPREFIX.
+
copy_dll (bool) – If True (not the default), a copy of the dll is created to enable simultaneous use of
+multiple fluids in multiprocessing.
+
copy_dll_directory (str) – If copy_dll is True, the DLL is copied to this directory.
+If None (default), the current working directory is used.
1.) Create RefProp instance: rp = RefProp(“R32”)
+2.) In case you want to calculate fluid properties (state variables) for a specific state: Use calc_state() function
+
+
Multiple inputs can be given (but you need to now two in order to define the values). For further
+information see function header.
+
+
+
3.) Further get-Functions implemented
+
get_gwp(): Global warming potential
+
get_odp(): Ozone depletion potential
+
get_safety(): Safety class
+
get_mass_fraction(): Mass fractions of pure substances in fluid
+
get_molar_mass(): Molar mass of fluid
+
get_mol_fraction(): Mol fractions of pure substances in fluid
+
get_comp_names(): Pure substances names in fluid
+
get_longname(): Long name of fluid within RefProp
+
get_critical_point(): Crit. Temperature and Pressure
+
get_version(): Version of wrapper and of RefProp dll
1.) PT does not work when state might be within the two-phase region!
+2.) Only functions for density are implemented. In case you know the specific volume instead use density
+
+
functions with inverse value!
+
+
+
3.) Quality can have values outside of ‘physical’ scope:
Does not necessarily need to have all state variables defined!
+
+
Args:
p (float): Pressure at the state in Pa.
+T (float): Temperature at the state in K.
+u (float): Inner energy at the state in J/kg.
+h (float): Enthalpy at the state in J/kg.
+s (float): Entropy at the state in J/(kg * K).
+v (float): Specific volume at the state in m^3/kg.
+q (float): Quality at the state (between 0 and 1).
+d (float): Density at the state in kg/m^3.
+
+
Methods:
__init__: Initializes the state class.
+__str__: Provides a string representation of the state.
+get_pretty_print: Formats the state with names, units, and descriptions.
Represents transport properties at a specific thermodynamic state.
+
+
Args:
lam (float): Thermal conductivity in W/(m*K).
+dyn_vis (float): Dynamic viscosity in Pa*s.
+kin_vis (float): Kinematic viscosity in m^2/s.
+Pr (float): Prandtl number.
+cp (float): Isobaric specific heat capacity in J/(kg*K).
+cv (float): Isochoric specific heat capacity in J/(kg*K).
+beta (float): Thermal expansion coefficient in 1/K.
+sur_ten (float): Surface tension in N/m.
+ace_fac (float): Acentric factor.
+state (ThermodynamicState): The state the transport properties belong to.
+
+
Methods:
__init__: Initializes the transport properties class.
+__str__: Provides a string representation of the transport properties.
+get_pretty_print: Formats the properties with names, units, and descriptions.
Calculate the thermodynamic state based on the specified mode and state variables.
+
This function calculates the thermodynamic state based on the chosen mode and provided state variables.
+The input state variables need to be in SI units.
+
+
Notes:
+
PT does not work when the state might fall within the two-phase region.
+
Only functions for density are implemented. In cases where you know the specific volume, use the density
+functions with the inverse value.
+
+
Quality (q) may have values outside the ‘physical’ scope:
+
q = -998: Subcooled liquid
+
q = 998: Superheated vapor
+
q = 999: Supercritical state
+
+
+
+
+
+
+
Possible modes include:
+
“PD”: Pressure, Density
+
“PH”: Pressure, Enthalpy
+
“PQ”: Pressure, Quality
+
“PS”: Pressure, Entropy
+
“PT”: Pressure, Temperature
+
“PU”: Pressure, Internal Energy
+
“TD”: Temperature, Density
+
“TH”: Temperature, Enthalpy
+
“TQ”: Temperature, Quality
+
“TS”: Temperature, Entropy
+
“TU”: Temperature, Internal Energy
+
“DH”: Density, Enthalpy
+
“DS”: Density, Entropy
+
“DU”: Density, Internal Energy
+
+
+
Args:
mode (str): Defines the given input state variables (see possible modes above).
+var1 (float): Value of the first state variable (as specified in the mode) in SI units.
+var2 (float): Value of the second state variable (as specified in the mode) in SI units.
+
+
Returns:
ThermodynamicState: A ThermodynamicState instance with state variables.
+
+
Raises:
AssertionError: If the given mode is not within the available options.
This class serves as the base for defining interfaces to access and compute media properties.
+
+
Methods:
calc_state: Calculate the thermodynamic state based on mode and state variables.
+calc_transport_properties: Calculate transport properties for a given state.
+get_critical_point: Retrieve critical point information.
+get_molar_mass: Retrieve molar mass information.
+get_two_phase_limits: Retrieve the two-phase limits for plotting.
+calc_mean_transport_properties: Calculate the average transport properties for given states.
Calculate the thermodynamic state based on the specified mode and state variables.
+
This function calculates the thermodynamic state based on the chosen mode and provided state variables.
+The input state variables need to be in SI units.
+
+
Notes:
+
PT does not work when the state might fall within the two-phase region.
+
Only functions for density are implemented. In cases where you know the specific volume, use the density
+functions with the inverse value.
+
+
Quality (q) may have values outside the ‘physical’ scope:
+
q = -998: Subcooled liquid
+
q = 998: Superheated vapor
+
q = 999: Supercritical state
+
+
+
+
+
+
+
Possible modes include:
+
“PD”: Pressure, Density
+
“PH”: Pressure, Enthalpy
+
“PQ”: Pressure, Quality
+
“PS”: Pressure, Entropy
+
“PT”: Pressure, Temperature
+
“PU”: Pressure, Internal Energy
+
“TD”: Temperature, Density
+
“TH”: Temperature, Enthalpy
+
“TQ”: Temperature, Quality
+
“TS”: Temperature, Entropy
+
“TU”: Temperature, Internal Energy
+
“DH”: Density, Enthalpy
+
“DS”: Density, Entropy
+
“DU”: Density, Internal Energy
+
+
+
Args:
mode (str): Defines the given input state variables (see possible modes above).
+var1 (float): Value of the first state variable (as specified in the mode) in SI units.
+var2 (float): Value of the second state variable (as specified in the mode) in SI units.
+
+
Returns:
ThermodynamicState: A ThermodynamicState instance with state variables.
+
+
Raises:
AssertionError: If the given mode is not within the available options.
Retrieve the two-phase limits for plotting a specified quantity.
+
This method returns the two-phase limits for a specified quantity (T, h, s, or p) in an array used for
+plotting purposes. It calculates the limits within the pressure range from p_min and quality (q) 0 to the
+critical pressure (pc), and then from the critical pressure to the pressure p_min and quality 1.
+
+
Args:
quantity (str): The specified quantity (T, h, s, or p).
+p_min (int, optional): The minimum pressure value to start iteration. Default is 100000 Pa.
+p_step (int, optional): The step size for pressure variation. Default is 5000 Pa.
+
+
Returns:
numpy.ndarray: An array containing the two-phase limits for the specified quantity.
+
+
Raises:
ValueError: If the given quantity is not supported (T, h, s, or p).
Return the states representing the boundaries of the two-phase section for the given fluid.
+
This function is primarily used for visualizing the two-phase section and validating the accuracy of calculations.
+
+
Args:
med_prop (MedProp): An instance of a valid MedProp-Class.
+p_step (int): The step size for pressure variation in Pa. Default is 1000 Pa.
+p_min (int): The minimum pressure in Pa from where to start calculation. Default is 1000 Pa.
+
+
Returns:
List[ThermodynamicState]: A list of ThermodynamicState instances representing the two-phase limits.
+
+
Notes:
The two-phase limits are computed by iterating over a range of pressures from the minimum pressure up to the
+critical point pressure (exclusive) with a specified step size. States at quality 0 (saturated liquid)
+and quality 1 (saturated vapor) are appended to form the two-phase boundary. The list is reversed to
+maintain the correct order for visualization purposes.
z (list or None) – Fluid composition. Should only be used, when a self-design mixture shall be used. Further information
+see notes.
+When you want to use a self-design mixture to as follows:
+- Fluid-name needs to contain all component names within mixture: “R32.FLD|R125.FLD”
+- z needs to be a list with molar fractions: [0.697, 0.303]
+- Used example would be similar to R410A
+
dll_path (string) – Specifier for the dll path used for RefProp,
+e.g. dll_path=’C:path_to_dllRefProp64.dll’.
+If None, the ref_prop_path and function get_dll_path are
+used to determine the dll path
+
use_error_check (boolean) – Specifier whether errors and warnings shall be checked when calling RefProp or not
+
use_warnings (boolean) – Specifier whether warnings shall be used
+
ref_prop_path (str) – Path to RefProp package. Default is the ENV variable RPPREFIX.
+
copy_dll (bool) – If True (not the default), a copy of the dll is created to enable simultaneous use of
+multiple fluids in multiprocessing.
+
copy_dll_directory (str) – If copy_dll is True, the DLL is copied to this directory.
+If None (default), the current working directory is used.
1.) Create RefProp instance: rp = RefProp(“R32”)
+2.) In case you want to calculate fluid properties (state variables) for a specific state: Use calc_state() function
+
+
Multiple inputs can be given (but you need to now two in order to define the values). For further
+information see function header.
+
+
+
3.) Further get-Functions implemented
+
get_gwp(): Global warming potential
+
get_odp(): Ozone depletion potential
+
get_safety(): Safety class
+
get_mass_fraction(): Mass fractions of pure substances in fluid
+
get_molar_mass(): Molar mass of fluid
+
get_mol_fraction(): Mol fractions of pure substances in fluid
+
get_comp_names(): Pure substances names in fluid
+
get_longname(): Long name of fluid within RefProp
+
get_critical_point(): Crit. Temperature and Pressure
+
get_version(): Version of wrapper and of RefProp dll
1.) PT does not work when state might be within the two-phase region!
+2.) Only functions for density are implemented. In case you know the specific volume instead use density
+
+
functions with inverse value!
+
+
+
3.) Quality can have values outside of ‘physical’ scope:
Does not necessarily need to have all state variables defined!
+
+
Args:
p (float): Pressure at the state in Pa.
+T (float): Temperature at the state in K.
+u (float): Inner energy at the state in J/kg.
+h (float): Enthalpy at the state in J/kg.
+s (float): Entropy at the state in J/(kg * K).
+v (float): Specific volume at the state in m^3/kg.
+q (float): Quality at the state (between 0 and 1).
+d (float): Density at the state in kg/m^3.
+
+
Methods:
__init__: Initializes the state class.
+__str__: Provides a string representation of the state.
+get_pretty_print: Formats the state with names, units, and descriptions.
Represents transport properties at a specific thermodynamic state.
+
+
Args:
lam (float): Thermal conductivity in W/(m*K).
+dyn_vis (float): Dynamic viscosity in Pa*s.
+kin_vis (float): Kinematic viscosity in m^2/s.
+Pr (float): Prandtl number.
+cp (float): Isobaric specific heat capacity in J/(kg*K).
+cv (float): Isochoric specific heat capacity in J/(kg*K).
+beta (float): Thermal expansion coefficient in 1/K.
+sur_ten (float): Surface tension in N/m.
+ace_fac (float): Acentric factor.
+state (ThermodynamicState): The state the transport properties belong to.
+
+
Methods:
__init__: Initializes the transport properties class.
+__str__: Provides a string representation of the transport properties.
+get_pretty_print: Formats the properties with names, units, and descriptions.
Function to calculate the flowsheet states for all given inputs.
+All results are stored as a .xlsx file in the given save-path
+
+
Args:
save_path (pathlib.Path): Location where to save the results as xlsx.
+heat_pump (BaseCycle): A valid flowsheet
+inputs (List[Inputs]): A list with all inputs to simulate
+**kwargs: Solver settings for the flowsheet
Run a full-factorial simulation to create performance maps
+used in other simulation tools like Modelica or to analyze
+the off-design of the flowsheet.
+The results are stored and returned as .sdf and .csv files.
+Currently, only varying T_eva_in, T_con_in, and n is implemented.
+However, changing this to more dimensions or other variables
+is not much work. In this case, please raise an issue.
+
+
Args:
heat_pump (BaseCycle): The flowsheet to use
+T_eva_in_ar: Array with inputs for T_eva_in
+T_con_in_ar: Array with inputs for T_con_in
+n_ar: Array with inputs for n_ar
+m_flow_con: Condenser mass flow rate
+m_flow_eva: Evaporator mass flow rate
+save_path: Where to save all results.
+dT_eva_superheating: Evaporator superheating
+dT_con_subcooling: Condenser subcooling
+use_multiprocessing:
+
+
True to use multiprocessing. May speed up the calculation. Default is False
+
+
+
save_plots:
True to save plots of each steady state point. Default is False
Function to calculate the heat pump design
+at a given nominal point.
+Args:
+
+
heat_pump (BaseCycle): A supported flowsheet
+inputs (Inputs):
+
+
The input values at the nominal point.
+If the mass flow rates are not given, you
+can use dT_con and dT_eva to iteratively calculate
+the mass flow rates in order to achieve the required
+temperature differences at the nominal point.
+
+
+
dT_con (float):
Condenser temperature difference to calculate mass flow rate
+
+
dT_eva (float):
Evaporator temperature difference to calculate mass flow rate
m_flow_eva_start: Guess start-value for iteration. Default 0.2
+m_flow_con_start: Guess start-value for iteration. Default 1
+accuracy: Minimal accuracy for mass flow rate iteration (in kg/s).
+
+
Default 0.001 kg/s
+
+
+
+
+
Returns:
+
dict: A dictionary with all flowsheet states and inputs containing
Generate and display visualizations based on data from an SDF (Structured Data File) dataset.
+This function generates various types of visualizations based on the provided parameters,
+including 3D scatter plots, 3D surface plots, and violin plots, and displays them using Matplotlib.
+
+
Args:
+
filepath_sdf (pathlib.Path):
The path to the SDF dataset file.
+
+
nd_data (str):
The name of the primary data to be plotted.
+
+
first_dimension (str):
The name of the first dimension for the visualization.
+
+
second_dimension (str):
The name of the second dimension for the visualization.
+
+
fluids (List[str], optional):
List of specific fluids to include in the visualization.
+Default is None, which includes all fluids.
+
+
flowsheets (List[str], optional):
List of specific flowsheets to include in the visualization.
+Default is None, which includes all flowsheets.
+
+
violin_plot_variable (str, optional):
The variable to be used for creating violin plots.
+Default is None, which disables violin plots.
+
+
third_dimension (str, optional):
The name of the third dimension for 4D visualizations.
+Default is None, which disables 4D plotting.
+
+
+
+
Raises:
KeyError: If the specified data or dimensions are not found in the dataset.
Merge given files and return a merged file.
+Be careful if both files contain the same combination.
+Then, the latter element of the list will overwrite the first one.
+
+
Args:
filepaths (list): List with paths to the files
+save_path (str): Save path for the new file
This example demonstrates how to use the classes MedProp (and its children),
+ThermodynamicState, and TransportProperties.
+Further, basic plotting is shown using this data.
+
First, let’s import the important classes from vclibpy’s
+media module:
We have two media property classes, CoolProp and RefProp.
+The latter requires a dll, which you have to purchase together with RefProp.
+Thus, in this example, we will use CoolProp. Pass the fluid_name to
+select the fluid you are going to use.
Let’s start and show how the media property classes work. You always
+call calc_state(). The documentation explains how to use it:
+
help(cool_prop.calc_state)
+
+
+
Let’s try and start with pressure of 2 bar (2e5 Pa) and 100 kJ/kg enthalpy:
+
state=cool_prop.calc_state("PH",2e5,100e3)
+
+
+
The state is an instance of ThermodynamicState:
+
print(type(state))
+
+
+
The state contains all important specific values:
+
print(state.get_pretty_print())
+
+
+
For these values, we are outside of the two phase region, as q (quality) is -1.
+You can play around with the possible options to get a better understanding.
With a given state, we can calculate the transport properties. Those include
+relevant information for component models, e.g. heat conductivity.
+For information on all properties, look at the documentation:
To plot fluid data, we may plot the two phase limits.
+While we have the function get_two_phase_limits in the media model,
+we will define it here again so that you can further learn how to use media.
+The idea is to loop over all pressure from some minimum value.
+Let’s use the pressure at -40 °C.
+to the maximum, which is the critical point.
+You can get the critical point using the function: get_critical_point:
+
p_min=cool_prop.calc_state("TQ",273.15-40,0).p# Pa
+T_crit,p_crit,d_crit=cool_prop.get_critical_point()
+p_max=p_crit
+
+
+
Let’s create two lists, q0 and q1 for states with quality of 0 and 1. Further,
+we loop only each 10000 Pa to reduce number of function calls.
+
p_step=10000# Pa
+q0=[]
+q1=[]
+forpinrange(int(p_min),int(p_max),p_step):
+ q0.append(cool_prop.calc_state("PQ",p,0))
+ q1.append(cool_prop.calc_state("PQ",p,1))
+
+
+
Now, we can plot these states, for example in a T-h Diagram.
+Note: [::-1] reverts the list, letting it start from the critical point.
+[state.Tforstateinq0] is a list comprehension, quite useful in Python.
+
T=[state.Tforstateinq0+q1[::-1]]
+h=[state.hforstateinq0+q1[::-1]]
+importmatplotlib.pyplotasplt
+plt.ylabel("$T$ in K")
+plt.xlabel("$h$ in J/kg")
+plt.plot(h,T,color="black")
+
+
+
Now, without any component models, let’s try to plot a closed vapor compression cycle:
+Assumptions:
+
+
No superheat nor subcooling
+
isobaric heat exchange
+
isentropic compression and expansion
+
0 °C evaporation and 40 °C condensation temperature
Try to use the skills you’ve learned in this example and tweak the assumptions
+and the plot format: Plot log(p)-h, T-s, or similar. Assume some level
+of superheat, non-isentropic compression etc.
+
After getting familiar with calling the refrigerant data module media, you will
+learn how to use the Compressor classes in the next example.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/examples/e2_compressor.html b/docs/0.1.1/docs/examples/e2_compressor.html
new file mode 100644
index 0000000..98a115f
--- /dev/null
+++ b/docs/0.1.1/docs/examples/e2_compressor.html
@@ -0,0 +1,248 @@
+
+
+
+
+
+
+
+
+ Compressor Example — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Before we can do any calculations, the compressor needs
+access to refrigerant data. Each component in VcLibPy has
+the property med_prop (media property) which you can set like this:
Now, you have to define the input state of the compressor.
+Each component has inlet and outlet states, which are, same as med_prop
+properties of the component.
+We assume a super-heated vapor at 1bar as an input state:
Last but not least, most functions in VcLibPy require
+the argument inputs and fs_state. The whole concept of the two
+classes are explained in the third example. For now, we just instantiate
+the classes and pass a relative compressor speed of 50 % (0.5) as an input.
While the constant efficiency compressor does not rely on any
+states to calculate the constant efficiencies, most other models do.
+Thus, we first want to calculate the outlet state for the given input state.
+We can do so by passing an outlet pressure to the function calc_state_outlet:
+
p_outlet=6e5
+print(f"{constant_efficiency_compressor.state_outlet=}")# still None
+constant_efficiency_compressor.calc_state_outlet(p_outlet=p_outlet,inputs=inputs,fs_state=fs_state)
+print(f"{constant_efficiency_compressor.state_outlet=}")# now calculated
+
+
+
Also, relevant results are automatically added to the fs_state:
+
print(fs_state)
+
+
+
You can play around with the compressor speed (which has no effect due to constant efficiencies)
+or the compression ratio. Let’s do the latter for the outlet temperature:
importmatplotlib.pyplotasplt
+plt.plot(ratios,np.array(T_outlets)-273.15)
+plt.ylabel("$T_\mathrm{Outlet}$ in °C")
+plt.xlabel("$\Pi$ in -")
+plt.show()
+
Now, let’s continue with the mass flow rate. Again, each component has the property
+m_flow, which always refers to the refrigerant mass flow rate.
+The function calc_m_flow calculates and set’s the mass flow rate to this property.
If mass flow rates and outlet states are calculated, we can calculate the
+electrical power consumption of the compressor. Note, that
+if you change input values here, you first have to calculate the
+mass flow rate and outlet state again, as this may influence the result.
Again, important metrics are added to the fs_state:
+
print(fs_state)
+
+
+
After learning the basics of each component and using Inputs and FlowsheetState
+for the first time, we will go deeper into these classes in the third example.
+You can alter the compressor in use by importing other compressors, such as
+RotaryCompressor or TenCoefficientCompressor. Check if you can use these components as well.
This example demonstrates how to use the classes Inputs,
+and FlowsheetState
+
All Variables in the Inputs and FlowsheetState will be saved in
+output formats like .csv or .sdf
+Thus, the concept of these two classes is important to
+understand and analyze simulations in VcLibPy.
All external boundary conditions which act on the vapor compression
+cycle may be inputs. This could be the compressor speed, ambient temperature,
+or the inlet temperatures and mass flow rates of the secondary side
+in the heat exchangers.
+You can see all default options by just printing the empty instance:
+
fromvclibpyimportInputs
+print(Inputs())
+
+
+
Currently, the flowsheets need all of these parameters, except for
+the ambient temperature. This is only required for heat loss estimations or
+efficiency calculations in certain models.
+Handling all the inputs in one object makes it easy for component models
+to access any relevant data it needs. Also, you can add new inputs
+as you like. For instance, let’s say you want to control the pressure
+level ratio, at which vapor is injected in the vapor-injection flowsheets.
+Here, the flowsheets can act on the input k_vapor_injection. The default is 1.
+You can set custom inputs like this:
Optional arguments of the set function are unit and description.
+You should pass those along with the name and value to make
+your results easier to analyze, for others and your future self:
+
inputs.set(
+ name="k_vapor_injection",value=1.05,
+ unit="-",
+ description="Calculates the injection pressure level according to "
+ "k_vapor_injection * np.sqrt(p_1 * p_2)"
+)
+
+
+
The set function registers a Variable in the inputs object.
+You can get different information types like this:
+
print(f"{inputs.get_variables()=}")
+print(f"{inputs.get_variable_names()=}")
+print(f"{inputs.get(name='k_vapor_injection')=}")
+print(f"{inputs.items()=}")# To loop over the variable dict.
+
The class FlowsheetState is essentially the same as Inputs.
+The only difference is its use, which is for outputs of the vapor
+compression cycle like COP, heat flow rate, temperatures, etc.
+Basically, anything a users might be interested in analyzing when
+simulating a steady state vapor compression cycle.
+As the outputs are flowsheet specific, we define no default
+Variables in the FlowsheetState as with Inputs.
+However, you can set variables the same way as with Inputs:
+
fromvclibpyimportFlowsheetState
+fs_state=FlowsheetState()
+print(fs_state)
+fs_state.set(name="some_interesting_output",value=3.14,
+ unit="-",description="This is just an example")
+print(fs_state)
+
+
+
As the fs_state is passed to all components, it’s important to
+use distinct names. If two components set a variable T_1, the latter
+one will override the first one.
+As the fs_state and inputs are mutable, no history is preserved.
+If you want to, for example, plot the history of the fs_state,
+you have to store copies of the instance:
This example demonstrates how to use the heat exchanger
+classes.
+
Contrary to the first examples, the heat exchanger is
+more complex, in the sense that the only current
+models follows a moving boundary epsNTU approach.
+Thus, you need to assume heat transfer correlations
+for different regimes.
+We will use the evaporator for this example. Let’s check the doc:
As you can see, we have to define a lot of parameters.
+Let’s model a simple air-to-refrigerant heat exchanger with
+constant heat transfer correlations. The areas are not that important
+for this example.
+For heat transfer, you can import models from the heat_transfer package
+inside the heat_exchangers package.
+You will find all options in the documentation of VcLibPy
To understand the heat exchanger functions in VcLibPy,
+you have to understand how we iterate to solve the closed
+cycle simulation.
+In reality, the expansion valve would
+adjust its opening until a certain degree of superheat
+is met. While this may oscillate in dynamic operation,
+we assume that the control is able to meet a constant
+degree of superheat in steady state.
+This means, we have to find the pressure level which
+ensures this superheat. So instead of iterating
+the valve opening, we iterate the evaporation pressure
+directly. The same holds for the condenser and subcooling.
+However, levels of subcooling are not directly controlled in
+real devices, at least in the standard cycle.
+The assumption of keeping the degree of subcooling as an input
+could be changed in future work.
+
For iteration, heat exchangers have the function calc.
+In order for it to work, you have to assign both inlet and
+outlet state, as well as the mass flow rate. Note that,
+as we assume the levels of superheat and subcooling, we
+will always have these states in our iterations.
+Further, the inputs need to contain the evaporator inlet temperature T_eva_in and
+the evaporator mass flow rate (secondary side):
+
fromvclibpyimportFlowsheetState,Inputs
+fs_state=FlowsheetState()
+fromvclibpy.mediaimportCoolProp
+med_prop=CoolProp(fluid_name="Propane")
+evaporator.med_prop=med_prop# We have to set it, same as in the second example.
+evaporator.m_flow=0.01
+
+
+
Also, we have to start the secondary med-prop. This is done for you
+in the calculations, but required to be an extra function to enable multi-processing:
+
evaporator.start_secondary_med_prop()
+
+
+
Let’s assume a superheat of 10 K and a condenser subcooling of 0 K.
+With an isenthalp expansion valve, the inlet and outlet are given.
+Further, let’s assume a condensation temperature of 40 °C and
+an air temperature of 2 °C, corresponding to the typical heat pump point A2W25
What do they mean?
+They are used in the iterative logic of VcLibPy and indicate that
+the heat exchanger is valid, if the error is smaller than a pre-defined
+margin and dT_min is greater than 0.
dT_min is, as assumed, close to zero. However, the error is very large.
+The error is calculated as follows: (Q_ntu/Q-1)*100.
+Q is the amount of heat required to be transported, Q_ntu is
+the amount possible to transfer according to NTU method.
+This means, a negative value of 100 means we could transport 2 times
+less heat than we want to.
+Thus, we have to iterate the pressure assumptions and lower it,
+as we need a higher temperature difference to the air.
+For this, we will use a loop:
The iteration of VcLibPy is largely based on this method, so nothing fancy.
+If the error is positive, we step back to the old value and decrease the
+step-size by a factor of 10. At the end, we iterate with a min_iteration_step,
+which is, by default 1 Pa.
+
Let’s plot the result:
+
importmatplotlib.pyplotasplt
+fig,ax=plt.subplots(2,1,sharex=True)
+ax[0].plot(p_evaporations/1e5,errors)
+ax[0].set_ylabel("Error in %")
+ax[1].plot(p_evaporations/1e5,dT_mins)
+ax[1].set_ylabel("$\Delta T$ in K")
+ax[1].set_xlabel("$p_\mathrm{Eva}$ in bar")
+plt.show()
+
As the result is still not good, let’s implement this really basic iteration logic.
+For iterating, we use a while-loop. Let’s define a max-iteration
+counter to avoid an infinite iteration.
+
max_iterations=100
+n_iteration=0
+p_eva_next=p_evaporation# Start with simple assumption
+p_step=10000# Pa
+min_step=1# Pa
+min_error=0.1# 0.1 % maximal error
+errors,dT_mins,p_evaporations=[],[],[]# Store for later plotting
+whilen_iteration<max_iterations:
+ evaporator.state_inlet=med_prop.calc_state("PH",p_eva_next,state_condenser_outlet.h)
+ T_eva=med_prop.calc_state("PQ",p_eva_next,1).T
+ evaporator.state_outlet=med_prop.calc_state("PT",p_eva_next,T_eva+inputs.get("dT_eva_superheating").value)
+ error,dT_min=evaporator.calc(inputs=inputs,fs_state=fs_state)
+ # Store for later plotting
+ errors.append(error)
+ dT_mins.append(dT_min)
+ p_evaporations.append(p_eva_next/1e5)
+ n_iteration+=1
+ iferror<min_error:
+ p_eva_next-=p_step
+ continue
+ eliferror>min_error:
+ p_eva_next+=p_step# Go back
+ ifp_step<=min_step:
+ print("Error: Can't solve any more accurate with given size of min_step")
+ break
+ p_step/=10
+ continue
+ else:
+ print(f"Converged")
+else:
+ print("Did not converged in the given max_iterations.")
+print(f"Solution: {error=}, {dT_min=}, p_evaporation={p_evaporations[-1]}. Took {n_iteration=}")
+
+
+
Again, let’s plot the iteration:
+
importmatplotlib.pyplotasplt
+fig,ax=plt.subplots(3,1,sharex=True)
+ax[0].plot(range(n_iteration),errors,marker="s")
+ax[0].set_ylabel("Error in %")
+ax[1].plot(range(n_iteration),dT_mins,marker="s")
+ax[1].set_ylabel("$\Delta T$ in K")
+ax[2].plot(range(n_iteration),p_evaporations,marker="s")
+ax[2].set_ylabel("$p_\mathrm{Eva}$ in bar")
+ax[2].set_xlabel("Iterations in -")
+plt.show()
+
+
+
You can see that the iterative process converges to an error
+Close to zero. However, it’s not really an efficient optimization.
+This could, and should, be optimized using optimization techniques.
What also helps to understand the heat exchanger better is to
+plot the states and secondary media. We can use the
+get_two_phase_limits function of med_prop to quickly plot
+those:
The valves are not that important for the vapor compression simulation,
+as we iterate the pressure levels directly. However, you can still check
+if a given valve cross-section area is large enough for your required level of
+superheat.
+
help(Bernoulli)
+
+
+
Let’s specify some dummy parameters:
+
d=5e-3# 5 mm diameter
+area=3.14*d**2/4
+expansion_valve=Bernoulli(A=area)
+
+
+
Again, we have to start a med-prop, and give some input state:
Let’s assume we want to match the mass flow rate of the last example:
+What opening would we require for the given cross section area?
+For this, we can use expansion_valve.ca
importmatplotlib.pyplotasplt
+plt.plot(d_mm_array,openings,marker="s")
+plt.ylabel("Opening in %")
+plt.xlabel("$d$ in mm")
+plt.show()
+
+
+
Looking only at this point, the diameter should not be smaller than 1 mm.
+You can tweak the assumptions around, check for different mass flow rates
+or different pressure levels.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/0.1.1/docs/examples/e6_simple_heat_pump.html b/docs/0.1.1/docs/examples/e6_simple_heat_pump.html
new file mode 100644
index 0000000..f5156e9
--- /dev/null
+++ b/docs/0.1.1/docs/examples/e6_simple_heat_pump.html
@@ -0,0 +1,239 @@
+
+
+
+
+
+
+
+
+ Example for a heat pump with a standard cycle — vclibpy 0.1.1 documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Let’s start the complete cycle simulation with the
+most basic flowsheet, the standard-cycle. As all flowsheets
+contain a condenser and an evaporator, we defined a common BaseCycle
+to avoid code-repetition.
+We can import this flowsheet and see how to use it. Note that
+modern coding IDEs like PyCharm will tell you which arguments belong
+to a class or function. If you don’t have that at hand, you need
+to look into the documentation.
We fist need to define the components in the cycle.
+Here we are using the components developed in the previous examples.
+Also, note again that the expansion valve model does not influence the results
+for the current algorithm. But, you could size the expansion valve
+using vclibpy, including off-design, but this is one for another example.
Now, we can generate the full-factorial performance map
+using all inputs. The results will be stored under the
+save-path. To see some logs, we can import the logging module
+and get, for example, all messages equal or above the INFO-level
Now, we can plot variables, for example as a scatter plot using matplotlib.
+You have to know the names, which are the column headers.
+
importmatplotlib.pyplotasplt
+x_name="n in - (Relative compressor speed)"
+y_name="COP in - (Coefficient of performance)"
+plt.scatter(df[x_name],df[y_name])
+plt.ylabel(y_name)
+plt.xlabel(x_name)
+plt.show()
+
+
+
Looking at the results, we see that a higher frequency often leads to lower COP values.
+However, other inputs (temperatures) have a greater impact on the COP.
+We can also use existing 3D-plotting scripts in vclibpy to analyze the
+dependencies. For this, we need the .sdf file. In the sdf, the field names are without
+the unit and description, as those are accessible in the file-format in other columns.
Example for a heat pump with vapor injection using a phase separator
+
Let’s use a flowsheet which is more complex, e.g. the vapor injection
+with a phase seperator.
+We can import this flowsheet and how to use it like this:
As it needs the same heat exchanger model as a standard heat pump,
+we will just use the ones from the standard cycle. Also, as
+the expansion valve model does not influence the results for
+the current algorithm, we will just use the same expansion-valve
+twice. Note, that you could size the two expansion valves
+using vclibpy, including off-design, but this is one for another
+example.
For the compressors, we need to specify low- and high-pressure
+compressors. To achieve a somewhat similar heat pump as the
+one in the standard-cycle example, we will assume that we
+use two smaller compressors instead of one larger one:
Now, we can generate the full-factorial performance map
+using all inputs. The results will be stored under the
+save-path. To see some logs, we can import the logging module
+and get, for example, all messages equal or above the INFO-level
Aside from the phase-separator flowsheet, we have one with
+an economizer (additional heat exchanger).
+The assumptions are similar, and the usage as well:
Repository with a Vapor Compression Library in Python for steady state process design and simulation.
+It enables use of RefProp and CoolProp as well as different compressors, heat exchangers and flowsheet configurations for heat pumps and chillers.
We recommend running our jupyter-notebook to be guided through a helpful tutorial.
+For this, we prepared several examples, which we encourage to check one by one.
+To use, you can either run the code locally or in a browser using juypter-notebook:
+
If the web-hosting is not available, you can run the notebooks locally with the following code:
Vering, C., Wüllhorst, F., Mehrfeld, P., & Müller, D. (2021). Towards an integrated design of heat pump systems: Application of process intensification using two-stage optimization. Energy Conversion and Management, 250, 114888. https://doi.org/10.1016/j.enconman.2021.114888
+
Vering, Christian; Müller, Dirk (Thesis advisor); Elbel, Stefan (Thesis advisor) (2023). Optimal design of heat pump systems for existing buildings, PhD thesis. https://doi.org/10.18154/RWTH-2023-04070
We gratefully acknowledge the financial support by the Federal Ministry for Economic Affairs and Climate Action (BMWK), promotional reference 03EN1022B, as well as the European Regional Development Fund (ERDF) (ERDF-0500029).
first party import "from vclibpy.components.heat_exchangers.heat_transfer import constant" should be placed before "from .heat_transfer import TwoPhaseHeatTransfer, HeatTransfer, calc_reynolds_pipe"
+
+
+
+
3
+
0
+
convention
+
wrong-import-order
+
C0411
+
+
first party import "from vclibpy.components.heat_exchangers.heat_transfer import wall" should be placed before "from .heat_transfer import TwoPhaseHeatTransfer, HeatTransfer, calc_reynolds_pipe"
first party import "from vclibpy.media import TransportProperties" should be placed before "from .heat_transfer import HeatTransfer, calc_reynolds_pipe"
+
+
+
+
51
+
8
+
convention
+
invalid-name
+
C0103
+
TurbulentFluidInPipeToWallTransfer.calc
+
Variable name "Re" doesn't conform to snake_case naming style
Module 'CoolProp.CoolProp' has no 'PT_INPUTS' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
28
+
15
+
info
+
c-extension-no-member
+
I1101
+
CoolProp
+
Module 'CoolProp.CoolProp' has no 'QT_INPUTS' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
29
+
15
+
info
+
c-extension-no-member
+
I1101
+
CoolProp
+
Module 'CoolProp.CoolProp' has no 'PSmass_INPUTS' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
30
+
15
+
info
+
c-extension-no-member
+
I1101
+
CoolProp
+
Module 'CoolProp.CoolProp' has no 'HmassP_INPUTS' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
31
+
15
+
info
+
c-extension-no-member
+
I1101
+
CoolProp
+
Module 'CoolProp.CoolProp' has no 'PQ_INPUTS' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
38
+
44
+
info
+
c-extension-no-member
+
I1101
+
CoolProp.__init__
+
Module 'CoolProp.CoolProp' has no 'AbstractState' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
48
+
16
+
info
+
c-extension-no-member
+
I1101
+
CoolProp.calc_state
+
Module 'CoolProp.CoolProp' has no 'PropsSI' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
50
+
16
+
info
+
c-extension-no-member
+
I1101
+
CoolProp.calc_state
+
Module 'CoolProp.CoolProp' has no 'PropsSI' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
52
+
16
+
info
+
c-extension-no-member
+
I1101
+
CoolProp.calc_state
+
Module 'CoolProp.CoolProp' has no 'PropsSI' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
54
+
16
+
info
+
c-extension-no-member
+
I1101
+
CoolProp.calc_state
+
Module 'CoolProp.CoolProp' has no 'PropsSI' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
56
+
16
+
info
+
c-extension-no-member
+
I1101
+
CoolProp.calc_state
+
Module 'CoolProp.CoolProp' has no 'PropsSI' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
58
+
16
+
info
+
c-extension-no-member
+
I1101
+
CoolProp.calc_state
+
Module 'CoolProp.CoolProp' has no 'PropsSI' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
60
+
16
+
info
+
c-extension-no-member
+
I1101
+
CoolProp.calc_state
+
Module 'CoolProp.CoolProp' has no 'PropsSI' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
88
+
12
+
convention
+
invalid-name
+
C0103
+
CoolProp.calc_transport_properties
+
Variable name "pr" doesn't conform to snake_case naming style
+
+
+
+
88
+
17
+
info
+
c-extension-no-member
+
I1101
+
CoolProp.calc_transport_properties
+
Module 'CoolProp.CoolProp' has no 'PropsSI' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
90
+
17
+
info
+
c-extension-no-member
+
I1101
+
CoolProp.calc_transport_properties
+
Module 'CoolProp.CoolProp' has no 'PropsSI' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
92
+
17
+
info
+
c-extension-no-member
+
I1101
+
CoolProp.calc_transport_properties
+
Module 'CoolProp.CoolProp' has no 'PropsSI' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
94
+
18
+
info
+
c-extension-no-member
+
I1101
+
CoolProp.calc_transport_properties
+
Module 'CoolProp.CoolProp' has no 'PropsSI' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
96
+
22
+
info
+
c-extension-no-member
+
I1101
+
CoolProp.calc_transport_properties
+
Module 'CoolProp.CoolProp' has no 'PropsSI' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
132
+
13
+
info
+
c-extension-no-member
+
I1101
+
CoolProp.get_critical_point
+
Module 'CoolProp.CoolProp' has no 'PropsSI' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
133
+
8
+
convention
+
invalid-name
+
C0103
+
CoolProp.get_critical_point
+
Variable name "pc" doesn't conform to snake_case naming style
+
+
+
+
133
+
13
+
info
+
c-extension-no-member
+
I1101
+
CoolProp.get_critical_point
+
Module 'CoolProp.CoolProp' has no 'PropsSI' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.
+
+
+
+
134
+
8
+
convention
+
invalid-name
+
C0103
+
CoolProp.get_critical_point
+
Variable name "dc" doesn't conform to snake_case naming style
+
+
+
+
134
+
13
+
info
+
c-extension-no-member
+
I1101
+
CoolProp.get_critical_point
+
Module 'CoolProp.CoolProp' has no 'PropsSI' member, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.