"""Gas class definition"""
from typing import Tuple, Any, Union
import os
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 g3d.defs import G3DEntity
from g3d.defs import FunctionByPass
[docs]
class Gas(G3DEntity):
"""
Gas: thermodynamic variables and gas relations (equation of state)
extends G3DEntity
"""
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""initialize Gas placeholder"""
self._gamma = 1.4
self._R = 287.0
self._viscosity = 1.7e-5
self._Pr = 0.72
super().__init__(*args, **kwargs)
def _set_default(self) -> None:
super()._set_default()
self._tag = 'g3d_gas'
self.__update_secondary_parameters()
def __update_secondary_parameters(self) -> None:
self.__gm1 = self._gamma - 1.0
self.__gp1 = self._gamma + 1.0
self.__one_over_gm1 = 1. / self.__gm1
self.__gm1_over_g = self.__gm1 / self._gamma
self.__g_over_gm1 = self._gamma / self.__gm1
self.__Cp = self._gamma * self._R / self.__gm1
self.__Cv = self._R / self.__gm1
def _set_from_args(self, *args: Any, **kwargs: Any) -> np.ndarray:
bps = self._get_function_evaluate_status(**kwargs)
if bps is FunctionByPass.SELF:
args_stat = np.ones(len(args))
else:
args_stat = super()._set_from_args(*args)
if bps in [FunctionByPass.EVALUATE, FunctionByPass.SELF]:
if np.sum(args_stat) > 0:
gas_data = []
for i in range(len(args)):
if args_stat[i] == 1:
arg = args[i]
if type(arg) is Gas:
args_stat[i] = 0
self.copy(arg)
elif type(arg) in [float, np.float64, np.float32, int, np.int64, np.int32]:
# append all float type values to a list and make a sub call to _set_from_args
args_stat[i] = 0
gas_data.append(np.float64(arg))
elif type(arg) is list:
args_stat[i] = 0
if self._list_is_all_floats(arg) and len(arg) >= 2:
# get gas data from list
self._gamma = arg[0]
self._R = arg[1]
if len(arg) >= 3:
self._viscosity = arg[2]
if len(arg) >= 4:
self._Pr = arg[3]
if len(gas_data) >= 2:
self._set_from_args(gas_data, bps=FunctionByPass.SELF)
return args_stat
[docs]
def set_from_key_value(self, key: str, value: Any) -> bool:
"""
sets entity parameters from specified key => value data
input: key, value
valid keys for Gas class:
'gamma', 'R', 'viscosity', 'Pr'
example: set_from_key_value(key, value)
"""
if super().set_from_key_value(key, value):
return True
elif key == 'gcnt' or key == 'gamma':
self._gamma = value
self.__update_secondary_parameters()
return True
elif key == 'R':
self._R = value
self.__update_secondary_parameters()
return True
elif key == 'visc' or key == 'viscosity':
self._viscosity = value
return True
elif key == 'Pr':
self._Pr = value
return True
return False
@property
def gamma(self) -> float:
"""ratio of specific heat Cp/Cv"""
return self._gamma
@gamma.setter
def gamma(self, val: float) -> None:
self._gamma = val
self.__update_secondary_parameters()
@property
def R(self) -> float:
"""gas constant (gas specific)"""
return self._R
@R.setter
def R(self, val: float) -> None:
self._R = val
self.__update_secondary_parameters()
@property
def viscosity(self) -> float:
"""viscosity"""
return self._viscosity
@viscosity.setter
def viscosity(self, val: float) -> None:
self._viscosity = val
@property
def Pr(self) -> float:
"""Prandtl number"""
return self._Pr
@Pr.setter
def Pr(self, val: float) -> None:
self._Pr = val
# secondary gas properties
@property
def gm1(self) -> float:
"""ratio of specific heats minus one"""
return self.__gm1
@property
def gp1(self) -> float:
"""ratio of specific heats plus one"""
return self.__gp1
@property
def one_over_gm1(self) -> float:
"""one over ratio of specific heats minus one"""
return self.__one_over_gm1
@property
def gm1_over_g(self) -> float:
"""ratio of specific heats minus one over ratio of specific heats"""
return self.__gm1_over_g
@property
def g_over_gm1(self) -> float:
"""ratio of specific heats over ratio of specific heats minus one"""
return self.__g_over_gm1
@property
def Cp(self) -> float:
"""specific heat at constant pressure"""
return self.__Cp
@property
def Cv(self) -> float:
"""specific heat at constant volume"""
return self.__Cv
[docs]
def copy(self, gas: 'Gas') -> None:
"""
copies gas properties from provided instance of class Gas
input: gas
example: copy(Gas g)
"""
super().copy(gas)
self._gamma = gas.gamma
self._R = gas.R
self._viscosity = gas.viscosity
self._Pr = gas.Pr
self.__update_secondary_parameters()
# -- data extract
[docs]
def get_gas_data(self) -> Tuple[float, float, float, float]:
"""
returns gas data
input: no input
example: gamma, R, Cp, Cv = get_gas_data()
"""
return self._gamma, self._R, self.__Cp, self.__Cv
# -- ideal gas law for perfect gases
[docs]
def get_pressure(self, T: Union[float, ndarray], ro: Union[float, ndarray]) -> Union[float, ndarray]:
"""
calculates pressure P [Pa] from provided temperature T [K] and density RO [kg/m^3]
input: temperature T [K], density RO [kg/m^3]
example: P = get_pressure(T, RO)
"""
return ro * self._R * T
[docs]
def get_temperature(self, P: Union[float, ndarray], ro: Union[float, ndarray]) -> Union[float, ndarray]:
"""
calculates temperature T [K] from provided pressure [Pa] and density [kg/m^3]
input: pressure P [Pa], density RO [kg/m^3]
example: T = get_temperature(P, RO)
"""
return P / (self._R * ro)
[docs]
def get_density(self, P: Union[float, ndarray], T: Union[float, ndarray]) -> Union[float, ndarray]:
"""
calculates density RO [kg/m^3] from provided pressure P [Pa] and temperature T [K]
input: pressure P [Pa], temperature T [K]
example: RO = get_density(P, T)
"""
return P / (self._R * T)
# -- speed of sound relation for perfect gases
[docs]
def get_speed_of_sound(self, T: Union[float, ndarray]) -> Union[float, ndarray]:
"""
calculates speed of sound C [m/s] from specified temperature T [K]
input: temperature T[K]
example: C = get_speed_of_sound(T)
"""
return np.sqrt(T * self._R * self._gamma)
[docs]
def get_temperature_from_speed_of_sound(self, a: Union[float, ndarray]) -> Union[float, ndarray]:
"""
calculates temperature T [K] from specified speed of sound C [m/s]
input: speed of sound C [m/s]
example: T = get_temperature_from_speed_of_sound(C)
"""
return a ** 2 / (self._R * self._gamma)
# -- energy <-> pressure relation
[docs]
def get_energy_from_primitive(self,
ro: Union[float, ndarray],
p: Union[float, ndarray],
u: Union[float, ndarray],
v: Union[float, ndarray] = 0.,
w: Union[float, ndarray] = 0.) -> Union[float, ndarray]:
"""
calculates energy (ro * e_o) from provided primitive variables
input: density, pressure, velocity
example: roe_o = et_energy_from_primitive(ro, p, u, v, w)
"""
return p * self.__one_over_gm1 + 0.5 * ro * (u * u + v * v + w * w)
[docs]
def get_pressure_from_conservative(self,
ro: Union[float, ndarray],
et: Union[float, ndarray],
ru: Union[float, ndarray],
rv: Union[float, ndarray] = 0.,
rw: Union[float, ndarray] = 0.) -> Union[float, ndarray]:
"""
calculates pressure from provided conservative variables
input: ro, re_o, ru, rv, rw
example: p = get_pressure_from_conservative(ro, re_o, ru, rv, rw)
"""
u = ru / ro
v = rv / ro
w = rw / ro
return self.__gm1 * (et - 0.5 * ro * (u * u + v * v + w * w))
# -- isentropic relations
[docs]
def isentropic_exp_p(self, x: Union[float, ndarray]) -> Union[float, ndarray]:
"""
calculates exponent in isentropic relation for pressure
input: T2/T1
example: exp = isentropic_exp_p(T2/T1
"""
return np.power(x, self.__g_over_gm1)
[docs]
def isentropic_exp_ro(self, x: Union[float, ndarray]) -> Union[float, ndarray]:
"""
calculates exponent in isentropic relation for density
input: T2/T1
example: exp = isentropic_exp_ro(T2/T1
"""
return np.power(x, self.__one_over_gm1)
[docs]
def inv_isentropic_exp_p(self, x: Union[float, ndarray]) -> Union[float, ndarray]:
"""
calculates one over exponent in isentropic relation for pressure
input: T2/T1
example: exp = inv_isentropic_exp_p(T2/T1
"""
return np.power(x, self.__gm1_over_g)
[docs]
def inv_isentropic_exp_ro(self, x: Union[float, ndarray]) -> Union[float, ndarray]:
"""
calculates one over exponent in isentropic relation for density
input: T2/T1
example: exp = inv_isentropic_exp_ro(T2/T1
"""
return np.power(x, self.__gm1)