Advertisement
Geometrian

Multi-Stage Rocket Delta-V Optimizer

Sep 30th, 2016
1,213
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.03 KB | None | 0 0
  1. #Delta-v Partition Optimizer
  2. #   Ian Mallett, 2016
  3. #   ian [^at^] geometrian.com
  4.  
  5. #This program takes your rocket configuration and calculates
  6. #   optimal partitioning of your rocket's delta-v into its
  7. #   stages.
  8.  
  9. #=========== BEGIN USER-CONFIGURABLE SECTION ===========
  10.  
  11. #Choose the minimum practical number of stages; more stages
  12. #   improves overall mass ratio but adds more dead weight
  13. #   and expense for a given delta-v.
  14.  
  15. #Stage parameters are:
  16. #   Specific impulse (s)
  17. #   Inert mass fraction (roughly ~0.08 to ~0.7)
  18.  
  19. stages = [ #Saturn V
  20.     (263.0,130000.0/2290000.0),
  21.     (421.0,40100.0/496200.0),
  22.     (421.0,13500.0/123000.0)
  23. ]
  24. M_0 = 48600.0 #Payload mass, kg
  25. d_vm = 17911.9 #Total delta-v for mission
  26.  
  27. ##stages = [ #example I made up (toy rocket)
  28. ##    (3000.0/9.80665,0.08)
  29. ##]
  30. ##M_0 = 1.0 #Payload mass, kg
  31. ##d_vm = 4000.0 #Total delta-v for mission
  32.  
  33. ##stages = [ #example I made up
  34. ##    (390.0, 0.08),
  35. ##    (400.0, 0.10),
  36. ##    (800.0, 0.30)
  37. ##]
  38. ##M_0 = 1000.0 #Payload mass, kg
  39. ##d_vm = 21000.0 #Total delta-v for mission
  40.  
  41. tol = 1e-9 #Solver tolerance for each fraction
  42.  
  43. #=========== END USER-CONFIGURABLE SECTION ===========
  44.  
  45.  
  46. from math import *
  47. import random
  48. import sys
  49.  
  50.  
  51. #Basic setup
  52. in_idle = "idlelib.run" in sys.modules
  53. g_0 = -9.80665 #Standard gravity
  54. n = len(stages)
  55. stages = stages[::-1]
  56.  
  57. #Validate input
  58. if n == 0:
  59.     raise ValueError("Must have at least one stage in rocket!")
  60. for I, f in stages:
  61.     if I<=0: raise ValueError("Specific impulse must be positive for every stage!")
  62.     if f<=0.0 or f>=1.0: raise ValueError("Inert mass fraction must lie in the open interval (0.0,1.0) for every stage!")
  63. if M_0 <= 0.0:
  64.     raise ValueError("Payload must have a positive mass!")
  65. if d_vm <= 0.0:
  66.     raise ValueError("Mission total delta-v must be positive!")
  67.  
  68. #Helper functions
  69. def get_total_mass(f_dvs):
  70.     total_mass = M_0
  71.     for i in range(n):
  72.         I = stages[i][0] #stage specific impulse
  73.         f = stages[i][1] #stage inert mass fraction
  74.         V_e = -g_0 * I #stage exhaust speed
  75.         assert f_dvs[i] > 0.0
  76.         d_vs = d_vm * f_dvs[i] #stage target delta v
  77.         R = e ** (d_vs / V_e) #stage mass ratio
  78.         if R*f >= 1.0: return False #Stage cannot be built!
  79.         M_i = total_mass * (R-1.0) / (1.0 - f*R)
  80.         assert M_i > 0.0
  81.         total_mass += M_i
  82.     return total_mass
  83. def get_normalized_fractions(f_dvs):
  84.     s = sum([f_dv for f_dv in f_dvs])
  85.     f_dvs = [f_dv/s for f_dv in f_dvs]
  86.     return f_dvs
  87.  
  88. print("Beginning solve for %d-stage rocket . . ."%n)
  89. #Find a feasible configuration
  90. print("Attempting to find a valid initial configuration . . .")
  91. iterations = 0
  92. best = False
  93. while best == False:
  94.     f_dvs = get_normalized_fractions( [random.random() for i in range(n)] )
  95.     best = get_total_mass(f_dvs)
  96.     iterations += 1
  97.     if iterations == 100000:
  98.         raise ValueError("Unable to find a valid initial configuration!  Rocket may be impossible or nearly impossible!")
  99.  
  100. #Optimize it
  101. print("Optimizing configuration . . .")
  102. failed = 0
  103. radius = 1.0
  104. while radius > tol:
  105.     f_dvs1 = list(f_dvs)
  106.  
  107.     rand_ind = random.randint(0,n-1)
  108.     while True:
  109.         rand_frac = f_dvs1[rand_ind] + radius*((random.random()**0.1)-0.5)
  110.         if rand_frac>0.0: break
  111.     f_dvs1[rand_ind] = rand_frac
  112.  
  113.     f_dvs1 = get_normalized_fractions(f_dvs1)
  114.  
  115.     proposed = get_total_mass(f_dvs1)
  116.     if proposed != False and proposed<best:
  117.         f_dvs = list(f_dvs1)
  118.         best = proposed
  119.         if failed < 10:
  120.             radius /= 0.8
  121.         failed = 0
  122.  
  123.         s = "Mass: %f, Fractions:"%best
  124.         for i in range(n): s+=" %f"%f_dvs[n-i-1]
  125.         if in_idle: print(s)
  126.         else: sys.stdout.write("\r"+s)
  127.     else:
  128.         failed += 1
  129.  
  130.     if failed > 10:
  131.         radius *= 0.5
  132.         failed = 0
  133. if not in_idle: sys.stdout.write("\r"+" "*80)
  134.  
  135. #Print results
  136. print("Completed optimization!")
  137. art = ["+---"+n*"-"+"-->"]
  138. stats = [""]
  139. total_mass_dry = 0.0
  140. total_mass_wet = 0.0
  141. for i in range(-1,n,1):
  142.     sp = (n - i)*" "
  143.     if i == -1:
  144.         art.append("|")
  145.         art.append("|   "+sp+"  ^")
  146.         art.append("|   "+sp+" / \\")
  147.         art.append("|   "+sp+"/   \\")
  148.         art.append("|   "+sp+"*---*")
  149.         stats.append("Stage %d (payload):"%(n+1))
  150.         stats.append("  Mass:                  %f kg"%M_0)
  151.         stats.append("")
  152.         stats.append("")
  153.         stats.append("")
  154.         total_mass_dry += M_0
  155.         total_mass_wet += M_0
  156.     else:
  157.         art.append("+----"+n*"-"+"->")
  158.         art.append("|    "+sp+"_____"+"__"*i)
  159.         art.append("|   "+sp+"/     "+"  "*i+"\\")
  160.         art.append("|   "+sp+"|     "+"  "*i+"|")
  161.         art.append("|   "+sp+"|     "+"  "*i+"|")
  162.         art.append("|   "+sp+"|     "+"  "*i+"|")
  163.         art.append("|   "+sp+"|     "+"  "*i+"|")
  164.         art.append("|   "+sp+"|     "+"  "*i+"|")
  165.         art.append("|   "+sp+"|     "+"  "*i+"|")
  166.         art.append("|   "+sp+"|     "+"  "*i+"|")
  167.         if i < n-1:
  168.             art.append("|   "+sp+"|     "+"  "*i+"|")
  169.             art.append("|   "+sp+"^^^^^^"+"^^"*i+"^")
  170.         else:
  171.             art.append("| "+sp+" /|     "+"  "*i+"|\\")
  172.             art.append("| "+sp+"<_^^^^^^"+"^^"*i+"^_>")
  173.         I = stages[i][0]
  174.         f = stages[i][1]
  175.         V_e = -g_0 * I
  176.         d_vs = d_vm * f_dvs[i]
  177.         R = e ** (d_vs / V_e)
  178.         M_i = total_mass_wet * (R-1.0) / (1.0 - f*R)
  179.         stats.append("")
  180.         stats.append("Stage %d:"%(n-i))
  181.         stats.append("  Mass (dry):            %f kg"%(M_i*f))
  182.         stats.append("  Mass (wet):            %f kg"%M_i)
  183.         stats.append("  Mass (propellant):     %f kg"%(M_i*(1.0-f)))
  184.         stats.append("  Specific impulse:      %f s"%I)
  185.         stats.append("  Exhaust velocity:      %f m/s"%V_e)
  186.         stats.append("  Delta-v:               %f m/s"%d_vs)
  187.         stats.append("  Delta-v fraction:      %f"%f_dvs[i])
  188.         stats.append("  Propellant fraction:   %f"%(1.0-f))
  189.         stats.append("  Inert fraction:        %f"%f)
  190.         stats.append("  Mass ratio:            %f"%R)
  191.         total_mass_wet += M_i
  192.         total_mass_dry += M_i * f
  193.  
  194. art.append(art[0])
  195. stats.append("")
  196.  
  197. total_mass_prop = total_mass_wet - total_mass_dry
  198. for i in range(8): art.append("")
  199. stats.append("Total:")
  200. stats.append("  Mass (dry):            %f kg"%total_mass_dry)
  201. stats.append("  Mass (wet):            %f kg"%total_mass_wet)
  202. stats.append("  Mass (propellant):     %f kg"%(total_mass_wet-total_mass_dry))
  203. stats.append("  Delta-v:               %f m/s"%d_vm)
  204. stats.append("  Propellant fraction:   %f"%(total_mass_prop/total_mass_wet))
  205. stats.append("  Inert fraction:        %f"%(total_mass_dry/total_mass_wet))
  206. stats.append("  Mass ratio:            %f"%(M_0/total_mass_wet))
  207.  
  208. assert len(art) == len(stats)
  209. max_len = 0
  210. for stat in stats:
  211.     max_len = max([max_len,len(stat)])
  212. lines = []
  213. for i in range(len(art)):
  214.     s = stats[i]
  215.     if len(art[i])!=0: s += " "*(max_len-len(stats[i])) + "   " + art[i]
  216.     print(s)
  217.  
  218. if not in_idle: input("ENTER to exit.")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement