FalconHSH作者
5个月5天前IP:山西
950286
5楼
引用tmn_A发表于3楼的内容
前几天看到了,不过这几天很忙没时间回,今天有点时间我仔细看了一下。首先我觉得楼主在推力与总冲的计算上是没有问题的,用了一些近似,认为喷嘴面积远小于内部截面积。如果要考虑喷嘴面积,那么火箭加速度会带来一...

感谢您的回复和鼓励,看到之后我也是思考了几天,在这里想和您讨论一下(望多多包涵)。首先我看了一下您对于软件积分的计算方法,又对比了一下我汉化的代码。由于我当时在AI帮助下选择了一种简单易行的方法,也方便我理解,因为诚然我没有高数基础,所以导致并不是很精密的推算。我发的软件中使用的矩形积分,您的中是梯形积分,精度会更高,让Grok看了一下,改一个函数就行。

这是汉化代码

import math
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
from tkinter import messagebox
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import matplotlib.pyplot 

# 设置中文字体(以黑体为例,可根据系统字体调整)
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'WenQuanYi Micro Hei']
plt.rcParams['axes.unicode_minus'] = False   # 解决负号显示异常
# 常量声明
delta_t = 0.0001
P_atm = float(1*101325)

# 仿真输出数据
array_time = []
array_h = []
array_Ft = []
array_mass = []
array_temperature = []
array_pressure = []
array_V_water = []
array_V_air = []

# 优化输出数据
array_opt_x = []
array_opt_Ic = []
array_opt_tc = []
array_opt_delta_v = []
array_opt_Ist = []
opt_variable_name = ""

# 导出数据
engine_name = ""
engine_diameter = 0
engine_lenght = 0
engine_dry_mass = 0
engine_full_mass = 0
engine_manufacturer = ""

# 效率数据
efficiency_sim_rod = 1
efficiency_sim_water = 1
efficiency_sim_gas = 1

efficiency_opt_rod = 1
efficiency_opt_water = 1
efficiency_opt_gas = 1


# 用于优化的仿真函数
def opt_sim(k_const, Rs, water_density, P_ins, At, V_air, V_water, Roc_mass, T, rod_lenght, rod_inside_diameter):
    try:
        # 局部输出变量声明
        Ic = float(0)
        max_h = float(0)
        total_time = float(0)
        delta_v = float(0)

        # 局部仿真变量
        s = float(0.0)
        vrod = float(0.0)

        # 考虑导向杆的存在修正初始空气体积
        V_air = V_air - At * rod_lenght

        # 定义绝热常数
        C_const = P_ins * pow(V_air, k_const)
        
        # 计算空气质量
        mass_air = P_ins * V_air / (T * Rs)
        mass_propelant = mass_air + V_water * water_density
        total_time = 0

        if(rod_inside_diameter > 0 and rod_lenght > 0):
            while(s < rod_lenght):
                # 更新时间
                total_time = total_time + delta_t

                # 计算推力和冲量
                Ft = P_ins * At * efficiency_opt_rod
                
                # 更新冲量
                Ic += Ft * delta_t

                arod = Ft / Roc_mass
                vrod += delta_t * arod
                delta_v = vrod

                s += vrod * delta_t + 0.5 * arod * delta_t * delta_t

        if(rod_inside_diameter == 0 and rod_lenght > 0):
            while(s < rod_lenght):
                total_time = total_time + delta_t

                # 计算推力和冲量
                Ft = P_ins * At * efficiency_opt_rod
                Ic += Ft * delta_t

                arod = Ft / Roc_mass
                vrod += delta_t * arod
                delta_v = vrod

                s += vrod * delta_t + 0.5 * arod * delta_t * delta_t

                delta_V = At * vrod * delta_t
                V_air += delta_V

                P_ins = C_const / pow(V_air, k_const)

                T = (P_ins * V_air) / (mass_air * Rs)

        # 水喷出阶段循环
        while(float(V_water) > float(0) and float(P_ins) > float(P_atm)):  
            total_time = total_time + delta_t 

            # 计算推力
            Ft = 2 * At * (P_ins - P_atm) * efficiency_opt_water
            Ic += Ft * delta_t

            ve = math.sqrt(2 * (P_ins - P_atm) / water_density)

            # 计算体积变化
            delta_V = At * ve * delta_t

            # 计算温度
            T = P_ins * V_air / (mass_air * Rs)

            # 更新质量
            Roc_mass = Roc_mass - delta_V * water_density

            # 更新速度增量
            delta_v = delta_v + delta_t * Ft / Roc_mass

            # 更新体积值
            V_air = V_air + delta_V
            V_water = V_water - delta_V
            
            # 计算压力
            P_ins = C_const * pow(V_air, -k_const)
            
        # 马赫数1的排气阶段
        while(P_ins > P_atm and P_ins/P_atm >= pow((k_const+1)/2, k_const/(k_const-1))):
            total_time = total_time + delta_t 

            Tt = T * 2 / (k_const + 1)

            dot_m = P_ins * At * pow(2/(k_const+1), 0.5 * (k_const+1)/(k_const-1)) * math.sqrt(k_const/(Rs*T))
            
            ve_air = math.sqrt(k_const * Rs * Tt)

            P_throat = pow(2 / (k_const + 1), k_const / (k_const - 1)) * P_ins
            Ft = (dot_m * ve_air + At * (P_throat - P_atm)) * efficiency_opt_gas
            Ic += Ft * delta_t
            
            delta_m = dot_m * delta_t
            
            # 更新火箭质量
            Roc_mass = Roc_mass - delta_m

            # 更新速度增量
            delta_v = delta_v + delta_t * Ft / Roc_mass

            mass_air = mass_air - delta_m

            T = (P_ins / Rs) * pow(V_air / (mass_air + delta_m), k_const) * pow(V_air / mass_air, 1 - k_const)

            P_ins = mass_air * Rs * T / V_air

        # 亚音速排气阶段
        while(P_ins > P_atm):     
            total_time = total_time + delta_t 

            Mach = math.sqrt(2 / (k_const-1) * (pow(P_ins / P_atm, (k_const-1) / k_const) - 1))

            Tt = T / (1 + 0.5 * (k_const-1) * Mach * Mach)

            ve_air = Mach * math.sqrt(k_const * Rs * Tt)

            density_chamber = mass_air / V_air
            density_throat = density_chamber / pow(1 + 0.5 * (k_const - 1) * Mach * Mach, 1 / (k_const-1))

            dot_m = At * density_throat * ve_air

            Ft = dot_m * ve_air * efficiency_opt_gas
            Ic += Ft * delta_t

            delta_m = dot_m * delta_t

            # 更新火箭质量
            Roc_mass = Roc_mass - delta_m

            # 更新速度增量
            delta_v = delta_v + delta_t * Ft / Roc_mass

            mass_air = mass_air - delta_m

            T = (P_ins / Rs) * pow(V_air / (mass_air + delta_m), k_const) * pow(V_air / mass_air, 1 - k_const)

            P_ins = mass_air * Rs * T / V_air
        
        return [Ic, total_time, Ic / (mass_propelant * 9.81), delta_v]
    except ValueError:
        error_label_r.config(text="输入无效。")


# 主优化函数
def optimalize():
    global opt_variable_name

    array_opt_x.clear()
    array_opt_Ic.clear()
    array_opt_tc.clear()
    array_opt_delta_v.clear()
    array_opt_Ist.clear()

    gas_name = choosen_gas_opt.get()

    if(gas_name == "空气"):
        k_const = 1.4
        Rs = 287
    if(gas_name == "氩气"):
        k_const = 1.67
        Rs = 208
    if(gas_name == "氦气"):
        k_const = 1.66
        Rs = 2077
    if(gas_name == "氢气"):
        k_const = 1.41
        Rs = 4124
    if(gas_name == "氮气"):
        k_const = 1.4
        Rs = 297 
    if(gas_name == "二氧化碳"):
        k_const = 1.3
        Rs = 188
    if(gas_name == "氙气"):
        k_const = 1.65
        Rs = 208

    try:
        if(gas_name == "自定义"):
            k_const = float(entry_combobox1_opt.get())
            Rs = float(entry_combobox2_opt.get())

        water_density = float(entry_6_r.get())

        # 读取输入值
        P_ins = float(entry_0_r.get())*100000
        At = 3.1415*pow(0.001*float(entry_1_r.get())/2, 2)
        V_total = float(entry_2_r.get())*0.001
        water_content = float(entry_3_r.get()) * 0.01
        V_air = float(V_total - water_content * V_total)
        V_water = float(water_content * V_total)
        dry_mass = float(entry_4_r.get())
        T = float(entry_5_r.get())+273.15
        rod_lenght = float(entry_launch_lenght_opt.get()) * 0.001
        rod_inside_diameter = float(entry_launch_diameter_opt.get()) * 0.001

        # 错误保护
        if(P_ins <= 0):
            raise ValueError
        if(k_const <= 0):
            raise ValueError
        if(Rs <= 0):
            raise ValueError
        if(water_density <= 0):
            raise ValueError
        if(At <= 0):
            raise ValueError
        if(V_total <= 0):
            raise ValueError
        if(water_content < 0 or water_content >= 1):
            raise ValueError
        if(dry_mass <= 0):
            raise ValueError
        if(T <= 0):
            raise ValueError
        if(rod_inside_diameter < 0):
            raise ValueError
        if(rod_lenght < 0):
            raise ValueError
        if(rod_inside_diameter > 0.001 * float(entry_1_r.get())):
            raise ValueError
        
        s = float(0.0)
        vrod = float(0.0)

        # 考虑导向杆的存在修正初始空气体积
        V_air = V_air - At * rod_lenght

        # 定义绝热常数
        C_const = P_ins * pow(V_air, k_const)

        # 计算空气质量
        mass_air = P_ins * V_air / (T * Rs)
        mass_propelant = mass_air + V_water * water_density

        Roc_mass = dry_mass + mass_propelant

        # 获取优化变量
        opt_var = choosen_opt_variable.get()
        opt_range_min = float(entry_opt_min.get())
        opt_range_max = float(entry_opt_max.get())
        opt_it = int(entry_opt_itteration.get())

        # 优化变量错误保护
        if(opt_range_max <= opt_range_min):
            raise ValueError
        if(opt_var == "含水量"):
            if(opt_range_min < 0 or opt_range_max >= 100):
                raise ValueError
        if(opt_range_min == 0 and (opt_var == "喉径" or opt_var == "容积")):
            raise ValueError
        if(opt_it <= 0 and opt_var != "气体温度"):
            raise ValueError
        if(opt_var == "气体温度" and opt_range_min < -273):
            raise ValueError

        opt_variable_name = opt_var

        if(opt_var == "压强"):
            opt_range_min *= 100000
            opt_range_max *= 100000
            opt_eps = (opt_range_max - opt_range_min) / float(opt_it)
            
            for i in range(opt_it + 1):
                array_opt_x.append(opt_range_min)

                mass_air = opt_range_min * V_air / (T * Rs)
                mass_propelant = mass_air + V_water * water_density

                Roc_mass = dry_mass + mass_propelant

                temp_data_opt = opt_sim(k_const, Rs, water_density, opt_range_min, At, V_air, V_water, Roc_mass, T, rod_lenght, rod_inside_diameter)
                array_opt_Ic.append(temp_data_opt[0])
                array_opt_tc.append(temp_data_opt[1])
                array_opt_Ist.append(temp_data_opt[2])
                array_opt_delta_v.append(temp_data_opt[3])

                opt_range_min += opt_eps

        if(opt_var == "喉径"):
            opt_range_min = 3.1415*pow(0.001*opt_range_min/2, 2)
            opt_range_max = 3.1415*pow(0.001*opt_range_max/2, 2)
            opt_eps = (opt_range_max - opt_range_min) / float(opt_it)
            
            for i in range(opt_it + 1):
                array_opt_x.append(opt_range_min)

                mass_air = P_ins * V_air / (T * Rs)
                mass_propelant = mass_air + V_water * water_density

                Roc_mass = dry_mass + mass_propelant

                temp_data_opt = opt_sim(k_const, Rs, water_density, P_ins, opt_range_min, V_air, V_water, Roc_mass, T, rod_lenght, rod_inside_diameter)
                array_opt_Ic.append(temp_data_opt[0])
                array_opt_tc.append(temp_data_opt[1])
                array_opt_Ist.append(temp_data_opt[2])
                array_opt_delta_v.append(temp_data_opt[3])

                opt_range_min += opt_eps

        if(opt_var == "容积"):
            opt_range_min *= 0.001
            opt_range_max *= 0.001
            opt_eps = (opt_range_max - opt_range_min) / float(opt_it)
            
            for i in range(opt_it + 1):
                array_opt_x.append(opt_range_min)

                V_air = opt_range_min - water_content * opt_range_min
                V_water = water_content * opt_range_min

                mass_air = P_ins * V_air / (T * Rs)
                mass_propelant = mass_air + V_water * water_density

                Roc_mass = dry_mass + mass_propelant

                temp_data_opt = opt_sim(k_const, Rs, water_density, P_ins, At, V_air, V_water, Roc_mass, T, rod_lenght, rod_inside_diameter)
                array_opt_Ic.append(temp_data_opt[0])
                array_opt_tc.append(temp_data_opt[1])
                array_opt_Ist.append(temp_data_opt[2])
                array_opt_delta_v.append(temp_data_opt[3])

                opt_range_min += opt_eps
        if(opt_var == "含水量"):
            opt_range_min *= 0.01
            opt_range_max *= 0.01
            opt_eps = (opt_range_max - opt_range_min) / float(opt_it)
            
            for i in range(opt_it + 1):
                array_opt_x.append(opt_range_min)

                V_air = float(V_total - opt_range_min * V_total)
                V_water = float(opt_range_min * V_total)

                mass_air = P_ins * V_air / (T * Rs)
                mass_propelant = mass_air + V_water * water_density

                Roc_mass = dry_mass + mass_propelant

                temp_data_opt = opt_sim(k_const, Rs, water_density, float(P_ins), At, V_air, V_water, Roc_mass, T, rod_lenght, rod_inside_diameter)
                array_opt_Ic.append(temp_data_opt[0])
                array_opt_tc.append(temp_data_opt[1])
                array_opt_Ist.append(temp_data_opt[2])
                array_opt_delta_v.append(temp_data_opt[3])

                opt_range_min += opt_eps

        if(opt_var == "干重"):
            opt_eps = (opt_range_max - opt_range_min) / float(opt_it)
            
            for i in range(opt_it + 1):
                array_opt_x.append(opt_range_min)

                mass_air = P_ins * V_air / (T * Rs)
                mass_propelant = mass_air + V_water * water_density

                Roc_mass = opt_range_min + mass_propelant

                temp_data_opt = opt_sim(k_const, Rs, water_density, float(P_ins), At, V_air, V_water, Roc_mass, T, rod_lenght, rod_inside_diameter)
                
                array_opt_Ic.append(temp_data_opt[0])
                array_opt_tc.append(temp_data_opt[1])
                array_opt_Ist.append(temp_data_opt[2])
                array_opt_delta_v.append(temp_data_opt[3])

                opt_range_min += opt_eps

        if(opt_var == "气体温度"):
            opt_range_min += 273.15
            opt_range_max += 273.15
            opt_eps = (opt_range_max - opt_range_min) / float(opt_it)
            
            for i in range(opt_it + 1):
                array_opt_x.append(opt_range_min)

                mass_air = P_ins * V_air / (opt_range_min * Rs)
                mass_propelant = mass_air + V_water * water_density

                Roc_mass = dry_mass + mass_propelant

                temp_data_opt = opt_sim(k_const, Rs, water_density, float(P_ins), At, V_air, V_water, Roc_mass, opt_range_min, rod_lenght, rod_inside_diameter)
                array_opt_Ic.append(temp_data_opt[0])
                array_opt_tc.append(temp_data_opt[1])
                array_opt_Ist.append(temp_data_opt[2])
                array_opt_delta_v.append(temp_data_opt[3])

                opt_range_min += opt_eps

        plot_opt_Ic()

        # 清除错误信息
        error_label_r.config(text="")
    except ValueError:
        error_label_r.config(text="输入无效。")


def plot_opt_Ic():
    try:
        # 清除上一个图形
        ax_right.clear()

        # 绘制新数据
        ax_right.plot(array_opt_x, array_opt_Ic, linestyle='-', color='b')

        # 设置标签和标题
        ax_right.set_xlabel(opt_variable_name)
        ax_right.set_ylabel('冲量 Ic [Ns]')
        ax_right.set_title('冲量曲线')

        # 更新画布
        canvas_right.draw()

        # 清除错误信息
        error_label_r.config(text="")

    except ValueError:
        error_label_r.config(text="输入无效。")

def plot_opt_tc():
    try:
        # 清除上一个图形
        ax_right.clear()

        # 绘制新数据
        ax_right.plot(array_opt_x, array_opt_tc, linestyle='-', color='b')

        # 设置标签和标题
        ax_right.set_xlabel(opt_variable_name)
        ax_right.set_ylabel('总时间 [s]')
        ax_right.set_title('工作时间曲线')

        # 更新画布
        canvas_right.draw()

        # 清除错误信息
        error_label_r.config(text="")

    except ValueError:
        error_label_r.config(text="输入无效。")

def plot_opt_Ist():
    try:
        # 清除上一个图形
        ax_right.clear()

        # 绘制新数据
        ax_right.plot(array_opt_x, array_opt_Ist, linestyle='-', color='b')

        # 设置标签和标题
        ax_right.set_xlabel(opt_variable_name)
        ax_right.set_ylabel('比冲 [s]')
        ax_right.set_title('比冲曲线')

        # 更新画布
        canvas_right.draw()

        # 清除错误信息
        error_label_r.config(text="")

    except ValueError:
        error_label_r.config(text="输入无效。")

def plot_opt_delta_v():
    try:
        # 清除上一个图形
        ax_right.clear()

        # 绘制新数据
        ax_right.plot(array_opt_x, array_opt_delta_v, linestyle='-', color='b')

        # 设置标签和标题
        ax_right.set_xlabel(opt_variable_name)
        ax_right.set_ylabel('速度增量 Δv [m/s]')
        ax_right.set_title('速度增量曲线')

        # 更新画布
        canvas_right.draw()

        # 清除错误信息
        error_label_r.config(text="")

    except ValueError:
        error_label_r.config(text="输入无效。")


# 主仿真函数
def simulate():
    global engine_dry_mass
    global engine_full_mass
    # 输出变量声明
    Ic = float(0)
    max_h = float(0)
    total_time = float(0)
    delta_v = float(0)

    gas_name = choosen_gas.get()

    try:
        if(gas_name == "空气"):
            k_const = 1.4
            Rs = 287
        if(gas_name == "氩气"):
            k_const = 1.67
            Rs = 208
        if(gas_name == "氦气"):
            k_const = 1.66
            Rs = 2077
        if(gas_name == "氢气"):
            k_const = 1.41
            Rs = 4124
        if(gas_name == "氮气"):
            k_const = 1.4
            Rs = 297 
        if(gas_name == "二氧化碳"):
            k_const = 1.3
            Rs = 188
        if(gas_name == "氙气"):
            k_const = 1.65
            Rs = 208
        if(gas_name == "自定义"):
            k_const = float(entry_combobox1.get())
            Rs = float(entry_combobox2.get())

        water_density = float(entry_6.get())
    
        # 清除总时间值
        total_time = 0
        Ic = 0
        delta_v = 0

        # 清空所有数组
        array_time.clear()
        array_h.clear()
        array_Ft.clear()
        array_mass.clear()
        array_temperature.clear()
        array_pressure.clear()
        array_V_water.clear()
        array_V_air.clear()

        # 读取输入值
        P_ins = float(entry_0.get())*100000
        At = 3.1415*pow(0.001*float(entry_1.get())/2, 2)
        V_total = float(entry_2.get())*0.001
        water_content = float(entry_3.get()) * 0.01
        V_air = V_total - water_content * V_total
        V_water = water_content * V_total
        dry_mass = float(entry_4.get())
        engine_dry_mass = dry_mass
        T = float(entry_5.get())+273.15
        rod_lenght = float(entry_launch_lenght.get()) * 0.001
        rod_inside_diameter = float(entry_launch_diameter.get()) * 0.001

        # 错误保护
        if(P_ins <= 0):
            raise ValueError
        if(k_const <= 0):
            raise ValueError
        if(Rs <= 0):
            raise ValueError
        if(water_density <= 0):
            raise ValueError
        if(At <= 0):
            raise ValueError
        if(V_total <= 0):
            raise ValueError
        if(water_content < 0 or water_content >= 1):
            raise ValueError
        if(dry_mass <= 0):
            raise ValueError
        if(T <= 0):
            raise ValueError
        if(rod_inside_diameter < 0):
            raise ValueError
        if(rod_lenght < 0):
            raise ValueError
        if(rod_inside_diameter >= 0.001 * float(entry_1.get())):
            raise ValueError
        
        s = float(0.0)
        vrod = float(0.0)

        # 考虑导向杆的存在修正初始空气体积
        V_air = V_air - At * rod_lenght

        # 定义绝热常数
        C_const = P_ins * pow(V_air, k_const)

        # 计算空气质量
        mass_air = P_ins * V_air / (T * Rs)
        mass_propelant = mass_air + V_water * water_density

        Roc_mass = dry_mass + mass_propelant
        engine_full_mass = Roc_mass

        if(rod_inside_diameter > 0 and rod_lenght > 0):
            while(s < rod_lenght):
                # 数组更新
                array_time.append(total_time)
                total_time = total_time + delta_t
                
                array_V_water.append(V_water)
                array_V_air.append(V_air)

                array_mass.append(Roc_mass)
                array_pressure.append(P_ins)
                array_temperature.append(T)

                # 计算推力和冲量
                Ft = (P_ins * At) * efficiency_sim_rod
                array_Ft.append(Ft)
                Ic += Ft * delta_t

                arod = Ft / Roc_mass
                vrod += delta_t * arod
                delta_v = vrod

                s += vrod * delta_t + 0.5 * arod * delta_t * delta_t

        if(rod_inside_diameter == 0 and rod_lenght > 0):
            while(s < rod_lenght):
                # 数组更新
                array_time.append(total_time)
                total_time = total_time + delta_t

                array_mass.append(Roc_mass)
                array_V_air.append(V_air)
                array_V_water.append(V_water)
                
                array_temperature.append(T)
                array_pressure.append(P_ins)

                # 计算推力和冲量
                Ft = (P_ins * At) * efficiency_sim_rod
                array_Ft.append(Ft)
                Ic += Ft * delta_t

                arod = Ft / Roc_mass
                vrod = vrod + delta_t * arod
                delta_v = vrod

                s += vrod * delta_t + 0.5 * arod * delta_t * delta_t

                delta_V = At * vrod * delta_t
                V_air += delta_V

                P_ins = C_const / pow(V_air, k_const)

                T = (P_ins * V_air) / (mass_air * Rs)

        # 水喷出阶段循环
        while(V_water > 0 and P_ins > P_atm):  
            # 更新时间数组用于绘图
            array_time.append(total_time)
            total_time = total_time + delta_t 

            # 计算推力
            Ft = (2 * At * (P_ins - P_atm)) * efficiency_sim_water
            array_Ft.append(Ft)
            Ic += Ft * delta_t

            ve = math.sqrt(2 * (P_ins - P_atm) / water_density)

            # 计算体积变化
            delta_V = At * ve * delta_t

            # 更新体积数组
            array_V_water.append(V_water)
            array_V_air.append(V_air)

            # 计算温度
            T = P_ins * V_air / (mass_air * Rs)
            array_temperature.append(T)

            # 更新质量数组
            array_mass.append(Roc_mass)
            Roc_mass = Roc_mass - delta_V * water_density

            # 更新速度增量
            delta_v = delta_v + delta_t * Ft / Roc_mass

            # 更新体积值
            V_air = V_air + delta_V
            V_water = V_water - delta_V
            
            # 计算压力并更新数组
            P_ins = C_const * pow(V_air, -k_const)
            array_pressure.append(P_ins)
            
        # 马赫数1的排气阶段
        while(P_ins > P_atm and P_ins/P_atm >= pow((k_const+1)/2, k_const/(k_const-1))):
            # 更新时间数组用于绘图
            array_time.append(total_time)
            total_time = total_time + delta_t 

            # 添加不变化的值用于绘图
            array_V_water.append(V_water)
            array_V_air.append(V_air)

            Tt = T * 2 / (k_const + 1)

            dot_m = P_ins * At * pow(2/(k_const+1), 0.5 * (k_const+1)/(k_const-1)) * math.sqrt(k_const/(Rs*T))
            ve_air = math.sqrt(k_const * Rs * Tt)
            P_throat = pow(2 / (k_const + 1), k_const / (k_const - 1)) * P_ins
            Ft = (dot_m * ve_air + At * (P_throat - P_atm)) * efficiency_sim_gas
            array_Ft.append(Ft)
            Ic += Ft * delta_t
            
            delta_m = dot_m * delta_t
            
            # 更新火箭质量
            Roc_mass = Roc_mass - delta_m
            array_mass.append(Roc_mass)

            # 更新速度增量
            delta_v = delta_v + delta_t * Ft / Roc_mass

            mass_air = mass_air - delta_m

            T = (P_ins / Rs) * pow(V_air / (mass_air + delta_m), k_const) * pow(V_air / mass_air, 1 - k_const)
            array_temperature.append(T)

            P_ins = mass_air * Rs * T / V_air
            array_pressure.append(P_ins)

        # 亚音速排气阶段
        while(P_ins > P_atm):     
            # 更新时间数组用于绘图
            array_time.append(total_time)
            total_time = total_time + delta_t 

            # 添加不变化的值用于绘图
            array_V_water.append(V_water)
            array_V_air.append(V_air)

            Mach = math.sqrt(2 / (k_const-1) * (pow(P_ins / P_atm, (k_const-1) / k_const) - 1))

            Tt = T / (1 + 0.5 * (k_const-1) * Mach * Mach)

            ve_air = Mach * math.sqrt(k_const * Rs * Tt)

            density_chamber = mass_air / V_air
            density_throat = density_chamber / pow(1 + 0.5 * (k_const - 1) * Mach * Mach, 1 / (k_const-1))

            dot_m = At * density_throat * ve_air

            Ft = (dot_m * ve_air) * efficiency_sim_gas
            array_Ft.append(Ft)
            Ic += Ft * delta_t

            delta_m = dot_m * delta_t

            # 更新火箭质量
            Roc_mass = Roc_mass - delta_m
            array_mass.append(Roc_mass)

            # 更新速度增量
            delta_v = delta_v + delta_t * Ft / Roc_mass

            mass_air = mass_air - delta_m

            T = (P_ins / Rs) * pow(V_air / (mass_air + delta_m), k_const) * pow(V_air / mass_air, 1 - k_const)
            array_temperature.append(T)

            P_ins = mass_air * Rs * T / V_air
            array_pressure.append(P_ins)

        # 清除错误信息
        error_label.config(text="")

        # 打印输出值
        text_widget_output = "冲量 Ic = {:.2f} Ns\n".format(Ic) + "工作时间 tc = {:.2f} s\n".format(total_time) + "比冲 Ist = {:.2f} s\n".format(Ic / (mass_propelant * 9.81)) + "速度增量 Δv = {:.2f} m/s\n\n".format(delta_v)
        
        text_widget.insert("1.0", text_widget_output)
        
        plot_Ft()

    except ValueError:
        error_label.config(text="输入无效。")

def plot_Ft():
    try:
        # 清除上一个图形
        ax_left.clear()

        # 绘制新数据
        ax_left.plot(array_time, array_Ft, linestyle='-', color='b')

        # 设置标签和标题
        ax_left.set_xlabel('时间 [s]')
        ax_left.set_ylabel('推力 [N]')
        ax_left.set_title('推力曲线')

        # 更新画布
        canvas_left.draw()

        # 清除错误信息
        error_label.config(text="")

    except ValueError:
        error_label.config(text="输入无效。")


def plot_mass():
    try:
        # 清除上一个图形
        ax_left.clear()

        # 绘制新数据
        ax_left.plot(array_time, array_mass, linestyle='-', color='b')

        # 设置标签和标题
        ax_left.set_xlabel('时间 [s]')
        ax_left.set_ylabel('质量 [kg]')
        ax_left.set_title('质量变化曲线')

        # 更新画布
        canvas_left.draw()

        # 清除错误信息
        error_label.config(text="")

    except ValueError:
        error_label.config(text="输入无效。")

def plot_temperature():
    try:
        # 清除上一个图形
        ax_left.clear()

        # 绘制新数据
        ax_left.plot(array_time, array_temperature, linestyle='-', color='b')

        # 设置标签和标题
        ax_left.set_xlabel('时间 [s]')
        ax_left.set_ylabel('温度 [K]')
        ax_left.set_title('温度曲线')

        # 更新画布
        canvas_left.draw()

        # 清除错误信息
        error_label.config(text="")

    except ValueError:
        error_label.config(text="输入无效。")


def plot_pressure():
    try:
        # 清除上一个图形
        ax_left.clear()

        # 将压力单位转换为 bar
        for i in range(len(array_pressure)):
            array_pressure[i] /= 100000

        # 绘制新数据
        ax_left.plot(array_time, array_pressure, linestyle='-', color='b')

        # 设置标签和标题
        ax_left.set_xlabel('时间 [s]')
        ax_left.set_ylabel('压强 [bar]')
        ax_left.set_title('压强曲线')

        # 更新画布
        canvas_left.draw()

        # 清除错误信息
        error_label.config(text="")

    except ValueError:
        error_label.config(text="输入无效。")


def plot_volume():
    try:
        # 清除上一个图形
        ax_left.clear()

        # 绘制新数据
        ax_left.plot(array_time, array_V_water, linestyle='-', color='b', label = "水")
        ax_left.plot(array_time, array_V_air, linestyle='-', color='c', label = "空气")
        
        # 设置标签和标题
        ax_left.set_xlabel('时间 [s]')
        ax_left.set_ylabel('体积 [m³]')
        ax_left.set_title('体积变化曲线')
        ax_left.legend()
        # 更新画布
        canvas_left.draw()  

        # 清除错误信息
        error_label.config(text="")

    except ValueError:
        error_label.config(text="输入无效。")


def save_to_file(entry_text):
    file_path = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=[("文本文件", "*.txt")])
    if file_path:
        with open(file_path, "w") as file:
            # frame_simulate
            file.write(choosen_gas.get()+"\n")
            file.write(entry_combobox1.get()+"\n")
            file.write(entry_combobox2.get()+"\n")
            file.write(entry_launch_lenght.get()+"\n")
            file.write(entry_launch_diameter.get()+"\n")

            # frame_left
            file.write(entry_0.get()+"\n")
            file.write(entry_1.get()+"\n")
            file.write(entry_2.get()+"\n")
            file.write(entry_3.get()+"\n")
            file.write(entry_4.get()+"\n")
            file.write(entry_5.get()+"\n")
            file.write(entry_6.get()+"\n")

            # frame_simulate_r
            file.write(choosen_opt_variable.get()+"\n")
            file.write(entry_opt_min.get()+"\n")
            file.write(entry_opt_max.get()+"\n")
            file.write(entry_opt_itteration.get()+"\n")
            file.write(choosen_gas_opt.get()+"\n")
            file.write(entry_combobox1_opt.get()+"\n")
            file.write(entry_combobox2_opt.get()+"\n")
            file.write(entry_launch_lenght_opt.get()+"\n")
            file.write(entry_launch_diameter_opt.get()+"\n")

            # frame_right
            file.write(entry_0_r.get()+"\n")
            file.write(entry_1_r.get()+"\n")
            file.write(entry_2_r.get()+"\n")
            file.write(entry_3_r.get()+"\n")
            file.write(entry_4_r.get()+"\n")
            file.write(entry_5_r.get()+"\n")
            file.write(entry_6_r.get()+"\n")


def load_from_file(entry_text):
    array_load_file = []
    file_path = filedialog.askopenfilename(filetypes=[("文本文件", "*.txt")])
    if file_path:
        with open(file_path, "r") as file:
            line = file.readline()
            # 继续读取行直到文件末尾
            while line:
                array_load_file.append(line.strip())
                line = file.readline()

    combobox.set(array_load_file[0])

    entry_combobox1.delete(0, tk.END)
    entry_combobox1.insert(0, array_load_file[1])
    entry_combobox2.delete(0, tk.END)
    entry_combobox2.insert(0, array_load_file[2])
    entry_launch_lenght.delete(0, tk.END)
    entry_launch_lenght.insert(0, array_load_file[3])
    entry_launch_diameter.delete(0, tk.END)
    entry_launch_diameter.insert(0, array_load_file[4])

    entry_0.delete(0, tk.END)
    entry_0.insert(0, array_load_file[5])
    entry_1.delete(0, tk.END)
    entry_1.insert(0, array_load_file[6])
    entry_2.delete(0, tk.END)
    entry_2.insert(0, array_load_file[7])
    entry_3.delete(0, tk.END)
    entry_3.insert(0, array_load_file[8])
    entry_4.delete(0, tk.END)
    entry_4.insert(0, array_load_file[9])
    entry_5.delete(0, tk.END)
    entry_5.insert(0, array_load_file[10])
    entry_6.delete(0, tk.END)
    entry_6.insert(0, array_load_file[11])

    combobox_variable_opt.set(array_load_file[12])

    entry_opt_min.delete(0, tk.END)
    entry_opt_min.insert(0, array_load_file[13])
    entry_opt_max.delete(0, tk.END)
    entry_opt_max.insert(0, array_load_file[14])
    entry_opt_itteration.delete(0, tk.END)
    entry_opt_itteration.insert(0, array_load_file[15])
    
    combobox_gas_opt.set(array_load_file[16])
    
    entry_combobox1_opt.delete(0, tk.END)
    entry_combobox1_opt.insert(0, array_load_file[17])
    entry_combobox2_opt.delete(0, tk.END)
    entry_combobox2_opt.insert(0, array_load_file[18])
    entry_launch_lenght_opt.delete(0, tk.END)
    entry_launch_lenght_opt.insert(0, array_load_file[19])
    entry_launch_diameter_opt.delete(0, tk.END)
    entry_launch_diameter_opt.insert(0, array_load_file[20])

    entry_0_r.delete(0, tk.END)
    entry_0_r.insert(0, array_load_file[21])
    entry_1_r.delete(0, tk.END)
    entry_1_r.insert(0, array_load_file[22])
    entry_2_r.delete(0, tk.END)
    entry_2_r.insert(0, array_load_file[23])
    entry_3_r.delete(0, tk.END)
    entry_3_r.insert(0, array_load_file[24])
    entry_4_r.delete(0, tk.END)
    entry_4_r.insert(0, array_load_file[25])
    entry_5_r.delete(0, tk.END)
    entry_5_r.insert(0, array_load_file[26])
    entry_6_r.delete(0, tk.END)
    entry_6_r.insert(0, array_load_file[27])
    
    
def export_to_file():
    entry_window = tk.Toplevel(root)
    entry_window.title("导出数据")

    label_export_name = ttk.Label(entry_window, text="发动机名称:")
    label_export_name.grid(row=0, column=0, columnspan=2, pady=5, sticky="w")

    entry_export_name = ttk.Entry(entry_window, width=10)
    entry_export_name.grid(row=0, column=2, pady=5)
    entry_export_name.insert(0, "0")

    label_export_diameter = ttk.Label(entry_window, text="发动机直径 [mm]:")
    label_export_diameter.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")

    entry_export_diameter = ttk.Entry(entry_window, width=10)
    entry_export_diameter.grid(row=1, column=2, pady=5)
    entry_export_diameter.insert(0, "0")

    label_export_lenght = ttk.Label(entry_window, text="发动机长度 [mm]:")
    label_export_lenght.grid(row=2, column=0, columnspan=2, pady=5, sticky="w")

    entry_export_lenght = ttk.Entry(entry_window, width=10)
    entry_export_lenght.grid(row=2, column=2, pady=5)
    entry_export_lenght.insert(0, "0")

    label_export_man = ttk.Label(entry_window, text="发动机制造商:")
    label_export_man.grid(row=3, column=0, columnspan=2, pady=5, sticky="w")

    entry_export_man = ttk.Entry(entry_window, width=10)
    entry_export_man.grid(row=3, column=2, pady=5)
    entry_export_man.insert(0, "0")

    ok_button = ttk.Button(entry_window, text="确定", command=lambda: save_and_close(entry_export_name, entry_export_diameter, entry_export_lenght, entry_export_man, entry_window))
    ok_button.grid(row=4, column=1, columnspan=2, pady=10)
    
def save_and_close(entry_export_name, entry_export_diameter, entry_export_lenght, entry_export_man, window):
    global engine_name
    global engine_diameter
    global engine_lenght
    global engine_manufacturer
    engine_name = entry_export_name.get()
    engine_diameter = entry_export_diameter.get()
    engine_lenght = entry_export_lenght.get()
    engine_manufacturer = entry_export_man.get()

    # 预期错误
    try:
        if(float(engine_diameter) <= 0 ):
            raise ValueError
        if(float(engine_lenght) <= 0):
            raise ValueError
        
        save_to_export()
        window.destroy()
    except ValueError:
        messagebox.showinfo("错误", "输入无效。")


def save_to_export():
    file_path = filedialog.asksaveasfilename(defaultextension=".eng", filetypes=[("发动机文件", "*.eng")])
    if file_path:
        with open(file_path, "w") as file:
            file.write(engine_name + " ")
            file.write(str(engine_diameter) + " ")
            file.write(str(engine_lenght) + " ")
            file.write("P" + " ")
            file.write(str(engine_dry_mass) + " ")
            file.write(str(engine_full_mass) + " ")
            file.write(engine_manufacturer + "\n")
            
            for el_t, el_F in zip(array_time, array_Ft):
                file.write(str(el_t) + " " + str(el_F) + "\n")

def efficiency_sim_window():
    entry_window = tk.Toplevel(root)
    entry_window.title("仿真效率设置")

    label_export_name = ttk.Label(entry_window, text="导向杆效率:")
    label_export_name.grid(row=0, column=0, columnspan=2, pady=5, sticky="w")

    entry_export_name = ttk.Entry(entry_window, width=10)
    entry_export_name.grid(row=0, column=2, pady=5)
    entry_export_name.insert(0, str(efficiency_sim_rod))

    label_export_diameter = ttk.Label(entry_window, text="液相效率:")
    label_export_diameter.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")

    entry_export_diameter = ttk.Entry(entry_window, width=10)
    entry_export_diameter.grid(row=1, column=2, pady=5)
    entry_export_diameter.insert(0, str(efficiency_sim_water))

    label_export_lenght = ttk.Label(entry_window, text="气相效率:")
    label_export_lenght.grid(row=2, column=0, columnspan=2, pady=5, sticky="w")

    entry_export_lenght = ttk.Entry(entry_window, width=10)
    entry_export_lenght.grid(row=2, column=2, pady=5)
    entry_export_lenght.insert(0, str(efficiency_sim_gas))

    ok_button = ttk.Button(entry_window, text="确定", command=lambda: efficiency_sim_save(entry_export_name, entry_export_diameter, entry_export_lenght, entry_window))
    ok_button.grid(row=4, column=1, columnspan=2, pady=10)
    
def efficiency_sim_save(entry_export_name, entry_export_diameter, entry_export_lenght, window):
    global efficiency_sim_rod
    global efficiency_sim_water
    global efficiency_sim_gas
    
    # 预期错误
    try:
        efficiency_sim_rod = float(entry_export_name.get())
        efficiency_sim_water = float(entry_export_diameter.get())
        efficiency_sim_gas = float(entry_export_lenght.get())

        if(efficiency_sim_rod <= 0 or efficiency_sim_water <= 0 or efficiency_sim_gas <= 0):
            raise ValueError
        if(efficiency_sim_rod > 100 or efficiency_sim_water > 100 or efficiency_sim_gas > 100):
            raise ValueError
        
        window.destroy()
    except ValueError:
        messagebox.showinfo("错误", "输入无效。")
        efficiency_sim_rod = 1
        efficiency_sim_water = 1
        efficiency_sim_gas = 1

def efficiency_opt_window():
    entry_window = tk.Toplevel(root)
    entry_window.title("优化效率设置")

    label_export_name = ttk.Label(entry_window, text="导向杆效率:")
    label_export_name.grid(row=0, column=0, columnspan=2, pady=5, sticky="w")

    entry_export_name = ttk.Entry(entry_window, width=10)
    entry_export_name.grid(row=0, column=2, pady=5)
    entry_export_name.insert(0, str(efficiency_opt_rod))

    label_export_diameter = ttk.Label(entry_window, text="液相效率:")
    label_export_diameter.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")

    entry_export_diameter = ttk.Entry(entry_window, width=10)
    entry_export_diameter.grid(row=1, column=2, pady=5)
    entry_export_diameter.insert(0, str(efficiency_opt_water))

    label_export_lenght = ttk.Label(entry_window, text="气相效率:")
    label_export_lenght.grid(row=2, column=0, columnspan=2, pady=5, sticky="w")

    entry_export_lenght = ttk.Entry(entry_window, width=10)
    entry_export_lenght.grid(row=2, column=2, pady=5)
    entry_export_lenght.insert(0, str(efficiency_opt_gas))

    ok_button = ttk.Button(entry_window, text="确定", command=lambda: efficiency_opt_save(entry_export_name, entry_export_diameter, entry_export_lenght, entry_window))
    ok_button.grid(row=4, column=1, columnspan=2, pady=10)
    
def efficiency_opt_save(entry_export_name, entry_export_diameter, entry_export_lenght, window):
    global efficiency_opt_rod
    global efficiency_opt_water
    global efficiency_opt_gas
    
    # 预期错误
    try:
        efficiency_opt_rod = float(entry_export_name.get())
        efficiency_opt_water = float(entry_export_diameter.get())
        efficiency_opt_gas = float(entry_export_lenght.get())

        if(efficiency_opt_rod <= 0 or efficiency_opt_water <= 0 or efficiency_opt_gas <= 0):
            raise ValueError
        if(efficiency_opt_rod > 100 or efficiency_opt_water > 100 or efficiency_opt_gas > 100):
            raise ValueError
        
        window.destroy()
    except ValueError:
        messagebox.showinfo("错误", "输入无效。")
        efficiency_opt_rod = 1
        efficiency_opt_water = 1
        efficiency_opt_gas = 1


# 创建主 Tkinter 窗口
root = tk.Tk()
root.title("水火箭仿真器")

menubar = tk.Menu(root)

file_menu = tk.Menu(menubar, tearoff=0)
file_menu.add_command(label="保存", command=lambda: save_to_file(entry_0))
file_menu.add_command(label="加载", command=lambda: load_from_file(entry_0))
file_menu.add_command(label="导出为 .eng", command=lambda: export_to_file())

menubar.add_cascade(label="文件", menu=file_menu)

root.config(menu=menubar)

# 设置窗口为全屏
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
root.geometry(f"{screen_width}x{screen_height}")

##########################################################################
# 左侧区域
##########################################################################

frame_simulate = ttk.Frame(root, padding="10")
frame_simulate.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

simulate_button = ttk.Button(frame_simulate, text="仿真", command=simulate)
simulate_button.grid(row=0, column=0, columnspan=1, pady=5, sticky="w")

efficiency_button = ttk.Button(frame_simulate, text="效率", command=efficiency_sim_window)
efficiency_button.grid(row=0, column=2, columnspan=1, pady=5, sticky="w")

# 显示所选选项的标签
label_gas = ttk.Label(frame_simulate, text="选择气体:")
label_gas.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")

# 创建 StringVar 来保存组合框的选定值
default_option = "空气"
choosen_gas = tk.StringVar(value=default_option)

# 创建组合框(下拉菜单)
options = ["空气", "氩气", "氦气", "氢气", "氮气", "二氧化碳", "氙气", "自定义"]
combobox = ttk.Combobox(frame_simulate, textvariable=choosen_gas, values=options, state="readonly", width=10)
combobox.grid(row=1, column=2, columnspan=2, pady=5, sticky="w")

label_custom_gas_info = ttk.Label(frame_simulate, text="自定义气体数据:")
label_custom_gas_info.grid(row=3, column=0, columnspan=2, pady=5, sticky="w")

label_combobox1 = ttk.Label(frame_simulate, text="比热比:")
label_combobox1.grid(row=4, column=0, columnspan=2, pady=5, sticky="w")

entry_combobox1 = ttk.Entry(frame_simulate, width=10)
entry_combobox1.grid(row=4, column=2, pady=5)
entry_combobox1.insert(0, "0")

label_combobox2 = ttk.Label(frame_simulate, text="气体常数 [J/(kg*K)]:")
label_combobox2.grid(row=5, column=0, columnspan=2, pady=5, sticky="w")

entry_combobox2 = ttk.Entry(frame_simulate, width=10)
entry_combobox2.grid(row=5, column=2, pady=5)
entry_combobox2.insert(0, "0")

# 导向杆数据
label_launch_lenght = ttk.Label(frame_simulate, text="导向杆长度 [mm]:")
label_launch_lenght.grid(row=6, column=0, columnspan=2, pady=5, sticky="w")

entry_launch_lenght = ttk.Entry(frame_simulate, width=10)
entry_launch_lenght.grid(row=6, column=2, pady=5)
entry_launch_lenght.insert(0, "0")

label_launch_diameter = ttk.Label(frame_simulate, text="导向杆内径 [mm]:")
label_launch_diameter.grid(row=7, column=0, columnspan=2, pady=5, sticky="w")

entry_launch_diameter = ttk.Entry(frame_simulate, width=10)
entry_launch_diameter.grid(row=7, column=2, pady=5)
entry_launch_diameter.insert(0, "0")

# 左侧输入框架
frame_left = ttk.Frame(root, padding="10")
frame_left.grid(row=1, column=1, sticky=(tk.W, tk.E, tk.N, tk.S))

label_0 = ttk.Label(frame_left, text="初始压强 [bar]:")
label_0.grid(row=0, column=0, columnspan=2, pady=5, sticky="w")

entry_0 = ttk.Entry(frame_left, width=10)
entry_0.grid(row=0, column=2, pady=5)
entry_0.insert(0, "0")

label_1 = ttk.Label(frame_left, text="喉部直径 [mm]:")
label_1.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")

entry_1 = ttk.Entry(frame_left, width=10)
entry_1.grid(row=1, column=2, pady=5)
entry_1.insert(0, "0")

label_2 = ttk.Label(frame_left, text="总容积 [L]:")
label_2.grid(row=2, column=0, columnspan=2,  pady=5, sticky="w")

entry_2 = ttk.Entry(frame_left, width=10)
entry_2.grid(row=2, column=2, pady=5)
entry_2.insert(0, "0")

label_3 = ttk.Label(frame_left, text="含水量 [%]:")
label_3.grid(row=3, column=0, columnspan=2, pady=5, sticky="w")

entry_3 = ttk.Entry(frame_left, width=10)
entry_3.grid(row=3, column=2, pady=5)
entry_3.insert(0, "0")

label_4 = ttk.Label(frame_left, text="干重 [kg]:")
label_4.grid(row=4, column=0, columnspan=2, pady=5, sticky="w")

entry_4 = ttk.Entry(frame_left, width=10)
entry_4.grid(row=4, column=2, pady=5)
entry_4.insert(0, "0")

label_5 = ttk.Label(frame_left, text="初始温度 [°C]:")
label_5.grid(row=5, column=0, columnspan=2, pady=5, sticky="w")

entry_5 = ttk.Entry(frame_left, width=10)
entry_5.grid(row=5, column=2, pady=5)
entry_5.insert(0, "0")

# 自定义液体密度
label_6 = ttk.Label(frame_left, text="液体密度 [kg/m³]:")
label_6.grid(row=6, column=0, columnspan=2, pady=5, sticky="w")

entry_6 = ttk.Entry(frame_left, width=10)
entry_6.grid(row=6, column=2, pady=5)
entry_6.insert(0, "1000")

frame_button = ttk.Frame(root, padding="10")
frame_button.grid(row=1, column=2, sticky=(tk.W, tk.E, tk.N, tk.S))

# 绘图按钮
plot_button = ttk.Button(frame_button, text="绘制推力", command=plot_Ft)
plot_button.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")

plot_button = ttk.Button(frame_button, text="绘制压强", command=plot_pressure)
plot_button.grid(row=2, column=0, columnspan=2, pady=5, sticky="w")

plot_button = ttk.Button(frame_button, text="绘制体积", command=plot_volume)
plot_button.grid(row=3, column=0, columnspan=2, pady=5, sticky="w")

plot_button = ttk.Button(frame_button, text="绘制质量", command=plot_mass)
plot_button.grid(row=4, column=0, columnspan=2, pady=5, sticky="w")

plot_button = ttk.Button(frame_button, text="绘制温度", command=plot_temperature)
plot_button.grid(row=5, column=0, columnspan=2, pady=5,sticky="w")

# 错误标签
error_label = ttk.Label(frame_button, text="", foreground="red")
error_label.grid(row=6, column=0, columnspan=2, pady=5, sticky="w")

frame_text_widget = ttk.Frame(root, padding="10")
frame_text_widget.grid(row=1, column=3, sticky=(tk.W, tk.E, tk.N, tk.S))

text_widget = tk.Text(frame_text_widget, height=11, width=30)
text_widget.grid(row=0, column=0, sticky="w")

# 左侧绘图区域
frame_plot_left = ttk.Frame(root, padding="10")
frame_plot_left.grid(row=0, column=0, columnspan=4, sticky=(tk.W, tk.E, tk.N, tk.S))

# 创建 Matplotlib 图形和画布嵌入到 Tkinter 窗口
fig_left, ax_left = plt.subplots(figsize=(screen_width / 200, screen_height / 150))
canvas_left = FigureCanvasTkAgg(fig_left, master=frame_plot_left)
canvas_widget = canvas_left.get_tk_widget()
canvas_widget.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))


##########################################################################
# 右侧区域(优化部分)
##########################################################################

frame_simulate_r = ttk.Frame(root, padding="10")
frame_simulate_r.grid(row=1, column=4, sticky=(tk.W, tk.E, tk.N, tk.S))

simulate_button = ttk.Button(frame_simulate_r, text="优化", command=optimalize)
simulate_button.grid(row=0, column=0, columnspan=2, pady=5, sticky="w")

# 显示所选选项的标签
label_choose_opt = ttk.Label(frame_simulate_r, text="选择优化变量")
label_choose_opt.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")

# 创建 StringVar 来保存组合框的选定值
choosen_opt_variable = tk.StringVar()

# 创建组合框(下拉菜单)
options_variable_opt = ["压强", "喉径", "容积", "含水量", "干重", "气体温度"]
combobox_variable_opt = ttk.Combobox(frame_simulate_r, textvariable=choosen_opt_variable, values=options_variable_opt, state="readonly", width=10)
combobox_variable_opt.grid(row=2, column=0, columnspan=2, pady=5, sticky="w")

# 优化范围
label_opt_range = ttk.Label(frame_simulate_r, text="优化范围:")
label_opt_range.grid(row=3, column=0, columnspan=2, pady=5, sticky="w")

entry_opt_min = ttk.Entry(frame_simulate_r, width=7)
entry_opt_min.grid(row=4, column=0, sticky="w", pady=5)
entry_opt_min.insert(0, "最小值")

entry_opt_max = ttk.Entry(frame_simulate_r, width=7)
entry_opt_max.grid(row=5, column=0, sticky="w", pady=5)
entry_opt_max.insert(0, "最大值")

# 迭代次数
label_opt_itteration = ttk.Label(frame_simulate_r, text="迭代次数:")
label_opt_itteration.grid(row=6, column=0, columnspan=2, pady=5, sticky="w")

entry_opt_itteration = ttk.Entry(frame_simulate_r, width=7)
entry_opt_itteration.grid(row=6, column=1, sticky="w", pady=5)
entry_opt_itteration.insert(0, "1")

efficiency_button = ttk.Button(frame_simulate_r, text="效率", command=efficiency_opt_window)
efficiency_button.grid(row=0, column=1, columnspan=2, pady=5, sticky="w")

# 右侧第二个框架
frame_overall_data_right = ttk.Frame(root, padding="10")
frame_overall_data_right.grid(row=1, column=5, sticky=(tk.W, tk.E, tk.N, tk.S))

# 显示所选选项的标签
label_gas_r = ttk.Label(frame_overall_data_right, text="选择气体")
label_gas_r.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")

# 创建 StringVar 来保存组合框的选定值
choosen_gas_opt = tk.StringVar(value=default_option)

# 创建组合框(下拉菜单)
options_gas_opt = ["空气", "氩气", "氦气", "氢气", "氮气", "二氧化碳", "氙气", "自定义"]
combobox_gas_opt = ttk.Combobox(frame_overall_data_right, textvariable=choosen_gas_opt, values=options_gas_opt, state="readonly", width=10)
combobox_gas_opt.grid(row=2, column=0, columnspan=2, pady=5, sticky="w")

label_custom_gas_info_opt = ttk.Label(frame_overall_data_right, text="自定义气体数据:")
label_custom_gas_info_opt.grid(row=3, column=0, columnspan=2, pady=5, sticky="w")

label_combobox1_opt = ttk.Label(frame_overall_data_right, text="比热比:")
label_combobox1_opt.grid(row=4, column=0, columnspan=2, pady=5, sticky="w")

entry_combobox1_opt = ttk.Entry(frame_overall_data_right, width=10)
entry_combobox1_opt.grid(row=4, column=2, pady=5)
entry_combobox1_opt.insert(0, "0")

label_combobox2_opt = ttk.Label(frame_overall_data_right, text="气体常数 [J/(kg*K)]:")
label_combobox2_opt.grid(row=5, column=0, columnspan=2, pady=5, sticky="w")

entry_combobox2_opt = ttk.Entry(frame_overall_data_right, width=10)
entry_combobox2_opt.grid(row=5, column=2, pady=5)
entry_combobox2_opt.insert(0, "0")

# 导向杆数据
label_launch_lenght_opt = ttk.Label(frame_overall_data_right, text="导向杆长度 [mm]:")
label_launch_lenght_opt.grid(row=6, column=0, columnspan=2, pady=5, sticky="w")

entry_launch_lenght_opt = ttk.Entry(frame_overall_data_right, width=10)
entry_launch_lenght_opt.grid(row=6, column=2, pady=5)
entry_launch_lenght_opt.insert(0, "0")

label_launch_diameter_opt = ttk.Label(frame_overall_data_right, text="导向杆内径 [mm]:")
label_launch_diameter_opt.grid(row=7, column=0, columnspan=2, pady=5, sticky="w")

entry_launch_diameter_opt = ttk.Entry(frame_overall_data_right, width=10)
entry_launch_diameter_opt.grid(row=7, column=2, pady=5)
entry_launch_diameter_opt.insert(0, "0")

# 右侧输入框架
frame_right = ttk.Frame(root, padding="10")
frame_right.grid(row=1, column=6, sticky=(tk.W, tk.E, tk.N, tk.S))

label_0_r = ttk.Label(frame_right, text="初始压强 [bar]:")
label_0_r.grid(row=0, column=0, columnspan=2, pady=5, sticky="w")

entry_0_r = ttk.Entry(frame_right, width=10)
entry_0_r.grid(row=0, column=2, pady=5)
entry_0_r.insert(0, "0")

label_1_r = ttk.Label(frame_right, text="喉部直径 [mm]:")
label_1_r.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")

entry_1_r = ttk.Entry(frame_right, width=10)
entry_1_r.grid(row=1, column=2, pady=5)
entry_1_r.insert(0, "0")

label_2_r = ttk.Label(frame_right, text="总容积 [L]:")
label_2_r.grid(row=2, column=0, columnspan=2, pady=5, sticky="w")

entry_2_r = ttk.Entry(frame_right, width=10)
entry_2_r.grid(row=2, column=2, pady=5)
entry_2_r.insert(0, "0")

label_3_r = ttk.Label(frame_right, text="含水量 [%]:")
label_3_r.grid(row=3, column=0, columnspan=2, pady=5, sticky="w")

entry_3_r = ttk.Entry(frame_right, width=10)
entry_3_r.grid(row=3, column=2, pady=5)
entry_3_r.insert(0, "0")

label_4_r = ttk.Label(frame_right, text="干重 [kg]:")
label_4_r.grid(row=4, column=0, columnspan=2, pady=5, sticky="w")

entry_4_r = ttk.Entry(frame_right, width=10)
entry_4_r.grid(row=4, column=2, pady=5)
entry_4_r.insert(0, "0")

label_5_r = ttk.Label(frame_right, text="初始温度 [°C]:")
label_5_r.grid(row=5, column=0, columnspan=2, pady=5, sticky="w")

entry_5_r = ttk.Entry(frame_right, width=10)
entry_5_r.grid(row=5, column=2, pady=5)
entry_5_r.insert(0, "0")

# 自定义液体密度
label_6_r = ttk.Label(frame_right, text="液体密度 [kg/m³]:")
label_6_r.grid(row=6, column=0, columnspan=2, pady=5, sticky="w")

entry_6_r = ttk.Entry(frame_right, width=10)
entry_6_r.grid(row=6, column=2, pady=5)
entry_6_r.insert(0, "1000")

frame_button_r = ttk.Frame(root, padding="10")
frame_button_r.grid(row=1, column=7, sticky=(tk.W, tk.E, tk.N, tk.S))

# 绘图按钮
plot_button_r = ttk.Button(frame_button_r, text="绘制冲量", command=plot_opt_Ic)
plot_button_r.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")

plot_button_r = ttk.Button(frame_button_r, text="绘制时间", command=plot_opt_tc)
plot_button_r.grid(row=2, column=0, columnspan=2, pady=5, sticky="w")

plot_button_r = ttk.Button(frame_button_r, text="绘制速度增量", command=plot_opt_delta_v)
plot_button_r.grid(row=3, column=0, columnspan=2, pady=5, sticky="w")

plot_button_r = ttk.Button(frame_button_r, text="绘制比冲", command=plot_opt_Ist)
plot_button_r.grid(row=4, column=0, columnspan=2, pady=5, sticky="w")

# 错误标签
error_label_r = ttk.Label(frame_button_r, text="", foreground="red")
error_label_r.grid(row=5, column=0, columnspan=2, pady=5, sticky="w")

# 右侧绘图区域
frame_plot_right = ttk.Frame(root, padding="10")
frame_plot_right.grid(row=0, column=4, columnspan=5, sticky=(tk.W, tk.E, tk.N, tk.S))

# 创建 Matplotlib 图形和画布嵌入到 Tkinter 窗口
fig_right, ax_right = plt.subplots(figsize=(screen_width / 200, screen_height / 150))
canvas_right = FigureCanvasTkAgg(fig_right, master=frame_plot_right)
canvas_widget_right = canvas_right.get_tk_widget()
canvas_widget_right.grid(row=0, column=0, columnspan=4, sticky=(tk.W, tk.E, tk.N, tk.S))

################################################

# 运行 Tkinter 事件循环
root.mainloop()

下面替换原代码中的整个 simulate() 函数

# MAIN SIMULATION FUNCTION
def simulate():
    global engine_dry_mass
    global engine_full_mass
    
    # Output variables declaration
    Ic = float(0)
    total_time = float(0)
    delta_v = float(0)

    gas_name = choosen_gas.get()

    try:
        if(gas_name == "Air"):
            k_const = 1.4
            Rs = 287
        if(gas_name == "Argon"):
            k_const = 1.67
            Rs = 208
        if(gas_name == "Helium"):
            k_const = 1.66
            Rs = 2077
        if(gas_name == "Hydrogen"):
            k_const = 1.41
            Rs = 4124
        if(gas_name == "Nitrogen"):
            k_const = 1.4
            Rs = 297 
        if(gas_name == "CO2"):
            k_const = 1.3
            Rs = 188
        if(gas_name == "Xenon"):
            k_const = 1.65
            Rs = 208
        if(gas_name == "Custom"):
            k_const = float(entry_combobox1.get())
            Rs = float(entry_combobox2.get())

        water_density = float(entry_6.get())
    
        # Clear total_time value
        total_time = 0
        Ic = 0
        delta_v = 0

        # Clear all arrays
        array_time.clear()
        array_h.clear()
        array_Ft.clear()
        array_mass.clear()
        array_temperature.clear()
        array_pressure.clear()
        array_V_water.clear()
        array_V_air.clear()

        # Entry values read
        P_ins = float(entry_0.get())*100000
        At = 3.1415*pow(0.001*float(entry_1.get())/2, 2)
        V_total = float(entry_2.get())*0.001
        water_content = float(entry_3.get()) * 0.01
        V_air = V_total - water_content * V_total
        V_water = water_content * V_total
        dry_mass = float(entry_4.get())
        engine_dry_mass = dry_mass
        T = float(entry_5.get())+273.15
        rod_lenght = float(entry_launch_lenght.get()) * 0.001
        rod_inside_diameter = float(entry_launch_diameter.get()) * 0.001

        # Error safety net
        if(P_ins <= 0):
            raise ValueError
        if(k_const <= 0):
            raise ValueError
        if(Rs <= 0):
            raise ValueError
        if(water_density <= 0):
            raise ValueError
        if(At <= 0):
            raise ValueError
        if(V_total <= 0):
            raise ValueError
        if(water_content < 0 or water_content >= 1):
            raise ValueError
        if(dry_mass <= 0):
            raise ValueError
        if(T <= 0):
            raise ValueError
        if(rod_inside_diameter < 0):
            raise ValueError
        if(rod_lenght < 0):
            raise ValueError
        if(rod_inside_diameter >= 0.001 * float(entry_1.get())):
            raise ValueError
        
        s = float(0.0)
        vrod = float(0.0)

        # V_air fix for existing rod
        V_air = V_air - At * rod_lenght

        # Define adiabatic constant
        C_const = P_ins * pow(V_air, k_const)

        # Calculate mass of air
        mass_air = P_ins * V_air / (T * Rs)
        mass_propelant = mass_air + V_water * water_density

        Roc_mass = dry_mass + mass_propelant
        engine_full_mass = Roc_mass

        # ==================== 模拟阶段(保持原有逻辑)====================

        if(rod_inside_diameter > 0 and rod_lenght > 0):
            while(s < rod_lenght):
                array_time.append(total_time)
                total_time = total_time + delta_t
                
                array_V_water.append(V_water)
                array_V_air.append(V_air)
                array_mass.append(Roc_mass)
                array_pressure.append(P_ins)
                array_temperature.append(T)

                Ft = (P_ins * At) * efficiency_sim_rod
                array_Ft.append(Ft)
                # Ic += Ft * delta_t   # 已删除

                arod = Ft / Roc_mass
                vrod += delta_t * arod
                delta_v = vrod

                s += vrod * delta_t + 0.5 * arod * delta_t * delta_t

        if(rod_inside_diameter == 0 and rod_lenght > 0):
            while(s < rod_lenght):
                array_time.append(total_time)
                total_time = total_time + delta_t

                array_mass.append(Roc_mass)
                array_V_air.append(V_air)
                array_V_water.append(V_water)
                array_temperature.append(T)
                array_pressure.append(P_ins)

                Ft = (P_ins * At) * efficiency_sim_rod
                array_Ft.append(Ft)
                # Ic += Ft * delta_t   # 已删除

                arod = Ft / Roc_mass
                vrod = vrod + delta_t * arod
                delta_v = vrod

                s += vrod * delta_t + 0.5 * arod * delta_t * delta_t

                delta_V = At * vrod * delta_t
                V_air += delta_V
                P_ins = C_const / pow(V_air, k_const)
                T = (P_ins * V_air) / (mass_air * Rs)

        # Loop for water phase
        while(V_water > 0 and P_ins > P_atm):  
            array_time.append(total_time)
            total_time = total_time + delta_t 

            Ft = (2 * At * (P_ins - P_atm)) * efficiency_sim_water
            array_Ft.append(Ft)
            # Ic += Ft * delta_t   # 已删除

            ve = math.sqrt(2 * (P_ins - P_atm) / water_density)
            delta_V = At * ve * delta_t

            array_V_water.append(V_water)
            array_V_air.append(V_air)
            T = P_ins * V_air / (mass_air * Rs)
            array_temperature.append(T)

            array_mass.append(Roc_mass)
            Roc_mass = Roc_mass - delta_V * water_density

            delta_v = delta_v + delta_t * Ft / Roc_mass

            V_air = V_air + delta_V
            V_water = V_water - delta_V
            
            P_ins = C_const * pow(V_air, -k_const)
            array_pressure.append(P_ins)

        # Loop for Mach 1 on exit (choked flow)
        while(P_ins > P_atm and P_ins/P_atm >= pow((k_const+1)/2, k_const/(k_const-1))):
            array_time.append(total_time)
            total_time = total_time + delta_t 

            array_V_water.append(V_water)
            array_V_air.append(V_air)

            Tt = T * 2 / (k_const + 1)
            dot_m = P_ins * At * pow(2/(k_const+1), 0.5 * (k_const+1)/(k_const-1)) * math.sqrt(k_const/(Rs*T))
            ve_air = math.sqrt(k_const * Rs * Tt)
            P_throat = pow(2 / (k_const + 1), k_const / (k_const - 1)) * P_ins
            Ft = (dot_m * ve_air + At * (P_throat - P_atm)) * efficiency_sim_gas
            array_Ft.append(Ft)
            # Ic += Ft * delta_t   # 已删除

            delta_m = dot_m * delta_t
            Roc_mass = Roc_mass - delta_m
            array_mass.append(Roc_mass)

            delta_v = delta_v + delta_t * Ft / Roc_mass
            mass_air = mass_air - delta_m

            T = (P_ins / Rs) * pow(V_air / (mass_air + delta_m), k_const) * pow(V_air / mass_air, 1 - k_const)
            array_temperature.append(T)

            P_ins = mass_air * Rs * T / V_air
            array_pressure.append(P_ins)

        # Loop for sub Mach 1 on exit
        while(P_ins > P_atm):     
            array_time.append(total_time)
            total_time = total_time + delta_t 

            array_V_water.append(V_water)
            array_V_air.append(V_air)

            Mach = math.sqrt(2 / (k_const-1) * (pow(P_ins / P_atm, (k_const-1) / k_const) - 1))
            Tt = T / (1 + 0.5 * (k_const-1) * Mach * Mach)
            ve_air = Mach * math.sqrt(k_const * Rs * Tt)

            density_chamber = mass_air / V_air
            density_throat = density_chamber / pow(1 + 0.5 * (k_const - 1) * Mach * Mach, 1 / (k_const-1))
            dot_m = At * density_throat * ve_air

            Ft = (dot_m * ve_air) * efficiency_sim_gas
            array_Ft.append(Ft)
            # Ic += Ft * delta_t   # 已删除

            delta_m = dot_m * delta_t
            Roc_mass = Roc_mass - delta_m
            array_mass.append(Roc_mass)

            delta_v = delta_v + delta_t * Ft / Roc_mass
            mass_air = mass_air - delta_m

            T = (P_ins / Rs) * pow(V_air / (mass_air + delta_m), k_const) * pow(V_air / mass_air, 1 - k_const)
            array_temperature.append(T)

            P_ins = mass_air * Rs * T / V_air
            array_pressure.append(P_ins)

        # ====================== 新增:梯形积分计算总冲 ======================
        Ic = calculate_impulse_trapezoidal()

        # Erase error message
        error_label.config(text="")

        # Print output values
        text_widget_output = "Ic = {:.2f} Ns\n".format(Ic) + \
                             "tc = {:.2f} s\n".format(total_time) + \
                             "Ist = {:.2f} s\n".format(Ic / (mass_propelant * 9.81)) + \
                             "delta_v = {:.2f} m/s\n\n".format(delta_v)
        
        text_widget.insert("1.0", text_widget_output)
        
        plot_Ft()

    except ValueError:
        error_label.config(text="Invalid input.")

def calculate_impulse_trapezoidal():
    if len(array_time) < 2 or len(array_Ft) < 2:
        return 0.0
    
    impulse = 0.0
    for i in range(1, len(array_time)):
        dt = array_time[i] - array_time[i-1]
        if dt > 0:
            impulse += 0.5 * (array_Ft[i-1] + array_Ft[i]) * dt
    return impulse

我有时间的话pyinstaller打包一下。

您还谈到了我的代码问题,我现在python只会写到循环,并且我想学一下TK库的使用。其他的话就是C语言的 STM32F103了,现在套装还没学完,我了解过Hal库,但是好像网上说建议先学标准库。我的角度计算很别扭是因为只学过个I2C通信计算MPU6050角速度…您说的F4系列好像401和407出名,如果有时间我也很想学。我先在其实很焦虑,代码写几行就不会了,没思路;买了十余本火箭的书,但是我的数理水平无法理解。您有什么建议吗?但是我发现 python和C的语言逻辑真的很像,或许在未来的某一天一通百通吧。


最后我想请您一个问题(可能描述有所磕绊):我想用现在的 STM32F103C8T6进行远距离通信,比如用 PWM的控制一个 SG90,您推荐用什么模块(就是我现在的环境,8266吗?),可以用笔计本电脑软件控制吗,我在这方面不太懂,希望您可以指点我一下,感谢大佬1f64f1f64f




评论
文号 / 950286

十步芳草
名片专栏发私信
学术分 0
总主题 4 帖总回复 75 楼拥有证书:进士 机友 笔友
注册于 2025/08/17 09:17:59最后登录 2026/10/31 09:17:56
主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:山西

个人简介

笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的