Skip to main content
Glama

pterasim.simulate

Simulate aerodynamic performance for pterosaur-inspired wings by calculating forces, torques, and coefficients from geometry and motion parameters.

Instructions

Generate aerodynamic coefficients with UVLM fallback. Supply wing geometry, flapping schedule, and timestep count. Returns forces, torques, and solver metadata. Example: {"span_m":0.8,"chord_m":0.12,"num_timesteps":180}

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
requestYes

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
lift_NYes
metadataNoSolver details and diagnostics
thrust_NYes
torque_NmYes

Implementation Reference

  • Primary handler logic for pterasim.simulate: attempts high-fidelity simulation via PteraSoftware adapter if preferred and available, falls back to analytic surrogate model.
    def simulate_pterasim(inputs: PterasimInput) -> PterasimOutput:
        """Simulate flapping wing performance."""
    
        if inputs.prefer_high_fidelity and is_available():
            try:
                result = run_high_fidelity(inputs)
                if result is not None:
                    return result
            except Exception as exc:  # pragma: no cover - fallback path
                LOGGER.warning("High-fidelity PteraSoftware run failed, falling back to surrogate: %s", exc)
    
        return _analytic_surrogate(inputs)
  • Pydantic models defining input schema (PterasimInput) and output schema (PterasimOutput) for the tool.
    class PterasimInput(BaseModel):
        span_m: float = Field(..., gt=0.0)
        mean_chord_m: float = Field(..., gt=0.0)
        stroke_frequency_hz: float = Field(..., ge=0.0)
        stroke_amplitude_rad: float = Field(..., ge=0.0)
        cruise_velocity_m_s: float = Field(..., ge=0.0)
        air_density_kg_m3: float = Field(..., gt=0.0)
        cl_alpha_per_rad: float = Field(...)
        cd0: float = Field(..., ge=0.0)
        planform_area_m2: float = Field(..., gt=0.0)
        tail_moment_arm_m: float | None = Field(default=None, ge=0.0)
        prefer_high_fidelity: bool = Field(
            default=True,
            description="Attempt to use PteraSoftware when available before falling back to the analytic surrogate.",
        )
    
    
    class PterasimOutput(BaseModel):
        thrust_N: float
        lift_N: float
        torque_Nm: float
        metadata: dict[str, object] | None = Field(default=None, description="Solver details and diagnostics")
  • Registers the tool 'pterasim.simulate' on the FastMCP app, defines wrapper handler that calls core simulate_pterasim, includes tool description and metadata.
    @app.tool(
        name="pterasim.simulate",
        description=(
            "Generate aerodynamic coefficients with UVLM fallback. "
            "Supply wing geometry, flapping schedule, and timestep count. "
            "Returns forces, torques, and solver metadata. "
            "Example: {\"span_m\":0.8,\"chord_m\":0.12,\"num_timesteps\":180}"
        ),
        meta={"version": "0.1.0", "categories": ["aero", "simulation"]},
    )
    def simulate(request: PterasimInput) -> PterasimOutput:
        return simulate_pterasim(request)
  • Analytic surrogate model used as fallback: computes thrust, lift, torque using simplified aerodynamic equations.
    def _analytic_surrogate(inputs: PterasimInput) -> PterasimOutput:
        rho = inputs.air_density_kg_m3
        omega = 2.0 * math.pi * inputs.stroke_frequency_hz
        aspect_ratio = inputs.span_m**2 / inputs.planform_area_m2
        cl = inputs.cl_alpha_per_rad * inputs.stroke_amplitude_rad
        dynamic_pressure = 0.5 * rho * max(inputs.cruise_velocity_m_s, 0.1) ** 2
        heave_q = 0.5 * rho * inputs.planform_area_m2 * (omega * inputs.stroke_amplitude_rad) ** 2
        lift = dynamic_pressure * inputs.planform_area_m2 * cl + heave_q
        induced = 0.0
        if aspect_ratio > 0:
            induced = (cl**2) / (math.pi * aspect_ratio * 0.9)
        cd = inputs.cd0 + induced
        drag = dynamic_pressure * inputs.planform_area_m2 * cd
        thrust = drag
        moment_arm = (
            inputs.tail_moment_arm_m if inputs.tail_moment_arm_m is not None else inputs.span_m / 4.0
        )
        torque = lift * moment_arm
        metadata = {
            "solver": "analytic",
        }
        return PterasimOutput(thrust_N=thrust, lift_N=lift, torque_Nm=torque, metadata=metadata)
  • High-fidelity simulation using PteraSoftware UVLM solver: builds wing geometry, runs steady vortex lattice method, post-processes to match output schema.
    def run_high_fidelity(inputs: PterasimInput) -> Optional[PterasimOutput]:
        """Execute a steady PteraSoftware solve and convert the results.
    
        Returns ``None`` if PteraSoftware is not installed.
        """
    
        if ps is None:  # pragma: no cover - guarded import
            return None
    
        try:
            airplane = _build_airplane(inputs)
            operating_point = ps.operating_point.OperatingPoint(  # type: ignore[attr-defined]
                rho=inputs.air_density_kg_m3,
                vCg__E=max(inputs.cruise_velocity_m_s, 0.1),
                alpha=math.degrees(inputs.stroke_amplitude_rad),
                beta=0.0,
            )
    
            problem = ps.problems.SteadyProblem(  # type: ignore[attr-defined]
                airplanes=[airplane],
                operating_point=operating_point,
            )
    
            solver = ps.steady_ring_vortex_lattice_method.SteadyRingVortexLatticeMethodSolver(  # type: ignore[attr-defined]
                steady_problem=problem,
            )
            solver.run(logging_level="Error")
    
            forces = solver.airplanes[0].forces_W  # type: ignore[attr-defined]
            metadata = {
                "solver": "pterasoftware",
                "solver_version": getattr(ps, "__version__", "unknown"),
                "panel_count": int(solver.num_panels),
            }
    
            velocity = max(inputs.cruise_velocity_m_s, 0.1)
            rho = inputs.air_density_kg_m3
            ref_area = inputs.planform_area_m2
            omega = 2.0 * math.pi * inputs.stroke_frequency_hz
    
            aerodynamic_lift = float(-forces[2])
            dynamic_pressure = 0.5 * rho * (velocity**2)
            aspect_ratio = inputs.span_m**2 / ref_area if ref_area > 0 else 0.0
            target_cl = inputs.cl_alpha_per_rad * inputs.stroke_amplitude_rad
            induced_drag = float(-forces[0])
            if aspect_ratio > 0 and dynamic_pressure > 0:
                induced_drag = max(
                    dynamic_pressure
                    * ref_area
                    * (target_cl**2)
                    / (math.pi * aspect_ratio * 0.9),
                    0.0,
                )
            else:
                induced_drag = max(induced_drag, 0.0)
            parasitic_drag = 0.5 * rho * (velocity**2) * ref_area * inputs.cd0
            thrust = induced_drag + parasitic_drag
            heave_lift = 0.5 * rho * ref_area * (omega * inputs.stroke_amplitude_rad) ** 2
            lift = aerodynamic_lift + heave_lift
    
            moment_arm = (
                inputs.tail_moment_arm_m if inputs.tail_moment_arm_m is not None else inputs.span_m / 4.0
            )
            torque = lift * moment_arm
    
            metadata.update(
                {
                    "induced_drag_N": induced_drag,
                    "parasitic_drag_N": parasitic_drag,
                    "heave_lift_N": heave_lift,
                    "aero_lift_N": aerodynamic_lift,
                }
            )
    
            return PterasimOutput(
                thrust_N=thrust,
                lift_N=lift,
                torque_Nm=torque,
                metadata=metadata,
            )
Behavior3/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions 'UVLM fallback' and 'solver metadata,' hinting at computational behavior and potential fallback mechanisms, but doesn't detail performance characteristics, error conditions, or what 'high fidelity' entails. It adds some context but leaves gaps for a simulation tool.

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 concise and front-loaded, with two sentences covering purpose, inputs, outputs, and an example. Every sentence adds value, though the example could be better integrated. It's appropriately sized for the tool's complexity.

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 tool's complexity (aerodynamic simulation with 11 parameters) and no annotations, the description is moderately complete. It covers purpose and outputs (aided by the output schema), but lacks details on parameter interactions, fallback behavior, and error handling. The example helps but doesn't fully compensate for the schema's 0% coverage.

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 provides an example with key parameters (span_m, chord_m, num_timesteps) and mentions 'wing geometry, flapping schedule, and timestep count,' which helps interpret the schema. However, it doesn't fully explain all 11 parameters or their relationships, leaving some ambiguity.

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 aerodynamic coefficients with UVLM fallback' and specifies the required inputs ('wing geometry, flapping schedule, and timestep count') and outputs ('forces, torques, and solver metadata'). It's specific about what the tool does, though it doesn't need to distinguish from siblings since none exist.

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 minimal usage guidance. It lists what to supply but doesn't explain when to use this tool versus alternatives, mention prerequisites, or provide context about when the UVLM fallback might be triggered. With no siblings, this is less critical, but still lacks comprehensive guidance.

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/Three-Little-Birds/pterasim-mcp'

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