"""Normal-shock calculator"""
import os
from typing import Union, Any
if os.environ.get('__USE_CUDA', '0') == '1':
import cupy as np
else:
import numpy as np
from .calculator import G3DCalculator
from g3d.defs import CFlowVariableType
[docs]
class G3DNormalShockCalculator(G3DCalculator):
"""Class collecting functions related to normal shocks"""
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""Initialize calculator parameters"""
super().__init__(*args, **kwargs)
def _set_default(self) -> None:
super()._set_default()
self.__PR = self._vars.add(name="PR", long_name="Pressure ratio")
self.__TR = self._vars.add(name="TR", long_name="Temperature ratio")
self.__RR = self._vars.add(name="RR", long_name="Density ratio")
self.__PoR = self._vars.add(name="PoR", long_name="Total pressure ratio")
self.__M1 = self._vars.add(name="M1", long_name="Mach number (upstream)")
self.__T1 = self._vars.add(name="T1", long_name="Temperature (upstream)", type=CFlowVariableType.TEMPERATURE)
self.__P1 = self._vars.add(name="P1", long_name="Pressure (upstream)", type=CFlowVariableType.PRESSURE)
self.__RO1 = self._vars.add(name="RO1", long_name="Density (upstream)", type=CFlowVariableType.DENSITY)
self.__Po1 = self._vars.add(name="Po1", long_name="Total pressure (upstream)", type=CFlowVariableType.PRESSURE)
self.__As1 = self._vars.add(name="As1", long_name="Choke area (upstream)", type=CFlowVariableType.AREA)
self.__M2 = self._vars.add(name="M2", long_name="Mach number (downstream)")
self.__T2 = self._vars.add(name="T2", long_name="Temperature (downstream)", type=CFlowVariableType.TEMPERATURE)
self.__P2 = self._vars.add(name="P2", long_name="Pressure (downstream)", type=CFlowVariableType.PRESSURE)
self.__RO2 = self._vars.add(name="RO2", long_name="Density (downstream)", type=CFlowVariableType.DENSITY)
self.__Po2 = self._vars.add(name="Po2", long_name="Total pressure (downstream)",
type=CFlowVariableType.PRESSURE)
self.__As2 = self._vars.add(name="As2", long_name="Choke area (downstream)", type=CFlowVariableType.AREA)
[docs]
def get_downstream_Mach(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculate Mach number downstream of normal shock (M2 []) as function of upstream Mach number (M1 [])
input: upstream Mach number (M1 [])
example: M2 = get_downstream_Mach(M1=M1)
"""
return self._eval(["M1"], self.__get_downstream_Mach, *args, **kwargs)
def __get_downstream_Mach(self, M1: float = None) -> float:
if M1 is None:
M1 = self.__M1.value
return np.sqrt((1. + 0.5 * self._gas.gm1 * M1 * M1) / (self._gas.gamma * M1 * M1 - 0.5 * self._gas.gm1))
[docs]
def get_upstream_Mach(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculate Mach number upstream of normal shock (M1 []) as function of downstream Mach number (M2 [])
input: downstream Mach number (M2 [])
example: M1 = get_upstream_Mach(M2=M2s)
"""
return self._eval(["M2"], self.__get_upstream_Mach, *args, **kwargs)
def __get_upstream_Mach(self, M2: float = None) -> float:
if M2 is None:
M2 = self.__M2.value
return self.__get_downstream_Mach(M2)
[docs]
def get_downstream_density(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculate density downstream of normal shock (RO2 [kg/m^3]) as function of upstream Mach number (M1 []),
and upstream density (RO1 [kg/m^3])
input: upstream Mach number (M1 []), upstream density (RO1 [kg/m^3], default value: 1.0)
example: RO2 = get_downstream_density(M1=M1, RO1=RO1)
"""
return self._eval(["M1", "RO1"], self.__get_downstream_density, *args, **kwargs)
def __get_downstream_density(self, M1: float = None, RO1: float = None) -> float:
if M1 is None:
M1 = self.__M1.value
if RO1 is None:
RO1 = self.__RO1.value
return RO1 * ((self._gas.gp1 * M1 * M1) / (2. + self._gas.gm1 * M1 * M1))
[docs]
def get_downstream_pressure(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculate pressure downstream of normal shock (P2 [Pa]) as function of upstream Mach number (M1 []),
and upstream pressure (P1 [Pa])
input: upstream Mach number (M1 []), upstream pressure (P1 [Pa], default value: 1.0)
example: P2 = get_downstream_pressure(M1=M1, P1=P1)
"""
return self._eval(["M1", "P1"], self.__get_downstream_pressure, *args, **kwargs)
def __get_downstream_pressure(self, M1: float = None, P1: float = None) -> float:
if M1 is None:
M1 = self.__M1.value
if P1 is None:
P1 = self.__P1.value
return P1 * (1.0 + ((2.0 * self._gas.gamma) / self._gas.gp1) * (M1 * M1 - 1.0))
[docs]
def get_downstream_total_pressure(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculate total pressure downstream of normal shock (Po2 [Pa]) as function of upstream Mach number (M1 []),
and upstream total pressure (Po1 [Pa])
input: upstream Mach number (M1 []), upstream total pressure (Po1 [Pa], default value: 1.0)
example: Po2 = get_downstream_total_pressure(M1=M1, Po1=Po1)
"""
return self._eval(["M1", "Po1"], self.__get_downstream_total_pressure, *args, **kwargs)
def __get_downstream_total_pressure(self, M1: float = None, Po1: float = None) -> float:
if M1 is None:
M1 = self.__M1.value
if Po1 is None:
Po1 = self.__Po1.value
M2 = self.__get_downstream_Mach(M1)
return Po1 * self.__get_downstream_pressure(M1, 1.0) * \
self._stagnation.get_total_pressure(M2, 1.0) / self._stagnation.get_total_pressure(M1, 1.0)
[docs]
def get_downstream_temperature(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculate temperature downstream of normal shock (T2 [K]) as function of upstream Mach number (M1 []),
and upstream temperature (T1 [K])
input: upstream Mach number (M1 []), upstream temperature (T1 [K], default value: 1.0)
example: T2 = get_downstream_pressure(M1=M1, T1=T1)
"""
return self._eval(["M1", "T1"], self.__get_downstream_temperature, *args, **kwargs)
def __get_downstream_temperature(self, M1: float = None, T1: float = None) -> float:
if M1 is None:
M1 = self.__M1.value
if T1 is None:
T1 = self.__T1.value
return (T1 * (1.0 + ((2.0 * self._gas.gamma) / self._gas.gp1) * (M1 * M1 - 1.0))
* ((2. + self._gas.gm1 * M1 * M1) / (self._gas.gp1 * M1 * M1)))
[docs]
def get_downstream_Astar(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculate choke cross-section area downstream of normal shock (As2 [m^2]) as function of
upstream Mach number (M1 []),and upstream choke cross-section area (As1 [m^2])
input: upstream Mach number (M1 []), upstream choke cross-section area (As1 [m^2], default value: 1.0)
example: As2 = get_downstream_Astar(M1=M1, As1=As1)
"""
return self._eval(["M1", "As1"], self.__get_downstream_Astar, *args, **kwargs)
def __get_downstream_Astar(self, M1: float = None, As1: float = None) -> float:
if M1 is None:
M1 = self.__M1.value
if As1 is None:
As1 = self.__As1.value
M2 = self.__get_downstream_Mach(M1)
return (As1 * (M2 / M1)
* np.sqrt(((2. + self._gas.gm1 * M1 ** 2)
/ (2. + self._gas.gm1 * M2 ** 2)) ** (self._gas.gp1 / self._gas.gm1)))
[docs]
def get_upstream_Astar(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculate choke cross-section area upstream of normal shock (As1 [m^2]) as function of
downstream Mach number (M2 []),and downstream choke cross-section area (As2 [m^2])
input: downstream Mach number (M2 []), downstream choke cross-section area (As2 [m^2], default value: 1.0)
example: As1 = get_upstream_Astar(M2=M2, As2=As2)
"""
return self._eval(["M2", "As2"], self.__get_upstream_Astar, *args, **kwargs)
def __get_upstream_Astar(self, M2: float = None, As2: float = None) -> float:
if M2 is None:
M2 = self.__M2.value
if As2 is None:
As2 = self.__As2.value
M1 = self.__get_upstream_Mach(M2)
return (As2 / ((M2 / M1)
* np.sqrt(((2. + self._gas.gm1 * M1 ** 2)
/ (2. + self._gas.gm1 * M2 ** 2)) ** (self._gas.gp1 / self._gas.gm1))))
[docs]
def get_upstream_Mach_from_pressure_ratio(self, *args: Any, **kwargs: Any) \
-> Union[np.float64, list[np.float64], float]:
"""
calculate upstream Mach number (M1 []) from normal shock pressure ratio (PR [])
input: pressure ratio (PR [])
example: M1 = get_upstream_Mach_from_pressure_ratio(PR=PR)
"""
return self._eval(["PR"], self.__get_upstream_Mach_from_pressure_ratio, *args, **kwargs)
def __get_upstream_Mach_from_pressure_ratio(self, PR: float = None) -> float:
if PR is None:
PR = self.__PR.value
return np.sqrt(1. + (self._gas.gp1 / (2. * self._gas.gamma)) * (PR - 1.))
[docs]
def get_upstream_Mach_from_density_ratio(self, *args: Any, **kwargs: Any) \
-> Union[np.float64, list[np.float64], float]:
"""
calculate upstream Mach number (M1 []) from normal shock density ratio (RR [])
input: density ratio (RR [])
example: M1 = get_upstream_Mach_from_density_ratio(RR=RR)
"""
return self._eval(["RR"], self.__get_upstream_Mach_from_density_ratio, *args, **kwargs)
def __get_upstream_Mach_from_density_ratio(self, RR: float = None) -> float:
if RR is None:
RR = self.__RR.value
return np.sqrt((2. * RR) / (self._gas.gp1 - self._gas.gm1 * RR))
[docs]
def get_upstream_Mach_from_temperature_ratio(self, *args: Any, **kwargs: Any) \
-> Union[np.float64, list[np.float64], float]:
"""
calculate upstream Mach number (M1 []) from normal shock temperature ratio (TR [])
upstream Mach number is calculated using the built-in Newton-Raphson solver
input: density ratio (TR [])
example: M1 = get_upstream_Mach_from_temperature_ratio(TR=TR)
"""
self._newton.reset()
self._newton.start_value = 2.0
return self._eval(["TR"], self.__get_upstream_Mach_from_temperature_ratio, *args, **kwargs)
def __get_upstream_Mach_from_temperature_ratio(self, TR: float = None) -> float:
if TR is None:
TR = self.__TR.value
# run Newton solver to find Mach number
newton = self._get_newton_kernel()
return newton.solve(self.__get_downstream_temperature, target_value=TR)
[docs]
def get_upstream_Mach_from_total_pressure_ratio(self, *args: Any, **kwargs: Any) \
-> Union[np.float64, list[np.float64], float]:
"""
calculate upstream Mach number (M1 []) from normal shock total pressure ratio (PoR [])
upstream Mach number is calculated using the built-in Newton-Raphson solver
input: density ratio (PoR [])
example: M1 = get_upstream_Mach_from_total_pressure_ratio(PoR=PoR)
"""
self._newton.reset()
self._newton.start_value = 2.0
return self._eval(["PoR"], self.__get_upstream_Mach_from_total_pressure_ratio, *args, **kwargs)
def __get_upstream_Mach_from_total_pressure_ratio(self, PoR: float = None) -> float:
if PoR is None:
PoR = self.__PoR.value
self.__Po1.value = 1.0
# run Newton solver to find Mach number
newton = self._get_newton_kernel()
return newton.solve(self.__get_downstream_total_pressure, target_value=PoR)
[docs]
def get_entropy_increase(self, *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]:
"""
calculate normal shock entropy increase from upstream Mach number (M1 [])
input: upstream Mach number (M1 [])
example: dS = get_entropy_increase(M1=M1)
"""
return self._eval(["M1"], self.__get_entropy_increase, *args, **kwargs)
def __get_entropy_increase(self, M1: float = None) -> float:
if M1 is None:
M1 = self.__M1.value
return -self._gas.R * np.log(self.__get_downstream_total_pressure(M1, 1.0))