{ "cells": [ { "cell_type": "markdown", "id": "2e0d0954", "metadata": {}, "source": [ "# Finite Wing with OOP (FiniteWing)\n", "\n", "This notebook recreates the finite wing workflow using the `FiniteWing` class from `src/aerodemo`." ] }, { "cell_type": "markdown", "id": "cc7c501e", "metadata": {}, "source": [ "## Python project setup\n", "\n", "Configure Python paths so modules in `src/` are discoverable in a clean, explicit way." ] }, { "cell_type": "code", "execution_count": 1, "id": "7a30c74c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Setting up environment...\n", "Notebook directory: F:\\agodemar\\AeroDemonstrator\\notebooks\\02_finite_wing\n", "Project root: F:\\agodemar\\AeroDemonstrator\n", "Using src path: F:\\agodemar\\AeroDemonstrator\\src\n", "Using aerodemo module path: F:\\agodemar\\AeroDemonstrator\\src\\aerodemo\n" ] } ], "source": [ "import os\n", "import sys\n", "from pathlib import Path\n", "\n", "print(\"Setting up environment...\")\n", "this_notebook_dir = Path.cwd().resolve()\n", "project_root = (this_notebook_dir / \"..\" / \"..\").resolve()\n", "src_root = project_root / \"src\"\n", "aerodemo_root = src_root / \"aerodemo\"\n", "\n", "print(f\"Notebook directory: {this_notebook_dir}\")\n", "print(f\"Project root: {project_root}\")\n", "\n", "if not src_root.is_dir():\n", " raise FileNotFoundError(f\"Could not find src directory at: {src_root}\")\n", "if not aerodemo_root.is_dir():\n", " raise FileNotFoundError(f\"Could not find aerodemo directory at: {aerodemo_root}\")\n", "\n", "# Add both roots: src for package imports, src/aerodemo for module-style imports\n", "for p in (src_root, aerodemo_root):\n", " p_str = str(p)\n", " if p_str not in sys.path:\n", " sys.path.insert(0, p_str)\n", "\n", "print(f\"Using src path: {src_root}\")\n", "print(f\"Using aerodemo module path: {aerodemo_root}\")" ] }, { "cell_type": "code", "execution_count": 2, "id": "75cff461", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Successfully imported FiniteWing, WingSegment, and AirfoilSpec.\n" ] } ], "source": [ "# Import OOP wing builder and related types\n", "import FiniteWing as finitewing_module\n", "from FiniteWing import FiniteWing\n", "from WingSegment import WingSegment\n", "from AirfoilSpec import AirfoilSpec\n", "\n", "# OpenVSP handle used by FiniteWing module\n", "vsp = finitewing_module.vsp\n", "\n", "print(\"Successfully imported FiniteWing, WingSegment, and AirfoilSpec.\")" ] }, { "cell_type": "markdown", "id": "d7cfce77", "metadata": {}, "source": [ "## Build wing geometry with FiniteWing" ] }, { "cell_type": "code", "execution_count": 3, "id": "154a0d6c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "FiniteWing:\n", " name: FW_OOP_01\n", " wing_id: None\n", " mirrored: True\n", " root_incidence_deg: 0.000 deg\n", " rotate_airfoils_with_dihedral: True\n", " root_cap_shape: Flat\n", " tip_cap_shape: Round\n", " root_cap_length: 0.000 m\n", " tip_cap_length: 0.200 m\n", " root_cap_strength: 1.000 (-)\n", " tip_cap_strength: 1.000 (-)\n", " location_xyz: (0.000, 0.000, 0.000) m\n", " rotation_xyz: (0.000, 0.000, 0.000) deg\n", " clear_model_first: True\n", " num_segments: 2\n", " segments:\n", " WingSegment:\n", " id: WS_INNER\n", " span: 7.000 m\n", " root_chord: 2.000 m\n", " tip_chord: 2.000 m\n", " sweep_deg: 0.000 deg\n", " dihedral_deg: 0.000 deg\n", " twist_deg: -2.000 deg\n", " sweep_ref_loc: 0.250 (-)\n", " twist_ref_loc: 0.250 (-)\n", " num_U: 10 (-)\n", " cluster_root: 1.000 (-)\n", " cluster_tip: 1.000 (-)\n", " WingSegment:\n", " id: WS_OUTER\n", " span: 7.000 m\n", " root_chord: 2.000 m\n", " tip_chord: 1.200 m\n", " sweep_deg: 0.000 deg\n", " dihedral_deg: 0.000 deg\n", " twist_deg: -4.000 deg\n", " sweep_ref_loc: 0.250 (-)\n", " twist_ref_loc: 0.250 (-)\n", " num_U: 16 (-)\n", " cluster_root: 1.000 (-)\n", " cluster_tip: 0.200 (-)\n", " num_station_airfoils: 3\n", " station_airfoils:\n", " AirfoilSpec:\n", " kind: naca4\n", " thickness: 0.120 (-)\n", " camber: 0.020 (-)\n", " camber_loc: 0.400 x/c\n", " sharp_te: True\n", " file_path: None\n", " xsec_type: None\n", " extra_parms: {}\n", " AirfoilSpec:\n", " kind: naca4\n", " thickness: 0.120 (-)\n", " camber: 0.020 (-)\n", " camber_loc: 0.400 x/c\n", " sharp_te: True\n", " file_path: None\n", " xsec_type: None\n", " extra_parms: {}\n", " AirfoilSpec:\n", " kind: naca4\n", " thickness: 0.100 (-)\n", " camber: 0.000 (-)\n", " camber_loc: 0.400 x/c\n", " sharp_te: True\n", " file_path: None\n", " xsec_type: None\n", " extra_parms: {}\n" ] } ], "source": [ "c_r = 2.0 # m\n", "c_t = 1.2 # m\n", "span = 14.0 # m\n", "dihedral_deg = 0.0 # deg\n", "\n", "# Define twist values relative to rigging angle, and compute absolute twist values\n", "rigging_deg = 0.0 # deg\n", "twist_rel_1_deg = -2.0 # deg\n", "twist_1_deg = rigging_deg + twist_rel_1_deg\n", "twist_rel_2_deg = -2.0 # deg\n", "twist_2_deg = rigging_deg + twist_rel_1_deg + twist_rel_2_deg\n", "\n", "segments = [\n", " WingSegment(\n", " id=\"WS_INNER\",\n", " span=span/2,\n", " root_chord=c_r,\n", " tip_chord=c_r,\n", " sweep_deg=0.0,\n", " dihedral_deg=dihedral_deg,\n", " twist_deg=twist_1_deg,\n", " num_U=10, # Optional: set number of chordwise points for this segment\n", " cluster_root=1.0, # Optional: cluster points towards root (0.0 to 1.0)\n", " cluster_tip=1.0, # Optional: cluster points towards tip (0.0 to 1.0)\n", " ),\n", " WingSegment(\n", " id=\"WS_OUTER\",\n", " span=span/2,\n", " root_chord=c_r,\n", " tip_chord=c_t,\n", " sweep_deg=0.0,\n", " dihedral_deg=dihedral_deg,\n", " twist_deg=twist_2_deg,\n", " num_U=16, # Optional: set number of chordwise points for this segment\n", " cluster_root=1.0, # Optional: cluster points towards root (0.0 to 1.0)\n", " cluster_tip=0.2, # Optional: cluster points towards tip (0.0 to 1.0)\n", " )\n", "]\n", "\n", "station_airfoils = [\n", " AirfoilSpec(kind=\"naca4\", camber=0.02, camber_loc=0.4, thickness=0.12),\n", " AirfoilSpec(kind=\"naca4\", camber=0.02, camber_loc=0.4, thickness=0.12),\n", " AirfoilSpec(kind=\"naca4\", camber=0.00, camber_loc=0.4, thickness=0.10),\n", "]\n", "\n", "wing = FiniteWing(\n", " name=\"FW_OOP_01\",\n", " mirrored=True,\n", " segments=segments,\n", " station_airfoils=station_airfoils,\n", " root_incidence_deg=rigging_deg,\n", " tip_cap_shape=\"Round\",\n", " tip_cap_length=0.20,\n", ")\n", "\n", "print(wing)" ] }, { "cell_type": "markdown", "id": "044042f7", "metadata": {}, "source": [ "## Build and save outputs" ] }, { "cell_type": "code", "execution_count": 4, "id": "e2a5123d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Built wing geometry ID: KWJRABRPTV\n", "Saved model to: F:\\agodemar\\AeroDemonstrator\\notebooks\\02_finite_wing\\01_finite_wing_vsp_oop\\01_finite_wing_oop.vsp3\n" ] } ], "source": [ "# Store all notebook outputs in a dedicated local folder\n", "output_dir = this_notebook_dir / \"01_finite_wing_vsp_oop\"\n", "output_dir.mkdir(parents=True, exist_ok=True)\n", "\n", "wing_id = wing.build()\n", "print(f\"Built wing geometry ID: {wing_id}\")\n", "\n", "vsp3_path = output_dir / \"01_finite_wing_oop.vsp3\"\n", "wing.save(str(vsp3_path))\n", "print(f\"Saved model to: {vsp3_path}\")" ] }, { "cell_type": "markdown", "id": "a8b2cf5d", "metadata": {}, "source": [ "## Run VSPAero and save analysis files" ] }, { "cell_type": "code", "execution_count": 5, "id": "2356ede8", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "================= Results:\n", "\tExecuting compute geometry...\n", "\tCOMPLETE\n", "\tExecuting aero-sweep-analysis...\n", "\tCOMPLETE\n", "\n", "Saved CSV to: F:\\agodemar\\AeroDemonstrator\\notebooks\\02_finite_wing\\01_finite_wing_vsp_oop\\01_finite_wing_oop_analysis_results.csv\n", "Files in output directory:\n", " - 01_finite_wing_oop.adb\n", " - 01_finite_wing_oop.adb.cases\n", " - 01_finite_wing_oop.case.1.quad.1.dat\n", " - 01_finite_wing_oop.csf\n", " - 01_finite_wing_oop.cuts\n", " - 01_finite_wing_oop.group.0\n", " - 01_finite_wing_oop.group.1\n", " - 01_finite_wing_oop.history\n", " - 01_finite_wing_oop.lod\n", " - 01_finite_wing_oop.polar\n", " - 01_finite_wing_oop.quad.cases\n", " - 01_finite_wing_oop.slc\n", " - 01_finite_wing_oop.vkey\n", " - 01_finite_wing_oop.vsp3\n", " - 01_finite_wing_oop.vspaero\n", " - 01_finite_wing_oop.vspgeom\n", " - 01_finite_wing_oop_analysis_results.csv\n" ] } ], "source": [ "import io\n", "from contextlib import redirect_stdout\n", "\n", "previous_cwd = Path.cwd()\n", "os.chdir(output_dir)\n", "try:\n", " # Compute geometry\n", " vsp.SetAnalysisInputDefaults(\"VSPAEROComputeGeometry\")\n", " vsp.SetIntAnalysisInput(\"VSPAEROComputeGeometry\", \"GeomSet\", [vsp.SET_NONE], 0)\n", " vsp.SetIntAnalysisInput(\"VSPAEROComputeGeometry\", \"ThinGeomSet\", [vsp.SET_ALL], 0)\n", "\n", " buf = io.StringIO()\n", " with redirect_stdout(buf):\n", " print(\"\\tExecuting compute geometry...\")\n", " compgeom_resid = vsp.ExecAnalysis(\"VSPAEROComputeGeometry\")\n", " print(\"\\tCOMPLETE\")\n", " vsp.PrintResults(compgeom_resid)\n", "\n", " # Sweep setup\n", " vsp.SetAnalysisInputDefaults(\"VSPAEROSweep\")\n", " vsp.SetIntAnalysisInput(\"VSPAEROSweep\", \"GeomSet\", [vsp.SET_NONE], 0)\n", " vsp.SetIntAnalysisInput(\"VSPAEROSweep\", \"ThinGeomSet\", [vsp.SET_ALL], 0)\n", " vsp.SetIntAnalysisInput(\"VSPAEROSweep\", \"RefFlag\", [1], 0)\n", " vsp.SetIntAnalysisInput(\"VSPAEROSweep\", \"Symmetry\", [1], 0)\n", "\n", " wid = vsp.FindGeomsWithName(\"FW_OOP_01\")\n", " if not wid:\n", " wid = vsp.FindGeomsWithName(\"WingGeom\")\n", " vsp.SetStringAnalysisInput(\"VSPAEROSweep\", \"WingID\", wid, 0)\n", "\n", " vsp.SetDoubleAnalysisInput(\"VSPAEROSweep\", \"AlphaStart\", [1.0], 0)\n", " vsp.SetIntAnalysisInput(\"VSPAEROSweep\", \"AlphaNpts\", [1], 0)\n", " vsp.SetDoubleAnalysisInput(\"VSPAEROSweep\", \"MachStart\", [0.1], 0)\n", " vsp.SetIntAnalysisInput(\"VSPAEROSweep\", \"MachNpts\", [1], 0)\n", " vsp.SetIntAnalysisInput(\"VSPAEROSweep\", \"WakeNumIter\", [8], 0)\n", "\n", " vsp.Update()\n", " print(\"\\tExecuting aero-sweep-analysis...\")\n", " rid = vsp.ExecAnalysis(\"VSPAEROSweep\")\n", " print(\"\\tCOMPLETE\")\n", " vsp.PrintResults(rid)\n", "\n", " csv_file_path = output_dir / \"01_finite_wing_oop_analysis_results.csv\"\n", " vsp.WriteResultsCSVFile(rid, str(csv_file_path))\n", "\n", " captured_logs = buf.getvalue()\n", " print(\"================= Results:\")\n", " print(captured_logs)\n", " print(f\"Saved CSV to: {csv_file_path}\")\n", "\n", " generated = sorted([p.name for p in output_dir.iterdir() if p.is_file()])\n", " print(\"Files in output directory:\")\n", " for name in generated:\n", " print(f\" - {name}\")\n", "finally:\n", " os.chdir(previous_cwd)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.15" } }, "nbformat": 4, "nbformat_minor": 5 }