Working with Solution Data#

This comprehensive tutorial covers all of MAPTOR’s solution data extraction capabilities. After completing this guide, you’ll understand how to access any optimization result using MAPTOR’s unified solution interface.

Prerequisites: Complete the Getting Started guide first to understand the basic problem-solving workflow.

Scope: This tutorial covers the complete solution access API, from basic trajectory extraction to comprehensive adaptive algorithm diagnostics.

Solution Access Framework#

MAPTOR provides a unified solution interface for accessing optimization results across all problem types. You systematically extract trajectories, metadata, and diagnostics using consistent access patterns that work for both single-phase and multiphase problems.

import maptor as mtor

# Solve any optimal control problem
solution = mtor.solve_adaptive(problem)

# Access results through unified interface
objective = solution.status["objective"]
trajectory = solution["position"]
final_value = trajectory[-1]

A solution contains all optimization results with automatic data organization, trajectory concatenation, and comprehensive metadata for analysis and visualization.

Status Validation#

Every solution provides complete optimization outcome information through the status property:

Basic Status Access:

# Essential validation
success = solution.status["success"]
objective = solution.status["objective"]
message = solution.status["message"]

Complete Status Information:

# All status components
status = solution.status
success = status["success"]           # bool: Optimization success
objective = status["objective"]       # float: Final objective value
total_time = status["total_mission_time"]  # float: Complete mission duration
message = status["message"]           # str: Detailed solver message

Status Validation Patterns:

# Success checking
if solution.status["success"]:
    print("Optimization succeeded")

# Objective extraction
optimal_cost = solution.status["objective"]

# Mission timing
duration = solution.status["total_mission_time"]

# Solver diagnostics
solver_info = solution.status["message"]

The status property provides immediate access to optimization outcomes and is essential for determining whether to proceed with data extraction.

Mission-Wide Data Access#

Use string keys to automatically access complete mission trajectories with automatic phase concatenation:

Basic Trajectory Access:

# Complete mission trajectories
time_states = solution["time_states"]       # All state time points
time_controls = solution["time_controls"]   # All control time points
position = solution["position"]             # Complete position trajectory
velocity = solution["velocity"]             # Complete velocity trajectory
thrust = solution["thrust"]                 # Complete control trajectory

Time Coordinate Access:

# State and control time arrays
state_times = solution["time_states"]
control_times = solution["time_controls"]

# Time span analysis
mission_start = state_times[0]
mission_end = state_times[-1]
total_duration = mission_end - mission_start

Variable Trajectory Access:

# State trajectories
altitude = solution["altitude"]
mass = solution["mass"]
angle = solution["angle"]

# Control trajectories
throttle = solution["throttle"]
steering = solution["steering"]
power = solution["power"]

Final Value Extraction:

# Mission endpoints
final_position = solution["position"][-1]
final_velocity = solution["velocity"][-1]
initial_mass = solution["mass"][0]
final_mass = solution["mass"][-1]

String key access automatically concatenates data from all phases containing the specified variable, providing seamless mission-wide analysis.

Phase-Specific Data Access#

Use tuple keys for granular control over individual phase data:

Single Phase Access:

# Phase-specific trajectories
phase1_position = solution[(1, "position")]
phase1_velocity = solution[(1, "velocity")]
phase1_thrust = solution[(1, "thrust")]

# Phase-specific time coordinates
phase1_state_times = solution[(1, "time_states")]
phase1_control_times = solution[(1, "time_controls")]

Multi-Phase Access:

# Access each phase individually
ascent_altitude = solution[(1, "altitude")]
coast_altitude = solution[(2, "altitude")]
descent_altitude = solution[(3, "altitude")]

# Phase-specific controls
ascent_thrust = solution[(1, "thrust")]
coast_thrust = solution[(2, "thrust")]    # May be zero
descent_thrust = solution[(3, "thrust")]

Phase Boundary Analysis:

# Phase transition values
phase1_final_mass = solution[(1, "mass")][-1]
phase2_initial_mass = solution[(2, "mass")][0]

# Continuity verification
altitude_transition = solution[(1, "altitude")][-1]
altitude_continuation = solution[(2, "altitude")][0]

Phase Comparison:

# Compare phase characteristics
phase1_duration = len(solution[(1, "time_states")])
phase2_duration = len(solution[(2, "time_states")])

# Phase-specific extrema
max_thrust_p1 = max(solution[(1, "thrust")])
max_thrust_p2 = max(solution[(2, "thrust")])

Tuple access pattern (phase_id, variable_name) provides complete control over which phase data to extract and enables detailed phase-specific analysis.

Variable Existence Validation#

Safely validate variable availability before accessing solution data:

Basic Existence Checking:

# String key validation
if "altitude" in solution:
    altitude_data = solution["altitude"]

# Tuple key validation
if (1, "thrust") in solution:
    thrust_data = solution[(1, "thrust")]

Multiple Variable Validation:

# Check multiple variables
required_vars = ["position", "velocity", "thrust"]
available_vars = [var for var in required_vars if var in solution]

# Conditional access
if "fuel_mass" in solution:
    fuel_trajectory = solution["fuel_mass"]
else:
    print("Fuel mass not tracked in this problem")

Phase-Specific Validation:

# Phase variable existence
if (2, "steering") in solution:
    steering_profile = solution[(2, "steering")]

# Multi-phase validation
phases_with_thrust = []
for phase_id in [1, 2, 3]:
    if (phase_id, "thrust") in solution:
        phases_with_thrust.append(phase_id)

Safe Access Patterns:

# Conditional trajectory extraction
trajectories = {}
for var_name in ["x", "y", "z", "vx", "vy", "vz"]:
    if var_name in solution:
        trajectories[var_name] = solution[var_name]

# Phase-conditional access
phase_data = {}
for phase_id in range(1, 4):
    if (phase_id, "altitude") in solution:
        phase_data[phase_id] = solution[(phase_id, "altitude")]

The in operator works with both string and tuple keys, enabling robust solution processing workflows.

Phase Information Analysis#

The phases property provides comprehensive metadata for detailed mission analysis:

Basic Phase Information:

# Available phases
phase_ids = list(solution.phases.keys())
num_phases = len(solution.phases)

# Single phase data
phase_data = solution.phases[1]

Timing Information:

# Phase timing
for phase_id, phase_data in solution.phases.items():
    times = phase_data["times"]
    initial_time = times["initial"]
    final_time = times["final"]
    duration = times["duration"]

Variable Information:

# Phase variables
for phase_id, phase_data in solution.phases.items():
    variables = phase_data["variables"]
    state_names = variables["state_names"]
    control_names = variables["control_names"]
    num_states = variables["num_states"]
    num_controls = variables["num_controls"]

Mesh Configuration:

# Mesh details
for phase_id, phase_data in solution.phases.items():
    mesh = phase_data["mesh"]
    polynomial_degrees = mesh["polynomial_degrees"]
    mesh_nodes = mesh["mesh_nodes"]
    num_intervals = mesh["num_intervals"]

Time Array Access:

# Direct time array access
for phase_id, phase_data in solution.phases.items():
    time_arrays = phase_data["time_arrays"]
    state_times = time_arrays["states"]
    control_times = time_arrays["controls"]

Integral Values:

# Phase integral extraction
for phase_id, phase_data in solution.phases.items():
    integrals = phase_data["integrals"]
    if integrals is not None:
        if isinstance(integrals, float):
            single_integral = integrals
        else:
            multiple_integrals = integrals

Each phase provides complete timing, variable, mesh, and integral information for comprehensive mission analysis.

Static Parameter Access#

Extract optimized design parameters that remain constant throughout the mission:

Parameter Availability:

# Check parameter existence
if solution.parameters is not None:
    print("Problem includes static parameters")
else:
    print("No static parameters")

Basic Parameter Access:

# Parameter extraction
if solution.parameters:
    param_values = solution.parameters["values"]
    param_count = solution.parameters["count"]
    param_names = solution.parameters["names"]

Named Parameter Access:

# With parameter names
params = solution.parameters
if params and params["names"]:
    for name, value in zip(params["names"], params["values"]):
        print(f"{name}: {value}")

Unnamed Parameter Access:

# Without parameter names
params = solution.parameters
if params:
    for i, value in enumerate(params["values"]):
        print(f"Parameter {i}: {value}")

Parameter Value Extraction:

# Direct value access
if solution.parameters:
    optimized_mass = solution.parameters["values"][0]
    optimized_thrust = solution.parameters["values"][1]
    design_parameter = solution.parameters["values"][2]

Static parameters represent optimization variables that remain constant throughout the mission but are determined by the solver.

Adaptive Algorithm Analysis#

Examine convergence and refinement performance for adaptive solutions:

Algorithm Status:

# Adaptive solution check
if solution.adaptive:
    print("Adaptive solution available")
else:
    print("Fixed mesh solution")

Convergence Information:

# Basic convergence data
if solution.adaptive:
    converged = solution.adaptive["converged"]
    iterations = solution.adaptive["iterations"]
    tolerance = solution.adaptive["target_tolerance"]

Phase-Specific Convergence:

# Per-phase convergence status
if solution.adaptive:
    phase_converged = solution.adaptive["phase_converged"]
    for phase_id, status in phase_converged.items():
        print(f"Phase {phase_id}: {'Converged' if status else 'Not converged'}")

Error Analysis:

# Error estimates
if solution.adaptive:
    final_errors = solution.adaptive["final_errors"]
    for phase_id, errors in final_errors.items():
        if errors:
            max_error = max(errors)
            mean_error = sum(errors) / len(errors)

Refinement Factors:

# Algorithm parameters
if solution.adaptive:
    gamma_factors = solution.adaptive["gamma_factors"]
    for phase_id, factors in gamma_factors.items():
        if factors is not None:
            refinement_data = factors

Complete Adaptive Analysis:

# Full adaptive diagnostics
adaptive = solution.adaptive
if adaptive:
    algorithm_converged = adaptive["converged"]
    total_iterations = adaptive["iterations"]
    target_accuracy = adaptive["target_tolerance"]
    phase_status = adaptive["phase_converged"]
    error_estimates = adaptive["final_errors"]
    normalization_factors = adaptive["gamma_factors"]

The adaptive property provides comprehensive algorithm performance data for understanding solution quality and refinement behavior.

Built-in Solution Methods#

MAPTOR provides convenient methods for common solution analysis tasks:

Comprehensive Plotting:

# Default plotting (all variables, all phases)
solution.plot()

# Specific phase plotting
solution.plot(phase_id=1)

# Selected variables
solution.plot("altitude", "velocity", "thrust")

# Phase-specific variables
solution.plot(1, "position", "velocity")

# Custom formatting
solution.plot(figsize=(16, 10), show_phase_boundaries=True)

Solution Summaries:

# Comprehensive summary (default)
solution.summary()

# Concise summary
solution.summary(comprehensive=False)

Plot Customization:

# All phases with boundaries
solution.plot(show_phase_boundaries=True)

# No phase boundaries
solution.plot(show_phase_boundaries=False)

# Large figure size
solution.plot(figsize=(20, 12))

Summary Control:

# Detailed diagnostics
solution.summary(comprehensive=True)

# Key information only
solution.summary(comprehensive=False)

Built-in methods provide immediate visualization and analysis capabilities without requiring custom plotting code.

Next Steps#

  • Problem Definition: Study Complete Problem Definition Guide to understand how solution structure relates to problem formulation

  • Complete Examples: Explore Examples Gallery for solution analysis in context of specific problems

  • API Reference: Use API Reference for detailed method signatures and advanced options