Example #1
0
def traj_3DOF_dt(t, y, params):
	# Function to be called by ODE solver when time integration of governing
	# equations is required
	V = y[0]
	gamma = y[1]
	h = y[2]
	r = y[3]

	R = params[0]
	g = params[1]
	beta = params[2]
	rho = params[3]
	C_L = params[4]
	C_D = params[5]

	L_D_ratio = C_L / C_D
	p_dyn = fcl.p_dyn(rho=rho, V=V)
	dy = np.zeros(4)

	# dvdt
	dy[0] = dv_dt(g, p_dyn, beta, gamma)
	# dgdt
	dy[1] = dgamma_dt(gamma, g, V, R, h, L_D_ratio, p_dyn, beta)
	# dhdt
	dy[2] = dh_dt(gamma, V)
	# drdt
	dy[3] = dr_dt(R, gamma, h, V)

	return dy
Example #2
0
	def initialise(self):
		self.h[0] = self.h_init
		self.V[0] = self.V_init
		self.gamma[0] = self.gamma_init
		self.g[0] = grav_sphere(self.g_0, self.R, self.h_init)

		self.solver_rho[0], self.solver_a[0], self.solver_p[0], \
			self.solver_T[0], self.solver_mu[0] = \
			interpolate_atmosphere(self, self.h_init)

		self.p_dyn[0] = fcl.p_dyn(rho=self.solver_rho[0], V=self.V[0])
		self.Ma[0] = self.V[0] / self.solver_a[0]
		self.mfp[0] = fcl.mean_free_path(self.solver_T[0], self.solver_p[0],
			self.atmosphere.d)
		self.Kn[0] = self.mfp[0] / self.spacecraft.L
		self.Re[0] = fcl.Reynolds(self.solver_rho[0], self.V[0], 
			self.spacecraft.L, self.solver_mu[0])

		if self.console_output == True:
			print 'MODEL INITIALISED.  INITIAL STEP COUNT: %i' % self.steps_storage

		return None
Example #3
0
	def simulate_dopri(self, dt=1E-2):
		"""
		Run trajectory calculations using explicit Runge-Kutta method of order
		4(5) from Dormand & Prince
		"""
		# Set timestep for ODE solver
		self.dt = dt
		self.time_steps = np.cumsum(self.dt * np.ones(self.steps_storage))

		# Create ODE object from SciPy using Dormand-Prince RK solver
		self.eq = integrate.ode(traj_3DOF_dt).set_integrator('dop853', nsteps=1E8,
			rtol=1E-10)

		# Set initial conditions
		y_init = [self.V_init, self.gamma_init, self.h_init, self.r[0]]
		self.eq.set_initial_value(y_init, t=self.time_steps[0])

		# Generate counter
		index = 1
		self.index = index

		# Initial conditions are: V, gamma, h, r.  These are at index = 0
		# Other parameters (like dynamic pressure and gravitational
		# attraction) are calculated for this step (also index = 0)
		# ODE solver then calculates V, gamma, h, and r at the next step (index = 1)
		# Then parameters and updated as above, and the loop continues.
		# So:
		# INIT:  Define V, gamma, h, r @ start
		#	 	Calculate parameters @ start
		# SOLVE: Find V, gamma, h, r
		#

		# Solve ODE system using conditional statement based on altitude
		while self.h[index-1] > 0:	

			# Update ODE solver parameters from spacecraft object and 
			# atmospheric model at each separate time step		
			if self.spacecraft.aero_coeffs_type == 'CONSTANT':
				params = [self.R, self.g[index-1], self.spacecraft.ballistic_coeff,
					self.solver_rho[index-1], self.spacecraft.Cl, self.spacecraft.Cd]
				self.eq.set_f_params(params)
				
			elif self.spacecraft.aero_coeffs_type == 'VARIABLE':
				self.spacecraft.update_aero(self.index, self.Re[index-1], 
					self.Ma[index-1], self.Kn[index-1])
				params = [self.R, self.g[index-1], self.spacecraft.ballistic_coeff[index-1],
					self.solver_rho[index-1], self.spacecraft.Cl[index-1], 
					self.spacecraft.Cd[index-1]]
				self.eq.set_f_params(params)

			# Solve ODE system (sol[V, gamma, h, r])
			self.sol[index, :] = self.eq.integrate(self.time_steps[index])

			# Unpack ODE solver results into storage structures
			self.V[index] = self.sol[index, 0]
			self.gamma[index] = self.sol[index, 1]
			self.h[index] = self.sol[index, 2]
			self.r[index] = self.sol[index, 3]

			# Interpolate for freestream density in atmosphere model
			# (this avoids a direct call to an atmosphere model, allowing more
			# flexibility when coding as different models have different interfaces)
			self.solver_rho[index], self.solver_a[index], \
				self.solver_p[index], self.solver_T[index], \
				self.solver_mu[index] = interpolate_atmosphere(self, self.h[index])

			# Calculate gravitational acceleration at current altitude
			self.g[index] = grav_sphere(self.g_0, self.R, self.h[index])

			# Calculate dynamic pressure iteration results
			self.p_dyn[index] = fcl.p_dyn(rho=params[3], V=self.sol[index, 0])

			# Calculate Mach, Knudsen, and Reynolds numbers
			self.Ma[index] = self.V[index] / self.solver_a[index]
			self.mfp[index] = fcl.mean_free_path(self.solver_T[index], 
				self.solver_p[index], self.atmosphere.d)
			self.Kn[index] = self.mfp[index] / self.spacecraft.L
			self.Re[index] = fcl.Reynolds(self.solver_rho[index], 
				self.V[index], self.spacecraft.L, self.solver_mu[index])

			# Save inputs for inspection
			self.solver_time[index] = self.eq.t
			self.y_input[index, :] = self.eq.y

			# Advance iteration counter
			index += 1
			self.index = index

			# Check if solution storage array has reached maximum size
			if index == len(self.sol)-10:
				self.extend()

			#print index
			# Print solution progress to check for stability
			if self.console_output == True:
				if np.mod(index, self.steps_storage/100) == 0:
					print 'ITERATION: %i; ALTITUDE: %f km' % (index, self.h[index-1]/1000)

		# Subtract 1 from counter so that indexing is more convenient later on
		self.index -= 1

		# Truncate solution arrays to remove trailing zeros
		self.truncate()

		# Perform final step calculations for p_dyn, g, etc.
		self.final_step_event()
		#self.final_step_assign()

		# Perform post solver calculations
		#self.post_calc()

		print 'TRAJECTORY COMPUTED (RK 4/5)'
		print '%i ITERATIONS, TIMESTEP = %f s, TOTAL TIME = %f s' % \
			(self.index, self.dt, self.solver_time[self.index-1])

		return [self.sol, self.h, self.y_input, self.p_dyn, self.Ma]