def get_trajectory(parameterization: type = asb.DynamicsPointMass2DCartesian, gravity=True, drag=True, plot=False): if parameterization is asb.DynamicsPointMass2DCartesian: dyn = parameterization( mass_props=asb.MassProperties(mass=1), x_e=0, z_e=0, u_e=u_e_0, w_e=w_e_0, ) elif parameterization is asb.DynamicsPointMass2DSpeedGamma: dyn = parameterization( mass_props=asb.MassProperties(mass=1), x_e=0, z_e=0, speed=speed_0, gamma=gamma_0, ) else: raise ValueError("Bad value of `parameterization`!") def derivatives(t, y): this_dyn = dyn.get_new_instance_with_state(y) if gravity: this_dyn.add_gravity_force() q = 0.5 * 1.225 * this_dyn.speed**2 if drag: this_dyn.add_force(Fx=-1 * (0.1)**2 * q, axes="wind") return this_dyn.unpack_state(this_dyn.state_derivatives()) res = integrate.solve_ivp( fun=derivatives, t_span=(time[0], time[-1]), t_eval=time, y0=dyn.unpack_state(), method="LSODA", # vectorized=True, rtol=1e-9, atol=1e-9, ) dyn = dyn.get_new_instance_with_state(res.y) if plot: import matplotlib.pyplot as plt import aerosandbox.tools.pretty_plots as p fig, ax = plt.subplots() p.plot_color_by_value(dyn.x_e, dyn.altitude, c=dyn.speed, colorbar=True) p.equal() p.show_plot("Trajectory", "$x_e$", "$z_e$") return dyn
def test_block_move_fixed_time(): opti = asb.Opti() n_timesteps = 300 time = np.linspace(0, 1, n_timesteps) dyn = asb.DynamicsPointMass1DHorizontal( mass_props=asb.MassProperties(mass=1), x_e=opti.variable(init_guess=np.linspace(0, 1, n_timesteps)), u_e=opti.variable(init_guess=1, n_vars=n_timesteps), ) u = opti.variable(init_guess=np.linspace(1, -1, n_timesteps)) dyn.add_force( Fx=u ) dyn.constrain_derivatives( opti=opti, time=time ) opti.subject_to([ dyn.x_e[0] == 0, dyn.x_e[-1] == 1, dyn.u_e[0] == 0, dyn.u_e[-1] == 0, ]) # effort = np.sum( # np.trapz(dyn.X ** 2) * np.diff(time) # ) effort = np.sum( # More sophisticated integral-of-squares integration (closed form correct) np.diff(time) / 3 * (u[:-1] ** 2 + u[:-1] * u[1:] + u[1:] ** 2) ) opti.minimize(effort) sol = opti.solve() dyn.substitute_solution(sol) assert dyn.x_e[0] == pytest.approx(0) assert dyn.x_e[-1] == pytest.approx(1) assert dyn.u_e[0] == pytest.approx(0) assert dyn.u_e[-1] == pytest.approx(0) assert np.max(dyn.u_e) == pytest.approx(1.5, abs=0.01) assert sol.value(u)[0] == pytest.approx(6, abs=0.05) assert sol.value(u)[-1] == pytest.approx(-6, abs=0.05)
def test_block_move_minimum_time(): opti = asb.Opti() n_timesteps = 300 time = np.linspace( 0, opti.variable(init_guess=1, lower_bound=0), n_timesteps, ) dyn = asb.DynamicsPointMass1DHorizontal( mass_props=asb.MassProperties(mass=1), x_e=opti.variable(init_guess=np.linspace(0, 1, n_timesteps)), u_e=opti.variable(init_guess=1, n_vars=n_timesteps), ) u = opti.variable(init_guess=np.linspace(1, -1, n_timesteps), lower_bound=-1, upper_bound=1) dyn.add_force( Fx=u ) dyn.constrain_derivatives( opti=opti, time=time ) opti.subject_to([ dyn.x_e[0] == 0, dyn.x_e[-1] == 1, dyn.u_e[0] == 0, dyn.u_e[-1] == 0, ]) opti.minimize( time[-1] ) sol = opti.solve() dyn.substitute_solution(sol) assert dyn.x_e[0] == pytest.approx(0) assert dyn.x_e[-1] == pytest.approx(1) assert dyn.u_e[0] == pytest.approx(0) assert dyn.u_e[-1] == pytest.approx(0) assert np.max(dyn.u_e) == pytest.approx(1, abs=0.01) assert sol.value(u)[0] == pytest.approx(1, abs=0.05) assert sol.value(u)[-1] == pytest.approx(-1, abs=0.05) assert np.mean(np.abs(sol.value(u))) == pytest.approx(1, abs=0.01)
def get_trajectory( parameterization: type = asb.DynamicsPointMass3DCartesian, gravity=True, drag=True, sideforce=True, plot=False, verbose=False, ): opti = asb.Opti() if parameterization is asb.DynamicsPointMass3DCartesian: dyn = parameterization( mass_props=asb.MassProperties(mass=1), x_e=opti.variable(np.linspace(0, 300, N)), y_e=opti.variable(np.linspace(0, 0, N), scale=1), z_e=opti.variable(np.linspace(0, 0, N), scale=100), u_e=opti.variable(np.linspace(100, 50, N)), v_e=opti.variable(np.linspace(0, 0, N), scale=1), w_e=opti.variable(np.linspace(-100, 50, N)), ) elif parameterization is asb.DynamicsPointMass3DSpeedGammaTrack: dyn = parameterization( mass_props=asb.MassProperties(mass=1), x_e=opti.variable(np.linspace(0, 300, N)), y_e=opti.variable(np.linspace(0, 0, N), scale=1), z_e=opti.variable(np.linspace(0, 0, N), scale=100), speed=opti.variable(np.linspace(100, 50, N)), gamma=opti.variable(np.linspace(0, 0, N)), track=opti.variable(np.linspace(0, 0, N)), ) else: raise ValueError("Bad value of `parameterization`!") if gravity: dyn.add_gravity_force() q = 0.5 * 1.225 * dyn.speed**2 if drag: dyn.add_force(Fx=-1 * (0.1)**2 * q, axes="wind") if sideforce: strouhal = 0.2 frequency = 5 # strouhal * dyn.speed / 0.1 dyn.add_force(Fy=q * 1 * (0.1)**2 * np.sin(2 * np.pi * frequency * time), axes="wind") dyn.constrain_derivatives( opti=opti, time=time, ) opti.subject_to([ dyn.x_e[0] == 0, dyn.y_e[0] == 0, dyn.z_e[0] == 0, dyn.u_e[0] == 100, dyn.v_e[0] == 0, dyn.w_e[0] == -100, ]) sol = opti.solve(verbose=verbose) dyn.substitute_solution(sol) if plot: import matplotlib.pyplot as plt import aerosandbox.tools.pretty_plots as p fig, ax = plt.subplots() p.plot_color_by_value(dyn.x_e, dyn.altitude, c=dyn.speed, colorbar=True) p.equal() p.show_plot("Trajectory", "$x_e$", "$z_e$") return dyn
def test_rocket(): ### Environment opti = asb.Opti() ### Time discretization N = 500 # Number of discretization points time_final = 100 # seconds time = np.linspace(0, time_final, N) ### Constants mass_initial = 500e3 # Initial mass, 500 metric tons z_e_final = -100e3 # Final altitude, 100 km g = 9.81 # Gravity, m/s^2 alpha = 1 / ( 300 * g ) # kg/(N*s), Inverse of specific impulse, basically - don't worry about this dyn = asb.DynamicsPointMass1DVertical( mass_props=asb.MassProperties( mass=opti.variable(init_guess=mass_initial, n_vars=N)), z_e=opti.variable( init_guess=np.linspace(0, z_e_final, N) ), # Altitude (negative due to Earth-axes convention) w_e=opti.variable(init_guess=-z_e_final / time_final, n_vars=N), # Velocity ) dyn.add_gravity_force(g=g) thrust = opti.variable(init_guess=g * mass_initial, n_vars=N) dyn.add_force(Fz=-thrust) dyn.constrain_derivatives( opti=opti, time=time, ) ### Fuel burn opti.constrain_derivative( derivative=-alpha * thrust, variable=dyn.mass_props.mass, with_respect_to=time, method="midpoint", ) ### Boundary conditions opti.subject_to([ dyn.z_e[0] == 0, dyn.w_e[0] == 0, dyn.mass_props.mass[0] == mass_initial, dyn.z_e[-1] == z_e_final, ]) ### Path constraints opti.subject_to([dyn.mass_props.mass >= 0, thrust >= 0]) ### Objective opti.minimize(-dyn.mass_props.mass[-1] ) # Maximize the final mass == minimize fuel expenditure ### Solve sol = opti.solve(verbose=False) print(f"Solved in {sol.stats()['iter_count']} iterations.") dyn.substitute_solution(sol) assert dyn.mass_props.mass[-1] == pytest.approx(290049.81034472014, rel=0.05) assert np.abs(dyn.w_e).max() == pytest.approx(1448, rel=0.05)