xfoil.compute_polar
Calculate airfoil lift and drag coefficients by running XFOIL analysis with specified Reynolds number and angle of attack sweep parameters.
Instructions
Run XFOIL for an airfoil at specified Reynolds angle of attack sweep. Input airfoil coordinates or NACA code plus sweep parameters. Returns lift/drag polar tables and solver metadata.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| request | Yes |
Implementation Reference
- src/xfoil_mcp/core.py:17-54 (handler)Core handler function implementing the tool logic: runs XFOIL subprocess, handles output, formats CSV polar data.def compute_polar(request: PolarRequest) -> PolarResponse: """Run XFOIL with the provided parameters and return a CSV payload.""" script, polar_path = _prepare_script(request) try: result = subprocess.run( [XFOIL_BIN], input=script.encode("utf-8"), cwd=polar_path.parent, check=False, capture_output=True, ) except FileNotFoundError as exc: # pragma: no cover raise RuntimeError("XFOIL binary not found") from exc if not polar_path.exists(): raise RuntimeError( "XFOIL did not create the polar output", ) if result.returncode != 0: # XFOIL often exits with code 2 even when the polar file is valid. We keep # the output but annotate the CSV with a comment so downstream users are # aware of the non-zero status. warning_comment = f"# xfoil exit code {result.returncode}" else: warning_comment = None lines = polar_path.read_text(encoding="utf-8").splitlines() if warning_comment: lines.insert(0, warning_comment) rows = list(csv.reader(lines)) if rows and rows[0] and "alpha" not in rows[0][0].lower(): rows.insert(0, ["alpha", "CL", "CD", "CM"]) csv_text = "\n".join(",".join(row) for row in rows) return PolarResponse(csv=csv_text)
- src/xfoil_mcp/tool.py:14-24 (registration)Tool registration via @app.tool decorator in build_tool function, providing the tool name, description, and thin wrapper handler.@app.tool( name="xfoil.compute_polar", description=( "Run XFOIL for an airfoil at specified Reynolds angle of attack sweep. " "Input airfoil coordinates or NACA code plus sweep parameters. " "Returns lift/drag polar tables and solver metadata." ), meta={"version": "0.1.1", "categories": ["aero", "analysis"]}, ) def polar(request: PolarRequest) -> PolarResponse: return compute_polar(request)
- src/xfoil_mcp/models.py:8-23 (schema)Pydantic models defining input schema (PolarRequest) and output schema (PolarResponse) for the tool.class PolarRequest(BaseModel): """Parameters required to run an XFOIL polar sweep.""" airfoil_name: str = Field(..., description="Identifier used when writing temporary files") airfoil_data: str = Field(..., description="Airfoil coordinate data in XFOIL DAT format") alphas: list[float] = Field(..., description="Angles of attack to analyse") reynolds: float = Field(..., gt=0.0, description="Reynolds number") mach: float = Field(0.0, ge=0.0, description="Mach number") iterations: int = Field(200, ge=10, le=10000, description="Maximum solver iterations") class PolarResponse(BaseModel): """CSV payload returned by `compute_polar`.""" csv: str = Field(..., description="CSV text containing XFOIL polar data")
- src/xfoil_mcp/core.py:57-83 (helper)Helper function that prepares the XFOIL input script and temporary file paths based on the request.def _prepare_script(request: PolarRequest) -> tuple[str, Path]: tmpdir = Path(tempfile.mkdtemp(prefix="xfoil_mcp_")) airfoil_path = tmpdir / f"{request.airfoil_name}.dat" polar_path = tmpdir / "polar.txt" airfoil_path.write_text(request.airfoil_data, encoding="utf-8") airfoil_name = airfoil_path.name polar_name = polar_path.name commands: Iterable[str] = [ f"LOAD {airfoil_name}", "PANE", "OPER", f"VISC {request.reynolds}", f"MACH {request.mach}", f"ITER {request.iterations}", "PACC", polar_name, "", ] alpha_cmds = [f"ALFA {alpha}" for alpha in request.alphas] if not alpha_cmds: raise RuntimeError("At least one angle of attack must be supplied") footer = ["PACC", "", "QUIT"] script = "\n".join([*commands, *alpha_cmds, *footer]) + "\n" return script, polar_path