"""One-dimensional flow with heat addition calculator"""
import os
from typing import Tuple, Union, Any
if os.environ.get('__USE_CUDA', '0') == '1':
import cupy as np
from cupy import ndarray
else:
import numpy as np
from numpy import ndarray
from .calculator import G3DCalculator
from .normal_shock import G3DNormalShockCalculator
from g3d.defs import CFlowRegime
from g3d.defs import CFlowVariableType
from g3d.defs import CFlowAdditionMode
[docs]
class G3DHeatCalculator(G3DCalculator):
"""Class collecting functions related to one-dimensional flow with heat addition"""
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""Initialize calculator parameters"""
super().__init__(*args, **kwargs)
self.__normal_shock = G3DNormalShockCalculator(update_code_type=False)
def _set_default(self) -> None:
super()._set_default()
self.__M = self._vars.add(name="M", long_name="Mach number")
self.__P = self._vars.add(name="P", long_name="Pressure", type=CFlowVariableType.PRESSURE)
self.__T = self._vars.add(name="T", long_name="Temperature", type=CFlowVariableType.TEMPERATURE)
self.__RO = self._vars.add(name="RO", long_name="Density", type=CFlowVariableType.DENSITY)
self.__To = self._vars.add(name="To", long_name="Total temperature", type=CFlowVariableType.TEMPERATURE)
self.__Po = self._vars.add(name="Po", long_name="Total pressure", type=CFlowVariableType.PRESSURE)
self.__Ps = self._vars.add(name="Ps", long_name="Pressure (choked flow)", type=CFlowVariableType.PRESSURE)
self.__Ts = self._vars.add(name="Ts", long_name="Temperature (choked flow)", type=CFlowVariableType.TEMPERATURE)
self.__ROs = self._vars.add(name="ROs", long_name="Density (choked flow)", type=CFlowVariableType.DENSITY)
self.__Tos = self._vars.add(name="Tos", long_name="Total temperature (choked flow)",
type=CFlowVariableType.TEMPERATURE)
self.__Pos = self._vars.add(name="Pos", long_name="Total pressure (choked flow)",
type=CFlowVariableType.PRESSURE)
self.__qs = self._vars.add(name="qs", long_name="added heat (choked flow)", dim="kJ/kg")
self.__q = self._vars.add(name="q", long_name="added heat (choked flow)", dim="kJ/kg")
self.__M1 = self._vars.add(name="M1", long_name="Mach number")
self.__P1 = self._vars.add(name="P1", long_name="Pressure", type=CFlowVariableType.PRESSURE)
self.__T1 = self._vars.add(name="T1", long_name="Temperature", type=CFlowVariableType.TEMPERATURE)
self.__M2 = self._vars.add(name="M2", long_name="Mach number")
self.__P2 = self._vars.add(name="P2", long_name="Pressure", type=CFlowVariableType.PRESSURE)
self.__T2 = self._vars.add(name="T2", long_name="Temperature", type=CFlowVariableType.TEMPERATURE)
def __Mach_factor(self, M: float = None) -> float:
if M is None:
M = self.__M.value
return self._gas.gp1 / (1. + self._gas.gamma * M * M)
[docs]
def get_Q_star(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
.. deprecated:: 2.6
redirects to :func:`get_q_star`
"""
return self.get_q_star(*args, **kwargs)
[docs]
def get_q_star(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculates choked flow heat addition from specified local total temperature (To [K])
and flow Mach number (M [])
input: Mach number (M []), total temperature (To [K])
example qs = get_q_star(M=M, To=To)
"""
return self._eval(["M", "To"], self.__get_q_star, *args, **kwargs)
def __get_q_star(self, M: float = None, To: float = None) -> float:
if M is None:
M = self.__M.value
if To is None:
To = self.__To.value
q_star = self._gas.Cp * To * (self.__get_To_star(M, 1.0) - 1.0)
return q_star
[docs]
def get_T_star(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculates choked flow temperature (Ts [K]) based on provided local
temperature (T [K]) and flow Mach number (M [])
input: flow Mach number (M []), temperature (T [K])
example: Ts = get_T_star(M=M, T=T)
"""
return self._eval(["M", "T"], self.__get_T_star, *args, **kwargs)
def __get_T_star(self, M: float = None, T: float = None) -> float:
if M is None:
M = self.__M.value
if T is None:
T = self.__T.value
return T / np.power(M * self.__Mach_factor(M), 2)
[docs]
def get_P_star(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculates choked flow pressure (Ps [Pa]) based on provided local
pressure (P [Pa]) and flow Mach number (M [])
input: flow Mach number (M []), pressure (P [Pa])
example: Ps = get_P_star(M=M, P=P)
"""
return self._eval(["M", "P"], self.__get_P_star, *args, **kwargs)
def __get_P_star(self, M: float = None, P: float = None) -> float:
if M is None:
M = self.__M.value
if P is None:
P = self.__P.value
return P / self.__Mach_factor(M)
[docs]
def get_RO_star(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculates choked flow density (ROs [kg/m^3]) based on provided local
density (RO [kg/m^3]) and flow Mach number (M [])
input: flow Mach number (M []), density (RO [kg/m^3])
example: ROs = get_RO_star(M=M, RO=RO)
"""
return self._eval(["M", "RO"], self.__get_RO_star, *args, **kwargs)
def __get_RO_star(self, M: float = None, RO: float = None) -> float:
if M is None:
M = self.__M.value
if RO is None:
RO = self.__RO.value
return RO * M * M * self.__Mach_factor(M)
[docs]
def get_P0_star(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
.. deprecated:: 2.6
redirects to :func:`get_Po_star`
"""
return self.get_Po_star(*args, **kwargs)
[docs]
def get_Po_star(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculates choked flow total pressure (Pos [Pa]) based on provided local
total pressure (Po [Pa]) and flow Mach number (M [])
input: flow Mach number (M []), total pressure (Po [Pa])
example: Pos = get_Po_star(M=M, Po=Po)
"""
return self._eval(["M", "Po"], self.__get_Po_star, *args, **kwargs)
def __get_Po_star(self, M: float = None, Po: float = None) -> float:
if M is None:
M = self.__M.value
if Po is None:
Po = self.__Po.value
return Po / (self.__Mach_factor(M) * np.power((2. + self._gas.gm1 * M * M) / self._gas.gp1,
self._gas.g_over_gm1))
[docs]
def get_T0_star(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
.. deprecated:: 2.6
redirects to :func:`get_To_star`
"""
return self.get_To_star(*args, **kwargs)
[docs]
def get_To_star(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculates choked flow total temperature (Tos [K]) based on provided local
total temperature (To [K]) and flow Mach number (M [])
input: flow Mach number (M []), total temperature (To [K])
example: Tos = get_To_star(M=M, To=To)
"""
return self._eval(["M", "To"], self.__get_To_star, *args, **kwargs)
def __get_To_star(self, M: float = None, To: float = None) -> float:
if M is None:
M = self.__M.value
if To is None:
To = self.__To.value
return To / (((self._gas.gp1 * M * M) / np.power((1. + self._gas.gamma * M * M), 2)) *
(2. + self._gas.gm1 * M * M))
[docs]
def get_T(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculates local temperature (T [K]) based on provided choked flow
temperature (Ts [K]) and flow Mach number (M [])
input: flow Mach number (M []), choked flow temperature (Ts [K])
example: T = get_T(M=M, Ts=Ts)
"""
return self._eval(["M", "Ts"], self.__get_T, *args, **kwargs)
def __get_T(self, M: float = None, Ts: float = None) -> float:
if M is None:
M = self.__M.value
if Ts is None:
Ts = self.__Ts.value
return Ts / self.__get_T_star(M, 1.)
[docs]
def get_P(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculates local pressure (P [Pa]) based on provided choked flow
pressure (Ps [Pa]) and flow Mach number (M [])
input: flow Mach number (M []), choked flow pressure (Ps [Pa])
example: P = get_P(M=M, Ps=Ps)
"""
return self._eval(["M", "Ps"], self.__get_P, *args, **kwargs)
def __get_P(self, M: float = None, Ps: float = None) -> float:
if M is None:
M = self.__M.value
if Ps is None:
Ps = self.__Ps.value
return Ps / self.__get_P_star(M, 1.)
[docs]
def get_RO(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculates local density (RO [kg/m^3]) based on provided choked flow
density (ROs [kg/m^3]) and flow Mach number (M [])
input: flow Mach number (M []), choked flow density (ROs [kg/m^3])
example: RO = get_RO(M=M, ROs=ROs)
"""
return self._eval(["M", "ROs"], self.__get_RO, *args, **kwargs)
def __get_RO(self, M: float = None, ROs: float = None) -> float:
if M is None:
M = self.__M.value
if ROs is None:
ROs = self.__ROs.value
return ROs / self.__get_RO_star(M, 1.)
[docs]
def get_P0(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
.. deprecated:: 2.6
redirects to :func:`get_Po`
"""
return self.get_Po(*args, **kwargs)
[docs]
def get_Po(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculates local total pressure (Po [Pa]) based on provided choked flow
total pressure (Pos [Pa]) and flow Mach number (M [])
input: flow Mach number (M []), choked flow total pressure (Pos [Pa])
example: Po = get_Po(M=M, Pos=Pos)
"""
return self._eval(["M", "Pos"], self.__get_Po, *args, **kwargs)
def __get_Po(self, M: float = None, Pos: float = None) -> float:
if M is None:
M = self.__M.value
if Pos is None:
Pos = self.__Pos.value
return Pos / self.__get_Po_star(M, 1.)
[docs]
def get_T0(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
.. deprecated:: 2.6
redirects to :func:`get_To`
"""
return self.get_To(*args, **kwargs)
[docs]
def get_To(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculates local total temperature (To [K]) based on provided choked flow
total temperature (Tos [K]) and flow Mach number (M [])
input: flow Mach number (M []), choked flow total temperature (Tos [K])
example: To = get_To(M=M, Tos=Tos)
"""
return self._eval(["M", "Tos"], self.__get_To, *args, **kwargs)
def __get_To(self, M: float = None, Tos: float = None) -> float:
if M is None:
M = self.__M.value
if Tos is None:
Tos = self.__Tos.value
return Tos / self.__get_To_star(M, 1.)
[docs]
def find_Mach_from_pressure_ratio(self, *args: Any, **kwargs: Any) -> Union[np.float64, list[np.float64], float]:
"""
.. deprecated:: 2.6
redirects to :func:`get_Mach_from_pressure_ratio`
"""
return self.get_Mach_from_pressure_ratio(*args, **kwargs)
[docs]
def get_Mach_from_pressure_ratio(self, *args: Any, **kwargs: Any) -> Union[np.float64, list[np.float64], float]:
"""
calculates local flow Mach number (M []) from provided local pressure (P [Pa])
and choked flow pressure (Ps [Pa])
input: local pressure (P [Pa]), choked flow pressure (Ps [Pa])
example: M = get_Mach_from_pressure_ratio(P=P, Ps=Ps)
"""
return self._eval(["P", "Ps"], self.__get_Mach_from_pressure_ratio, *args, **kwargs)
def __get_Mach_from_pressure_ratio(self, P: float = None, Ps: float = None) -> float:
if P is None:
P = self.__P.value
if Ps is None:
Ps = self.__Ps.value
return np.sqrt((self._gas.gp1 * Ps - P) / (self._gas.gamma * P))
[docs]
def find_Mach_from_temperature_ratio(self, *args: Any, **kwargs: Any) -> Union[np.float64, list[np.float64], float]:
"""
.. deprecated:: 2.6
redirects to :func:`get_Mach_from_temperature_ratio`
"""
return self.get_Mach_from_temperature_ratio(*args, **kwargs)
[docs]
def get_Mach_from_temperature_ratio(self, *args: Any, **kwargs: Any) -> Union[np.float64, list[np.float64], float]:
"""
calculates local flow Mach number (M []) from provided local temperature (T [K])
and choked flow temperature (Ts [K])
the flow is assumed to be subsonic unless mode='supersonic' is specified
input: local temperature (T [K]), choked flow temperature (Ts [K])
additional input: mode = 'subsonic' (default if not specified) or mode = 'supersonic'
example: M = get_Mach_from_temperature_ratio(T=T, Ts=Ts)
"""
self._reset_eval(mode=CFlowRegime.SUBSONIC)
self._newton.reset()
return self._eval(["T", "Ts"], self.__get_Mach_from_temperature_ratio, *args, **kwargs)
def __get_Mach_from_temperature_ratio(self, TR: float = None) -> float:
T = self.__T.value
if TR is None:
Ts = self.__Ts.value
TR = Ts / T
# calculate target value
newton = self._get_newton_kernel()
newton.target_value = TR
# find a start value by doing a coarse search
newton.start_value = 1.0
newton.end_value = 0.1
if self._mode == CFlowRegime.SUPERSONIC:
newton.end_value = 10.
newton.lower_bound = 1.
else:
newton.upper_bound = 1.
if T != 1.:
self.__T.value = 1.
newton.find_start_value(self.__get_T_star)
# run Newton solver to find Mach number
M = newton.solve(self.__get_T_star)
if T != 1.:
self.__T.value = T
return M
[docs]
def find_Mach_from_total_temperature_ratio(self, *args: Any, **kwargs: Any) \
-> Union[np.float64, list[np.float64], float]:
"""
.. deprecated:: 2.6
redirects to :func:`get_Mach_from_total_temperature_ratio`
"""
return self.get_Mach_from_total_temperature_ratio(*args, **kwargs)
[docs]
def get_Mach_from_total_temperature_ratio(self, *args: Any, **kwargs: Any) \
-> Union[np.float64, list[np.float64], float]:
"""
calculates local flow Mach number (M []) from provided local total temperature (To [K])
and choked flow total temperature (Tos [K])
the flow is assumed to be subsonic unless mode='supersonic' is specified
input: local total temperature (To [K]), choked flow total temperature (Tos [K])
additional input: mode = 'subsonic' (default if not specified) or mode = 'supersonic'
example: M = get_Mach_from_total_temperature_ratio(To=To, Tos=Tos)
"""
self._reset_eval(mode=CFlowRegime.SUBSONIC)
self._newton.reset()
return self._eval(["To", "Tos"], self.__get_Mach_from_total_temperature_ratio, *args, **kwargs)
def __get_Mach_from_total_temperature_ratio(self, TR: float = None) -> float:
To = self.__To.value
if TR is None:
Tos = self.__Tos.value
TR = Tos / To
# calculate target value
newton = self._get_newton_kernel()
newton.target_value = TR
# find a start value by doing a coarse search
newton.start_value = 1.0
newton.end_value = 0.1
if self._mode == CFlowRegime.SUPERSONIC:
newton.end_value = 10.
newton.lower_bound = 1.
else:
newton.upper_bound = 1.
if To != 1.:
self.__To.value = 1.
newton.find_start_value(self.__get_To_star)
# run Newton solver to find Mach number
M = newton.solve(self.__get_To_star)
if To != 1.:
self.__To.value = To
return M
[docs]
def find_Mach_from_Q_star_and_T0(self, *args: Any, **kwargs: Any) -> Union[np.float64, list[np.float64], float]:
"""
.. deprecated:: 2.6
redirects to :func:`get_Mach_from_q_star_and_To`
"""
return self.get_Mach_from_q_star_and_To(*args, **kwargs)
[docs]
def find_Mach_from_q_star_and_To(self, *args: Any, **kwargs: Any) -> Union[np.float64, list[np.float64], float]:
"""
.. deprecated:: 2.6
redirects to :func:`get_Mach_from_q_star_and_To`
"""
return self.get_Mach_from_q_star_and_To(*args, **kwargs)
[docs]
def get_Mach_from_q_star_and_To(self, *args: Any, **kwargs: Any) -> Union[np.float64, list[np.float64], float]:
"""
calculates local flow Mach number (M []) from provided local total temperature (To [K])
and choked flow heat addition (qs [J/kg])
the flow is assumed to be subsonic unless mode='supersonic' is specified
input: choked flow heat addition (qs [J/kg]), local total temperature (To [K])
additional input: mode = 'subsonic' (default if not specified) or mode = 'supersonic'
example: M = get_Mach_from_q_star_and_To(qs=qs, To=To)
"""
self._reset_eval(mode=CFlowRegime.SUBSONIC)
self._newton.reset()
return self._eval(["qs", "To"], self.__get_Mach_from_q_star_and_To, *args, **kwargs)
def __get_Mach_from_q_star_and_To(self, qs: float = None, To: float = None) -> float:
if qs is None:
qs = self.__qs.value
if To is None:
To = self.__To.value
Tos = qs / self._gas.Cp + To
TR = Tos / To
return self.__get_Mach_from_total_temperature_ratio(TR)
[docs]
def find_Mach_from_Q_star_and_T0s(self, *args: Any, **kwargs: Any) -> Union[np.float64, list[np.float64], float]:
"""
.. deprecated:: 2.6
redirects to :func:`get_Mach_from_q_star_and_Tos`
"""
return self.get_Mach_from_q_star_and_Tos(*args, **kwargs)
[docs]
def find_Mach_from_q_star_and_Tos(self, *args: Any, **kwargs: Any) -> Union[np.float64, list[np.float64], float]:
"""
.. deprecated:: 2.6
redirects to :func:`get_Mach_from_q_star_and_Tos`
"""
return self.get_Mach_from_q_star_and_Tos(*args, **kwargs)
[docs]
def get_Mach_from_q_star_and_Tos(self, *args: Any, **kwargs: Any) -> Union[np.float64, list[np.float64], float]:
"""
calculates local flow Mach number (M []) from provided choked flow total temperature (Tos [K])
and choked flow heat addition (qs [J/kg])
the flow is assumed to be subsonic unless mode='supersonic' is specified
input: choked flow heat addition (qs [J/kg]), choked flow total temperature (Tos [K])
additional input: mode = 'subsonic' (default if not specified) or mode = 'supersonic'
example: M = get_Mach_from_q_star_and_Tos(qs=qs, Tos=Tos)
"""
self._reset_eval(mode=CFlowRegime.SUBSONIC)
self._newton.reset()
return self._eval(["qs", "Tos"], self.__get_Mach_from_q_star_and_Tos, *args, **kwargs)
def __get_Mach_from_q_star_and_Tos(self, qs: float = None, Tos: float = None) -> float:
if qs is None:
qs = self.__qs.value
if Tos is None:
Tos = self.__Tos.value
To = Tos - qs / self._gas.Cp
TR = Tos / To
return self.__get_Mach_from_total_temperature_ratio(TR)
# ================================================================
# TWO-STATION PROBLEMS
# ================================================================
def __calculate_heat(self, M1: float, To: float, q: float) -> float:
if self._add_mode in [CFlowAdditionMode.FACTOR, CFlowAdditionMode.PERCENT]:
if self._add_mode is CFlowAdditionMode.PERCENT:
q /= 100.
if q < 0:
q *= self.__get_q_star(M1, To) / (1 + q)
else:
q *= self.__get_q_star(M1, To)
self._add_mode = CFlowAdditionMode.ABSOLUTE
return q
[docs]
def get_upstream_flow_state_after_overchoking(self, *args: Any, **kwargs: Any) \
-> Union[Tuple[np.float64, ...], list[Tuple[np.float64, ...]]]:
"""
calculates upstream static flow conditions if the added heat exceeds the choked flow heat addition for
the provided upstream total condition (Po [Pa], To [K])
the inlet static flow conditions are obtained such that the provided exit pressure is matched
input: inlet total temperature (To [K]), inlet total pressure (Po [Pa]), exit static pressure (P2 [Pa]),
added heat (q [J/kg])
example: M1, T1, P1 = get_upstream_flow_state_after_overchoking(To=To, Po=Po, P2=P2, q=q)
"""
self._reset_eval(mode=CFlowRegime.SUBSONIC)
self._newton.reset()
return self._eval(["To", "Po", "P2", "q"],
self.__get_upstream_flow_state_after_overchoking, *args, **kwargs, outdim=3)
def __get_upstream_flow_state_after_overchoking(self, To: float = None, Po: float = None, P2: float = None,
q: float = None) -> tuple[float, float, float]:
if To is None:
To = self.__To.value
if Po is None:
Po = self.__Po.value
if P2 is None:
P2 = self.__P2.value
if q is None:
q = self.__q.value
# calculate starting guess of the station 1 Mach number
Tos = q / self._gas.Cp + To
M1 = self.__get_Mach_from_q_star_and_Tos(q, Tos)
P1 = self._stagnation.get_static_pressure(M=M1, Po=Po)
T1 = self._stagnation.get_static_temperature(M=M1, To=To)
Pe = self.__get_downstream_pressure(M1, T1, P1, q)
while Pe < P2:
M1 *= 0.9
P1 = self._stagnation.get_static_pressure(M=M1, Po=Po)
T1 = self._stagnation.get_static_temperature(M=M1, To=To)
Pe = self.__get_downstream_pressure(M1, T1, P1, q)
dM = 0.1
while np.abs(Pe - P2) / P2 > self._newton.tolerance and dM > 1.e-10:
M1_tmp = M1 + dM
P1_tmp = self._stagnation.get_static_pressure(M=M1_tmp, Po=Po)
T1_tmp = self._stagnation.get_static_temperature(M=M1_tmp, To=To)
Pe_tmp = self.__get_downstream_pressure(M1_tmp, T1_tmp, P1_tmp, q)
if Pe_tmp > P2:
M1 = M1_tmp
P1 = P1_tmp
T1 = T1_tmp
Pe = Pe_tmp
else:
dM /= 10.
return M1, T1, P1
[docs]
def get_upstream_flow_state_after_overchoking_2(self, *args: Any, **kwargs: Any) \
-> Union[Tuple[np.float64, ...], list[Tuple[np.float64, ...]]]:
"""
calculates upstream static flow conditions if the added heat exceeds the choked flow heat addition for
the provided upstream total condition (M1 [], T1 [K], P1 [Pa])
input: inlet Mach number (M1 []), inlet temperature (T1 [K]), inlet pressure (P1 [Pa]),
added heat (q [J/kg])
example: M1, T1, P1 = get_upstream_flow_state_after_overchoking_2(M1=M1, T1=T1, P1=P1, q=q)
"""
self._reset_eval(mode=CFlowRegime.SUBSONIC)
self._newton.reset()
return self._eval(["M1", "T1", "P1", "q"],
self.__get_upstream_flow_state_after_overchoking_2, *args, **kwargs, outdim=3)
def __get_upstream_flow_state_after_overchoking_2(self, M1: float = None, T1: float = None,
P1: float = None, q: float = None) -> tuple[float, float, float]:
if M1 is None:
M1 = self.__M1.value
if T1 is None:
T1 = self.__T1.value
if P1 is None:
P1 = self.__P1.value
if q is None:
q = self.__q.value
Po = self._stagnation.get_total_pressure(M=M1, P=P1)
To = self._stagnation.get_total_temperature(M=M1, T=T1)
Ps = self.__get_P_star(M1, P1)
q = self.__calculate_heat(M1, To, q)
return self.__get_upstream_flow_state_after_overchoking(To, Po, Ps, q)
[docs]
def get_downstream_pressure(self, *args: Any, **kwargs: Any) -> Union[np.float64, list[np.float64], float]:
"""
two-station problem:
calculates downstream pressure (P2 [Pa]) based on upstream temperature (T1 [K]),
upstream pressure (P1 [Pa]), upstream Mach number (M1 []), and added heat (q [J/kg])
input: upstream Mach number (M1 []), upstream static temperature (T1 [K]),
upstream static pressure (P1 [Pa]), added heat (q [J/kg])
additional input: mode = 'subsonic' (default if not specified) or mode = 'supersonic'
example: P2 = get_downstream_pressure(M1=M1, T1=T1, P1=P1, q=q, mode='subsonic')
"""
self._reset_eval(mode=CFlowRegime.SUBSONIC)
self._newton.reset()
return self._eval(["M1", "T1", "P1", "q"], self.__get_downstream_pressure, *args, **kwargs)
def __get_downstream_pressure(self, M1: float = None, T1: float = None, P1: float = None, q: float = None) -> float:
if M1 is None:
M1 = self.__M1.value
if T1 is None:
T1 = self.__T1.value
if P1 is None:
P1 = self.__P1.value
if q is None:
q = self.__q.value
To = self._stagnation.get_total_temperature(M=M1, T=T1)
Tos = self.__get_To_star(M1, To)
Ps = self.__get_P_star(M1, P1)
qs1 = self.__get_q_star(M1, To)
q = self.__calculate_heat(M1, To, q)
qs2 = qs1 - q
if qs2 < 0:
return -1
M2 = self.__get_Mach_from_q_star_and_Tos(qs2, Tos)
P2 = self.__get_P(M2, Ps)
return P2
[docs]
def get_upstream_pressure(self, *args: Any, **kwargs: Any) -> Union[np.float64, list[np.float64], float]:
"""
two-station problem:
calculates upstream pressure (P1 [Pa]) based on downstream temperature (T2 [K]),
downstream pressure (P2 [Pa]), downstream Mach number (M2 []), and added heat (q [J/kg])
input: downstream Mach number (M2 []), downstream static temperature (T2 [K]),
downstream static pressure (P2 [Pa]), added heat (q [J/kg])
additional input: mode = 'subsonic' (default if not specified) or mode = 'supersonic'
example: P1 = get_upstream_pressure(M2=M2, T2=T2, P2=P2, q=q, mode='subsonic')
"""
self._reset_eval(mode=CFlowRegime.SUBSONIC)
self._newton.reset()
return self._eval(["M2", "T2", "P2", "q"], self.__get_upstream_pressure, *args, **kwargs)
def __get_upstream_pressure(self, M2: float = None, T2: float = None, P2: float = None, q: float = None) -> float:
if M2 is None:
M2 = self.__M2.value
if T2 is None:
T2 = self.__T2.value
if P2 is None:
P2 = self.__P2.value
if q is None:
q = self.__q.value
return self.__get_downstream_pressure(M2, T2, P2, -q)
[docs]
def get_downstream_temperature(self, *args: Any, **kwargs: Any) -> Union[np.float64, list[np.float64], float]:
"""
two-station problem:
calculates downstream temperature (T2 [K]) based on upstream temperature (T1 [K]),
upstream Mach number (M1 []), and added heat (q [J/kg])
input: upstream Mach number (M1 []), upstream static temperature (T1 [K]),
added heat (q [J/kg])
additional input: mode = 'subsonic' (default if not specified) or mode = 'supersonic'
example: T2 = get_downstream_temperature(M1=M1, T1=T1, q=q, mode='subsonic')
"""
self._reset_eval(mode=CFlowRegime.SUBSONIC)
self._newton.reset()
return self._eval(["M1", "T1", "q"], self.__get_downstream_temperature, *args, **kwargs)
def __get_downstream_temperature(self, M1: float = None, T1: float = None, q: float = None) -> float:
if M1 is None:
M1 = self.__M1.value
if T1 is None:
T1 = self.__T1.value
if q is None:
q = self.__q.value
To = self._stagnation.get_total_temperature(M=M1, T=T1)
Tos = self.__get_To_star(M1, To)
Ts = self.__get_T_star(M1, T1)
qs1 = self.__get_q_star(M1, To)
q = self.__calculate_heat(M1, To, q)
qs2 = qs1 - q
if qs2 < 0:
return -1
M2 = self.__get_Mach_from_q_star_and_Tos(qs2, Tos)
T2 = self.__get_T(M2, Ts)
return T2
[docs]
def get_upstream_temperature(self, *args: Any, **kwargs: Any) -> Union[np.float64, list[np.float64], float]:
"""
two-station problem:
calculates upstream temperature (T1 [K]) based on downstream temperature (T2 [K]),
downstream Mach number (M2 []), and added heat (q [J/kg])
input: downstream Mach number (M2 []), downstream static temperature (T2 [K]),
added heat (q [J/kg])
additional input: mode = 'subsonic' (default if not specified) or mode = 'supersonic'
example: T1 = get_upstream_temperature(M2=M2, T2=T2, q=q, mode='subsonic')
"""
self._reset_eval(mode=CFlowRegime.SUBSONIC)
self._newton.reset()
return self._eval(["M2", "T2", "q"], self.__get_upstream_temperature, *args, **kwargs)
def __get_upstream_temperature(self, M2: float = None, T2: float = None, q: float = None) -> float:
if M2 is None:
M2 = self.__M2.value
if T2 is None:
T2 = self.__T2.value
if q is None:
q = self.__q.value
return self.__get_downstream_temperature(M2, T2, -q)
[docs]
def get_downstream_flow_state(self, *args: Any, **kwargs: Any) \
-> Union[Tuple[np.float64, ...], list[Tuple[np.float64, ...]]]:
"""
two-station problem:
calculates downstream flow state (Mach number M2 [], temperature T2 [K], pressure P2 [Pa]) based on
upstream flow state (Mach number M1 [], temperature T1 [K], pressure P1 [Pa])
and added heat (q [J/kg])
input: upstream Mach number (M1 []), upstream static temperature (T1 [K]),
upstream static pressure (P1 [Pa]), added heat (q [J/kg])
additional input: mode = 'subsonic' (default if not specified) or mode = 'supersonic'
example: M2, T2, P2 = get_downstream_flow_state(M1=M1, T1=T1, P1=P1, q=q, mode='subsonic')
"""
self._reset_eval(mode=CFlowRegime.SUBSONIC)
self._newton.reset()
return self._eval(["M1", "T1", "P1", "q"], self.__get_downstream_flow_state, *args, **kwargs, outdim=3)
def __get_downstream_flow_state(self, M1: float = None, T1: float = None, P1: float = None,
q: float = None) -> tuple[float, float, float]:
if M1 is None:
M1 = self.__M1.value
if T1 is None:
T1 = self.__T1.value
if P1 is None:
P1 = self.__P1.value
if q is None:
q = self.__q.value
if M1 > 1.:
self._mode = CFlowRegime.SUPERSONIC
To = self._stagnation.get_total_temperature(M=M1, T=T1)
Tos = self.__get_To_star(M1, To)
Ts = self.__get_T_star(M1, T1)
Ps = self.__get_P_star(M1, P1)
qs1 = self.__get_q_star(M1, To)
q = self.__calculate_heat(M1, To, q)
qs2 = qs1 - q
if qs2 < 0:
print('over choking')
return -1, -1, -1
M2 = self.__get_Mach_from_q_star_and_Tos(qs2, Tos)
T2 = self.__get_T(M2, Ts)
P2 = self.__get_P(M2, Ps)
return M2, T2, P2
[docs]
def get_upstream_flow_state(self, *args: Any, **kwargs: Any) \
-> Union[Tuple[np.float64, ...], list[Tuple[np.float64, ...]]]:
"""
two-station problem:
calculates upstream flow state (Mach number M1 [], temperature T1 [K], pressure P1 [Pa]) based on
downstream flow state (Mach number M2 [], temperature T2 [K], pressure P2 [Pa])
and added heat (q [J/kg])
input: downstream Mach number (M2 []), downstream static temperature (T2 [K]),
downstream static pressure (P2 [Pa]), added heat (q [J/kg])
additional input: mode = 'subsonic' (default if not specified) or mode = 'supersonic'
example: M1, T1, P1 = get_upstream_flow_state(M2=M2, T2=T2, P2=P2, q=q, mode='subsonic')
"""
self._reset_eval(mode=CFlowRegime.SUBSONIC)
self._newton.reset()
return self._eval(["M2", "T2", "P2", "q"], self.__get_upstream_flow_state, *args, **kwargs, outdim=3)
def __get_upstream_flow_state(self, M2: float = None, T2: float = None, P2: float = None,
q: float = None) -> tuple[float, float, float]:
if M2 is None:
M2 = self.__M2.value
if T2 is None:
T2 = self.__T2.value
if P2 is None:
P2 = self.__P2.value
if q is None:
q = self.__q.value
return self.__get_downstream_flow_state(M2, T2, P2, -q)
# ================================================================
# PROCESS DIAGRAM DATA GENERATION
# ================================================================
[docs]
def process_diagram_get_HS(self, M: Union[float, list[float], ndarray]) \
-> Union[Tuple[float, float], Tuple[list[float], list[float]], Tuple[ndarray, ndarray]]:
"""
extract HS-diagram data for one dimensional flow with heat addition
input: Mach number (M [])
example: H, DS = process_diagram_get_HS(M)
"""
H = self.__get_T_star(M, 1.) ** (-1)
DS = np.log(M ** 2 * (self._gas.gp1 / (1. + self._gas.gamma * M ** 2)) ** (self._gas.gp1 / self._gas.gamma))
return H, DS
[docs]
def process_diagram_get_PVH(self, v: Union[float, list[float], ndarray],
p1: float, v1: float, T1: float, q: float) -> Union[float, list[float], ndarray]:
"""
extract PVH-diagram data for one dimensional flow with heat addition
input: specific volume (v [m^3/kg]), reference pressure (p1 [Pa]), reference specific volume (v1 [m^3/kg]),
reference temperature (T1 [K]), added heat (q [J/kg])
example: H = process_diagram_get_PVH(v, p1, v1, T1, q)
"""
return (p1 * ((v / v1) - self._gas.gp1 / self._gas.gm1 - 2. * q / (self._gas.R * T1))
/ (1. - self._gas.gp1 / self._gas.gm1 * v / v1))
def __process_diagram_TS_get_delta(self, M: float, T: float, To: float, dTo: float) -> tuple[float, float, float]:
M2 = M * M
C1 = (1. + 0.5 * self._gas.gm1 * M2) * dTo / To
dT = T * (1. - self._gas.gamma * M2) * C1 / (1. - M2)
dS = self._gas.Cv * self._gas.gamma * C1
dM = M * 0.5 * (1. + self._gas.gamma * M2) * C1 / (1. - M2)
return dT, dS, dM
[docs]
def process_diagram_get_TS(self, T1: float, S1: float, M1: float, Q: float, N: int) -> Tuple[np.ndarray, ...]:
"""
extract Ts-diagram data for one dimensional flow with friction
input: initial temperature (T1 [K]), initial entropy (S1 [J/K]), initial Mach number (M1 []),
added heat (Q [J/kg]), number of points (N)
example: T, S = process_diagram_get_TS(T1, S1, M1, Q, N)
"""
T = T1
S = S1
M = M1
To = self._stagnation.get_total_temperature(M1, T1)
dTo = Q / float(N) / self._gas.Cp
T_array = np.zeros(N)
S_array = np.zeros(N)
T_array[0] = T
S_array[0] = S
for i in range(1, N):
dT, dS, dM = self.__process_diagram_TS_get_delta(M, T, To, dTo)
T = T + dT
S = S + dS
M = M + dM
To = To + dTo
T_array[i] = T
S_array[i] = S
return T_array, S_array