Skip to main content
Glama

porkchop_plot_analysis

Analyze optimal interplanetary transfer windows by generating porkchop plots for departure and arrival date combinations between celestial bodies.

Instructions

Generate porkchop plot for interplanetary transfer opportunities.

Args: departure_body: Departure celestial body name arrival_body: Arrival celestial body name departure_date_range: Range of departure dates (ISO format) arrival_date_range: Range of arrival dates (ISO format) analysis_options: Optional analysis settings

Returns: JSON string with porkchop plot data

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
departure_bodyYes
arrival_bodyYes
departure_date_rangeYes
arrival_date_rangeYes
analysis_optionsNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • Registration of the porkchop_plot_analysis tool in the FastMCP server.
    mcp.tool(porkchop_plot_analysis)
    mcp.tool(monte_carlo_uncertainty_analysis)
  • The MCP tool handler: a wrapper that attempts to delegate to trajopt.porkchop_plot_analysis (stubbed) and returns JSON or error message. Type hints define input schema.
    def porkchop_plot_analysis(
        departure_body: str,
        arrival_body: str,
        departure_date_range: list[str],
        arrival_date_range: list[str],
        analysis_options: dict | None = None,
    ) -> str:
        """Generate porkchop plot for interplanetary transfer opportunities.
    
        Args:
            departure_body: Departure celestial body name
            arrival_body: Arrival celestial body name
            departure_date_range: Range of departure dates (ISO format)
            arrival_date_range: Range of arrival dates (ISO format)
            analysis_options: Optional analysis settings
    
        Returns:
            JSON string with porkchop plot data
        """
        try:
            from ..integrations.trajopt import porkchop_plot_analysis as _porkchop
    
            result = _porkchop(
                departure_body,
                arrival_body,
                departure_date_range,
                arrival_date_range,
                analysis_options or {},
            )
    
            return json.dumps(result, indent=2)
    
        except ImportError:
            return (
                "Porkchop plot analysis not available - install space trajectory packages"
            )
        except Exception as e:
            logger.error(f"Porkchop plot error: {str(e)}", exc_info=True)
            return f"Porkchop plot error: {str(e)}"
  • Import of porkchop_plot_analysis from optimization module for server registration.
    from .tools.optimization import (
        genetic_algorithm_optimization,
        monte_carlo_uncertainty_analysis,
        optimize_thrust_profile,
        particle_swarm_optimization,
        porkchop_plot_analysis,
        trajectory_sensitivity_analysis,
    )
  • Comprehensive helper implementation of porkchop plot analysis logic, including planetary positions, Lambert transfers, C3 computation, and optimal transfer identification. Not directly linked to the registered tool.
    def porkchop_plot_analysis(
        departure_body: str = "Earth",
        arrival_body: str = "Mars",
        departure_dates: list[str] = None,
        arrival_dates: list[str] = None,
        min_tof_days: int = 100,
        max_tof_days: int = 400,
    ) -> dict[str, Any]:
        """
        Generate porkchop plot analysis for interplanetary transfers.
    
        Args:
            departure_body: Departure celestial body (default: Earth)
            arrival_body: Arrival celestial body (default: Mars)
            departure_dates: List of departure dates (ISO format)
            arrival_dates: List of arrival dates (ISO format)
            min_tof_days: Minimum time of flight (days)
            max_tof_days: Maximum time of flight (days)
    
        Returns:
            Dictionary containing transfer analysis grid
        """
        # Default date ranges if not provided
        if departure_dates is None:
            # Earth-Mars synodic period is ~26 months, sample over 2 years
            departure_dates = [
                "2025-01-01T00:00:00",
                "2025-03-01T00:00:00",
                "2025-05-01T00:00:00",
                "2025-07-01T00:00:00",
                "2025-09-01T00:00:00",
                "2025-11-01T00:00:00",
                "2026-01-01T00:00:00",
                "2026-03-01T00:00:00",
                "2026-05-01T00:00:00",
                "2026-07-01T00:00:00",
                "2026-09-01T00:00:00",
                "2026-11-01T00:00:00",
            ]
    
        if arrival_dates is None:
            # Generate arrival dates based on TOF constraints
            arrival_dates = [
                "2025-06-01T00:00:00",
                "2025-08-01T00:00:00",
                "2025-10-01T00:00:00",
                "2025-12-01T00:00:00",
                "2026-02-01T00:00:00",
                "2026-04-01T00:00:00",
                "2026-06-01T00:00:00",
                "2026-08-01T00:00:00",
                "2026-10-01T00:00:00",
                "2026-12-01T00:00:00",
                "2027-02-01T00:00:00",
                "2027-04-01T00:00:00",
            ]
    
        # Simplified planetary positions (circular orbits for demonstration)
        # In production, would use SPICE kernels or JPL ephemeris
        earth_orbit_radius = 1.0  # AU
        mars_orbit_radius = 1.524  # AU
        earth_period_days = 365.25
        mars_period_days = 686.98
    
        def get_planet_position(body: str, date_iso: str) -> list[float]:
            """Get simplified planet position at given date."""
            # Parse date (simplified)
            import datetime
    
            try:
                dt = datetime.datetime.fromisoformat(date_iso.replace("Z", "+00:00"))
            except Exception:
                dt = datetime.datetime.fromisoformat(date_iso)
    
            # Days since J2000
            j2000 = datetime.datetime(2000, 1, 1, 12, 0, 0)
            days_since_j2000 = (dt - j2000).total_seconds() / 86400
    
            if body.lower() == "earth":
                # Earth mean anomaly
                mean_anomaly = 2 * math.pi * days_since_j2000 / earth_period_days
                x = earth_orbit_radius * math.cos(mean_anomaly)
                y = earth_orbit_radius * math.sin(mean_anomaly)
                return [x * 1.496e11, y * 1.496e11, 0.0]  # Convert AU to meters
            elif body.lower() == "mars":
                # Mars mean anomaly
                mean_anomaly = 2 * math.pi * days_since_j2000 / mars_period_days
                x = mars_orbit_radius * math.cos(mean_anomaly)
                y = mars_orbit_radius * math.sin(mean_anomaly)
                return [x * 1.496e11, y * 1.496e11, 0.0]  # Convert AU to meters
            else:
                # Default to Earth position
                mean_anomaly = 2 * math.pi * days_since_j2000 / earth_period_days
                x = earth_orbit_radius * math.cos(mean_anomaly)
                y = earth_orbit_radius * math.sin(mean_anomaly)
                return [x * 1.496e11, y * 1.496e11, 0.0]
    
        # Generate porkchop data grid
        porkchop_data = []
    
        for dep_date in departure_dates:
            for arr_date in arrival_dates:
                # Calculate time of flight
                import datetime
    
                try:
                    dep_dt = datetime.datetime.fromisoformat(
                        dep_date.replace("Z", "+00:00")
                    )
                except Exception:
                    dep_dt = datetime.datetime.fromisoformat(dep_date)
    
                try:
                    arr_dt = datetime.datetime.fromisoformat(
                        arr_date.replace("Z", "+00:00")
                    )
                except Exception:
                    arr_dt = datetime.datetime.fromisoformat(arr_date)
    
                tof_days = (arr_dt - dep_dt).total_seconds() / 86400
    
                # Filter by TOF constraints
                if tof_days < min_tof_days or tof_days > max_tof_days:
                    continue
    
                # Get planetary positions
                r1 = get_planet_position(departure_body, dep_date)
                r2 = get_planet_position(arrival_body, arr_date)
    
                # Calculate transfer using simplified Lambert solver
                try:
                    mu_sun = 1.32712440018e20  # Sun's gravitational parameter (m³/s²)
                    tof_seconds = tof_days * 86400
    
                    # Use simplified Lambert solver
                    lambert_result = lambert_solver_simple(r1, r2, tof_seconds, mu_sun)
    
                    # Calculate characteristic energy (C3)
                    if "v1_vec_ms" in lambert_result:
                        v1 = lambert_result["v1_vec_ms"]
                    elif "initial_velocity_ms" in lambert_result:
                        v1 = lambert_result["initial_velocity_ms"]
                    else:
                        v1 = [0, 0, 0]  # Fallback
    
                    # Earth's orbital velocity at 1 AU
                    earth_v_orbit = math.sqrt(mu_sun / (1.496e11))  # m/s
    
                    # Excess velocity magnitude
                    v_inf_vec = [
                        v1[i] - earth_v_orbit if i == 1 else v1[i] for i in range(3)
                    ]
                    v_inf_mag = math.sqrt(sum(v**2 for v in v_inf_vec))
    
                    # Characteristic energy C3 (km²/s²)
                    c3 = (v_inf_mag / 1000) ** 2  # Convert to km/s then square
    
                    # Delta-V estimate (simplified)
                    delta_v_ms = v_inf_mag
    
                    porkchop_data.append(
                        {
                            "departure_date": dep_date,
                            "arrival_date": arr_date,
                            "time_of_flight_days": tof_days,
                            "c3_km2_s2": c3,
                            "delta_v_ms": delta_v_ms,
                            "departure_position_m": r1,
                            "arrival_position_m": r2,
                            "transfer_feasible": c3 < 100.0,  # Reasonable C3 limit
                        }
                    )
    
                except Exception as e:
                    # Add failed transfer case
                    porkchop_data.append(
                        {
                            "departure_date": dep_date,
                            "arrival_date": arr_date,
                            "time_of_flight_days": tof_days,
                            "c3_km2_s2": float("inf"),
                            "delta_v_ms": float("inf"),
                            "departure_position_m": r1,
                            "arrival_position_m": r2,
                            "transfer_feasible": False,
                            "error": str(e),
                        }
                    )
    
        # Find optimal transfer
        feasible_transfers = [t for t in porkchop_data if t["transfer_feasible"]]
    
        optimal_transfer = None
        if feasible_transfers:
            # Find minimum C3 transfer
            optimal_transfer = min(feasible_transfers, key=lambda x: x["c3_km2_s2"])
    
        # Generate summary statistics
        if feasible_transfers:
            c3_values = [t["c3_km2_s2"] for t in feasible_transfers]
            tof_values = [t["time_of_flight_days"] for t in feasible_transfers]
    
            summary_stats = {
                "feasible_transfers": len(feasible_transfers),
                "total_transfers_computed": len(porkchop_data),
                "min_c3_km2_s2": min(c3_values),
                "max_c3_km2_s2": max(c3_values),
                "mean_c3_km2_s2": sum(c3_values) / len(c3_values),
                "min_tof_days": min(tof_values),
                "max_tof_days": max(tof_values),
                "mean_tof_days": sum(tof_values) / len(tof_values),
            }
        else:
            summary_stats = {
                "feasible_transfers": 0,
                "total_transfers_computed": len(porkchop_data),
                "min_c3_km2_s2": None,
                "max_c3_km2_s2": None,
                "mean_c3_km2_s2": None,
                "min_tof_days": min_tof_days,
                "max_tof_days": max_tof_days,
                "mean_tof_days": (min_tof_days + max_tof_days) / 2,
            }
    
        return {
            "departure_body": departure_body,
            "arrival_body": arrival_body,
            "transfer_grid": porkchop_data,
            "optimal_transfer": optimal_transfer,
            "summary_statistics": summary_stats,
            "constraints": {
                "min_tof_days": min_tof_days,
                "max_tof_days": max_tof_days,
                "max_feasible_c3_km2_s2": 100.0,
            },
            "note": "Simplified analysis using circular planetary orbits. Use SPICE kernels for production applications.",
        }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It states the tool 'generates' a plot, implying a read-only or computational operation, but doesn't clarify if it's resource-intensive, has rate limits, requires specific permissions, or what the 'analysis_options' might entail. The mention of 'JSON string' return is basic but doesn't detail the output structure or potential errors.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with a clear purpose statement followed by 'Args' and 'Returns' sections. It's appropriately sized for a 5-parameter tool, with no redundant information. However, the 'Args' section could be more integrated into the flow rather than a separate list, slightly affecting readability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (interplanetary analysis with 5 parameters), no annotations, and an output schema exists (implied by 'Returns' note), the description is moderately complete. It covers the purpose and parameters but lacks behavioral details (e.g., computational cost) and doesn't leverage the output schema to explain the return value beyond 'JSON string'. This leaves gaps for an AI agent to fully understand the tool's operation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It lists all 5 parameters with brief explanations (e.g., 'Departure celestial body name'), adding meaningful context beyond the schema's minimal titles. However, it doesn't specify formats (e.g., valid body names) or details for 'analysis_options', leaving some ambiguity. Since there are parameters, it doesn't reach the highest score.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Generate porkchop plot for interplanetary transfer opportunities.' This specifies the action (generate), the resource (porkchop plot), and the domain (interplanetary transfer opportunities). However, it doesn't explicitly differentiate from sibling tools like 'hohmann_transfer' or 'orbital_rendezvous_planning' which might also involve transfer calculations, so it doesn't reach the highest score.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention sibling tools like 'hohmann_transfer' or 'orbital_rendezvous_planning' that might be related, nor does it specify prerequisites or contexts for choosing this analysis method. The only implied usage is for interplanetary transfers, but this is too vague for effective tool selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cheesejaguar/aerospace-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server