I'm calculating the injectors, and I've added the complete calculation code and tested it using pytest. Can you suggest a better way to structure the code so that I can easily add functions in the future and reuse entire classes for calculating two-component injectors?
jet_injector.py:
from abc import ABC
from dataclasses import dataclass
from enum import Enum
from functools import cached_property
from math import pi, exp, sqrt
@dataclass(frozen=True)
class Injector(ABC):
density: float
diameter: float
length: float
mass_flow_rate: float
viscosity: float
@cached_property
def injector_nozzle_area(self) -> float:
return pi * self.diameter**2 / 4
@cached_property
def reynolds_number(self) -> float:
return 4 * self.mass_flow_rate / (pi * self.diameter * self.viscosity)
@cached_property
def relative_length_injector(self) -> float:
return self.length / self.diameter
class Reynolds(Enum):
LAMINAR = 2000
TURBULENT = 10_000
@dataclass(frozen=True)
class LiquidJetInjector(Injector):
density_comb: float
sigma_fuel: float
@cached_property
def average_speed(self) -> float:
return self.mass_flow_rate / (self.density * self.injector_nozzle_area)
@cached_property
def linear_hydraulic_resistance(self) -> float:
if self.reynolds_number < Reynolds.LAMINAR.value:
return 64 / self.reynolds_number
elif Reynolds.LAMINAR.value <= self.reynolds_number <= Reynolds.TURBULENT.value:
return 0.3164 * self.reynolds_number**-0.25
return 0.031
@cached_property
def injector_losses_inlet(self) -> float:
if self.reynolds_number < Reynolds.LAMINAR.value:
return 2.2 - 0.726 * exp(
-74.5 * self.viscosity * self.length / self.mass_flow_rate
)
return 1 + 2.65 * self.linear_hydraulic_resistance
@cached_property
def injector_flow_coefficient(self) -> float:
return 1 / sqrt(
self.injector_losses_inlet
+ self.linear_hydraulic_resistance * self.length / self.diameter
)
@cached_property
def pressure_drop_injector(self) -> float:
return self.mass_flow_rate**2 / (
2
* self.density
* self.injector_flow_coefficient**2
* self.injector_nozzle_area**2
)
@cached_property
def weber_criterion(self) -> float:
return (
self.density_comb * self.average_speed**2 * self.diameter / self.sigma_fuel
)
@cached_property
def media_diameter_spray_droplets(self) -> float:
return self.diameter * (27 * pi / 4) ** (1 / 3) * self.weber_criterion ** (-1 / 3)
@dataclass(frozen=True)
class GasJetInjector(Injector):
combustion_pressure: float
pressure_drop_internal_circuit: float
gas_constant_gen_gas: float
temperature_gen_gas: float
entropy_expansion_ratio: float
@cached_property
def injector_pressure(self) -> float:
return self.combustion_pressure + self.pressure_drop_internal_circuit
@cached_property
def density_gen_gas(self) -> float:
return self.injector_pressure / (
self.gas_constant_gen_gas * self.temperature_gen_gas
)
@cached_property
def average_speed(self) -> float:
return self.mass_flow_rate / (self.density_gen_gas * self.injector_nozzle_area)
@cached_property
def injector_flow_coefficient(self) -> float:
return ((sqrt(1.23 ** 2 + 232 * self.length / (self.reynolds_number * self.diameter)) - 1.23)
/ (116 * self.length / (self.reynolds_number * self.diameter)))
@cached_property
def injector_nozzle_area_outlet(self) -> float:
return self.mass_flow_rate / (self.injector_flow_coefficient * self.density_gen_gas * (self.pressure_drop_internal_circuit / self.injector_pressure) ** (1 / self.entropy_expansion_ratio) * sqrt(2 * self.entropy_expansion_ratio / (self.entropy_expansion_ratio - 1) * self.gas_constant_gen_gas * self.temperature_gen_gas * (1 - (self.pressure_drop_internal_circuit / self.injector_pressure) ** ((self.entropy_expansion_ratio - 1) / self.entropy_expansion_ratio))))
@cached_property
def diameter_injector(self) -> float:
return sqrt(4 * self.injector_nozzle_area_outlet / pi)
@cached_property
def discrepancy(self) -> float:
return (self.diameter_injector - self.diameter) / self.diameter_injector
test_jet_injector.py:
import pytest
from src.fluxion.engine.jet_injector import LiquidJetInjector, GasJetInjector
class TestLiquidJetInjector:
@pytest.fixture(scope="function")
def liquid_jet_injector(self):
liquid_injector = LiquidJetInjector(
density=800,
diameter=0.002,
length=0.003,
mass_flow_rate=0.05,
viscosity=0.0015,
density_comb=1.2,
sigma_fuel=0.028,
)
return liquid_injector
def test_injector_nozzle_area(self, liquid_jet_injector):
assert liquid_jet_injector.injector_nozzle_area == pytest.approx(3.1415926535898e-06)
def test_reynolds_number(self, liquid_jet_injector):
assert liquid_jet_injector.reynolds_number == pytest.approx(21220.6590789194)
def test_average_speed(self, liquid_jet_injector):
assert liquid_jet_injector.average_speed == pytest.approx(1.9894367886487e+01)
def test_relative_length_injector(self, liquid_jet_injector):
assert liquid_jet_injector.relative_length_injector == pytest.approx(1.5)
def test_linear_hydraulic_resistance(self, liquid_jet_injector):
assert liquid_jet_injector.linear_hydraulic_resistance == pytest.approx(0.031)
def test_injector_losses_inlet(self, liquid_jet_injector):
assert liquid_jet_injector.injector_losses_inlet == pytest.approx(1.08215)
def test_injector_flow_coefficient(self, liquid_jet_injector):
assert liquid_jet_injector.injector_flow_coefficient == pytest.approx(0.941283307)
def test_pressure_drop_injector(self, liquid_jet_injector):
assert liquid_jet_injector.pressure_drop_injector == pytest.approx(1.7868149049676e+05)
def test_weber_criterion(self, liquid_jet_injector):
assert liquid_jet_injector.weber_criterion == pytest.approx(3.3924503451676e+01)
def test_media_diameter_spray_droplets(self, liquid_jet_injector):
assert liquid_jet_injector.media_diameter_spray_droplets == pytest.approx(1.71005486466707e-03)
class TestGasInjector:
@pytest.fixture(scope="function")
def gas_jet_injector(self):
gas_injector = GasJetInjector(
density=5.0,
diameter=0.005,
length=0.01,
mass_flow_rate=0.1,
viscosity=0.00002,
combustion_pressure=5_000_000,
pressure_drop_internal_circuit=500_000,
gas_constant_gen_gas=300,
temperature_gen_gas=800,
entropy_expansion_ratio=1.2,
)
return gas_injector
def test_injector_nozzle_area(self, gas_jet_injector):
assert gas_jet_injector.injector_nozzle_area == pytest.approx(1.9634954084936e-05)
def test_reynolds_number(self, gas_jet_injector):
assert gas_jet_injector.reynolds_number == pytest.approx(1273239.5447351600)
def test_average_speed(self, gas_jet_injector):
assert gas_jet_injector.average_speed == pytest.approx(222.2381751)
def test_relative_length_injector(self, gas_jet_injector):
assert gas_jet_injector.relative_length_injector == pytest.approx(2)
def test_injector_pressure(self, gas_jet_injector):
assert gas_jet_injector.injector_pressure == pytest.approx(5500000)
def test_density_gen_gas(self, gas_jet_injector):
assert gas_jet_injector.density_gen_gas == pytest.approx(22.91666667)
def test_injector_flow_coefficient(self, gas_jet_injector):
assert gas_jet_injector.injector_flow_coefficient == pytest.approx(0.812959177)
def test_injector_nozzle_area_outlet(self, gas_jet_injector):
assert gas_jet_injector.injector_nozzle_area_outlet == pytest.approx(4.0646157686e-05)
def test_diameter_injector(self, gas_jet_injector):
assert gas_jet_injector.diameter_injector == pytest.approx(0.007193907)
def test_discrepancy(self, gas_jet_injector):
assert gas_jet_injector.discrepancy == pytest.approx(0.304967367)
This is the methodical manual by which everything was considered: training manual
Injectora subclass ofABCor am I missing something? If there is a rationale for this, perhaps a comment would be appropriate. \$\endgroup\$