Source code for g3d.calc.calculators.heat

"""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