Уместен ли монолитный код?

Я написал код для написания расчетов форсунок ЖРД, но получается так, что все расчетные формулы завязаны друг на друге, также получилось и в коде. Приемлемый ли это вариант для задачи подобного рода или стоит сделать функции переиспользуемыми?

Методичка (формулы на страницах 38 - 47): методичка Егорычев В.С.

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)

Ответы (2 шт):

Автор решения: Viktor

Создавать функции имеет смысл для чего-то. Самое очевидное - для переиспользования кода в разных частях программы. Менее очевидное, но тоже часто используемое - для удобства чтения основного кода. Ну и еще возможны требования от того, кто будет принимать ваш код. Так что... Решать вам, отталкиваясь от вашей конкретной ситуации.

→ Ссылка
Автор решения: Python Tasteless

Кратко: да, монолит уместен, если ты не планируешь масшабировать проект.

Для небольших проектов-скриптов, части которого не должны как-либо взаимодействовать с другими программами или сервисами, монолитная структура уместна. Если же ты собираешься импортировать какой либо свой код, а код имеет монолитную структуру, можно задумать о преобразовании файла в полноценный модуль.

→ Ссылка