def get_hodographic_trajectory(shaping_object: tudatpy.kernel.simulation. shape_based_thrust.HodographicShaping, trajectory_parameters: list, specific_impulse: float, output_path: str = None): """ It computes the analytical hodographic trajectory and saves the results to a file, if desired. This function analytically calculates the hodographic trajectory from the Hodographic Shaping object. It retrieves both the trajectory and the acceleration profile; if desired, both are saved to files as follows: * hodographic_trajectory.dat: Cartesian states of semi-analytical trajectory; * hodographic_thrust_acceleration.dat: Thrust acceleration in inertial, Cartesian, coordinates, along the semi-analytical trajectory. NOTE: The independent variable (first column) does not represent the usual time (seconds since J2000), but instead denotes the time since departure. Parameters ---------- shaping_object: tudatpy.kernel.simulation.shape_based_thrust.HodographicShaping Hodographic shaping object. trajectory_parameters : list of floats List of trajectory parameters to be optimized. specific_impulse : float Constant specific impulse of the spacecraft. output_path : str (default: None) If and where to save the benchmark results (if None, results are NOT written). Returns ------- none """ # Set time parameters start_time = 0.0 final_time = get_trajectory_time_of_flight(trajectory_parameters) # Set number of data points number_of_data_points = 10000 # Compute step size step_size = (final_time - start_time) / (number_of_data_points - 1) # Create epochs vector epochs = np.linspace(start_time, final_time, number_of_data_points) # Create specific impulse lambda function specific_impulse_function = lambda t: specific_impulse # Retrieve thrust acceleration profile from shaping object # NOTE TO THE STUDENTS: do not uncomment # thrust_acceleration_profile = shaping_object.get_thrust_acceleration_profile( # epochs, # specific_impulse_function) # Retrieve trajectory from shaping object trajectory_shape = shaping_object.get_trajectory(epochs) # If desired, save results to files if output_path is not None: # NOTE TO THE STUDENTS: do not uncomment # save2txt(thrust_acceleration_profile, # 'hodographic_thrust_acceleration.dat', # output_path) save2txt(trajectory_shape, 'hodographic_trajectory.dat', output_path)
def compare_benchmarks(first_benchmark: dict, second_benchmark: dict, output_path: str, filename: str) -> dict: """ It compares the results of two benchmark runs. It uses an 8th-order Lagrange interpolator to compare the state (or the dependent variable, depending on what is given as input) history. The difference is returned in form of a dictionary and, if desired, written to a file named filename and placed in the directory output_path. Parameters ---------- first_benchmark : dict State (or dependent variable history) from the first benchmark. second_benchmark : dict State (or dependent variable history) from the second benchmark. output_path : str If and where to save the benchmark results (if None, results are NOT written). filename : str Name of the output file. Returns ------- benchmark_difference : dict Interpolated difference between the two benchmarks' state (or dependent variable) history. """ # Create 8th-order Lagrange interpolator for first benchmark benchmark_interpolator = interpolators.create_one_dimensional_interpolator(first_benchmark, interpolators.lagrange_interpolation(8)) # Calculate the difference between the benchmarks print('Calculating benchmark differences...') # Initialize difference dictionaries benchmark_difference = dict() # Calculate the difference between the states and dependent variables in an iterative manner for second_epoch in second_benchmark.keys(): benchmark_difference[second_epoch] = benchmark_interpolator.interpolate(second_epoch) - \ second_benchmark[second_epoch] # Write results to files if output_path is not None: save2txt(benchmark_difference, filename, output_path) # Return the interpolator return benchmark_difference
def generate_benchmarks( benchmark_step_size, simulation_start_epoch: float, specific_impulse: float, bodies: tudatpy.kernel.simulation.environment_setup.SystemOfBodies, benchmark_propagator_settings: tudatpy.kernel.simulation. propagation_setup.propagator.MultiTypePropagatorSettings, thrust_parameters: list, are_dependent_variables_present: bool, output_path: str = None): """ Function to generate to accurate benchmarks. This function runs two propagations with two different integrator settings that serve as benchmarks for the nominal runs. The state and dependent variable history for both benchmarks are returned and, if desired, they are also written to files (to the directory ./SimulationOutput/benchmarks/) in the following way: * benchmark_1_states.dat, benchmark_2_states.dat The numerically propagated states from the two benchmarks. * benchmark_1_dependent_variables.dat, benchmark_2_dependent_variables.dat The dependent variables from the two benchmarks. Parameters ---------- simulation_start_epoch : float The start time of the simulation in seconds. bodies : tudatpy.kernel.simulation.environment_setup.SystemOfBodies, System of bodies present in the simulation. benchmark_propagator_settings Propagator settings object which is used to run the benchmark propagations. thrust_parameters List that represents the thrust parameters for the spacecraft. are_dependent_variables_present : bool If there are dependent variables to save. output_path : str (default: None) If and where to save the benchmark results (if None, results are NOT written). Returns ------- return_list : list List of state and dependent variable history in this order: state_1, state_2, dependent_1_ dependent_2. """ ### CREATION OF THE TWO BENCHMARKS ### # Define benchmarks' step sizes first_benchmark_step_size = 2.0 # s second_benchmark_step_size = 2.0 * first_benchmark_step_size # Create integrator settings for the first benchmark, using a fixed step size RKDP8(7) integrator # (the minimum and maximum step sizes are set equal, while both tolerances are set to inf) benchmark_integrator = propagation_setup.integrator benchmark_integrator_settings = benchmark_integrator.runge_kutta_variable_step_size( simulation_start_epoch, first_benchmark_step_size, propagation_setup.integrator.RKCoefficientSets.rkdp_87, first_benchmark_step_size, first_benchmark_step_size, np.inf, np.inf) # Create Shape Optimization Problem object for first benchmark first_benchmark = ShapeOptimizationProblem(bodies, benchmark_integrator_settings, benchmark_propagator_settings, specific_impulse) print('Running first benchmark...') # Set new thrust parameters and evaluate fitness first_benchmark.fitness(thrust_parameters) # Create integrator settings for the second benchmark in the same way benchmark_integrator = propagation_setup.integrator benchmark_integrator_settings = benchmark_integrator.runge_kutta_variable_step_size( simulation_start_epoch, second_benchmark_step_size, propagation_setup.integrator.RKCoefficientSets.rkdp_87, second_benchmark_step_size, second_benchmark_step_size, np.inf, np.inf) # Create Shape Optimization Problem object for second benchmark second_benchmark = ShapeOptimizationProblem(bodies, benchmark_integrator_settings, benchmark_propagator_settings, specific_impulse) print('Running second benchmark...') # Set new thrust parameters and evaluate fitness second_benchmark.fitness(thrust_parameters) ### WRITE BENCHMARK RESULTS TO FILE ### # Retrieve state history first_benchmark_states = first_benchmark.get_last_run_propagated_state_history( ) second_benchmark_states = second_benchmark.get_last_run_propagated_state_history( ) # Write results to files if output_path is not None: save2txt(first_benchmark_states, 'benchmark_1_states.dat', output_path) save2txt(second_benchmark_states, 'benchmark_2_states.dat', output_path) # Add items to be returned return_list = [first_benchmark_states, second_benchmark_states] ### DO THE SAME FOR DEPENDENT VARIABLES ### if are_dependent_variables_present: # Retrieve dependent variable history first_benchmark_dependent_variable = first_benchmark.get_last_run_dependent_variable_history( ) second_benchmark_dependent_variable = second_benchmark.get_last_run_dependent_variable_history( ) # Write results to file if output_path is not None: save2txt(first_benchmark_dependent_variable, 'benchmark_1_dependent_variables.dat', output_path) save2txt(second_benchmark_dependent_variable, 'benchmark_2_dependent_variables.dat', output_path) # Add items to be returned return_list.append(first_benchmark_dependent_variable) return_list.append(second_benchmark_dependent_variable) return return_list
} # Check if the propagation was run successfully propagation_outcome = dynamics_simulator.integration_completed_successfully( ) dict_to_write['Propagation run successfully'] = propagation_outcome # Note if results were written to files dict_to_write['Results written to file'] = write_results_to_file # Note if benchmark was run dict_to_write['Benchmark run'] = use_benchmark # Note if dependent variables were present dict_to_write[ 'Dependent variables present'] = are_dependent_variables_to_save # Save results to a file if write_results_to_file: save2txt(state_history, 'state_history.dat', output_path) save2txt(dependent_variable_history, 'dependent_variable_history.dat', output_path) save2txt(dict_to_write, 'ancillary_simulation_info.txt', output_path) ### BENCHMARK COMPARISON #### # Compare the simulation to the benchmarks and write differences to files if use_benchmark: # Initialize containers state_difference = dict() # Loop over the propagated states and use the benchmark interpolators # NOTE TO STUDENTS: it can happen that the benchmark ends earlier than the regular simulation, due to # the shorter step size. Therefore, the following lines of code will be forced to extrapolate the # benchmark states (or dependent variables), producing a warning. Be aware of it! for epoch in state_history.keys():
def main(): # Load spice kernels. spice_interface.load_standard_spice_kernels() # Set simulation start epoch. simulation_start_epoch = 0.0 # Set numerical integration fixed step size. fixed_step_size = 1.0 # Set simulation end epoch. simulation_end_epoch = constants.JULIAN_DAY ########################################################################### # CREATE ENVIRONMENT ###################################################### ########################################################################### bodies_to_create = ["Sun", "Earth", "Moon", "Mars", "Venus"] env = Environment(bodies_to_create, user_defined_bodies=None, frame_origin="SSB", frame_orientation="ECLIPJ2000", custom_body_settings=None, alternative_kernels=None, start_epoch=simulation_start_epoch, end_epoch=simulation_end_epoch, fixed_dt=fixed_step_size, epoch_margin=300.0) ########################################################################### # CREATE VEHICLE ########################################################## ########################################################################### env.vehicles = ["Delfi-C3"] env.bodies["Delfi-C3"].set_constant_body_mass(400.0) ########################################################################### # CREATE VEHICLE - ENVIRONMENT INTERFACE ################################## ########################################################################### # Create aerodynamic interface settings for Delfi-C3. env.set_aerodynamic_coefficient_interface( target_body_name="Delfi-C3", reference_area=4.0, coefficients=1.2 * np.ones(3), coefficients_in_aerodynamic_frame=True, coefficients_in_negative_axis_direction=True, model="constant_coefficients") # Create radiation pressure interface settings for Delfi-C3. env.set_radiation_pressure_interface(target_body_name="Delfi-C3", reference_area=4.0, coefficient=1.2, occulting=["Earth"], source="Sun", model="cannon_ball") ########################################################################### # FINALIZE BODIES ######################################################### ########################################################################### # Finalize body creation. env.finalize_environment(frame_origin="SSB", frame_orientation="J2000") ########################################################################### # CREATE ACCELERATIONS #################################################### ########################################################################### # Define bodies that are propagated. bodies_to_propagate = env.vehicles # Define central bodies. central_bodies = ["Earth"] # Define unique (Sun, Earth) accelerations acting on Delfi-C3. accelerations_of_delfi_c3 = dict( Sun=[ simulation_setup.Acceleration.canon_ball_radiation_pressure() # AccelerationSettings(AvailableAcceleration.cannon_ball_radiation_pressure) # LEGACY DESIGN. ], Earth=[ simulation_setup.Acceleration.spherical_harmonic_gravity(5, 5), # SphericalHarmonicAccelerationSettings(5, 5), # LEGACY DESIGN. simulation_setup.Acceleration.aerodynamic() # AccelerationSettings(AvailableAcceleration.aerodynamic) # LEGACY DESIGN. ]) # Define other point mass accelerations acting on Delfi-C3. for other in set(bodies_to_create).difference({"Sun", "Earth"}): accelerations_of_delfi_c3[other] = [ simulation_setup.Acceleration.point_mass_gravity() ] # Create global accelerations dictionary. acceleration_dict = {"Delfi-C3": accelerations_of_delfi_c3} # Create acceleration models. acceleration_models = simulation_setup.create_acceleration_models_dict( bodies_to_create, acceleration_dict, bodies_to_propagate, central_bodies) ########################################################################### # CREATE PROPAGATION SETTINGS ############################################# ########################################################################### # Set initial conditions for the Asterix satellite that will be # propagated in this simulation. The initial conditions are given in # Keplerian elements and later on converted to Cartesian elements. # Set Keplerian elements for Delfi-C3 earth_gravitational_parameter = env.bodies[ "Earth"].gravity_field_model.get_gravitational_parameter() # LEGACY DESIGN. # KEI = orbital_element_conversions.KeplerianElementIndices # asterix_initial_state_in_keplerian_elements = np.zeros(6) # kep_state = asterix_initial_state_in_keplerian_elements # kep_state[int(KEI.semi_major_axis_index)] = 7500.0E3 # kep_state[int(KEI.eccentricity_index)] = 0.1 # kep_state[int(KEI.inclination_index)] = np.deg2rad(85.3) # kep_state[int(KEI.argument_of_periapsis_index)] = np.deg2rad(235.7) # kep_state[int(KEI.longitude_of_ascending_node_index)] = np.deg2rad(23.4) # kep_state[int(KEI.true_anomaly_index)] = np.deg2rad(139.87) # system_initial_state = corbital_element_conversions.onvert_keplerian_to_cartesian_elements( # kep_state, earth_gravitational_parameter) # REVISED CONTEMPORARY DESIGN. system_initial_state = elements.keplerian2cartesian( mu=earth_gravitational_parameter, a=7500.0E3, ecc=0.1, inc=np.deg2rad(85.3), raan=np.deg2rad(23.4), argp=np.deg2rad(235.7), nu=np.deg2rad(139.87)) # Create propagation settings. propagator_settings = propagators.TranslationalStatePropagatorSettings( central_bodies, acceleration_models, bodies_to_propagate, system_initial_state, simulation_end_epoch) # Create numerical integrator settings. integrator_settings = numerical_integrators.IntegratorSettings( numerical_integrators.AvailableIntegrators.rungeKutta4, simulation_start_epoch, fixed_step_size) ########################################################################### # PROPAGATE ORBIT ######################################################### ########################################################################### # Create simulation object and propagate dynamics. dynamics_simulator = propagators.SingleArcDynamicsSimulator( bodies, integrator_settings, propagator_settings, True) result = dynamics_simulator.get_equations_of_motion_numerical_solution() ########################################################################### # PRINT INITIAL AND FINAL STATES ########################################## ########################################################################### print(f""" Single Earth-Orbiting Satellite Example. The initial position vector of Delfi-C3 is [km]: \n{ result[simulation_start_epoch][:3] / 1E3} The initial velocity vector of Delfi-C3 is [km]: \n{ result[simulation_start_epoch][3:] / 1E3} After {simulation_end_epoch} seconds the position vector of Delfi-C3 is [km]: \n{ result[simulation_end_epoch][:3] / 1E3} And the velocity vector of Delfi-C3 is [km]: \n{ result[simulation_start_epoch][3:] / 1E3} """) ########################################################################### # SAVE RESULTS ############################################################ ########################################################################### io.save2txt( solution=result, filename="singlePerturbedSatellitePropagationHistory.dat", directory="./tutorial_2", ) # Final statement (not required, though good practice in a __main__). return 0
# define the variables we need dependent_variables_to_save = [ propagation_setup.dependent_variable.total_acceleration("CubeSat"), propagation_setup.dependent_variable.keplerian_state("CubeSat", "Sun"), ] # create propagation settings propagator_settings = propagation_setup.propagator.translational( central_bodies, acceleration_models, bodies_to_propagate, initial_state, simulation_end_epoch, output_variables=dependent_variables_to_save) # numerical integrator settings fixed_step_size = 10.0 integrator_settings = propagation_setup.integrator.runge_kutta_4( simulation_start_epoch, fixed_step_size) # # propagate orbit dynamics_simulator = propagation_setup.SingleArcDynamicsSimulator( bodies, integrator_settings, propagator_settings) states = dynamics_simulator.state_history dependent_variables = dynamics_simulator.dependent_variable_history save2txt( solution=states, filename="PropagationHistory_3U_CubeSat_thrust.dat", directory="./", # default = "./" # default = None )
def main(): # Load spice kernels. spice_interface.load_standard_spice_kernels() # Set simulation start epoch. simulation_start_epoch = 0.0 # Set numerical integration fixed step size. fixed_step_size = 1.0 # Set simulation end epoch. simulation_end_epoch = constants.JULIAN_DAY ########################################################################### # CREATE ENVIRONMENT ###################################################### ########################################################################### bodies_to_create = ["Sun", "Earth", "Moon", "Mars", "Venus"] env = Environment(tudatpy_bodies=bodies_to_create, custom_bodies=None, frame_origin="SSB", frame_orientation="ECLIPJ2000", start_epoch=simulation_start_epoch, end_epoch=simulation_end_epoch, epoch_margin=300.0) ########################################################################### # CREATE VEHICLE ########################################################## ########################################################################### env["Delfi-C3"] = Body(env) env["Delfi-C3"].set_constant_body_mass(400.0) ########################################################################### # CREATE VEHICLE - ENVIRONMENT INTERFACE ################################## ########################################################################### # Create aerodynamic coefficient interface settings reference_area = 4.0 aerodynamic_coefficient = 1.2 aero_c_settings = simulation_setup.ConstantAerodynamicCoefficientSettings( reference_area, aerodynamic_coefficient * np.ones(3), are_coefficients_in_aerodynamic_frame=True, are_coefficients_in_negative_axis_direction=True) # Create and set aerodynamic coefficients object env._bodies["Delfi-C3"].set_aerodynamic_coefficient_interface( simulation_setup.create_aerodynamic_coefficient_interface( aero_c_settings, "Delfi-C3")) # TODO: Simplify (post 1.0.0 work) # Create radiation pressure settings reference_area_radiation = 4.0 radiation_pressure_coefficient = 1.2 occulting_bodies = ["Earth"] rad_press_settings = simulation_setup.CannonBallRadiationPressureInterfaceSettings( "Sun", reference_area_radiation, radiation_pressure_coefficient, occulting_bodies) # Create and set radiation pressure settings env._bodies["Delfi-C3"].set_radiation_pressure_interface( "Sun", simulation_setup.create_radiation_pressure_interface( rad_press_settings, "Delfi-C3", env._bodies)) # # Create aerodynamic interface settings for Delfi-C3. # env["Delfi-C3"]._set_aerodynamic_coefficient_interface( # reference_area=4.0, # coefficients=1.2 * np.ones(3), # coefficients_in_aerodynamic_frame=True, # coefficients_in_negative_axis_direction=True, # model="constant_coefficients") # # # Create radiation pressure interface settings for Delfi-C3. # env["Delfi-C3"]._set_radiation_pressure_interface( # reference_area=4.0, # coefficient=1.2, # occulting=["Earth"], # source="Sun", # model="cannon_ball") ########################################################################### # FINALIZE BODIES ######################################################### ########################################################################### # Finalize body creation. env.finalize_environment() ########################################################################### # CREATE ACCELERATIONS #################################################### ########################################################################### # Define bodies that are propagated. bodies_to_propagate = ["Delfi-C3"] # Define central bodies. central_bodies = ["Earth"] # Define unique (Sun, Earth) accelerations acting on Delfi-C3. accelerations_of_delfi_c3 = dict( Sun=[ simulation_setup.Acceleration.canon_ball_radiation_pressure() # AccelerationSettings(AvailableAcceleration.cannon_ball_radiation_pressure) # LEGACY DESIGN. ], Earth=[ simulation_setup.Acceleration.spherical_harmonic_gravity(5, 5), # SphericalHarmonicAccelerationSettings(5, 5), # LEGACY DESIGN. simulation_setup.Acceleration.aerodynamic() # AccelerationSettings(AvailableAcceleration.aerodynamic) # LEGACY DESIGN. ]) # Define other point mass accelerations acting on Delfi-C3. for other in set(bodies_to_create).difference({"Sun", "Earth"}): accelerations_of_delfi_c3[other] = [ simulation_setup.Acceleration.point_mass_gravity() ] # Create global accelerations dictionary. acceleration_dict = {"Delfi-C3": accelerations_of_delfi_c3} # Create acceleration models. acceleration_models = simulation_setup.create_acceleration_models_dict( env._bodies, acceleration_dict, bodies_to_propagate, central_bodies) ########################################################################### # CREATE PROPAGATION SETTINGS ############################################# ########################################################################### # Set initial conditions for the Asterix satellite that will be # propagated in this simulation. The initial conditions are given in # Keplerian elements and later on converted to Cartesian elements. # Set Keplerian elements for Delfi-C3 earth_gravitational_parameter = env[ "Earth"].gravity_field_model.get_gravitational_parameter() # REVISED CONTEMPORARY DESIGN. system_initial_state = elements.keplerian2cartesian( mu=earth_gravitational_parameter, a=7500.0E3, ecc=0.1, inc=np.deg2rad(85.3), raan=np.deg2rad(23.4), argp=np.deg2rad(235.7), nu=np.deg2rad(139.87)) # Create propagation settings. propagator_settings = propagators.TranslationalStatePropagatorSettings( central_bodies, acceleration_models, bodies_to_propagate, system_initial_state, simulation_end_epoch) # Create numerical integrator settings. integrator_settings = numerical_integrators.IntegratorSettings( numerical_integrators.AvailableIntegrators.rungeKutta4, simulation_start_epoch, fixed_step_size) ########################################################################### # PROPAGATE ORBIT ######################################################### ########################################################################### t0 = time.time() # Create simulation object and propagate dynamics. print(env._bodies) dynamics_simulator = propagators.SingleArcDynamicsSimulator( env._bodies, integrator_settings, propagator_settings, True) print("dynamics_simulator: ", time.time() - t0) result = dynamics_simulator.get_equations_of_motion_numerical_solution() ########################################################################### # PRINT INITIAL AND FINAL STATES ########################################## ########################################################################### print(f""" Single Earth-Orbiting Satellite Example. The initial position vector of Delfi-C3 is [km]: \n{ result[simulation_start_epoch][:3] / 1E3} The initial velocity vector of Delfi-C3 is [km/s]: \n{ result[simulation_start_epoch][3:] / 1E3} After {simulation_end_epoch} seconds the position vector of Delfi-C3 is [km]: \n{ result[simulation_end_epoch][:3] / 1E3} And the velocity vector of Delfi-C3 is [km/s]: \n{ result[simulation_start_epoch][3:] / 1E3} """) ########################################################################### # SAVE RESULTS ############################################################ ########################################################################### io.save2txt( solution=result, filename="singlePerturbedSatellitePropagationHistory.dat", directory="./tutorial_2_prototype", ) # Final statement (not required, though good practice in a __main__). return 0
def main(): """ The problem describes the orbit design around a small body (asteroid Itokawa). DYNAMICAL MODEL Itokawa spherical harmonics, cannonball radiation pressure from Sun, point-mass third-body from Sun, Jupiter, Saturn, Earth, Mars PROPAGATION TIME 5 days INTEGRATOR RKF7(8) with tolerances 1E-8 TERMINATION CONDITIONS In addition to 5 day time, minimum distance from Itokaw's center of mass: 150 m (no crashing), maximum distance from center of mass: 5 km (no escaping) DESIGN VARIABLES Initial values of semi-major axis, eccentricity, inclination, and longitude of node OBJECTIVES 1. good coverage: the mean value of the absolute longitude w.r.t. Itokawa over the full propagation should be maximized; 2. close orbit: the mean value of the distance should be minimized. """ ########################################################################### # CREATE SIMULATION SETTINGS ############################################## ########################################################################### # Load spice kernels spice_interface.load_standard_kernels() # Define Itokawa radius itokawa_radius = 161.915 # Set simulation start and end epochs mission_initial_time = 0.0 mission_duration = 5.0 * 86400.0 # Set boundaries on the design variables design_variable_lb = (300, 0.0, 0.0, 0.0) design_variable_ub = (2000, 0.3, 180, 360) # Set termination conditions minimum_distance_from_com = 150.0 + itokawa_radius maximum_distance_from_com = 5.0E3 + itokawa_radius # Create simulation bodies bodies = create_simulation_bodies(itokawa_radius) ########################################################################### # CREATE ACCELERATIONS #################################################### ########################################################################### bodies_to_propagate = ["Spacecraft"] central_bodies = ["Itokawa"] # Create acceleration models. acceleration_models = get_acceleration_models(bodies_to_propagate, central_bodies, bodies) # Create numerical integrator settings. integrator_settings = propagation_setup.integrator.runge_kutta_variable_step_size( mission_initial_time, 1.0, propagation_setup.integrator.RKCoefficientSets.rkf_78, 1.0E-6, 86400.0, 1.0E-8, 1.0E-8) ########################################################################### # CREATE PROPAGATION SETTINGS ############################################# ########################################################################### # Define list of dependent variables to save dependent_variables_to_save = get_dependent_variables_to_save() # Create propagation settings termination_settings = get_termination_settings(mission_initial_time, mission_duration, minimum_distance_from_com, maximum_distance_from_com) # Define (Cowell) propagator settings with mock initial state propagator_settings = propagation_setup.propagator.translational( central_bodies, acceleration_models, bodies_to_propagate, np.zeros(6), termination_settings, output_variables=dependent_variables_to_save) ########################################################################### # OPTIMIZE ORBIT WITH PYGMO ############################################### ########################################################################### # Fix seed for reproducibility fixed_seed = 17031861 # Instantiate orbit problem orbitProblem = AsteroidOrbitProblem(bodies, integrator_settings, propagator_settings, mission_initial_time, mission_duration, design_variable_lb, design_variable_ub) # Select Moead algorithm from pygmo, with one generation algo = pg.algorithm(pg.nsga2(gen=1, seed=fixed_seed)) # Create pygmo problem using the UDP instantiated above prob = pg.problem(orbitProblem) # Initialize pygmo population with 48 individuals population_size = 48 pop = pg.population(prob, size=population_size, seed=fixed_seed) # Set the number of evolutions number_of_evolutions = 50 # Initialize containers fitness_list = [] population_list = [] # Evolve the population recursively for gen in range(number_of_evolutions): print('Evolving population; at generation ' + str(gen)) # Evolve the population pop = algo.evolve(pop) # Store the fitness values and design variables for all individuals fitness_list.append(pop.get_f()) population_list.append(pop.get_x()) ########################################################################### # ANALYZE FIRST AND LAST GENERATIONS ###################################### ########################################################################### dump_results_to_file = False # Get output path output_path = os.getcwd() + '/PygmoExampleSimulationOutput/' # Retrieve first and last generations for further analysis pops_to_analyze = {0: 'initial', number_of_evolutions - 1: 'final'} # Initialize containers simulation_output = dict() # Loop over first and last generations for population_index, population_name in pops_to_analyze.items(): current_population = population_list[population_index] # Save fitness and population members if dump_results_to_file: # Create directory if not os.path.isdir(output_path): os.mkdir(output_path) np.savetxt(output_path + 'Fitness_' + population_name + '.dat', fitness_list[population_index]) np.savetxt(output_path + 'Population_' + population_name + '.dat', population_list[population_index]) # Current generation's dictionary generation_output = dict() # Loop over all individuals of the populations for individual in range(population_size): # Retrieve orbital parameters current_orbit_parameters = current_population[individual] # Propagate orbit and compute fitness orbitProblem.fitness(current_orbit_parameters) # Retrieve state and dependent variable history current_states = orbitProblem.get_last_run_dynamics_simulator( ).state_history current_dependent_variables = orbitProblem.get_last_run_dynamics_simulator( ).dependent_variable_history # Save results to dict generation_output[individual] = [ current_states, current_dependent_variables ] # Write data to files if dump_results_to_file: save2txt( current_dependent_variables, population_name + '_dependent_variables' + str(individual) + '.dat', output_path) save2txt( current_states, population_name + '_states' + str(individual) + '.dat', output_path) # Append to global dictionary simulation_output[population_index] = [ generation_output, fitness_list[population_index], population_list[population_index] ] ########################################################################### # ANALYZE RESULTS ######################################################### ########################################################################### # Set font size for plots font = {'size': 12} matplotlib.rc('font', **font) # Create dictionaries decision_variable_names = { 0: 'Semi-major axis [m]', 1: 'Eccentricity', 2: 'Inclination [deg]', 3: 'Longitude of the node [deg]' } decision_variable_range = { 0: [800.0, 1300.0], 1: [0.10, 0.17], 2: [90.0, 95.0], 3: [250.0, 270.0] } decision_variable_symbols = { 0: r'$a$', 1: r'$e$', 2: r'$i$', 3: r'$\Omega$' } decision_variable_units = {0: r' m', 1: r' ', 2: r' deg', 3: r' deg'} # Loop over populations for population_index in simulation_output.keys(): # Retrieve current population current_generation = simulation_output[population_index] # Plot Pareto fronts for all design variables fig, axs = plt.subplots(2, 2, figsize=(14, 8)) fig.suptitle('Generation ' + str(population_index), fontweight='bold', y=0.95) current_fitness = current_generation[1] current_population = current_generation[2] for ax_index, ax in enumerate(axs.flatten()): cs = ax.scatter(np.deg2rad(current_fitness[:, 0]), current_fitness[:, 1], 40, current_population[:, ax_index], marker='.') cbar = fig.colorbar(cs, ax=ax) cbar.ax.set_ylabel(decision_variable_names[ax_index]) ax.grid('major') if ax_index > 1: ax.set_xlabel(r'Objective 1: coverage [$deg^{-1}$] ') if ax_index == 0 or ax_index == 2: ax.set_ylabel(r'Objective 2: proximity [$m$]') # Save figure fig.savefig('pareto_generation_' + str(population_index) + '.png', bbox_inches='tight') # Plot histogram for last generation, semi-major axis fig, axs = plt.subplots(2, 2, figsize=(12, 8)) fig.suptitle('Final orbits by decision variable', fontweight='bold', y=0.95) last_pop = simulation_output[number_of_evolutions - 1][2] for ax_index, ax in enumerate(axs.flatten()): ax.hist(last_pop[:, ax_index], bins=30) # Prettify ax.set_xlabel(decision_variable_names[ax_index]) if ax_index % 2 == 0: ax.set_ylabel('Occurrences in the population') # Save figure fig.savefig('histograms_final_generation.png', bbox_inches='tight') # Plot orbits of initial and final generation fig = plt.figure(figsize=(12, 6)) fig.suptitle('Initial and final orbit bundle', fontweight='bold', y=0.95) title = {0: 'Initial orbit bundle', 1: 'Final orbit bundle'} # Loop over populations for ax_index, population_index in enumerate(simulation_output.keys()): current_ax = fig.add_subplot(1, 2, 1 + ax_index, projection='3d') # Retrieve current population current_generation = simulation_output[population_index] current_population = current_generation[2] # Loop over individuals for ind_index, individual in enumerate(current_population): # Plot orbit state_history = list(current_generation[0][ind_index][0].values()) state_history = np.vstack(state_history) current_ax.plot(state_history[:, 0], state_history[:, 1], state_history[:, 2], linewidth=0.5) # Prettify current_ax.set_xlabel('X [m]') current_ax.set_ylabel('Y [m]') current_ax.set_zlabel('Z [m]') current_ax.set_title(title[ax_index], y=1.0, pad=15) # Save figure fig.savefig('orbit_bundles_initial_final_gen.png', bbox_inches='tight') # Plot orbits of final generation divided by parameters fig = plt.figure(figsize=(12, 8)) fig.suptitle('Final orbit bundle by decision variable', fontweight='bold', y=0.95) # Retrieve current population current_generation = simulation_output[number_of_evolutions - 1] # Plot Pareto fronts for all design variables current_population = current_generation[2] # Loop over decision variables for var in range(4): # Create axis current_ax = fig.add_subplot(2, 2, 1 + var, projection='3d') # Loop over individuals for ind_index, individual in enumerate(current_population): # Set plot color according to boundaries if individual[var] < decision_variable_range[var][0]: plt_color = 'r' label = decision_variable_symbols[var] + ' < ' + str(decision_variable_range[var][0]) + \ decision_variable_units[var] elif decision_variable_range[var][0] < individual[ var] < decision_variable_range[var][1]: plt_color = 'b' label = str(decision_variable_range[var][0]) + ' < ' + \ decision_variable_symbols[var] + \ ' < ' + str(decision_variable_range[var][1]) + decision_variable_units[var] else: plt_color = 'g' label = decision_variable_symbols[var] + ' > ' + str(decision_variable_range[var][1]) + \ decision_variable_units[var] # Plot orbit state_history = list(current_generation[0][ind_index][0].values()) state_history = np.vstack(state_history) current_ax.plot(state_history[:, 0], state_history[:, 1], state_history[:, 2], color=plt_color, linewidth=0.5, label=label) # Prettify current_ax.set_xlabel('X [m]') current_ax.set_ylabel('Y [m]') current_ax.set_zlabel('Z [m]') current_ax.set_title(decision_variable_names[var], y=1.0, pad=10) handles, decision_variable_legend = current_ax.get_legend_handles_labels( ) decision_variable_legend, ids = np.unique(decision_variable_legend, return_index=True) handles = [handles[i] for i in ids] current_ax.legend(handles, decision_variable_legend, loc='lower right', bbox_to_anchor=(0.3, 0.6)) # Save figure fig.savefig('orbit_bundle_final_gen_by_variable.png', bbox_inches='tight') # Show plot plt.show()