Exemplo n.º 1
0
    def __init__(self):
        super(OpenBurnApplication, self).__init__()
        self.motor = OpenBurnMotor()
        self.current_design_filename: str = None

        self.undo_stack = QUndoStack(self)
        self.propellant_db = PropellantDatabase('user/propellants.json')
        self.settings = SettingsDatabase('user/settings.json')
Exemplo n.º 2
0
    def setUp(self):
        """Set up the test data"""
        self.settings = SimSettings(twophase=0.85, timestep=0.01)
        self.propellant = SimplePropellant("68/10", 0.0341, 0.2249, 4706, 0.058, 1.226)
        # using a list comprehension to create four unique grain objects
        self.grains = [CylindricalCoreGrain(diameter=2, length=4, core_diameter=1, burning_faces=2,
                                            propellant=self.propellant)
                       for _ in range(0, 4)]
        self.nozzle = ConicalNozzle(throat=0.5, exit=2, half_angle=15, throat_len=0.25)
        self.motor = OpenBurnMotor()
        self.motor.set_grains(self.grains)
        self.motor.set_nozzle(self.nozzle)

        self.results = sim.run_sim(self.motor, self.settings)
Exemplo n.º 3
0
    def calc_mass_flux(cls, motor: OpenBurnMotor, x_val: float) -> float:
        """
        Calculates the mass flux at the given x coordinate
        :param x_val: x value of the point to find mass flux,
            where x = 0 is at the head end and x = len is the end of the propellant surface.

        :param motor:
        :return: Mass flux in lbs/sec/in^2
        """
        grain = motor.get_grain_at_x(x_val)
        if grain:
            return motor.get_upstream_mass_flow(x_val) / grain.get_port_area()

        raise Exception(f"Grain not found at x value: {x_val}")
Exemplo n.º 4
0
class OpenBurnApplication(QObject):
    """
    Encapsulates application context that is shared across many UI
    elements and modules:
        propellant database,
        settings,
        undo stack,
        and current design info
    """
    def __init__(self):
        super(OpenBurnApplication, self).__init__()
        self.motor = OpenBurnMotor()
        self.current_design_filename: str = None

        self.undo_stack = QUndoStack(self)
        self.propellant_db = PropellantDatabase('user/propellants.json')
        self.settings = SettingsDatabase('user/settings.json')

    def reset_design(self):
        self.motor = OpenBurnMotor()

    def save_current_design(self):
        self.save_design(self.current_design_filename)

    def save_design(self, filename: str):
        with open(filename, 'w+') as f:
            data = self.motor.to_json()
            f.write(data)
        self.current_design_filename = filename

    def load_design(self, filename: str):
        with open(filename, 'r') as f:
            data = f.read()
            self.motor = OpenBurnMotor.from_json(data)
Exemplo n.º 5
0
 def calc_isp(cls, motor: OpenBurnMotor, settings: SimSettings) -> float:
     """
     Calculates Isp given a motor
     Isp = F / mdot * g
     :param settings:
     :param motor:
     :return: Isp in seconds
     """
     return cls.calc_thrust(motor, settings) / motor.get_mass_flow()
Exemplo n.º 6
0
 def setUp(self):
     """Set up the test data"""
     self.propellant = SimplePropellant("68/10", 0.0341, 0.2249, 4706, 0.058, 1.226)
     # using a list comprehension to create four unique grain objects
     self.grains = [CylindricalCoreGrain(diameter=2, length=4, core_diameter=1, burning_faces=2,
                                         propellant=self.propellant)
                    for _ in range(0, 4)]
     self.nozzle = ConicalNozzle(throat=0.5, exit=2, half_angle=15, throat_len=0.25)
     self.motor = OpenBurnMotor()
     self.motor.set_grains(self.grains)
     self.motor.set_nozzle(self.nozzle)
Exemplo n.º 7
0
class InternalBallisticsTest(unittest.TestCase):
    def setUp(self):
        """Set up the test data"""
        self.settings = SimSettings(twophase=0.85, timestep=0.01)
        self.propellant = SimplePropellant("68/10", 0.0341, 0.2249, 4706, 0.058, 1.226)
        # using a list comprehension to create four unique grain objects
        self.grains = [CylindricalCoreGrain(diameter=2, length=4, core_diameter=1, burning_faces=2,
                                            propellant=self.propellant)
                       for _ in range(0, 4)]
        self.nozzle = ConicalNozzle(throat=0.5, exit=2, half_angle=15, throat_len=0.25)
        self.motor = OpenBurnMotor()
        self.motor.set_grains(self.grains)
        self.motor.set_nozzle(self.nozzle)

        self.results = sim.run_sim(self.motor, self.settings)

    # def test_basic_sim(self):
    #     """Outputs basic sim info"""
    #     print("\nResults:")
    #     print("Kn Range: ", self.results.get_kn_range())
    #     print("Burn Time: (s)", self.results.get_burn_time())
    #     print("Max Pressure: (psi)", self.results.get_max_presure())
    #     print("Average Isp: (s)", self.results.get_avg_isp())
    #     print("Max Isp: (s)", self.results.get_max_isp())
    #     print("Max Mass flux: (lb/sec/in^2)", self.results.get_max_mass_flux())
    #
    #     avg_thrust_lb = self.results.get_avg_thrust()
    #     avg_thrust_n = convert_magnitude(avg_thrust_lb, 'lbf', 'newton')
    #     print("Avg Thrust: (lbs)", avg_thrust_lb)
    #     print("Avg Thrust: (newtons)", avg_thrust_n)
    #
    #     impulse_lb = self.results.get_total_impulse()
    #     impulse_n = convert_magnitude(impulse_lb, 'lbf', 'newton')
    #     print("Total Impulse (lb-sec)", impulse_lb)
    #     print("Total Impulse (newton-sec)", impulse_n)

    def test_kn(self):
        kn_low, kn_high = self.results.get_kn_range()
        self.assertAlmostEqual(kn_low, 350, places=-1)
        self.assertAlmostEqual(kn_high, 390, places=-1)
Exemplo n.º 8
0
class InternalBallisticsTest(unittest.TestCase):
    def setUp(self):
        """Set up the test data"""
        self.propellant = SimplePropellant("68/10", 0.0341, 0.2249, 4706,
                                           0.058, 1.226)
        # using a list comprehension to create four unique grain objects
        self.grains = [
            CylindricalCoreGrain(diameter=2,
                                 length=4,
                                 core_diameter=1,
                                 burning_faces=2,
                                 propellant=self.propellant)
            for _ in range(0, 4)
        ]
        self.nozzle = ConicalNozzle(throat=0.5,
                                    exit=2,
                                    half_angle=15,
                                    throat_len=0.25)
        self.motor = OpenBurnMotor()
        self.motor.set_grains(self.grains)
        self.motor.set_nozzle(self.nozzle)

    def test_json_in(self):
        out = self.motor.to_json()
        uuid_out = self.motor.uuid

        in_ = self.motor.from_json(out)
        uuid_in = in_.uuid
        self.assertIsInstance(in_, OpenBurnMotor)
        self.assertNotEqual(uuid_out, uuid_in)
Exemplo n.º 9
0
    def calc_chamber_pressure(cls, motor: OpenBurnMotor,
                              settings: SimSettings) -> float:
        """
        Calculates chamber pressure assuming a steady-state chamber
        p = (Kn * a * rho * C* )^(1/(1-n))
        see Rocket Propulsion Elements, Eq. ??

        :param motor:
        :param settings: controls two phase flow efficacy which affects C*
        :return: steady-state chamber pressure, in lbs/in^3
        """
        rho_slugs = convert_magnitude(motor.avg_propellant.rho, 'lb_per_in3',
                                      'slug_per_in3')

        cstar = motor.avg_propellant.cstar  # * settings.two_phase_flow_eff
        exp = 1 / (1 - motor.avg_propellant.n)

        base = motor.get_kn() * motor.avg_propellant.a * rho_slugs * cstar
        return base**exp
Exemplo n.º 10
0
class InternalBallisticsTest(unittest.TestCase):
    def setUp(self):
        """Set up the test data"""
        self.propellant = SimplePropellant("68/10", 0.0341, 0.2249, 4706, 0.058, 1.226)
        # using a list comprehension to create four unique grain objects
        self.grains = [CylindricalCoreGrain(diameter=2, length=4, core_diameter=1, burning_faces=2,
                                            propellant=self.propellant)
                       for _ in range(0, 4)]
        self.nozzle = ConicalNozzle(throat=0.5, exit=2, half_angle=15, throat_len=0.25)
        self.motor = OpenBurnMotor()
        self.motor.set_grains(self.grains)
        self.motor.set_nozzle(self.nozzle)

    def test_json_in(self):
        out = self.motor.to_json()
        uuid_out = self.motor.uuid

        in_ = self.motor.from_json(out)
        uuid_in = in_.uuid
        self.assertIsInstance(in_, OpenBurnMotor)
        self.assertNotEqual(uuid_out, uuid_in)
Exemplo n.º 11
0
 def load_design(self, filename: str):
     with open(filename, 'r') as f:
         data = f.read()
         self.motor = OpenBurnMotor.from_json(data)
Exemplo n.º 12
0
 def reset_design(self):
     self.motor = OpenBurnMotor()
Exemplo n.º 13
0
    def run_sim(cls, motor: OpenBurnMotor,
                settings: SimSettings) -> "SimResults":
        """
        Regression simulation
        Calculates internal ballistics regression and info
        :param motor: the initial motor, regressing at discrete time steps, with settings controlled by
        :param settings:
        :returns SimResults: an object that encapsulates the results of the simulation run"""

        iterations = 0
        total_burn_time = 0
        total_impulse = 0
        num_burnout = 0

        data = []
        prev_data = None

        while num_burnout < motor.get_num_grains():
            current_data = SimDataPoint()

            # clone motor data for initial condition
            if iterations == 0:
                current_data.motor = deepcopy(motor)
            else:
                current_data.motor = deepcopy(prev_data.motor)

            current_motor = current_data.motor

            # regression simulation for each grain
            for grain in current_motor.grains:
                burnrate = cls.calc_steady_state_burn_rate(
                    current_motor, grain, settings)
                current_data.burn_rate = burnrate

                grain.burn(burnrate, settings.time_step)

                if grain.is_burned_out():
                    num_burnout += 1

            # set simulation data for this time step after regression
            current_data.pressure = cls.calc_chamber_pressure(
                current_motor, settings)
            current_data.time = total_burn_time
            current_data.thrust = cls.calc_thrust(current_motor, settings)
            current_data.mass_flux = cls.calc_mass_flux(
                current_motor, current_motor.get_length())
            current_data.isp = cls.calc_isp(current_motor, settings)
            current_data.kn = current_motor.get_kn()

            # add data to results
            data.append(current_data)

            # update motor info
            total_impulse += current_data.thrust * settings.time_step
            total_burn_time += settings.time_step

            # set up for next iteration
            iterations += 1
            prev_data = current_data

            # fallback failure state: MAX_SIM_TIME second burn time
            if total_burn_time > MAX_SIM_TIME:
                raise SimulationException(
                    f"Error! Simulation exceeded {MAX_SIM_TIME} seconds.")

        results = SimResults(data_points=data,
                             total_impulse=total_impulse,
                             burn_time=total_burn_time)
        return results