def set_design_static(gamma, p_chamber, p_exit, c_star, OF, ox_flow):
        """
        Static method, return the main nozzle parameters for de given design conditions.
        :param gamma: flow isentropic parameter
        :param p_chamber: chamber pressure in Pascals
        :param p_exit: desired exit pressure (ie atmospheric pressure) in Pascals
        :param c_star: expected combustion c*
        :param ox_flow: flow of oxidizer into the chamber
        """

        # Determination of parameters based on isoentropic flow and c* definition

        throat_area = c_star * ox_flow * (1 + 1 / OF) / p_chamber

        exit_mach = iso.mach_via_pressure(gamma, p_chamber, p_exit)

        expansion_ratio = iso.expansion_via_mach(gamma, exit_mach)

        exit_area = throat_area * expansion_ratio

        return throat_area, expansion_ratio, exit_area
Ejemplo n.º 2
0
    def set_design_static(gamma, p_chamber, p_exit, c_star, isp, thrust):
        """
        Static method, return the main nozzle parameters for de given design conditions.
        :param gamma: flow isentropic parameter
        :param p_chamber: chamber pressure in Pascals
        :param p_exit: desired exit pressure (ie atmospheric pressure) in Pascals
        :param c_star: expected combustion c*
        :param isp: expected rocket specific impulse in seconds
        :param thrust: expected rocket thrust
        """

        # Determination of parameters based on isentropic flow and c* definition

        throat_area = c_star * thrust / isp / 9.81 / p_chamber

        exit_mach = iso.mach_via_pressure(gamma, p_chamber, p_exit)

        expansion_ratio = iso.expansion_via_mach(gamma, exit_mach)

        exit_area = throat_area * expansion_ratio

        return throat_area, expansion_ratio, exit_area
Ejemplo n.º 3
0
    def run_balanced_nozzle_analysis(self, ox_flow, safety_thickness, dt, max_burn_time=None, tol_press=1e-3, **kwargs):
        # Check the inputs
        assert ox_flow > 0, "Oxidizer flow not greater than 0, check your inputs to CombustionModule. \n"
        assert safety_thickness > 0, "Safety thickness not greater than 0, check your inputs to CombustionModule. \n"
        assert dt > 0, "Simulation time-increment not greater than 0, check your inputs to CombustionModule. \n"

        # ------------------- Set the output flag:
        if 'file_name' in kwargs:
            flag_output = True
        else:
            flag_output = False

        # Call for workspace definition methods
        g0, R, pression_atmo, a, n, m, rho_fuel, initial_chamber_pressure, eta_comb = self.unpack_data_dictionary()

        # -------------------- Initialize simulation_variables if required -------------

        pression_chambre = initial_chamber_pressure

        # ---------------------------- MAIN SIMULATION LOOP -----------------------------

        # Set a counter to keep-track of the loop
        k = 1

        # Set the flag for the maximum burn-time
        flag_burn = True

        while self.geometry.min_bloc_thickness() > safety_thickness and flag_burn:

            # ------------------- Perform block solution with pressure coupling  ---------------------

            solution, of_ratio, m_ox, m_fuel, total_mass_flow,\
            g_ox, g_f, pression_chambre, t_chamber, gamma, \
            gas_molar_mass, cea_c_star, son_vel, rho_ch = self._solve_pressure_loop(pression_chambre,
                                                                                    ox_flow, rho_fuel, tol_press)

            # ----------------------- Perform the regression of the block ---------------
            self.geometry.regress(solution, self.regression_model, ox_flow, dt)
            r = R / gas_molar_mass  # Specific gaz constant

            # Determine the flow port exit-speed
            u_ch = calculate_flow_speed(cross_section=self.geometry.mesh.cells[-1].return_area_data(),
                                        mass_flow=total_mass_flow,
                                        density=rho_ch)

            # ------------------------- Determine Propulsion Performances ------------------
            mach_exit = iso.exit_mach_via_expansion(gamma=gamma, expansion=self.nozzle.get_expansion_ratio(),
                                                    supersonic=True)
            exit_pressure = pression_chambre / iso.pressure_ratio(gamma, mach_exit)
            t_exit = t_chamber / iso.temperature_ratio(gamma, mach_exit)
            v_exit = math.sqrt(gamma * r * t_exit) * mach_exit

            thrust = self.nozzle.get_nozzle_effeciency() * (
                total_mass_flow * v_exit + (exit_pressure - pression_atmo) *
                self.nozzle.get_exit_area())

            # Calculate the nozzle equation validation
            nozzle_p = total_mass_flow * cea_c_star / (self.nozzle.get_throat_area() * pression_chambre)

            isp = thrust / total_mass_flow / g0

            # ------------------------------ Results updates --------------------------------
            self.results['run_values']['time'].append(k * dt)
            self.results["run_values"]["thrust"].append(thrust)
            self.results["run_values"]["isp"].append(isp)
            self.results["run_values"]["pressure"].append(pression_chambre)
            self.results["run_values"]["temperature"].append(t_chamber)
            self.results["run_values"]["of"].append(of_ratio)
            self.results["run_values"]["Go"].append(g_ox)
            self.results["run_values"]["nozzle_param"].append(nozzle_p)
            self.results["run_values"]["c_star"].append(cea_c_star)
            self.results["run_values"]["chamber_sound_speed"].append(son_vel)
            self.results["run_values"]["chamber_rho"].append(rho_ch)
            self.results["run_values"]["mass_flow"].append(total_mass_flow)
            self.results["run_values"]["mass_flow_ox"].append(ox_flow)
            self.results["run_values"]["mass_flow_f"].append(m_fuel)
            self.results["run_values"]["chamber_speed"].append(u_ch)


            # ------------------------- Output the Geometry to file -----------------------

            if flag_output:
                if k * dt in kwargs['times']:
                    self.geometry.print_geometry_to_file(kwargs['file_name'], k * dt)

            # Update the loop
            k += 1

            # Update the flag for burn-time
            if max_burn_time:
                flag_burn = k * dt <= max_burn_time

        # ------------------------ POST-PROCESS ------------------------

        # Convert to numpy arrays
        self.results['run_values'] = {key: asarray(value) for key, value in self.results["run_values"].items()}

        # Post-process the data
        self.post_process_data(dt=dt)
Ejemplo n.º 4
0
    def run_simulation_constant_fuel_sliver(self, ox_flow, safety_thickness, dt, max_burn_time=None):
        """
        Simulate the hybrid rocket burn.
        Every value is expressed in ISU unless specified otherwise. Simulation is only valid for normal ground
        atmosphere conditions (1 bar, 293 K).
        :param ox_flow: value of input oxidizer flow
        :param safety_thickness: minimum allowed thickness for fuel slivers, which conditions burn termination
        :param dt: time increment
        :return: TBD
        """

        # Check the inputs
        assert ox_flow > 0, "Oxidizer flow not greater than 0, check your inputs to CombustionModule. \n"
        assert safety_thickness > 0, "Safety thickness not greater than 0, check your inputs to CombustionModule. \n"
        assert dt > 0, "Simulation time-increment not greater than 0, check your inputs to CombustionModule. \n"

        # Call for workspace definition methods
        g0, R, pression_atmo, a, n, m, rho_fuel, initial_chamber_pressure = self.unpack_data_dictionary()

        # -------------------- Initialize simulation_variables if required -------------

        # regression_rate, fuel_flow, total_mass_flow, OF, T_chambre, gamma, masse_molaire_gaz, c_star = \
        #     self.initialize_variables()

        pression_chambre = initial_chamber_pressure

        # ---------------------------- MAIN SIMULATION LOOP -----------------------------

        # Set a counter to keep-track of the loop
        k = 0

        # Set the flag for the maximum burn-time
        flag_burn = True

        while self.geometry.min_bloc_thickness() > safety_thickness and flag_burn:

            # Regression rate and mass flow calculations
            fuel_flow = self.geometry.compute_fuel_rate(rho=rho_fuel, ox_flow=ox_flow,
                                                        a=a, n=n, m=m)
            total_mass_flow = ox_flow + fuel_flow
            of_ratio = ox_flow / fuel_flow

            # Data extraction using the interpolator and OF ratio from already defined variables
            # Check lookup_from_cea method which variables have been chosen

            t_chamber, gamma, gas_molar_mass, cea_c_star = self.lookup_from_cea(desired_of_ratio=of_ratio)
            r = R / gas_molar_mass  # Specific gaz constant

            # Calculate chamber conditions and motor performance
            # Chamber pressure is not recalculated as it is assumed constant

            mach_exit = iso.exit_mach_via_expansion(gamma=gamma, expansion=self.nozzle.get_expansion_ratio(),
                                                    supersonic=True)
            exit_pressure = pression_chambre / iso.pressure_ratio(gamma, mach_exit)
            t_exit = t_chamber / iso.temperature_ratio(gamma, mach_exit)
            v_exit = math.sqrt(gamma * r * t_exit) * mach_exit

            thrust = self.nozzle.get_nozzle_effeciency() * (total_mass_flow*v_exit + (exit_pressure-pression_atmo) *
                                                            self.nozzle.get_exit_area())

            isp = thrust / total_mass_flow / g0

            # Results updates
            self.results['run_values']['time'].append(k * dt)
            self.results["run_values"]["thrust"].append(thrust)
            self.results["run_values"]["isp"].append(isp)
            self.results["run_values"]["pressure"].append(pression_chambre)
            self.results["run_values"]["temperature"].append(t_chamber)
            self.results["run_values"]["of"].append(of_ratio)

            # Verify it is a single port_number before updating the port number
            if isinstance(self.geometry, OneCircularPort):
                self.results["run_values"]["radius"].append(self.geometry.get_port_radius())
            else:
                # Append a 0 if there it is not a OneCircularPort geometry
                self.results["run_values"]["radius"].append(0)

            # Update the geometry and nozzle
            self.geometry.regress(ox_flow=ox_flow, a=a, n=n, m=m, dt=dt)
            self.nozzle.erode(dt)

            # Update the loop
            k += 1

            # Update the flag for burn-time
            if max_burn_time:
                flag_burn = k * dt <= max_burn_time

        # ------------------------ POST-PROCESS ------------------------

        # Convert to numpy arrays
        self.results['run_values'] = {key: asarray(value) for key, value in self.results["run_values"].items()}

        # Post-process the data
        self.post_process_data(dt=dt)
Ejemplo n.º 5
0
    def run_simulation_constant_fuel_sliver(self, ox_flow, safety_thickness, dt, max_burn_time=None, **kwargs):
        """
        Simulate the hybrid rocket burn.
        Every value is expressed in ISU unless specified otherwise. Simulation is only valid for normal ground
        atmosphere conditions (1 bar, 293 K).
        :param ox_flow: value of input oxidizer flow
        :param safety_thickness: minimum allowed thickness for fuel slivers, which conditions burn termination
        :param dt: time-step for the simulation
        :param max_burn_time: maximum burn time (if inputted)
        :return: TBD
        """

        # Check the inputs
        assert ox_flow > 0, "Oxidizer flow not greater than 0, check your inputs to CombustionModule. \n"
        assert safety_thickness > 0, "Safety thickness not greater than 0, check your inputs to CombustionModule. \n"

        # Call for workspace definition methods
        g0, R, pression_atmo, a, n, m, rho_fuel, initial_chamber_pressure, eta_comb = self.unpack_data_dictionary()

        # -------------------- Initialize simulation_variables if required -------------

        # regression_rate, fuel_flow, total_mass_flow, OF, T_chambre, gamma, masse_molaire_gaz, c_star = \
        #     self.initialize_variables()
        pression_chambre = initial_chamber_pressure

        # ---------------------------- MAIN SIMULATION LOOP -----------------------------

        # Set the flag for the maximum burn-time
        flag_burn = True
        time = 0

        # Get the thickness equivalent to 5 pixels
        dr = self.geometry.getMetersPerPixel() * 5

        while self.geometry.min_bloc_thickness() > safety_thickness and flag_burn:

            # print( self.geometry.totalSurfaceArea() / self.geometry.get_length() / 2 / math.pi)

            # Regression rate and mass flow calculations
            fuel_flow = self.geometry.compute_fuel_rate(rho=rho_fuel, ox_flow=ox_flow)
            total_mass_flow = ox_flow + fuel_flow
            of_ratio = ox_flow / fuel_flow
            Go = ox_flow / self.geometry.totalCrossSectionArea()
            delta_t = dr / self.geometry.regressionModel.computeRegressionRate(self.geometry, ox_flow)

            # Call CEA process to obtain thermochemical variables
            t_chamber, gamma, gas_molar_mass, cea_c_star, son_vel, rho_ch = self.run_thermochemical_analysis(
                of_ratio, pression_chambre/10**5)
            r = R / gas_molar_mass  # Specific gaz constant

            # Determine the flow port-speed
            u_ch = calculate_flow_speed(cross_section=self.geometry.totalCrossSectionArea(),
                                        mass_flow=total_mass_flow,
                                        density=rho_ch)

            # Calculate cea_c_star to consider the combustion efficiency parameter
            # cea_c_star = eta_comb * cea_c_star

            # Calculate chamber conditions and motor performance
            # Chamber pressure is not recalculated as it is assumed constant

            mach_exit = iso.exit_mach_via_expansion(gamma=gamma, expansion=self.nozzle.get_expansion_ratio(),
                                                    supersonic=True)
            exit_pressure = pression_chambre / iso.pressure_ratio(gamma, mach_exit)
            t_exit = t_chamber / iso.temperature_ratio(gamma, mach_exit)
            v_exit = math.sqrt(gamma * r * t_exit) * mach_exit

            thrust = self.nozzle.get_nozzle_effeciency() * (
            total_mass_flow * v_exit + (exit_pressure - pression_atmo) *
            self.nozzle.get_exit_area())

            # Calculate the nozzle equation validation
            nozzle_p = total_mass_flow * cea_c_star / (self.nozzle.get_throat_area() * pression_chambre)
            # print(nozzle_p)

            isp = thrust / total_mass_flow / g0

            # Results updates
            self.results['run_values']['time'].append(time)
            self.results["run_values"]["thrust"].append(thrust)
            self.results["run_values"]["isp"].append(isp)
            self.results["run_values"]["pressure"].append(pression_chambre)
            self.results["run_values"]["temperature"].append(t_chamber)
            self.results["run_values"]["of"].append(of_ratio)
            self.results["run_values"]["Go"].append(Go)
            self.results["run_values"]["nozzle_param"].append(nozzle_p)
            self.results["run_values"]["c_star"].append(cea_c_star)
            self.results["run_values"]["chamber_sound_speed"].append(son_vel)
            self.results["run_values"]["chamber_rho"].append(rho_ch)
            self.results["run_values"]["mass_flow"].append(total_mass_flow)
            self.results["run_values"]["mass_flow_ox"].append(ox_flow)
            self.results["run_values"]["mass_flow_f"].append(fuel_flow)
            self.results["run_values"]["chamber_speed"].append(u_ch)
            self.results["run_values"]["hydraulic_port_diameter"].append(self.geometry.get_hydraulic_diameter())

            # Verify it is a single port_number before updating the port number
            if isinstance(self.geometry, (OneCircularPort,
                                          ThreeCircularPorts,
                                          MultipleCircularPortsWithCircularCenter)):
                self.results["run_values"]["radius"].append(self.geometry.get_port_radius())

                # Compute regression rate
                self.results["run_values"]["regression_rate"].append(dr / delta_t)
            else:
                # Append a 0 if there it is not a OneCircularPort geometry
                self.results["run_values"]["radius"].append(nan)
                self.results["run_values"]["regression_rate"].append(nan)

            # Update the geometry and nozzle
            self.geometry.regress(ox_flow=ox_flow, dt=delta_t)
            self.nozzle.erode(delta_t)

            time += delta_t

            # Update the flag for burn-time
            if max_burn_time:
                flag_burn = time <= max_burn_time

        # ------------------------ POST-PROCESS ------------------------

        # Convert to numpy arrays
        self.results['run_values'] = {key: asarray(value) for key, value in self.results["run_values"].items()}

        # Post-process the data
        self.post_process_data(dt=dt)