From 1ce07ee9c1fe12df43431f2ee3db5db5a35a51aa Mon Sep 17 00:00:00 2001 From: jellepoland Date: Fri, 24 Jul 2026 09:59:02 +0200 Subject: [PATCH 01/13] Improve plotting geometry and update tutorial Co-Authored-By: Claude Opus 4.8 (1M context) --- .../struc_geometry_manually_adjusted.yaml | 188 ++++++----- examples/TUDELFT_V3_KITE/tutorial.py | 302 +++++++++--------- src/VSM/plot_geometry_plotly.py | 13 +- 3 files changed, 256 insertions(+), 247 deletions(-) diff --git a/data/TUDELFT_V3_KITE/CAD_derived_geometry/struc_geometry_manually_adjusted.yaml b/data/TUDELFT_V3_KITE/CAD_derived_geometry/struc_geometry_manually_adjusted.yaml index fdc68f0..4987dc6 100644 --- a/data/TUDELFT_V3_KITE/CAD_derived_geometry/struc_geometry_manually_adjusted.yaml +++ b/data/TUDELFT_V3_KITE/CAD_derived_geometry/struc_geometry_manually_adjusted.yaml @@ -1,8 +1,7 @@ - ############################## ## General ################### ############################## -bridle_point_node: [0,0,0] #[x,y,z] --> location of kcu +bridle_point_node: [0, 0, 0] #[x,y,z] --> location of kcu ## Mass pulley_mass: !!float 0.1 #[kg] #TODO: would be better to define these per pulley @@ -15,7 +14,6 @@ dyneema: youngs_modulus: 550000000 #[Pa] -> k = E A / l0 damping_per_stiffness: 0.0 # [/s] -> c = damping_per_stiffness * k - ########################### ## Wing ################### ########################### @@ -117,38 +115,38 @@ wing_elements: # line name, measured rest length "l0" [m], stiffness "k" [N/m], damping "c" [N.s/m], mass "m" [kg], linktype headers: [name, l0, k, c, m, linktype] data: - # leading-edge tube (LE[i] -- LE[i+1]) - - [le_1, 0.984642, 2e3, 0, 1, "default"] - - [le_2, 1.327663, 2e3, 0, 1, "default"] - - [le_3, 1.326240, 2e3, 0, 1, "default"] - - [le_4, 1.323655, 2e3, 0, 1, "default"] - - [le_5, 1.347595, 2e3, 0, 1, "default"] - - # struts (chords) - - [strut_1, 1.074728, 2e3, 0, 1, "default"] - - [strut_2, 1.511911, 2e3, 0, 1, "default"] - - [strut_3, 1.960560, 2e3, 0, 1, "default"] - - [strut_4, 2.154185, 2e3, 0, 1, "default"] - - [strut_5, 2.265501, 2e3, 0, 1, "default"] - - # trailing-edge wire (TE[i] -- TE[i+1]) - - [te_1, 0.903854, 1e3, 0, 0.2, "default"] - - [te_2, 1.351057, 1e3, 0, 0.2, "default"] - - [te_3, 1.350587, 1e3, 0, 0.2, "default"] - - [te_4, 1.350462, 1e3, 0, 0.2, "default"] - - [te_5, 1.321072, 1e3, 0, 0.2, "default"] - - # diagonal springs (both directions per panel) - - [dia_1a, 1.360356, 1e3, 0, 0.1, "default"] - - [dia_1b, 1.780032, 1e3, 0, 0.1, "default"] - - [dia_2a, 2.108078, 1e3, 0, 0.1, "default"] - - [dia_2b, 2.251513, 1e3, 0, 0.1, "default"] - - [dia_3a, 2.449188, 1e3, 0, 0.1, "default"] - - [dia_3b, 2.455523, 1e3, 0, 0.1, "default"] - - [dia_4a, 2.601005, 1e3, 0, 0.1, "default"] - - [dia_4b, 2.563136, 1e3, 0, 0.1, "default"] - - [dia_5a, 2.629214, 1e3, 0, 0.1, "default"] - - [dia_5b, 2.629214, 1e3, 0, 0.1, "default"] + # leading-edge tube (LE[i] -- LE[i+1]) + - [le_1, 0.984642, 2e3, 0, 1, 'default'] + - [le_2, 1.327663, 2e3, 0, 1, 'default'] + - [le_3, 1.326240, 2e3, 0, 1, 'default'] + - [le_4, 1.323655, 2e3, 0, 1, 'default'] + - [le_5, 1.347595, 2e3, 0, 1, 'default'] + + # struts (chords) + - [strut_1, 1.074728, 2e3, 0, 1, 'default'] + - [strut_2, 1.511911, 2e3, 0, 1, 'default'] + - [strut_3, 1.960560, 2e3, 0, 1, 'default'] + - [strut_4, 2.154185, 2e3, 0, 1, 'default'] + - [strut_5, 2.265501, 2e3, 0, 1, 'default'] + + # trailing-edge wire (TE[i] -- TE[i+1]) + - [te_1, 0.903854, 1e3, 0, 0.2, 'default'] + - [te_2, 1.351057, 1e3, 0, 0.2, 'default'] + - [te_3, 1.350587, 1e3, 0, 0.2, 'default'] + - [te_4, 1.350462, 1e3, 0, 0.2, 'default'] + - [te_5, 1.321072, 1e3, 0, 0.2, 'default'] + + # diagonal springs (both directions per panel) + - [dia_1a, 1.360356, 1e3, 0, 0.1, 'default'] + - [dia_1b, 1.780032, 1e3, 0, 0.1, 'default'] + - [dia_2a, 2.108078, 1e3, 0, 0.1, 'default'] + - [dia_2b, 2.251513, 1e3, 0, 0.1, 'default'] + - [dia_3a, 2.449188, 1e3, 0, 0.1, 'default'] + - [dia_3b, 2.455523, 1e3, 0, 0.1, 'default'] + - [dia_4a, 2.601005, 1e3, 0, 0.1, 'default'] + - [dia_4b, 2.563136, 1e3, 0, 0.1, 'default'] + - [dia_5a, 2.629214, 1e3, 0, 0.1, 'default'] + - [dia_5b, 2.629214, 1e3, 0, 0.1, 'default'] ############################# ## Bridle ################### @@ -177,71 +175,71 @@ bridle_particles: bridle_connections: # name, ci, cj, ck (this is the third node, only exist when a pulley) - headers: [name, ci, cj, ck] + headers: [name, ci, cj, ck] data: # front lines - - [a6,25,1,33] # pulley - - [A5,3,21] - - [A3,5,21] - - [A2,7,22] - - [A1,9,22] - - [A1,11,23] - - [A2,13,23] - - [A3,15,24] - - [A5,17,24] - - [a6,26,19,35] # pulley - - [AII,21,25] - - [AI,22,25] - - [AI,23,26] - - [AII,24,26] - - [amain,25,0] - - [amain,26,0] + - [a6, 25, 1, 33] # pulley + - [A5, 3, 21] + - [A3, 5, 21] + - [A2, 7, 22] + - [A1, 9, 22] + - [A1, 11, 23] + - [A2, 13, 23] + - [A3, 15, 24] + - [A5, 17, 24] + - [a6, 26, 19, 35] # pulley + - [AII, 21, 25] + - [AI, 22, 25] + - [AI, 23, 26] + - [AII, 24, 26] + - [amain, 25, 0] + - [amain, 26, 0] # rear lines - - [br6,33,2,36] # pulley - - [br5,4,27] - - [br3,6,27] - - [br2,8,28] - - [br1,10,28] - - [br1,12,29] - - [br2,14,29] - - [br3,16,30] - - [br5,18,30] - - [br6,35,20,37] # pulley - - [BRII,27,31] - - [BRI,28,31] - - [BRI,29,32] - - [BRII,30,32] - - [BR-main-1,31,33] - - [BR-main-1,32,35] - - [M-line,36,33,34] # pulley - - [M-line,34,35,37] # pulley - - [Steering Tape,36,0] - - [Power Tape,34,0] - - [Steering Tape,37,0] + - [br6, 33, 2, 36] # pulley + - [br5, 4, 27] + - [br3, 6, 27] + - [br2, 8, 28] + - [br1, 10, 28] + - [br1, 12, 29] + - [br2, 14, 29] + - [br3, 16, 30] + - [br5, 18, 30] + - [br6, 35, 20, 37] # pulley + - [BRII, 27, 31] + - [BRI, 28, 31] + - [BRI, 29, 32] + - [BRII, 30, 32] + - [BR-main-1, 31, 33] + - [BR-main-1, 32, 35] + - [M-line, 36, 33, 34] # pulley + - [M-line, 34, 35, 37] # pulley + - [Steering Tape, 36, 0] + - [Power Tape, 34, 0] + - [Steering Tape, 37, 0] bridle_lines: # line name, measured rest length "l0" [m], stiffness "k" [N/m], damping "c" [N.s/m], diameter "d" [m], density "rho" [kg/m^3] # dyneema has rho = 970 - headers: [name, l0, diameter, material, linktype] + headers: [name, l0, d, material, linktype] data: - # front lines - - [a6, 11.372, 0.005, dyneema, "pulley"] - - [A5, 2.782, 0.005, dyneema, "noncompressive"] - - [A3, 3.253, 0.005, dyneema, "noncompressive"] - - [A2, 3.352, 0.005, dyneema, "noncompressive"] - - [A1, 3.520, 0.005, dyneema, "noncompressive"] - - [AII, 3.188, 0.01, dyneema, "noncompressive"] - - [AI, 3.420, 0.01, dyneema, "noncompressive"] - - [amain, 4.297, 0.02, dyneema, "noncompressive"] - # rear lines - - [br1, 4.448, 0.002, dyneema, "noncompressive"] - - [br2, 4.300, 0.002, dyneema, "noncompressive"] - - [br3, 4.560, 0.002, dyneema, "noncompressive"] - - [br5, 4.047, 0.002, dyneema, "noncompressive"] - - [br6, 14.172, 0.002, dyneema, "pulley"] - - [BRI, 2.388, 0.002, dyneema, "noncompressive"] - - [BRII, 1.890, 0.002, dyneema, "noncompressive"] - - [BR-main-1, 0.874, 0.002, dyneema, "noncompressive"] - - [M-line, 2.196, 0.002, dyneema, "pulley"] - - [Steering Tape, 1.506, 0.002, dyneema, "noncompressive"] - - [Power Tape, 3.129, 0.002, dyneema, "noncompressive"] \ No newline at end of file + # front lines + - [a6, 11.372, 0.005, dyneema, 'pulley'] + - [A5, 2.782, 0.005, dyneema, 'noncompressive'] + - [A3, 3.253, 0.005, dyneema, 'noncompressive'] + - [A2, 3.352, 0.005, dyneema, 'noncompressive'] + - [A1, 3.520, 0.005, dyneema, 'noncompressive'] + - [AII, 3.188, 0.01, dyneema, 'noncompressive'] + - [AI, 3.420, 0.01, dyneema, 'noncompressive'] + - [amain, 4.297, 0.02, dyneema, 'noncompressive'] + # rear lines + - [br1, 4.448, 0.002, dyneema, 'noncompressive'] + - [br2, 4.300, 0.002, dyneema, 'noncompressive'] + - [br3, 4.560, 0.002, dyneema, 'noncompressive'] + - [br5, 4.047, 0.002, dyneema, 'noncompressive'] + - [br6, 14.172, 0.002, dyneema, 'pulley'] + - [BRI, 2.388, 0.002, dyneema, 'noncompressive'] + - [BRII, 1.890, 0.002, dyneema, 'noncompressive'] + - [BR-main-1, 0.874, 0.002, dyneema, 'noncompressive'] + - [M-line, 2.196, 0.002, dyneema, 'pulley'] + - [Steering Tape, 1.506, 0.002, dyneema, 'noncompressive'] + - [Power Tape, 3.129, 0.002, dyneema, 'noncompressive'] diff --git a/examples/TUDELFT_V3_KITE/tutorial.py b/examples/TUDELFT_V3_KITE/tutorial.py index f5460fd..11ce873 100644 --- a/examples/TUDELFT_V3_KITE/tutorial.py +++ b/examples/TUDELFT_V3_KITE/tutorial.py @@ -49,7 +49,7 @@ def main(): PROJECT_DIR = Path(__file__).resolve().parents[2] ### 2. defining settings - n_panels = 50 + n_panels = 100 spanwise_panel_distribution = "uniform" solver_base_version = Solver(reference_point=np.array([0.0, 0.0, 0.0])) @@ -62,28 +62,30 @@ def main(): file_path=(cad_derived_geometry_dir / "aero_geometry_CAD_CFD_polars.yaml"), spanwise_panel_distribution=spanwise_panel_distribution, ) - body_aero_CAD_CFD_polars_with_bridles = BodyAerodynamics.instantiate( - n_panels=n_panels, - # file_path=(cad_derived_geometry_dir / "config_kite_CAD_CFD_polars.yaml"), - file_path=(cad_derived_geometry_dir / "config_kite_CAD_CFD_polars.yaml"), - spanwise_panel_distribution=spanwise_panel_distribution, - bridle_path=( - cad_derived_geometry_dir / "struc_geometry_manually_adjusted.yaml" - ), - ) - body_aero_CAD_neuralfoil = BodyAerodynamics.instantiate( - n_panels=n_panels, - file_path=(cad_derived_geometry_dir / "aero_geometry_CAD_neuralfoil.yaml"), - spanwise_panel_distribution=spanwise_panel_distribution, - ) - body_aero_masure_regression = BodyAerodynamics.instantiate( - n_panels=n_panels, - file_path=( - cad_derived_geometry_dir / "aero_geometry_CAD_masure_regression.yaml" - ), - ml_models_dir=(Path(PROJECT_DIR) / "data" / "ml_models"), - spanwise_panel_distribution=spanwise_panel_distribution, - ) + # body_aero_CAD_CFD_polars_with_bridles = BodyAerodynamics.instantiate( + # n_panels=n_panels, + # file_path=( + # cad_derived_geometry_dir + # / "config_kite_CAD_CFD_polars_converged_fitted_pchip.yaml" + # ), + # spanwise_panel_distribution=spanwise_panel_distribution, + # bridle_path=( + # cad_derived_geometry_dir / "struc_geometry_manually_adjusted.yaml" + # ), + # ) + # body_aero_CAD_neuralfoil = BodyAerodynamics.instantiate( + # n_panels=n_panels, + # file_path=(cad_derived_geometry_dir / "aero_geometry_CAD_neuralfoil.yaml"), + # spanwise_panel_distribution=spanwise_panel_distribution, + # ) + # body_aero_masure_regression = BodyAerodynamics.instantiate( + # n_panels=n_panels, + # file_path=( + # cad_derived_geometry_dir / "aero_geometry_CAD_masure_regression.yaml" + # ), + # ml_models_dir=(Path(PROJECT_DIR) / "data" / "ml_models"), + # spanwise_panel_distribution=spanwise_panel_distribution, + # ) # Step 2: Set inflow conditions for each aerodynamic object """ @@ -97,40 +99,40 @@ def main(): body_aero_CAD_CFD_polars.va_initialize( Umag, angle_of_attack, side_slip, body_rates=yaw_rate ) - body_aero_CAD_CFD_polars_with_bridles.va_initialize( - Umag, - angle_of_attack, - side_slip, - body_rates=yaw_rate, - body_axis=np.array([0, 0, 1]), - ) - body_aero_CAD_neuralfoil.va_initialize( - Umag, - angle_of_attack, - side_slip, - body_rates=yaw_rate, - body_axis=np.array([0, 0, 1]), - ) - body_aero_masure_regression.va_initialize( - Umag, - angle_of_attack, - side_slip, - body_rates=yaw_rate, - body_axis=np.array([0, 0, 1]), - ) + # body_aero_CAD_CFD_polars_with_bridles.va_initialize( + # Umag, + # angle_of_attack, + # side_slip, + # body_rates=yaw_rate, + # body_axis=np.array([0, 0, 1]), + # ) + # body_aero_CAD_neuralfoil.va_initialize( + # Umag, + # angle_of_attack, + # side_slip, + # body_rates=yaw_rate, + # body_axis=np.array([0, 0, 1]), + # ) + # body_aero_masure_regression.va_initialize( + # Umag, + # angle_of_attack, + # side_slip, + # body_rates=yaw_rate, + # body_axis=np.array([0, 0, 1]), + # ) # Step 3: Plot the kite geometry using Matplotlib """ Visualize the panel mesh, control points, and aerodynamic centers for the selected BodyAerodynamics object. """ - plot_geometry( - body_aero_CAD_CFD_polars, - title="TUDELFT_V3_KITE", - data_type=".pdf", - save_path=".", - is_save=False, - is_show=True, - ) + # plot_geometry( + # body_aero_CAD_CFD_polars, + # title="TUDELFT_V3_KITE", + # data_type=".pdf", + # save_path=".", + # is_save=False, + # is_show=True, + # ) # Step 4: Create an interactive plot using Plotly """ @@ -138,7 +140,7 @@ def main(): This allows for interactive exploration of the geometry and panel arrangement. """ interactive_plot( - body_aero_CAD_CFD_polars_with_bridles, + body_aero_CAD_CFD_polars, vel=Umag, angle_of_attack=angle_of_attack, side_slip=side_slip, @@ -154,105 +156,105 @@ def main(): # / "interactive_plot.html", ) - # Step 5: Plot polar curves for different angles of attack and side slip angles, and save results - """ - Compare the aerodynamic performance of different models by plotting lift, drag, and side force coefficients - as a function of angle of attack (alpha sweep) and side slip (beta sweep). - Literature/CFD data can be included for validation. - """ - save_folder = Path(PROJECT_DIR) / "results" / "TUDELFT_V3_KITE" + # # Step 5: Plot polar curves for different angles of attack and side slip angles, and save results + # """ + # Compare the aerodynamic performance of different models by plotting lift, drag, and side force coefficients + # as a function of angle of attack (alpha sweep) and side slip (beta sweep). + # Literature/CFD data can be included for validation. + # """ + # save_folder = Path(PROJECT_DIR) / "results" / "TUDELFT_V3_KITE" - # Step 5a: Plot alpha sweep (angle of attack) - path_cfd_lebesque_alpha = ( - Path(PROJECT_DIR) - / "data" - / "TUDELFT_V3_KITE" - / "3D_polars_literature" - / "CFD_RANS_Rey_10e5_Poland2025_alpha_sweep_beta_0.csv" - ) - path_wt_data = ( - Path(PROJECT_DIR) - / "data" - / "TUDELFT_V3_KITE" - / "3D_polars_literature" - / "V3_CL_CD_CS_alpha_sweep_for_beta_0_WindTunnel_Poland_2025_Rey_560e4.csv" - ) - plot_polars( - solver_list=[ - solver_base_version, - solver_base_version, - solver_base_version, - solver_base_version, - ], - body_aero_list=[ - body_aero_CAD_CFD_polars, - body_aero_CAD_CFD_polars_with_bridles, - body_aero_CAD_neuralfoil, - body_aero_masure_regression, - ], - label_list=[ - "VSM CAD CFD Polars", - "VSM CAD CFD Polars with Bridles", - "VSM CAD NeuralFoil", - "VSM CAD Masure Regression", - "CFD_RANS_Rey_10e5_Poland2025_alpha_sweep_beta_0", - "Wind Tunnel Data Poland 2025", - ], - literature_path_list=[path_cfd_lebesque_alpha, path_wt_data], - angle_range=[0, 5, 8, 10, 12, 15, 20, 25], - angle_type="angle_of_attack", - angle_of_attack=0, - side_slip=0, - yaw_rate=0, - Umag=Umag, - title="alphasweep", - data_type=".pdf", - save_path=Path(save_folder), - is_save=True, - is_show=False, - ) + # # Step 5a: Plot alpha sweep (angle of attack) + # path_cfd_lebesque_alpha = ( + # Path(PROJECT_DIR) + # / "data" + # / "TUDELFT_V3_KITE" + # / "3D_polars_literature" + # / "CFD_RANS_Rey_10e5_Poland2025_alpha_sweep_beta_0.csv" + # ) + # path_wt_data = ( + # Path(PROJECT_DIR) + # / "data" + # / "TUDELFT_V3_KITE" + # / "3D_polars_literature" + # / "windtunnel_alpha_sweep_beta_00_0_Poland_2025_Rey_5e5.csv" + # ) + # plot_polars( + # solver_list=[ + # solver_base_version, + # solver_base_version, + # solver_base_version, + # solver_base_version, + # ], + # body_aero_list=[ + # body_aero_CAD_CFD_polars, + # body_aero_CAD_CFD_polars_with_bridles, + # body_aero_CAD_neuralfoil, + # body_aero_masure_regression, + # ], + # label_list=[ + # "VSM CAD CFD Polars", + # "VSM CAD CFD Polars with Bridles", + # "VSM CAD NeuralFoil", + # "VSM CAD Masure Regression", + # "CFD_RANS_Rey_10e5_Poland2025_alpha_sweep_beta_0", + # "Wind Tunnel Data Poland 2025", + # ], + # literature_path_list=[path_cfd_lebesque_alpha, path_wt_data], + # angle_range=[0, 5, 8, 10, 12, 15, 20, 25], + # angle_type="angle_of_attack", + # angle_of_attack=0, + # side_slip=0, + # yaw_rate=0, + # Umag=Umag, + # title="alphasweep", + # data_type=".pdf", + # save_path=Path(save_folder), + # is_save=True, + # is_show=False, + # ) - # Step 5b: Plot beta sweep (side slip) - path_cfd_lebesque_beta = ( - Path(PROJECT_DIR) - / "data" - / "TUDELFT_V3_KITE" - / "3D_polars_literature" - / "CFD_RANS_Rey_10e5_Poland2025_beta_sweep_alpha_13_02.csv" - ) - plot_polars( - solver_list=[ - solver_base_version, - solver_base_version, - solver_base_version, - solver_base_version, - ], - body_aero_list=[ - body_aero_CAD_CFD_polars, - body_aero_CAD_CFD_polars_with_bridles, - body_aero_CAD_neuralfoil, - body_aero_masure_regression, - ], - label_list=[ - "VSM CAD CFD Polars", - "VSM CAD CFD Polars with Bridles", - "VSM CAD NeuralFoil", - "VSM CAD Masure Regression", - "CFD_RANS_Rey_10e5_Poland2025_beta_sweep_alpha_13_02", - ], - literature_path_list=[path_cfd_lebesque_beta], - angle_range=[0, 3, 6, 9, 12], - angle_type="side_slip", - angle_of_attack=6.8, - side_slip=0, - yaw_rate=0, - Umag=3.15, - title="betasweep", - data_type=".pdf", - save_path=Path(save_folder), - is_save=True, - is_show=True, - ) + # # Step 5b: Plot beta sweep (side slip) + # path_cfd_lebesque_beta = ( + # Path(PROJECT_DIR) + # / "data" + # / "TUDELFT_V3_KITE" + # / "3D_polars_literature" + # / "CFD_RANS_Re1e6_beta_sweep_alpha_13_Vire2022_CorrectedByPoland2025.csv" + # ) + # plot_polars( + # solver_list=[ + # solver_base_version, + # solver_base_version, + # solver_base_version, + # solver_base_version, + # ], + # body_aero_list=[ + # body_aero_CAD_CFD_polars, + # body_aero_CAD_CFD_polars_with_bridles, + # body_aero_CAD_neuralfoil, + # body_aero_masure_regression, + # ], + # label_list=[ + # "VSM CAD CFD Polars", + # "VSM CAD CFD Polars with Bridles", + # "VSM CAD NeuralFoil", + # "VSM CAD Masure Regression", + # "CFD_RANS_Re1e6_beta_sweep_alpha_13_Vire2022_CorrectedByPoland2025", + # ], + # literature_path_list=[path_cfd_lebesque_beta], + # angle_range=[0, 3, 6, 9, 12], + # angle_type="side_slip", + # angle_of_attack=6.8, + # side_slip=0, + # yaw_rate=0, + # Umag=3.15, + # title="betasweep", + # data_type=".pdf", + # save_path=Path(save_folder), + # is_save=True, + # is_show=True, + # ) if __name__ == "__main__": diff --git a/src/VSM/plot_geometry_plotly.py b/src/VSM/plot_geometry_plotly.py index 236b62b..abf7140 100644 --- a/src/VSM/plot_geometry_plotly.py +++ b/src/VSM/plot_geometry_plotly.py @@ -411,7 +411,7 @@ def add_bridle_system(fig: go.Figure, wing_aero: object, is_first: bool = True) elif hasattr(wing_aero, "_bridle_line_system") and wing_aero._bridle_line_system: # Handle basic bridle line system (backward compatibility) for i, bridle_line in enumerate(wing_aero._bridle_line_system): - p1, p2, _, _, diameter, _ = bridle_line + p1, p2, diameter = bridle_line # Calculate thickness based on diameter (simple scaling) thickness = max(2, min(12, diameter * 1000)) # Assume diameter in meters @@ -859,4 +859,13 @@ def interactive_plot( fig.write_html(save_path) if is_show: - fig.show() + import tempfile + import webbrowser + + with tempfile.NamedTemporaryFile( + suffix=".html", delete=False, mode="w", encoding="utf-8" + ) as f: + tmp_path = f.name + fig.write_html(f, include_plotlyjs=True, full_html=True) + print(f"Interactive plot saved to: {tmp_path}") + webbrowser.open(f"file://{tmp_path}") From 3b92415ccdbb512eb6b8dd8464b17661f9e2b0a9 Mon Sep 17 00:00:00 2001 From: jellepoland Date: Fri, 24 Jul 2026 11:55:02 +0200 Subject: [PATCH 02/13] Add CLAUDE.md with repo guidance for Claude Code Co-Authored-By: Claude Opus 4.8 (1M context) --- CLAUDE.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..fe3990e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,46 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Repository purpose + +Python implementation of the Vortex Step Method (VSM): an enhanced lifting-line aerodynamic solver coupled with 2D viscous airfoil polars, aimed at low-aspect-ratio wings and leading-edge inflatable (LEI) kites. Scientific software — physical correctness, numerical stability, and reproducibility outrank abstraction and stylistic churn. **Read `AGENTS.md` before changing solver/aerodynamic logic** (sign conventions, reference frames, and circulation definitions must never change silently). + +## Commands + +```bash +source venv/bin/activate # project venv at ./venv — required; system python lacks deps +pip install -e .[dev] # editable install (dev = pytest, pytest-cov, black) + +pytest # full suite (~35 s), config in pytest.ini (testpaths = tests) +pytest tests/Solver/test_solver.py # one file +pytest tests/Solver/test_solver.py::test_name # one test +black src tests # formatting (used by the project) +``` + +Examples run as plain scripts: `python examples/TUDELFT_V3_KITE/tutorial.py`. CI (`.github/workflows/testing.yml`) runs pytest with coverage on Python 3.10. + +## Architecture (big picture) + +Data flow: **geometry YAML → `BodyAerodynamics.instantiate()` → `Wing`/`Panel` mesh → `Solver.solve()` → results dict → plotting**. + +- `src/VSM/core/BodyAerodynamics.py` — central orchestrator. `BodyAerodynamics.instantiate(n_panels, file_path=, spanwise_panel_distribution=..., bridle_path=...)` reads the YAML's `wing_sections` (headers `[airfoil_id, LE_x..z, TE_x..z]`) and `wing_airfoils` tables, builds polars per airfoil, adds sections to a `Wing`, and refines to `n_panels`. YAML readers index columns **by header name**, so extra columns (e.g. `VUP_x..z` from newer SurfplanAdapter exports) are ignored harmlessly. +- `src/VSM/core/AirfoilAerodynamics.py` — polar generation per airfoil type: `breukels_regression`, `masure_regression` (needs `ml_models_dir`; ~2.3 GB models downloaded from Zenodo into `data/ml_models/`, not in git), `neuralfoil`, `polars` (CSV), `inviscid`. Batch entry point: `from_yaml_entry_batch()`. +- `src/VSM/core/WingGeometry.py` (`Wing`), `Panel.py`, `Filament.py`, `Wake.py` — mesh & vortex elements. **Panels retain only LE/TE points + interpolated polar data**; airfoil `info_dict` parameters (`t`, `chord`, …) are discarded during instantiation — anything needing them later must re-read the YAML. +- `src/VSM/core/Solver.py` — iterative circulation (gamma) loop; optional Anderson acceleration and Li/Gaunaa spanwise artificial viscosity for post-stall stabilization. Returns a results dict (forces, moments, distributions). +- Top-level `src/VSM/` modules build on core: `stability_derivatives.py`, `trim_angle.py`, plus plotting: `plotting.py` (polars/distributions), `plot_geometry_matplotlib.py` (`plot_geometry`), `plot_geometry_plotly.py` (`interactive_plot` — the primary interactive 3D wing view, incl. bridle rendering), `plot_styling.py`. +- Performance-critical vector math is `numba`-jitted (`core/utils.py`). + +## Data layout + +`data//` holds per-kite inputs, e.g. `data/TUDELFT_V3_KITE/`: +- `CAD_derived_geometry/` and `Surfplan_derived_geometry/` — `aero_geometry_*.yaml` variants (one per airfoil-model type) + `struc_geometry_*.yaml` (bridle/particle data, used via `bridle_path`). +- `Surfplan_export/` — raw Surfplan export (`.txt` + `profiles/*.dat`). Converted to VSM YAMLs by the sibling [SurfplanAdapter](https://github.com/awegroup/SurfplanAdapter) project, which also handles the Surfplan→VSM coordinate transform. +- Different YAML sources use different global frames (CAD-derived vs Surfplan-derived origins differ by ~10 m in z) — never mix coordinates across YAML families. + +## Conventions that matter + +- Body-fixed reference frame: **x rearward (LE→TE), y right wing, z up**; α positive nose-up, β positive wind from port. Aircraft-frame conversion via `stability_derivatives.map_derivatives_to_aircraft_frame()`. +- Wing sections are ordered/symmetric tip-to-tip; `airfoil_id` in `wing_sections` must match `wing_airfoils`. +- Style (`docs/style_guide.md`): typed Google docstrings, `snake_case`, path variables end `_path`, directories end `_dir`. +- Tests include verification cases against analytical solutions (`tests/verification_cases/`: elliptical, swept, curved wing, horseshoe vortices) — solver changes must keep these passing; add regression tests when numerical behaviour intentionally changes and explain what changed and why in the commit. From 2eb7fe948de6bcb1fda5b9990110b21a9b7b7bb7 Mon Sep 17 00:00:00 2001 From: jellepoland Date: Fri, 24 Jul 2026 11:55:21 +0200 Subject: [PATCH 03/13] Feat: Fancy Plotly plot with inflatable tubes and curved canopy Add an opt-in 'fancy' mode to interactive_plot that renders the physical inflatable structure of a LEI kite: the leading-edge tube, the chordwise strut tubes, and the curved single-skin canopy. Passing surfplan_dir (a raw Surfplan export: a .txt plus profiles/) triggers a cached SurfplanAdapter conversion; the generated tube-diameter tables and airfoil top-surfaces are then drawn on the wing. Omitting it leaves the plain plot unchanged. - src/VSM/plotly/surfplan_runner.py: run + cache the SurfplanAdapter pipeline - src/VSM/plotly/tube_geometry.py: LE tube (diameter from leading_edge_tubes table) and profile-curved strut tubes (paired tip-to-tip with strut_tubes) - src/VSM/plotly/canopy_geometry.py: loft airfoil upper contours into a canopy - All geometry anchored on the drawn panels, so it is independent of the Surfplan export's coordinate frame - interactive_plot gains surfplan_dir / is_with_canopy / is_with_tube_rings - SurfplanAdapter added as a dependency; tests + tutorial step added Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 5 +- .../Surfplan_export/TUDELFT_V3_KITE.txt | 214 ++++++++++ .../Surfplan_export/profiles/V3D_AVL.avl | 116 +++++ .../Surfplan_export/profiles/prof_1.dat | 151 +++++++ .../Surfplan_export/profiles/prof_1.png | Bin 0 -> 32884 bytes .../Surfplan_export/profiles/prof_10.dat | 150 +++++++ .../Surfplan_export/profiles/prof_10.png | Bin 0 -> 27808 bytes .../Surfplan_export/profiles/prof_11.dat | 147 +++++++ .../Surfplan_export/profiles/prof_11.png | Bin 0 -> 26726 bytes .../Surfplan_export/profiles/prof_12.dat | 150 +++++++ .../Surfplan_export/profiles/prof_12.png | Bin 0 -> 27674 bytes .../Surfplan_export/profiles/prof_2.dat | 151 +++++++ .../Surfplan_export/profiles/prof_2.png | Bin 0 -> 29174 bytes .../Surfplan_export/profiles/prof_3.dat | 151 +++++++ .../Surfplan_export/profiles/prof_3.png | Bin 0 -> 28812 bytes .../Surfplan_export/profiles/prof_4.dat | 151 +++++++ .../Surfplan_export/profiles/prof_4.png | Bin 0 -> 28645 bytes .../Surfplan_export/profiles/prof_5.dat | 151 +++++++ .../Surfplan_export/profiles/prof_5.png | Bin 0 -> 28548 bytes .../Surfplan_export/profiles/prof_6.dat | 150 +++++++ .../Surfplan_export/profiles/prof_6.png | Bin 0 -> 28785 bytes .../Surfplan_export/profiles/prof_7.dat | 150 +++++++ .../Surfplan_export/profiles/prof_7.png | Bin 0 -> 28076 bytes .../Surfplan_export/profiles/prof_8.dat | 150 +++++++ .../Surfplan_export/profiles/prof_8.png | Bin 0 -> 28054 bytes .../Surfplan_export/profiles/prof_9.dat | 150 +++++++ .../Surfplan_export/profiles/prof_9.png | Bin 0 -> 27972 bytes examples/TUDELFT_V3_KITE/tutorial.py | 23 + pyproject.toml | 1 + src/VSM/plot_geometry_plotly.py | 90 +++- src/VSM/plotly/__init__.py | 24 ++ src/VSM/plotly/canopy_geometry.py | 230 ++++++++++ src/VSM/plotly/surfplan_runner.py | 95 +++++ src/VSM/plotly/tube_geometry.py | 397 ++++++++++++++++++ tests/plotting/test_tube_geometry.py | 196 +++++++++ 35 files changed, 3190 insertions(+), 3 deletions(-) create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/TUDELFT_V3_KITE.txt create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/V3D_AVL.avl create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_1.dat create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_1.png create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_10.dat create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_10.png create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_11.dat create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_11.png create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_12.dat create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_12.png create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_2.dat create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_2.png create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_3.dat create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_3.png create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_4.dat create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_4.png create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_5.dat create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_5.png create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_6.dat create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_6.png create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_7.dat create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_7.png create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_8.dat create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_8.png create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_9.dat create mode 100644 data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_9.png create mode 100644 src/VSM/plotly/__init__.py create mode 100644 src/VSM/plotly/canopy_geometry.py create mode 100644 src/VSM/plotly/surfplan_runner.py create mode 100644 src/VSM/plotly/tube_geometry.py create mode 100644 tests/plotting/test_tube_geometry.py diff --git a/.gitignore b/.gitignore index cdd06fd..910c9b8 100644 --- a/.gitignore +++ b/.gitignore @@ -177,4 +177,7 @@ cython_debug/ #.idea/ #Ignore old thesis -Aerostructural_model_LEI \ No newline at end of file +Aerostructural_model_LEI + +# Generated cache from the fancy-plot SurfplanAdapter conversion +vsm_processed/ \ No newline at end of file diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/TUDELFT_V3_KITE.txt b/data/TUDELFT_V3_KITE/Surfplan_export/TUDELFT_V3_KITE.txt new file mode 100644 index 0000000..717e097 --- /dev/null +++ b/data/TUDELFT_V3_KITE/Surfplan_export/TUDELFT_V3_KITE.txt @@ -0,0 +1,214 @@ +Filename: TUDELFT_V3.sle +Design name: 14 hydrav1 07 +Revision: 1.0 +Designer: +Export Date: jeu. 13/02/2025 16:57:57 + +3d rib positions + LE X; Y; Z; TE X; Y; Z; VUP X; Y; Z +24 + 4,156287; 0,916618; 0,520832; 4,156287; 0,916618; -0,864107; 1,352509; 0,297952; 0,000000 + 3,967978; 1,791356; 0,709238; 3,955506; 1,787553; -1,011852; 1,646267; 0,501952; -0,013040 + 3,805350; 2,209568; 0,790880; 3,789238; 2,199439; -1,189372; 1,676461; 1,053982; -0,019032 + 3,501223; 2,540205; 0,864219; 3,485499; 2,522089; -1,256822; 1,390325; 1,601815; -0,023989 + 3,134335; 2,796493; 0,929298; 3,116753; 2,773988; -1,310901; 1,379198; 1,765306; -0,028558 + 2,798300; 3,092998; 0,986158; 2,780285; 3,065722; -1,354755; 1,290075; 1,953351; -0,032688 + 2,393198; 3,288677; 1,034831; 2,380855; 3,254511; -1,390237; 0,824015; 2,280780; -0,036327 + 1,959729; 3,398648; 1,075348; 1,946587; 3,361473; -1,418530; 0,831221; 2,351276; -0,039430 + 1,553762; 3,587398; 1,107733; 1,540958; 3,547442; -1,440421; 0,777640; 2,426595; -0,041958 + 1,111239; 3,671702; 1,132004; 1,107796; 3,627957; -1,456442; 0,203071; 2,580468; -0,043881 + 0,664252; 3,658834; 1,148177; 0,660721; 3,613797; -1,466944; 0,204434; 2,607118; -0,045175 + 0,225013; 3,741837; 1,156262; 0,220752; 3,696209; -1,472144; 0,244401; 2,617018; -0,045826 + -0,225013; 3,741837; 1,156262; -0,220752; 3,696209; -1,472144; -0,244401; 2,617018; -0,045826 + -0,664252; 3,658834; 1,148177; -0,660721; 3,613797; -1,466944; -0,204434; 2,607118; -0,045175 + -1,111239; 3,671702; 1,132004; -1,107796; 3,627957; -1,456442; -0,203071; 2,580468; -0,043881 + -1,553762; 3,587398; 1,107733; -1,540958; 3,547442; -1,440421; -0,777640; 2,426595; -0,041958 + -1,959729; 3,398648; 1,075348; -1,946587; 3,361473; -1,418530; -0,831221; 2,351276; -0,039430 + -2,393198; 3,288677; 1,034831; -2,380855; 3,254511; -1,390237; -0,824015; 2,280780; -0,036327 + -2,798300; 3,092998; 0,986158; -2,780285; 3,065722; -1,354755; -1,290075; 1,953351; -0,032688 + -3,134335; 2,796493; 0,929298; -3,116753; 2,773988; -1,310901; -1,379198; 1,765306; -0,028558 + -3,501223; 2,540205; 0,864219; -3,485499; 2,522089; -1,256822; -1,390325; 1,601815; -0,023989 + -3,805350; 2,209568; 0,790880; -3,789238; 2,199439; -1,189372; -1,676461; 1,053982; -0,019032 + -3,967978; 1,791356; 0,709238; -3,955506; 1,787553; -1,011852; -1,646267; 0,501952; -0,013040 + -4,156287; 0,916618; 0,520832; -4,156287; 0,916618; -0,864107; -1,352509; 0,297952; 0,000000 + +3d curved wingtip positions + LE X; Y; Z; TE X; Y; Z; VUP X; Y; Z +7 + 4,100957; 0,921215; -0,522180; 4,152601; 0,932607; -0,866820; 1,357902; 0,301698; -0,000239 + 4,105533; 0,971623; -0,263184; 4,141856; 0,979216; -0,874727; 1,373623; 0,312615; -0,000937 + 4,099444; 1,049881; -0,028112; 4,124523; 1,054402; -0,887481; 1,398983; 0,330226; -0,002063 + 4,085799; 1,154511; 0,183243; 4,101072; 1,156125; -0,904737; 1,433293; 0,354052; -0,003586 + 4,065552; 1,283430; 0,368472; 4,071974; 1,282342; -0,926149; 1,475864; 0,383616; -0,005476 + 4,039112; 1,434330; 0,522929; 4,037700; 1,431014; -0,951369; 1,526010; 0,418440; -0,007702 + 4,006563; 1,604842; 0,639766; 3,998721; 1,600098; -0,980052; 1,583040; 0,458044; -0,010233 + +LE tube + Centre X; Y; Z; Diam(mm) +38 + 4,047384; 0,902987; -0,806316; 0,112452 + 4,053468; 0,918198; -0,576557; 0,113170 + 4,052015; 0,967544; -0,320048; 0,115263 + 4,042557; 1,045392; -0,087800; 0,118639 + 4,025810; 1,149644; 0,119933; 0,123207 + 4,002306; 1,277897; 0,300671; 0,128875 + 3,972396; 1,427448; 0,449839; 0,135551 + 3,936225; 1,595488; 0,560835; 0,143143 + 3,967429; 1,791188; 0,633460; 0,151561 + 3,804690; 2,209154; 0,709853; 0,162061 + 3,500590; 2,539476; 0,778809; 0,170831 + 3,133636; 2,795597; 0,840178; 0,178254 + 2,797590; 3,091923; 0,893902; 0,184529 + 2,392716; 3,287341; 0,939956; 0,189772 + 1,959218; 3,397202; 0,978330; 0,194060 + 1,553266; 3,585851; 1,009025; 0,197442 + 1,111106; 3,670013; 1,032043; 0,199952 + 0,664116; 3,657099; 1,047385; 0,201615 + 0,224849; 3,740081; 1,055068; 0,202418 + -0,224849; 3,740081; 1,055068; 0,202418 + -0,664116; 3,657099; 1,047385; 0,201615 + -1,111106; 3,670013; 1,032043; 0,199952 + -1,553266; 3,585851; 1,009025; 0,197442 + -1,959218; 3,397202; 0,978330; 0,194060 + -2,392716; 3,287341; 0,939956; 0,189772 + -2,797590; 3,091923; 0,893902; 0,184529 + -3,133636; 2,795597; 0,840178; 0,178254 + -3,500590; 2,539476; 0,778809; 0,170831 + -3,804690; 2,209154; 0,709853; 0,162061 + -3,967429; 1,791188; 0,633460; 0,151561 + -3,936225; 1,595488; 0,560835; 0,143143 + -3,972396; 1,427448; 0,449839; 0,135551 + -4,002306; 1,277897; 0,300671; 0,128875 + -4,025810; 1,149644; 0,119933; 0,123207 + -4,042557; 1,045392; -0,087800; 0,118639 + -4,052015; 0,967544; -0,320048; 0,115263 + -4,053468; 0,918198; -0,576557; 0,113170 + -4,047384; 0,902987; -0,806316; 0,112452 + +Strut 2 + Centre X; Y; Z; Diam(m) +7 + 3,992381; 1,798912; 0,562351; 0,105697 + 4,013188; 1,805140; 0,397453; 0,099891 + 4,000806; 1,801365; 0,121209; 0,097265 + 3,980386; 1,795139; -0,157776; 0,092566 + 3,959762; 1,788850; -0,439564; 0,087908 + 3,938932; 1,782499; -0,724157; 0,083203 + 3,919906; 1,776698; -0,984103; 0,078907 + +Strut 5 + Centre X; Y; Z; Diam(m) +7 + 3,165429; 2,835935; 0,767330; 0,110490 + 3,198103; 2,878112; 0,526561; 0,128371 + 3,183835; 2,859850; 0,166347; 0,124894 + 3,159895; 2,829208; -0,197326; 0,117965 + 3,135714; 2,798257; -0,564655; 0,111190 + 3,111292; 2,766998; -0,935639; 0,104346 + 3,088872; 2,738302; -1,276210; 0,098064 + +Strut 8 + Centre X; Y; Z; Diam(m) +8 + 1,979699; 3,454888; 0,903062; 0,131981 + 2,006196; 3,530089; 0,681343; 0,141738 + 2,001981; 3,518165; 0,340947; 0,141175 + 1,987651; 3,477629; -0,004804; 0,133769 + 1,973148; 3,436605; -0,353542; 0,126804 + 1,958521; 3,395229; -0,705269; 0,119783 + 1,943769; 3,353502; -1,059985; 0,112703 + 1,930428; 3,315762; -1,380803; 0,106299 + +Strut 11 + Centre X; Y; Z; Diam(m) +8 + 0,669201; 3,721894; 0,970331; 0,140834 + 0,676930; 3,820506; 0,734465; 0,142401 + 0,675849; 3,806722; 0,377913; 0,142268 + 0,672073; 3,758564; 0,015391; 0,134521 + 0,668249; 3,709798; -0,350263; 0,127292 + 0,664392; 3,660613; -0,719051; 0,120005 + 0,660502; 3,611011; -1,090973; 0,112657 + 0,656965; 3,565894; -1,429263; 0,105972 + +Strut 14 + Centre X; Y; Z; Diam(m) +8 + -0,669201; 3,721894; 0,970331; 0,140834 + -0,676930; 3,820506; 0,734465; 0,142401 + -0,675849; 3,806722; 0,377913; 0,142268 + -0,672073; 3,758564; 0,015391; 0,134521 + -0,668249; 3,709798; -0,350263; 0,127292 + -0,664392; 3,660613; -0,719051; 0,120005 + -0,660502; 3,611011; -1,090973; 0,112657 + -0,656965; 3,565894; -1,429263; 0,105972 + +Strut 17 + Centre X; Y; Z; Diam(m) +8 + -1,979699; 3,454888; 0,903062; 0,131981 + -2,006196; 3,530089; 0,681343; 0,141738 + -2,001981; 3,518165; 0,340947; 0,141175 + -1,987651; 3,477629; -0,004804; 0,133769 + -1,973148; 3,436605; -0,353542; 0,126804 + -1,958521; 3,395229; -0,705269; 0,119783 + -1,943769; 3,353502; -1,059985; 0,112703 + -1,930428; 3,315762; -1,380803; 0,106299 + +Strut 20 + Centre X; Y; Z; Diam(m) +7 + -3,165429; 2,835935; 0,767330; 0,110490 + -3,198103; 2,878112; 0,526561; 0,128371 + -3,183835; 2,859850; 0,166347; 0,124894 + -3,159895; 2,829208; -0,197326; 0,117965 + -3,135714; 2,798257; -0,564655; 0,111190 + -3,111292; 2,766998; -0,935639; 0,104346 + -3,088872; 2,738302; -1,276210; 0,098064 + +Strut 23 + Centre X; Y; Z; Diam(m) +7 + -3,992381; 1,798912; 0,562351; 0,105697 + -4,013188; 1,805140; 0,397453; 0,099891 + -4,000806; 1,801365; 0,121209; 0,097265 + -3,980386; 1,795139; -0,157776; 0,092566 + -3,959762; 1,788850; -0,439564; 0,087908 + -3,938932; 1,782499; -0,724157; 0,083203 + -3,919906; 1,776698; -0,984103; 0,078907 + +3d Bridle + TopX; Y; Z; BottomX; Y; Z; Name ; Length; Material; Diameter + 0,658922; 3,590858; 1,123196; 0,682069; 3,407109; 1,014197; a1 ; 0,214896; ; 2,000000 + 1,937954; 3,337051; 1,051436; 1,898143; 3,164487; 0,942461; a2 ; 0,207939; ; 2,000000 + 3,097783; 2,749708; 0,907650; 3,052266; 2,575994; 0,791988; a3 ; 0,213602; ; 2,000000 + 3,921859; 1,777294; 0,692391; 3,825134; 1,648422; 0,616812; a4 ; 0,177977; ; 2,000000 + 0,682069; 3,407109; 1,014197; 0,887905; 1,721653; 0,769331; A1 ; 1,715543; ; 2,000000 + 1,898143; 3,164487; 0,942461; 1,532504; 1,590653; 0,731702; A2 ; 1,629437; ; 2,000000 + 3,052266; 2,575994; 0,791988; 2,644934; 1,015550; 0,607355; A3 ; 1,623267; ; 2,000000 + 3,825134; 1,648422; 0,616812; 2,172485; -0,743695; 0,618402; A4 ; 2,907486; ; 2,000000 + 0,887905; 1,721653; 0,769331; 1,113164; -0,195037; 0,711827; AI ; 1,930738; ; 3,000000 + 1,532504; 1,590653; 0,731702; 1,113164; -0,195037; 0,711827; AII ; 1,834374; ; 3,000000 + 2,644934; 1,015550; 0,607355; 2,172485; -0,743695; 0,618402; AIII ; 1,821612; ; 3,000000 + 1,113164; -0,195037; 0,711827; 0,941442; -3,901961; 0,633144; amain ; 3,711733; ; 4,000000 + 2,172485; -0,743695; 0,618402; 0,941442; -3,901961; 0,633144; amain2 ; 3,389739; ; 4,000000 + 0,941442; -3,901961; 0,633144; 0,910001; -4,099458; 0,630632; a5.1 ; 0,200000; ; 5,000000 + 0,659453; 3,597625; 0,966125; 0,682069; 3,407109; 1,014197; b1 ; 0,197785; ; 2,000000 + 1,939381; 3,341088; 0,901698; 1,898143; 3,164487; 0,942461; b2 ; 0,185876; ; 2,000000 + 3,097418; 2,749241; 0,773223; 3,052266; 2,575994; 0,791988; b3 ; 0,180014; ; 2,000000 + 3,908574; 1,773243; 0,589225; 3,825134; 1,648422; 0,616812; b4 ; 0,152655; ; 2,000000 + 0,670112; 3,733556; 0,362112; 0,887905; 1,721653; 0,769331; e1 ; 2,064223; ; 2,000000 + 1,977801; 3,449767; 0,326140; 1,532504; 1,590653; 0,731702; e2 ; 1,954245; ; 2,000000 + 3,148563; 2,814703; 0,256835; 2,644934; 1,015550; 0,607355; e3 ; 1,900910; ; 2,000000 + 0,654966; 3,540403; -1,204082; 1,155974; -0,902939; -0,473306; br1 ; 4,530820; ; 2,000000 + 1,920720; 3,288303; -1,167853; 1,155974; -0,902939; -0,473306; br2 ; 4,316681; ; 2,000000 + 3,069960; 2,714095; -1,085876; 1,921865; -1,309919; -0,396111; br3 ; 4,241059; ; 2,000000 + 3,891515; 1,768042; -0,839226; 1,921865; -1,309919; -0,396111; br4 ; 3,680993; ; 2,000000 + 4,142726; 0,875716; -0,615380; 3,821673; 0,392361; -0,630484; br5 ; 0,580461; ; 2,000000 + 4,137746; 0,864315; -0,791897; 3,821673; 0,392361; -0,630484; br6 ; 0,590505; ; 2,000000 + 1,155974; -0,902939; -0,473306; 1,087879; -3,228214; -0,089481; BR1 ; 2,357724; ; 2,000000 + 1,921865; -1,309919; -0,396111; 1,087879; -3,228214; -0,089481; BR2 ; 2,114098; ; 2,000000 + 3,821673; 0,392361; -0,630484; 0,954417; -3,906964; 0,021095; BR3 ; 5,208639; ; 2,000000 + 1,087879; -3,228214; -0,089481; 0,954417; -3,906964; 0,021095; BRI ; 0,700529; ; 3,000000 + 0,954417; -3,906964; 0,021095; 0,910001; -4,099458; 0,052295; brmain ; 0,200000; ; 4,000000 + diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/profiles/V3D_AVL.avl b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/V3D_AVL.avl new file mode 100644 index 0000000..4519c16 --- /dev/null +++ b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/V3D_AVL.avl @@ -0,0 +1,116 @@ +Empty Project +0.0 | Mach +0 0 0.0 | iYsym iZsym Zsym + + 25.000 2.630 11.180 | Sref Cref Bref + 0.00 0.00 0.00 | Xref Yref Zref + 0.00 | CDp (optional) + + + +#================================================= +SURFACE | (keyword) +14 hydrav1 07 +#Nchord Cspace [ Nspan Sspace ] + 10 1.0 + +INDEX | (keyword) +111 | Lsurf + +YDUPLICATE +0.0 + +SCALE +1.0 1.0 1.0 + +TRANSLATE +0.0 0.0 0.0 + +ANGLE +0.0 | dAinc + +#__________________________________ +SECTION | (keyword) + 0.0000 0.2250 0.0000 2.6288 0.9989 3 0 | Xle Yle Zle Chord Ainc [ Nspan Sspace ] + +AFIL 0.0 1.0 +G:\Shared drives\Hardware\01 - Kite and Bridle (KB)\Archived kites\V3.25\V3.25D\VSM Package\prof_1.dat + +#______________ +SECTION | (keyword) + 0.0081 0.6643 -0.0830 2.6155 0.9897 3 0 | Xle Yle Zle Chord Ainc [ Nspan Sspace ] + +AFIL 0.0 1.0 +G:\Shared drives\Hardware\01 - Kite and Bridle (KB)\Archived kites\V3.25\V3.25D\VSM Package\prof_2.dat + +#______________ +SECTION | (keyword) + 0.0243 1.1112 -0.0701 2.5888 0.9712 3 0 | Xle Yle Zle Chord Ainc [ Nspan Sspace ] + +AFIL 0.0 1.0 +G:\Shared drives\Hardware\01 - Kite and Bridle (KB)\Archived kites\V3.25\V3.25D\VSM Package\prof_3.dat + +#______________ +SECTION | (keyword) + 0.0485 1.5538 -0.1544 2.5485 0.9433 3 0 | Xle Yle Zle Chord Ainc [ Nspan Sspace ] + +AFIL 0.0 1.0 +G:\Shared drives\Hardware\01 - Kite and Bridle (KB)\Archived kites\V3.25\V3.25D\VSM Package\prof_4.dat + +#______________ +SECTION | (keyword) + 0.0809 1.9597 -0.3432 2.4942 0.9058 3 0 | Xle Yle Zle Chord Ainc [ Nspan Sspace ] + +AFIL 0.0 1.0 +G:\Shared drives\Hardware\01 - Kite and Bridle (KB)\Archived kites\V3.25\V3.25D\VSM Package\prof_5.dat + +#______________ +SECTION | (keyword) + 0.1214 2.3932 -0.4532 2.4253 0.8582 3 0 | Xle Yle Zle Chord Ainc [ Nspan Sspace ] + +AFIL 0.0 1.0 +G:\Shared drives\Hardware\01 - Kite and Bridle (KB)\Archived kites\V3.25\V3.25D\VSM Package\prof_6.dat + +#______________ +SECTION | (keyword) + 0.1701 2.7983 -0.6488 2.3411 0.8000 3 0 | Xle Yle Zle Chord Ainc [ Nspan Sspace ] + +AFIL 0.0 1.0 +G:\Shared drives\Hardware\01 - Kite and Bridle (KB)\Archived kites\V3.25\V3.25D\VSM Package\prof_7.dat + +#______________ +SECTION | (keyword) + 0.2270 3.1343 -0.9453 2.2404 0.7304 3 0 | Xle Yle Zle Chord Ainc [ Nspan Sspace ] + +AFIL 0.0 1.0 +G:\Shared drives\Hardware\01 - Kite and Bridle (KB)\Archived kites\V3.25\V3.25D\VSM Package\prof_8.dat + +#______________ +SECTION | (keyword) + 0.2920 3.5012 -1.2016 2.1212 0.6480 3 0 | Xle Yle Zle Chord Ainc [ Nspan Sspace ] + +AFIL 0.0 1.0 +G:\Shared drives\Hardware\01 - Kite and Bridle (KB)\Archived kites\V3.25\V3.25D\VSM Package\prof_9.dat + +#______________ +SECTION | (keyword) + 0.3654 3.8053 -1.5323 1.9803 0.5506 3 0 | Xle Yle Zle Chord Ainc [ Nspan Sspace ] + +AFIL 0.0 1.0 +G:\Shared drives\Hardware\01 - Kite and Bridle (KB)\Archived kites\V3.25\V3.25D\VSM Package\prof_10.dat + +#______________ +SECTION | (keyword) + 0.4470 3.9680 -1.9505 1.7211 0.4341 3 0 | Xle Yle Zle Chord Ainc [ Nspan Sspace ] + +AFIL 0.0 1.0 +G:\Shared drives\Hardware\01 - Kite and Bridle (KB)\Archived kites\V3.25\V3.25D\VSM Package\prof_11.dat + +#______________ +SECTION | (keyword) + 0.6354 4.1563 -2.8252 1.3849 0.0000 3 0 | Xle Yle Zle Chord Ainc [ Nspan Sspace ] + +AFIL 0.0 1.0 +G:\Shared drives\Hardware\01 - Kite and Bridle (KB)\Archived kites\V3.25\V3.25D\VSM Package\prof_12.dat + +#______________ diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_1.dat b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_1.dat new file mode 100644 index 0000000..0c7db24 --- /dev/null +++ b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_1.dat @@ -0,0 +1,151 @@ +rib_1 + 1.0000000 0.0000000 + 0.9577769 0.0053610 + 0.9081751 0.0116589 + 0.8585733 0.0179567 + 0.8089715 0.0242546 + 0.7887696 0.0268196 + 0.7593697 0.0305525 + 0.7097680 0.0368503 + 0.6601662 0.0431482 + 0.6105644 0.0494461 + 0.5983523 0.0509966 + 0.5609626 0.0557439 + 0.5113608 0.0620418 + 0.4617590 0.0683397 + 0.4301315 0.0723554 + 0.4121573 0.0746375 + 0.3625555 0.0809354 + 0.3129537 0.0872333 + 0.2944570 0.0895817 + 0.2864751 0.0899438 + 0.2631344 0.0910025 + 0.2431549 0.0919087 + 0.2231755 0.0928149 + 0.2031960 0.0937211 + 0.1832166 0.0946273 + 0.1750000 0.0950000 + 0.1696407 0.0944874 + 0.1632797 0.0938790 + 0.1433727 0.0919749 + 0.1234657 0.0900708 + 0.1222984 0.0899592 + 0.1039075 0.0859758 + 0.0941392 0.0838600 + 0.0842841 0.0817254 + 0.0818568 0.0807479 + 0.0751055 0.0780291 + 0.0658445 0.0742997 + 0.0565836 0.0705702 + 0.0529896 0.0691229 + 0.0478730 0.0658051 + 0.0395115 0.0603832 + 0.0311500 0.0549613 + 0.0292787 0.0537479 + 0.0290956 0.0535627 + 0.0273482 0.0517953 + 0.0256109 0.0500381 + 0.0256009 0.0500279 + 0.0238535 0.0482605 + 0.0221062 0.0464932 + 0.0203588 0.0447258 + 0.0186115 0.0429584 + 0.0168641 0.0411910 + 0.0151168 0.0394236 + 0.0133694 0.0376563 + 0.0132474 0.0375328 + 0.0121593 0.0355013 + 0.0109895 0.0333173 + 0.0098198 0.0311333 + 0.0086500 0.0289492 + 0.0074877 0.0267791 + 0.0066048 0.0244503 + 0.0057275 0.0221362 + 0.0048502 0.0198222 + 0.0039729 0.0175081 + 0.0030956 0.0151940 + 0.0022184 0.0128799 + 0.0021212 0.0123489 + 0.0013119 0.0099645 + 0.0008206 0.0074949 + 0.0003294 0.0050253 + 0.0001647 0.0025126 + 0.0000000 0.0000000 + 0.0001647 -0.0025126 + 0.0003294 -0.0050253 + 0.0008206 -0.0074949 + 0.0013119 -0.0099645 + 0.0021212 -0.0123489 + 0.0029306 -0.0147333 + 0.0040443 -0.0169917 + 0.0051580 -0.0192500 + 0.0065570 -0.0213437 + 0.0079559 -0.0234373 + 0.0096161 -0.0253305 + 0.0112764 -0.0272236 + 0.0131695 -0.0288839 + 0.0150627 -0.0305441 + 0.0171563 -0.0319430 + 0.0192500 -0.0333420 + 0.0215083 -0.0344557 + 0.0237667 -0.0355694 + 0.0261511 -0.0363788 + 0.0285355 -0.0371881 + 0.0310051 -0.0376794 + 0.0334747 -0.0381706 + 0.0359874 -0.0383353 + 0.0385000 -0.0385000 + 0.0410126 -0.0383353 + 0.0435253 -0.0381706 + 0.0459949 -0.0376794 + 0.0484645 -0.0371881 + 0.0508489 -0.0363788 + 0.0532333 -0.0355694 + 0.0554917 -0.0344557 + 0.0577500 -0.0333420 + 0.0598437 -0.0319430 + 0.0619373 -0.0305441 + 0.0638305 -0.0288839 + 0.0657236 -0.0272236 + 0.0673839 -0.0253305 + 0.0690441 -0.0234373 + 0.0704430 -0.0213437 + 0.0718420 -0.0192500 + 0.0729557 -0.0169917 + 0.0740694 -0.0147333 + 0.0751055 -0.0127256 + 0.0818568 -0.0003134 + 0.0842841 0.0036228 + 0.0941392 0.0180415 + 0.1039075 0.0299185 + 0.1222984 0.0475825 + 0.1234657 0.0485200 + 0.1433727 0.0624018 + 0.1632797 0.0725964 + 0.1696407 0.0752301 + 0.1750000 0.0772601 + 0.1832166 0.0800012 + 0.2031960 0.0851738 + 0.2231755 0.0884966 + 0.2431549 0.0902580 + 0.2631344 0.0907111 + 0.2864751 0.0889438 + 0.2944570 0.0885817 + 0.3129537 0.0862333 + 0.3625555 0.0799354 + 0.4121573 0.0736375 + 0.4301315 0.0713554 + 0.4617590 0.0673397 + 0.5113608 0.0610418 + 0.5609626 0.0547439 + 0.5983523 0.0499966 + 0.6105644 0.0484461 + 0.6601662 0.0421482 + 0.7097680 0.0358503 + 0.7593697 0.0295525 + 0.7887696 0.0258196 + 0.8089715 0.0232546 + 0.8585733 0.0169567 + 0.9081751 0.0106589 + 0.9577769 0.0043610 + 1.0000000 -0.0010000 diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_1.png b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_1.png new file mode 100644 index 0000000000000000000000000000000000000000..6694b81cfd0a45f83f4fb2ef2849fdd1533b1572 GIT binary patch literal 32884 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 literal 0 HcmV?d00001 diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_10.dat b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_10.dat new file mode 100644 index 0000000..5bec58d --- /dev/null +++ b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_10.dat @@ -0,0 +1,150 @@ +rib_10 + 1.0000000 0.0000000 + 0.9577769 0.0036927 + 0.9081751 0.0080307 + 0.8585733 0.0123687 + 0.8089715 0.0167067 + 0.7887696 0.0184735 + 0.7593697 0.0210447 + 0.7097680 0.0253827 + 0.6601662 0.0297207 + 0.6105644 0.0340587 + 0.5983523 0.0351267 + 0.5609626 0.0383967 + 0.5113608 0.0427347 + 0.4617590 0.0470727 + 0.4301315 0.0498387 + 0.4121573 0.0514107 + 0.3625555 0.0557487 + 0.3129537 0.0600867 + 0.2944570 0.0617043 + 0.2864751 0.0620433 + 0.2631344 0.0629229 + 0.2431549 0.0635471 + 0.2231755 0.0641713 + 0.2031960 0.0647955 + 0.1832166 0.0654197 + 0.1750000 0.0656764 + 0.1696407 0.0654557 + 0.1632797 0.0651937 + 0.1433727 0.0642449 + 0.1234657 0.0630378 + 0.1222984 0.0629670 + 0.1039075 0.0607509 + 0.0941392 0.0594096 + 0.0842841 0.0580564 + 0.0818568 0.0575028 + 0.0751055 0.0559631 + 0.0658445 0.0536768 + 0.0565836 0.0513126 + 0.0529896 0.0503951 + 0.0478730 0.0484757 + 0.0395115 0.0451268 + 0.0311500 0.0416897 + 0.0292787 0.0409204 + 0.0290956 0.0408128 + 0.0273482 0.0397851 + 0.0256109 0.0387634 + 0.0256009 0.0387575 + 0.0238535 0.0376639 + 0.0221062 0.0365435 + 0.0203588 0.0354231 + 0.0186115 0.0343027 + 0.0168641 0.0331009 + 0.0151168 0.0317578 + 0.0133694 0.0305365 + 0.0132474 0.0304573 + 0.0121593 0.0292922 + 0.0109895 0.0279934 + 0.0098198 0.0265927 + 0.0086500 0.0251605 + 0.0074877 0.0237382 + 0.0066048 0.0223433 + 0.0057275 0.0208784 + 0.0048502 0.0193234 + 0.0039729 0.0175881 + 0.0038336 0.0172924 + 0.0031147 0.0156584 + 0.0022544 0.0131243 + 0.0013942 0.0105902 + 0.0008721 0.0079655 + 0.0003501 0.0053408 + 0.0001750 0.0026704 + 0.0000000 0.0000000 + 0.0001750 -0.0026704 + 0.0003501 -0.0053408 + 0.0008721 -0.0079655 + 0.0013942 -0.0105902 + 0.0022544 -0.0131243 + 0.0031147 -0.0156584 + 0.0042983 -0.0180585 + 0.0054819 -0.0204587 + 0.0069687 -0.0226838 + 0.0084554 -0.0249089 + 0.0102199 -0.0269209 + 0.0119844 -0.0289330 + 0.0139964 -0.0306975 + 0.0160085 -0.0324619 + 0.0182336 -0.0339487 + 0.0204587 -0.0354355 + 0.0228588 -0.0366191 + 0.0252590 -0.0378027 + 0.0277931 -0.0386629 + 0.0303272 -0.0395232 + 0.0329519 -0.0400452 + 0.0355766 -0.0405673 + 0.0382470 -0.0407424 + 0.0409174 -0.0409174 + 0.0435878 -0.0407424 + 0.0462582 -0.0405673 + 0.0488829 -0.0400452 + 0.0515076 -0.0395232 + 0.0540417 -0.0386629 + 0.0565758 -0.0378027 + 0.0589759 -0.0366191 + 0.0613761 -0.0354355 + 0.0636012 -0.0339487 + 0.0658263 -0.0324619 + 0.0678383 -0.0306975 + 0.0698503 -0.0289330 + 0.0716148 -0.0269209 + 0.0733793 -0.0249089 + 0.0748661 -0.0226838 + 0.0763529 -0.0204587 + 0.0775365 -0.0180585 + 0.0787201 -0.0156584 + 0.0818568 -0.0097543 + 0.0842841 -0.0056869 + 0.0941392 0.0077849 + 0.1039075 0.0179685 + 0.1222984 0.0319512 + 0.1234657 0.0326843 + 0.1433727 0.0430118 + 0.1632797 0.0502675 + 0.1696407 0.0520978 + 0.1750000 0.0534903 + 0.1832166 0.0553847 + 0.2031960 0.0589190 + 0.2231755 0.0611435 + 0.2431549 0.0623109 + 0.2631344 0.0626067 + 0.2864751 0.0610433 + 0.2944570 0.0607043 + 0.3129537 0.0590867 + 0.3625555 0.0547487 + 0.4121573 0.0504107 + 0.4301315 0.0488387 + 0.4617590 0.0460727 + 0.5113608 0.0417347 + 0.5609626 0.0373967 + 0.5983523 0.0341267 + 0.6105644 0.0330587 + 0.6601662 0.0287207 + 0.7097680 0.0243827 + 0.7593697 0.0200447 + 0.7887696 0.0174735 + 0.8089715 0.0157067 + 0.8585733 0.0113687 + 0.9081751 0.0070307 + 0.9577769 0.0026927 + 1.0000000 -0.0010000 diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_10.png b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_10.png new file mode 100644 index 0000000000000000000000000000000000000000..0591d8d88bd48c7cfe16c4004cfe72ca83c12156 GIT binary patch literal 27808 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%0DMV#q1PGv%YmxrBcZOVx}& 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 tsjs^I8g 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 GANwzQe-my1 literal 0 HcmV?d00001 diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_12.dat b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_12.dat new file mode 100644 index 0000000..68cc973 --- /dev/null +++ b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_12.dat @@ -0,0 +1,150 @@ +rib_12 + 1.0000000 0.0000000 + 0.9577769 0.0017841 + 0.9081751 0.0038799 + 0.8585733 0.0059757 + 0.8089715 0.0080716 + 0.7887696 0.0089252 + 0.7593697 0.0101674 + 0.7097680 0.0122632 + 0.6601662 0.0143591 + 0.6105644 0.0164549 + 0.5983523 0.0169709 + 0.5609626 0.0185507 + 0.5113608 0.0206466 + 0.4617590 0.0227424 + 0.4301315 0.0240788 + 0.4121573 0.0248383 + 0.3625555 0.0269341 + 0.3129537 0.0290299 + 0.2944570 0.0298115 + 0.2864751 0.0301487 + 0.2631344 0.0311350 + 0.2431549 0.0319791 + 0.2231755 0.0328233 + 0.2031960 0.0336675 + 0.1832166 0.0345117 + 0.1750000 0.0348589 + 0.1696407 0.0350854 + 0.1632797 0.0353541 + 0.1433727 0.0361953 + 0.1234657 0.0370364 + 0.1222984 0.0370857 + 0.1039075 0.0378628 + 0.0941392 0.0382755 + 0.0842841 0.0386920 + 0.0818568 0.0387945 + 0.0751055 0.0390798 + 0.0658445 0.0394711 + 0.0565836 0.0398624 + 0.0529896 0.0400142 + 0.0478730 0.0402304 + 0.0395115 0.0405837 + 0.0311500 0.0394836 + 0.0292787 0.0389883 + 0.0290956 0.0389347 + 0.0273482 0.0383752 + 0.0256109 0.0377306 + 0.0256009 0.0377266 + 0.0238535 0.0369842 + 0.0221062 0.0361423 + 0.0203588 0.0351935 + 0.0186115 0.0341292 + 0.0168641 0.0329380 + 0.0151168 0.0316056 + 0.0133694 0.0301133 + 0.0132474 0.0300025 + 0.0121593 0.0289732 + 0.0109895 0.0277767 + 0.0098198 0.0264746 + 0.0086500 0.0250505 + 0.0074877 0.0234927 + 0.0066048 0.0221960 + 0.0057275 0.0207906 + 0.0048502 0.0192431 + 0.0039729 0.0175158 + 0.0038037 0.0171576 + 0.0030904 0.0155363 + 0.0022369 0.0130220 + 0.0013834 0.0105076 + 0.0008653 0.0079034 + 0.0003473 0.0052991 + 0.0001737 0.0026496 + 0.0000000 0.0000000 + 0.0001737 -0.0026496 + 0.0003473 -0.0052991 + 0.0008653 -0.0079034 + 0.0013834 -0.0105076 + 0.0022369 -0.0130220 + 0.0030904 -0.0155363 + 0.0042648 -0.0179177 + 0.0054391 -0.0202991 + 0.0069143 -0.0225069 + 0.0083895 -0.0247147 + 0.0101402 -0.0267110 + 0.0118910 -0.0287073 + 0.0138873 -0.0304581 + 0.0158836 -0.0322088 + 0.0180914 -0.0336840 + 0.0202991 -0.0351592 + 0.0226806 -0.0363335 + 0.0250620 -0.0375079 + 0.0275763 -0.0383614 + 0.0300907 -0.0392149 + 0.0326949 -0.0397330 + 0.0352992 -0.0402510 + 0.0379487 -0.0404246 + 0.0405983 -0.0405983 + 0.0432479 -0.0404246 + 0.0458974 -0.0402510 + 0.0485017 -0.0397330 + 0.0511059 -0.0392149 + 0.0536202 -0.0383614 + 0.0561346 -0.0375079 + 0.0585160 -0.0363335 + 0.0608974 -0.0351592 + 0.0631052 -0.0336840 + 0.0653130 -0.0322088 + 0.0673093 -0.0304581 + 0.0693056 -0.0287073 + 0.0710563 -0.0267110 + 0.0728071 -0.0247147 + 0.0742823 -0.0225069 + 0.0757574 -0.0202991 + 0.0769318 -0.0179177 + 0.0781062 -0.0155363 + 0.0818568 -0.0093260 + 0.0842841 -0.0062041 + 0.0941392 0.0029782 + 0.1039075 0.0091624 + 0.1222984 0.0170947 + 0.1234657 0.0175025 + 0.1433727 0.0229102 + 0.1632797 0.0265216 + 0.1696407 0.0273820 + 0.1750000 0.0280259 + 0.1832166 0.0288782 + 0.2031960 0.0303387 + 0.2231755 0.0310903 + 0.2431549 0.0312669 + 0.2631344 0.0309707 + 0.2864751 0.0291487 + 0.2944570 0.0288115 + 0.3129537 0.0280299 + 0.3625555 0.0259341 + 0.4121573 0.0238383 + 0.4301315 0.0230788 + 0.4617590 0.0217424 + 0.5113608 0.0196466 + 0.5609626 0.0175507 + 0.5983523 0.0159709 + 0.6105644 0.0154549 + 0.6601662 0.0133591 + 0.7097680 0.0112632 + 0.7593697 0.0091674 + 0.7887696 0.0079252 + 0.8089715 0.0070716 + 0.8585733 0.0049757 + 0.9081751 0.0028799 + 0.9577769 0.0007841 + 1.0000000 -0.0010000 diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_12.png b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_12.png new file mode 100644 index 0000000000000000000000000000000000000000..2cdc56cda30bb2aadb4467a097facce356729a2a GIT binary patch literal 27674 zcmeFZWmJ{h8a9l&)vcnOy%ifUK%_-VLBK{tK)OMcR=T87x1b{7qLftWmTpiHDG?+@ zT12{}oAqAPbKdy=d}F-h`}v(c#_`~iwbt`IbI$v|YR<>kWhAz5-M^KFhGx6um5cH; zG#dtKXx6L#xe2dKGJayg{|Vb%QngVqH?XnSwbG}N*0r&?V{UWD_~s!yeJg8Yb2C0} z-V@v>I1d@w*jQK#^YEDduixM{w=(1juu`?cr);sfqGnA)L#Iprw=P~h&X|Vgwuj`! zbBcGv#yah7b~TrMoHSH8?Dx<04Oh0v$Lx9M?tbWRohQfHi?_aBzjgiA-62=~wG}V! z;ye^D{$c$QE#ud(p3MC1ICyxAUyjHGlaz80v)@@tc=^OY*y*aSa4Vtw)=@s|0c|hc zy&m{}8X9*ghrELI|NhZ^$M09z{#Cz@{BUC3dg0&yaO~{a&E$u-fBcE(fd8J;tYi57 z!_Gr5H;^B0tpCI2_Y2qD|MMYR{`(>SeJ1~%Cp7q79hHy% zI;xkh8dlA@+iPiVY$QW?9gX{^a|BcD*tUmTF-|62G4|R%HO}3lQw3)9zy?k8x9;mg#NA4qot+Qro3`gD^;HK7 z*o~^X%r>j2DNE4Mj4NrL+f4KJAIgM4cd=8hu+1Q@;gIOcud;-$2N8F=iX7NgQp2o% zR=x@mxIH}I?N;~8(u`M z?Z_p1Y(Eft$!oX1l-59PXq-Q*>~Oe!S5;8&IvR_#S6(8R!E-;ncw%oC*VkW{z%`SB zQA$!?nr+e1&sNWT<6-ACKduxh=CW)5{=`(RLW9Lg+{m8404GnR`}z6Vq^QJRd$CJj zzb2>|BVs$=ky4W>4Hr(7_{YT=1~JlMnCZ1T$Q#2>TNe~;&9zya z(qHYSu13Co{W{C6Q<$Dj{zJ3UdYZSUT=(}K-~I`TlK1Vdw=|B z%Fve@>n_#dP)TjtKzGKErD7e;_tN)cg?X$jEXEkgrG;K!`kgzEN($Q!2i&}MOG`_D zh9;vnRys7xc0{?Nx3|J~1W)*SW2|&$bWMJS0*L0@3hMA;n8kV z(Q)a^ij)Z#)^FgzBrYvW+2B~}x~iC@%r*8c{lel@pLKxvY?Fc`rh}Hc5WY~M%wsz} zM&#L1iQ#YQw;aFU+sto2uBntD&vIc|t=Q4_$#rp?!RwL@zk13T*10UtjZvZ_U6&-q zS7#KStP&hwd5Z9bBo52CslJ*x?*l@Kn)y~!^Ybk}3?hn&iforIUL3r)nXw*&`uiS1 z1scjzCeG!y-bwg8=J?~o7CeZ;Xqn#>p<_#K$^EVzQwehxZ$qL| zQqYcrr(UisPLE)9sl2(QmK5eN%0Ym%G3qVyE~y?DX&Pzjm0LTV|vst+Y=VgC(CJACr-Si$BDYyJN7F zx>h*+HHCwRXW-qSKZTNI*?YO-O6KoddmgT_*+y1YHeAd__>#&Tid+1RclQ%^9QsY4 z-1p_r9v}D6t-P5YeZ177=RRsq9|gq_|5>mWHKh9tVPdOOP%E2KG=vtHmV_fR)l=H? zt>f^8Bk%Wz1lJlgCMd-HoS29luG&uCN8RqBd;Hk3%a@+)dgke=p;K6Fn5Tc1j+Qnc zRp_{zn_Jwv9@MRw`U`YJivyANy4!0S8=|I^~ce3g#5CZ)#er{AE>cXhQ+1B;1QwTGgN_$rUL(VncnW_nCm zSiSrSy|vySc@Y|$X7tKD>FjOkJ7lbK=iJ?C6#lRg7Z>07#~*stp^ZId4HTA!h8X#~ zc{Xd#v5JlFZY~PF=`M;^j`(8h;(RicS^M;MnQq#r|530kC9S^) z!%Qhz9v5+$t`!ot8-4!w-+zZiv=`VW)eJuzwVX|y8Ov>`V3GtDUS7fw^`@URvLdwbWQM>lyc>>qtg%Erq0j1;oH8C=i8p8;u{2R zf91?J@7A^+_}n~_R+=gwbJh6U8{LAtldqGKJUMUdZD`Svday|{PPV<~YEXdVETzEK zf9sylE4LgSS|7M7cl>o@ul&uMU;m)~oYJ$joa=l4@RJ+EZYlCYIdgF-YP?>ydf@Zt zgBiW7$BtFk)JR!cT4o%JwB2lWSfu@FZf@?Gq02+c9l|e}MC?BChO1-E{H~wF9r>Az z5FTou^OlthTr?1N)pVS^v@F_R!1&2?yT7n~j;E2mUi!BQ(fP?|_wS!{qk0@h-?3ju zRn`}>qxyPh^QiT7k5jcNj7s;`YbR`46ge$<7I$wUiz$Bcq`aJ*9Cw<8g~c1p*v~FS zSAs{)_#CEw%2`;P7;BWyGfqz5wqwUvln_Rg7kr*1{SC148n?ztG1hz3&Vfj8(Ue4UcV7n)Y`q;eS|GnCcL_%<=N4S(xbBj zmAXu=;tu(f>7}JE2OqqB>*l2N^Rvsy&h<9b&F;z14u0OYoAWs4Yw$z5(ArqklD^O3 zp$^f{xLcbIXv;`Nw4EDOla%jC9yED!ZQ$#=V$0r-OOq8$N+`Ux6J1(qB}<89mOk|K zOtKyMnwrXZ@}BM$h1hR}S?zb^Q`8&w=v<*^&nqyvE@(^jef&%&BEW~i|HzSK^};zn zXXmRUGsbk`BCHiwLIXc3h1A0H_twil{+r5u?%cjYF86UN&6-!Ta?;VTftujD7~AyL zJ6EqqUGkG%Y(Kt9R8d%LX9nfsR(+r(ANmeht%FXlKX7CMg~H@x7Rt9O}--nGxTlxOYz#-ShO0o^(UHn$e$ z0u;Yy^BnSLiM;VAU0v8JJ*N@f>c(wtX*1omcWE$GH1qp@NYjUcp&*#S4&hn=#= zS7K$yCyJ(hBHY#-DsH*CQSUg^wpN{pc3q7gY8>-rm8LAs$0fZH$+MGw^2pEU;Ntx4 zsd-k}wz9==&9Tqn8r8vdesKf(5C8e&xVbi6+@TkIhSd`a95v6%T>HAqdb`UT`DQ1~ zo$sW7bFyS+SuIpeF&6jn%G6o)eL|55^YdX`wLfk5$@S(Djxf>NI%|up)AM5^%M)AN z*KZuvTAMI9bhd!HAy1=2VOs~I8|!#^%7y~wOP4MU)rnJ^S~Cq9R2uzlv2<#DM2s7l zMX4*(q}nkX_kVh!o}u?x3`kWm%|JF>Sjks>El=BNOhB>$a4w0SL)i!qE9A6w?D`EG zvTv4ejWad)^!yc&`v}koyLM4tR7ZEW&bxQ-)aKeg-RL;wbShUhx-~OqAi~<#+al9z zPfWPTSd9-uLxkwup@@j@Lw)*8mZewzT5efcj45)6ab8^0GII_SN^>c?_u&IGm)24! znRwB8PU?zJk9dhY%s#kT6Aq*9A8tm=6Fmt zC(+t4R8zB=nib7F^md)`fQ;AL+H!Z_hsRlUAD5O7u&c?lt2JD|@@RGWZx-54`^Z;E zE{&?_@Hy5f-Mram+OPdgB-e#aZR+#9?q2!x_pP7j`u;S}PxwTsKI^i(!rf&L_mzRq zYBqDp#b-n=O0)+**vx00)IA0S@3di(5ID?gzy$D7Q-EN;{Zm80cb)|m+u z3w4f6@XbH@@ZsVxc>UFRnZAcx=^MI=ofJBcojiH6dG=^yUdP@&d*a?b*qj7t zx;0Ptk5m`E3UkZ$a8of7AKm^yWML;xcP&o9*JvklR37=>eg6!E;M2zKU*fcLdoEzf-WZ zjCkTZujpLVGCNT$&o-^78Mf-QEI2ojrZwHrS5xxDXHL#0D~&?QU>%AOy)EAJ#>u`v z(&E=#4*Q7{+UwSB*-HCV<#U+i)ND4p#Ev9qm+;bDx7GROrfL`GJ2q5%-Y&yWwP?p! zCr`Y+mCm2PpOkb|(r3u^-b{6>pueBr%iArfLj?cI%TpSyQHW}Tgb%HvQAeI}kWVX` zdSZOvz(DfaHN{XtQ@y<&VoQTZ;|9!%w(7)OrC(fF7<{-#yZ$-vt)C+!p*MBRizYo9 z1;2dxLZD)Zpy}|g6|KX(ymB1M$@RBTk7kr>N~(QRL^yx)7?w zx^*9Cm#Nz&=G~T?UCI)iN{d310!)3%tC% zAR7bPI?+;gxi9kaPW63yp|E{F|CPykN=M;tf*3`?DnN_bQOk1}h1}Lwre%va^_ZRoz@eiqspAKUZbyQ4JNh>jZl&TQtL!nT1{q@%?qE_7PYH1N_{ysi7W*S1r zSY`V{Kx(O%Zn?5?y5Hfr#EvCjgNKj)G+wkaF5hb0%)WelNQhnTe4@5iqPkL|eEFIH z^M@mJbkoj*Clr=1^-s;^&cON|KIQamQTU)y&FnZ;ZQB01^Mu>I^=uNjJu888lc7oA?0&(4@GLgrnXX~lCPb`xDG)qxywMmNy>F9scKsi=?u z{A>c)DBDdxJj&) zTFy{K!q>Tfp{}EuKMA^4rQlsW{aJ)9B2$$!Lp>$oPT=WHw0mDdod7gcPt+>Rfh6%7 z^1!Lfwi698UQc#&CGy_-c!jbwI}!&$#N@|^$0l7x`MWt)U;g@KQ5zy4RlxW;(xHE! zX=j0wICUix=&E;ZrpRHs8V?9-tD(O(6om39;r>t!93c|{NU(0)xKY3LcI}Sdigt;Z zRAuSm=9=bgOE>DuQJ48}(E#eCXEBGX*7A4+%z@kyw@Umx;v#;gE>^bHvI(^%WA~8FZCc>CL}6hJQpJ7R90RA zM3b%w;x;uH3fR_SS@l`bMA(S{Oebi4HH#FmpIT;?Eo@u+1)->QC?)dZdnRA0gH^=pyA z=b7twl2=^^3~DvC4$NEk+@BeWYw%>cD&gB!b4D~~H`i#*EnmwT8h4GTWw*9LwDU?C zvpw~3(xI{1#g1$eJ_k|P+PGR=`T|ulQnd5hu`=U{_^Y<_`Ye{cXZmo}$#=&=-yd2@%^;f5L>sj;?JiMN~F zsgzHnYmv{N|J=LS4T^nfo+>i5(P9F{|1x+`r8BPH_uy&D@VtfWNXwo;W&v}vi8jwB zpnsRC$E(__J6T8dBO)63>b^a2=q#8Kqj66@J5SBZPh56O)tuY8flmC6_+-z>ZMyl0 zh`BT&?kfG=+o-JT@;2vg8_dgHS&VaGTo5$X)Y@q+eE0f(fvZbPx7ozHw3T1F&%EE( z;Kg(Gl2<{22aS7S&y#WLKGA*uqMtS2D^xQ`Wx0AKkiBz}lNq0DT3kHV-Ob+7o%-}U z-61|anYzkHeg4O!s~ZZwk6)CKVwL&E6N;&kbo&-JF#SJU8bl^moZJ>4(6yxn zBVSXlUcD+0Etgqxd43`|E-nt$a_IH`BgHx9xzybJ39$;C1|yTx5ZbPMR|Tg?vcmudOB_Xoggs(@o7-PZE&-@nf+NwEL?c&8HZKWAFa{|#&E zJdyTXckg~N=RC2+pF4rU6EIORKOaZ(qw{5QBfV2hT;TlhZR9QMQU`+ z4cZHu*ScRi+FtmZy6<&VOl}uNY~k(Nx+6Mb##4GIL~EAoY1W*~npefSWrVFRZrz&E zo#x6)TXwoKj{cXql0o*>$B#Y!{+qV-PAgxvmQ@`Vd~&OsfgM#=n9A9B zHNiag|5!_I-MX~_dSx6SugUkf_tfl0A8y-YLKJi0{o$sBC^m&yqSQfDRDdq0kX#DD zMg)voAD_y3?dIZlTXPm&{OR8YSq4p)S<;cZIxk?_#_t08a_g2YU+Q07px?bawzJTl zqhgjS1{~fr)EGx-1X^Mu^ycB_q~QL#$VmM(fh1;Mc8vsi!SK(5=}fMEOEVzLS>odj zmqJc`akYhvq9=yIqj40vBX|MOX6v7S`c=&Go9v}V&{SUeE;}~tW<^I6%4MxRKc;6k zPYpoL=m-H?9UL?Y7qREQw3u%_uxsZ||Covheq##ZafHBA2vy`#%eWZnGWWuRb|2;D zDm1!bQ2dtLX~oAR9p^jk$@M}Qxxi=mIY4)B$VvStU9QWfMDC=_l<(1r2l7=fx|4K+O z?wKvW>cmBw({z{~dQxK``VYdq`HCkOF+52cHds3`D11J>wCUQtZN3kR9q1}#bw6U# zd>HX}b#!n@knm;PFYf9b#4wM(2#mn481JXx@ap`-Ec=Nr#oWhFpI)QhCRpZ`gS!OX(YWdt*L_ zi)PQD}u3>IuwBE_kSLvSM-`m0? z%GxrZt()Gl$ZwbHuTDpk)Jz%oUndy)bL6(>M9hs!gPNk64oS(!Q&Uqiv-t-Kspvr$ zMIHH7M!xcxEc|l798z)|%Z`|uVo$EGf1{Y|>+SuuQC3K4tWA1maez-hd;EG`ged*L z{`C?C=5)#dKv*ch39hS4Y;DR@rs>xGpX9Dwc|@QDs4O&+8cU5)?>mB_v}M;J2cIz) zPAYQ>U(7Q-o2#C(m5#0@UM`B?b>%K-?G=g*Rx)svW@>7L@l$9!km0V$l{hb2E#@!C zo7&iHkUZCxV_H~LtRVi*f~UZ)R^e8~*RMVd8~gh7n_r&msg4wvmkKT@Fc`lDu{bq# zeFrVX-AkdoLOeV?8#iy}5ENA2qGp@V|DeIzsc&(5Kq*aIbQZ9RSNS&tgY!c?8k28t zdbn~4=?>yld%Z{d?zvkPK1zy;0r>JH&?Ox{gHQEvc#cE9Bmeqf({`&n^Y1n@8G&93 zJI=&o9!C*d2iI>yd$J?fTn7kC8rVN5EiDb2GMV6_zus;@#p^fKX{qvO6}a>5M2dE? zI+hW;Mo#iaU#2wmY?C1xA5IxFJ0Fb z4S^MnVH**2s_oE6Pn=73x!3;?*#zZIiZb;x|Cm>KeT3*V`Koa6JKT3;0GonR)}Tv9 zE9XgCV~~ZA6S`qL4t`5I|8VQbP@G7-dG|Y*niO4C#l(Qf%TJ#tcDrh%im$0gjdyO2 zx1Z3(8#B~JA^+;_Ws{}hROeT?^n_qN)~l5B=h=;x=VoDpT7|50Y0YpztF3>d8(Gy3 zFe|kOPrjHRs!;arme1)C@W@c^1@8X3VcM1@8O)<&r>Q4Z!exj4VA2%-%w{#H1w?5G z%M_DQJp2bKHwAWMA+FR_dzQ|o@E+V_?&|6hr+m}1^D{Gw0MXfob&Tjk@mB-cTkS@% zOluk&6%0RzS+a{;_dmV=-~r@PY!Mp)fM@xsn#*k@Kr)WB1tvy=u%@26W*BeTyFWEGs2$6+2|n}8w_Cnd?IN?8?QgyRvw$CYc{M^94HU5-S4`V$ zz*=?!&EhCb7&SBNGOM#^-Nj(2Wy9g&(#TN+PH8H%H=iA9l)zt$#?qZ#T@CZ#% z{!yR&P(-6(bj4$$s2AF$2%2@oM~FHlI!yN`TD$ltLv)so6w`viCG5QL2F7}lT-4>E z2Xtb7%q|N(o{8GU1w;!0EE$49t8(qyQ-jZ84()Yb>FMc?KOVCxW*Ny5(_$-Bb4Zqf ze1InOtU`bvaFl!nTx$F#1J`r#Ko+ zk90u*yavC+7n&*FxiXL? zUK9QWvNeKE?>Je$hyGvRQ*_HbPHUHWGWj$>AOh#y<>25@WgwEeJW;BXq?}UVdgNVx zTwLuj2H`A8>b#q8;`xoCcLbN4sjLx3i9+4ZO66V$60~Q#bItu3o!{KMf4_)@r4rbT z+#T#MnAHp$bbR;no<}oLCVn*|BR7glMcouEP+?4<5FzvNDbM@bO??6QRHl@(xJYX&WyFUOnx)T-VYaD01w}PcnM2@ zS3S$9M{)Q5{U$}f-f#1OlUi%QhdJxLW#_R|5v9(H89gH-x1CpKTkvlm$2$wROJ2Tv zH}6)sj@xSU8&%GaC@GCf6F^)CC{OXH;V*i_62r*s{S@76Vb6aKC+c}A#L-5{vn=PO zo;&9O;E|g8Y+3Tkl@q&pwC^x8Gv^hsTkQO8j(rr>EVQfrhLnQkSet-~O8Fc0l#ek| z!L&)uA>*cw^W&xgTpC{o>t8*P%eT@$%FWHK)xMoctmD`8G#_M127O&!-OEty!+ms8 zHS_t!{r7w5I-|&5MqLl9#tb#O>*RFcg69qas1#2@HDi)wnA}#@;bfm?#2B>s>CrL>H_7F|TfacDT$(X1J3)I358Q*f*Y|+f z^|98>3O_xhDOyj^mffBCBH{fdNYctl?Y}A|> zc#G+P;GGl9`w&sFYMalj6T6*-%WLlK8lEV>)=Fb%=Co0ZLRJJ(ktRx(Q;9(H`nHp@ z_F={oH{4Ox5>fdRKxYZ&!z24~>UJ{CY1O|VppVG9zwRNkG(u90{PJkV|v=mQ9n zNYCf6NCgFymX`Kf0{r|O8X9{3gDZdZqc$Xl)Fw!Sz(d-XBqTW4*d$>J7C6o(_6AiD zrDtFm^6W^DCzFhdOOAPWDhfD*+_ z^Q|IQK>OQzviGhBfiAiF)A5(9MFlhTr@}XlEqj|p(sFIk^Zy6(a0rf z0no^y+wG#Kj{#^&I8%P8VJ@xEuCskw zQ8dh}m$i9 z(nYX#6Lg(NuJAjJb#cX1An&$aLwx2{=p z;=T-3+VE4lUs^NecdzPLCFs{g`-p4i{PA$moa+Csq`iiO(AYyOErB$F< zCpsSnj5xq~S{oP-PJKN1a1tU_SRoglJZps-5{tP#1n*6u*m0J?vWl@6Sn36RYZ$9E zRLzkp)=09R74s}~36@1&A$LorI+hf!A_(n!zp#x_thB_y*jP%ij;k6#n166^FzMi) zjEDK=DYQg)&+b4#WIjN0sUkDX{%_*xzPkTK^#c&D8$J$|E%(l z8Bv+#=(M5Ji#X%JT$Qk5ms|e6IDfUi71@B0QdXYs!x*t%Z01<-RRypj0EB=Y#9V-n>v6XUXa_@4zGBssTM&tr z2)4{Ucy3Ou=v_cA5b_g5#R6#QE4m+S(x76NRcx;nROTy}AG&H~G!=TyB8wOFC= z-)X@L=XQLI{PpX8$nqowacQta6%jDe`%Ek7>gu}x%-uJHk~+=~vmxlDj%ZTvB4+gJ z%5Xl-f4aj`F$>(1y9cjP?b7-uc z=?isDML9)7in<&rmE=Fv<)X4*@EW`P#kRs0*YoS`S@B!oIZBxZwa?f796BF*?(!Fd ze_J&NPrvr_`&Y2%-{4{RWQp&a%vcNCKc%c`e+t-sW8mw=5t5XMxcg%Z0tU!uUIMCm zh8)9dG6~Tq5u9jvrCp5pOpH{_^Svr9$EFXSKH|-2N_G8l@%?EM*=fhpgJ715Qdt8X zQDdmMsAvWxnd8ivju_huOaJ_HG0DdGf~94{e0O1i;0we0Lr#0R_G;Mc0)D%eckEy4t(F znqs{0)E!JZPGR0ubvk*Pc5`#7>$#aW#3L|75CFJ${lqZbBAD#x@v5xNDb@Fa|;6N`-tAD&x{>Jyti6OfO!TIMw zdKJROwx9iKF>*VQfCON5f>;7M)#L#W6m!iwXFl=O8G)Xv*>!mB;YkFYhLUz2OR_0S zg0B4I$1bZ-cx!~&AdIMhkmoG=;Ry1NJ_GO|UJ+3S+^hNpUmU9LOK4@nW4E9!pysdz zRaRDVI;FH8K2a}LouBI2-cGyQ#6-k?&1rUJEcf>yAaDC2CGF+%!h#V5r~P~ePj78~ zK!^dBk(y%%5{bi5%+%9wJ|w89*vTH$SPBNStaT`&lS(KzU(nl=-o8CcIyOkiSM&#F zNpu%E2$V2ubP{lNZF!baSd1(P^UX>}B#jindp_-*q56yQ`Pm{JapUS;#x2~-E%!EP zX+=HFRFB!C{l<+tnyD$endXql;-X}3zBYvgEgg0*HIHhGxRw%iZIzy$zHc~@=0!Db zfOUF$S^?z%T5-miu!+`831ErF(bi0*I2i^K9u5ISCL$JUA2J1fHNmq(agins(UK+| zdFjBJ3FtmXNTV$E`ie74l9C!L6P}2hNkpN-qwz1WpGZYLkNKv98>2sXa8z~tA)Dy^ z`_YrVh{UeuWSK`M-43wyKPKt6a(Lq(-z3ZsQ*5oc`OK=%s`mX|KB_c0jgt3%0a(IG zi0|CSQ08_T415lUrt%8?jRdWVscXx|sf9P*tta_yD7i9ykagwJE&>r9!fU48C87%2 zbf4a(_=lr?i%b+n;n@csmpjjLHGs^TneoDjYJ*JuDqbPew&*hsQ^ZO1$$*<*Sypx- zRLES$HWb9|I7)|f2)|_jK3AKBL?JmhocW69qEI{#hsMFKnfHcdZY$^)oX*un;1cG( z)D_wXYlM4u6>IDlZdzOYS=Hb&pI>kp#h@}iYtv9+E0O|Dz}-j_TtRbG!5B3Y%z>g{ zY_^kuAq8KOhz_ToY2f|Q`#>DQvarS2zIs1;R1YGk2yuT`omRfpB|7mHi=eKCZ7r4~ z+~elOvq6OHdozeAWBP&X>Xd39z5Mh#JZZqVz@dt#5EZ|~bimICotK{0{snyzCj4>P zh%>(<5Lc~35wkVvdH;}vYod}ZdfpSDZ_=C?0}qaX8{acij|9!Tl1WJ~h{TJKB1IS> zY)G|qJ2xzE3Q(f>y4?~*_ApVq6Cp>nnIB$)E(@=kXAEQNA_CQOX5^iVF6rtD+79al zdbwrICpWdxT5$Y_CL$kIeF7Fh4rk$Uf9;R6hdca7tT}X?Cm(LwaWEdLL}g1$i`-h1 zf-Lbo@On8kHuP-Qg`Ul1U6G&@w{}rHkTyZW$i`5P$wqLZwv+G%0oJz)0T9TPBI?Cd ze{B*;Q=zIxMA~-CLk``zG`}<`0MAQE_+)QSm51kvx8qQz#D3T?3V_LVT@IR2zT>z zop>5LU=r3%h^SLe?-8}BI!AOE(v?YsVAJ;fgM?e8-z-14yat*>rmP9Xb9VXYM+G)R zuOT@!@=9mIav}%^_{kWfPGn{eIwsflmKE$mU}I*!*qdg*jte<^s4a0smPxN*l(G#a z&;tq>`Q{(Jy~xs;k{mYy3(#g6M$}T2ZAZV!AtT!Ym4sA~rW`bw<63l+RpMS6*`lGf zNc;ype|{XbDsJFz8XpsEZ{k83)J3GQM%;ZbNANIVoW)M_pRb=~I;mG_T6Z3NdLVT; zR2!)`d1xy9&I=asU7F;yQ#DTqa;SWs%romOATkujEg9E%vf0J^|4?zKI7=B06~pT9 znaMNnKEGci$46|n3B7Z9W;AnIOF6gwvDb7{{i|7<1q;8e^aX$`ZY?~e&?405i$dFJ zEkyo3(>fp&>^gWb8Occ&=I6asvuoErnYL`%whbcxD%s{Bk=uvrN%Tz8@O#f+RmW4Z z9jrf0u+dU~sJYG0%D(`_q&M$4SP!m{Mr<+6lDPi`RSkCj{Wrhkj1g+idrSQ}RQTo( zBlACtk$O(lDJ>xr8ld<#R1XrP#rm{u7e*UP1d1j#1v4oQ5~?xy;giRY!ooF3+oA zZ^0%y#?iam!QPr6Sk00G3O$sJBiZd4aS$t`)!KRxFqjvgU$kll{6v9UF~9HWzW0f^ODivZ;$^qt7j zR|^unhLu!4Nk7uC0-u@uQNGLi<%r|_BoKjtkq%ZMK?-L1GRV%8;mtIx6Ar|d3q&@( zAUMj{7JmPJgN2#-I6Hgw5c?7mHu85eo#xF%VRI5kuS=%l#CPZ!Uoqz!o!7jDy6O?i zJm?!06-73-keO=y^0dnTLyT>2m+MM?0rN(Bb;$vljjf3qxycc3UD<{e3f5yGO{w8= zI}ZLrYRp49WlF9!L%JcIWn59+bkmh>zl{Y;I;a}Lt}A&~1D{i1$aKaz;#BVwOAH_@GjdQgQ< zMzS*~Qb5se3CI9be(t)=p_CX+pwf7!Mx^u9<0x$%m)Ss4&`EJ3!Aj7faj6PXCxnda z@wGE}?`gBzWW+Oi7XeRBI~H6e(*G<(Fe5;y;l?;$X7XKxcwqfRkFe4rn929h!$a6r zhE!cV+c?)eFS_UQWMN0ct<2I)QR2hsD;68E2!jiV#X6B*n1)X+&RSwBjia zPEMiy4#xr1No@pz(Vjr^PJyw{7fycn#~V~=QaeNsR{gD30gdHyn*F4o}^YJq^okW zBlEJ}{JD)Nh6?J74|jWk>w!Kv&56Hly(Sk`PJG+Qd>kaLjAYpuUAfynI99)>}l@U)vxJ#Ik)h#Cl8w;Lf z05&IGd3FS>*bm$v-LhA80Q-ChYRfY379)(bE&Z0K;GJ(Ti5Tm1D_dQmXbw?_`UGys zAdO(}jkC*nmW4kj8cl+QWc;0{=Rm*;u-}eIqq1U)A1tLzrVNj!%t6+*`TlHO)sPBZKOj#4HV2@7|NcG6>{b*H zMMOmS?A%Op->^Oj;loTMfD4}8S8(0TpVMR-1bK;Qvb|M({vX20}`QI#tZ+9EFvIIFCd-GyJt>Pgp=61T_68NE~ zpl0zlxBi~#fJH+HPf$`l`+wHu*b_CJY^20Qvnlp8AzGp))j~Py@^+YKvY4-*1A04; z^YinUZ+wR={Ha+VY12^XdNsy3a`5+f?4(1>mB&!b0+x!HP9vDdfzAxtSKDB>ekbp* zNGoi8ge8JM;n-aH-@Zd85<3~nJ2=S4g9rgGU{djeO zqI+iLjc%A&xJ!kn*5x+Z8}$Yjn~nI!dmyY6_rn{DAG;zvnoR__wF>l9Q@+}m@1&>K zUszn+CnhHL5xX85iaP-_yh&{cwEd4WqM(Mk;EklfK71LdlMwShEE!(xe$oRO*mD{1 zdAsCqYYFZOCdz*DSqK*z!Ci4igwF#$gC!tW`&TwGv99B=$>mC2U2Z|#{)lP#6kf{b zZ{XlC%O*iG_K`9V&BNeMcS$GWF)=WdCgLM?_g*Fz5{QJzmv6iSc;=4O!_sHRwo1g+Kd+uPc!4{g}rtNCIlNDpu-r~#2df9D- zMcr6sXZ2~wpbjd% zxp59H^%}UsS2)HD^G&iL{F3L+odfDhRnNJT@aD}M{yl-jAv9q%&wz3{g$yc z^Xey^v8#q;9k_H#H0i_^UXl@!jyR(RRUog=RbgbiQzrC4lB+`IoV0n8R3dd46~xE( zbEG%{BBvQQV-qQ4Cdm5N!SiM?Fl0X*V2+B1_B`fWJbp|<*{4JUktF~#fkqp6%x$pF z*jwcRN#Lg64Tz`Fk_XKji1d~g^m@Qx7|5i9-_m;bWf&f#fd2EVb=s#fm zmS*Uu+&o{xK!j0mcAp6)4E>)w&VVw+O&3zfqAl-)S70uaLXs#H%3a9z285JWWl5Cd8oN`BuVOX7xI6ZH#$ zhCBjj(5lX=$D^a;)G|E~;a{?*j-qWmYhqlpd-(`5h;!>Y*cG8tFk-z!cW{9$j zo+bSBQPXZSx$ZX8H!2Q&V)$qPiVlyNk_g9IzBEol^ z$x3b7&XbqOzV7A$GokYR?oaVOT`$jVAQ20!X_?tYQuPSu=8pE|?xZ1y>8tb{SDOjwegYjQ zUzTFW3K1Uo>dr&~(szClHA3?tb8r@_h6#{pYHBPSi|>}_ZGSGb5@{V4gOZJnO%Uw| ze~#d$m-k%q8IqJvzVrPf_gS;)rg=tetbHQb%Ls<1m)VL4kGV+oU1dJn3(84Kf}zr8 zr{<;@CgHA9v6Tp}RCTkGr4*w76kmE6wD8>G1}J>*++xNGHssR69#?6x?{eC*P}X!D+#h+ zHR^S<5+4^NE7U@wa_#mikFkC1!diLy@&qV2B{{e!$3+*TD0ua~L7^sU>K~*7x9{Xn;o^qjnoeyy4FgAoX z0x`xDM-AS&{cKwUgV zmWJuR-{Jpk$3|i=7Z4f=S{Xp=*1s(pj*8mm^BQg1xDgSD=szP7l%_DlF8h`9ZpHxO zaRUx&c!}wT_}Q*6XhdO-(ss~yaC45xHIX&|;UFG^Mluyg7myuMM`*SQg&rbzG6_ME zeGNp)#fc#mB{1Xo`4yVrCYX%OenJTve$J0G`9L? zdWY2?%b{Auj!KUoKh9-%X$FW%v{q=65#ui1#{tGEVfT|TZje~BQQ1V=SS^HppYY~h z1;bngT+>e7tPdwF5R0J+CpJjRBM8F}uCeQZ8_6hwgtF_D6hWAK1(obJnsmeLx?n^k z5cfA6_XAQ$e@gEe)yn+nBdm{Km(Z<7Z(!%h4<~hYl$Wd zVYQNLB8uQ2s1C2;{9V=7w*ShT2VsQl7$uu7wF+z`p_`I@#z1MW8slWUzlhx{>*(mHsBAR5FZkf;>;D`?;8p(m$fYNGOW(xEeqUHp;XxXUXsrbN zqvc6a5W$IsX?h##ip&T)@|Oe+Td462DwXqN?W_ok#k~tc2TLKx&G;4~BF1p$?sW)7 zK0CJ%k_j??>uoN{uAs0Fjc17Cz!(zlK|OT|xkalAQFpNZ90WW?g#8+`yPJ@kx{Z2? zLvBhThI0TkVc*ncOb%+W{=A-{Qs}$lcU%+a(r_jY0iAgFyJn?#TEB1r435{))Y_i+ z*Y6(i?_0#2SB$>$>{_=uYLy};kxk0&Z1q`Wp0I7ZVd!c`vq>|O8nFO6O>%bAN_UWs z39YrOn0<<^g_wW>h-~btqd$KAP}y=QJjRbc3`rQZMC31xQ}>{J0!NeN04QHzDxJrj zy&izk#Pt>cmei_9My(+^AUr^h!-vmp+`5Yd=@4g6p=}Wt%}A{M-t|3boeN;4W6#4q zGr;X5m|-Pj#cWs~Z;Q!JH%P5t0QkdFeI{^H4dVYQU{>(;t=m_D$7P^-Aio>>TjL?A z*<0TWn#k@TjHV*ot_jB**U+#A*Q$0HoLEfLLB)AD5E85XkL;wy2viH(WcJTl1T-8nnD8 zvqVS06%FD+$V`TzqC_|jv$>$k4T+5-?@4+HIa0(C&0+?nt){zMyw>U6>dMLtFlQ3{ zHIvSQY+?q(_c_kZ{n_%;Dn^JfA9%QpXT~v2qk&RHr$6z@q&d9*XN!-mOr+S@uH?p} zXU=G%(W~G}StK`XqP>nENG3XN)ed|G55I*Yt>`rghoQ%QuD5jmbd%SNvF(fZLG0he ztU_F+5SyELqW2b0%EW1q(RC;MVI+u{g-l`ixW4D|_lff2yfOM2 zQsPJ@BNis`&LHMfGx%Zn?z}Ft*@wa+#g08@iP#;whI`gXCFq?DCdtJD^d=K2OG8U{ z$39~|BK{7yq^055D3sL%Xs@}ZxANxoW?YW7f`4OsCK(?C2L@g4eWrY9ghc3tLdl8B z7RaR$M3trTB0|@U`^?s_*$rz)0!28#ZwaOP3+^zBya<#~;mcH-bnjb6BbonLal^Px z;&fv&#Nm1qF-EDWAsUac_MpO$;44n0=$=|zJ&@aq%xKP?*4J1=1h&)#6}%4vk`-TD z=p{NM@x9PJJHPz-tkRK`RC0s@EG4o@5;Ri@WbYQcZzwrR0bJySk30WyUN zSb=OHgA7C?Yos$}eCYM}HR3_y2q&Uql5>v&5ORk1EnYj`wuf62$p==Ffg3##bJeFjuaO#C*x+`EX zaV&|g<&%Mo)fKeUMgYpp>~Rb4r#UY|p&|dL>H3`0voJ687lhu5IPwP3`3`IKT(fYT zj3r;3wsSrfzYTrQ1jrb2KF_-Hiy4_U<3J{;oZJ%MPtsl>5p|SFr96wCT_e~VaSiK0 z0mA!1eN2%S%xXmhdO1@#KCxhZp^#F!2A+vq{`iMmrz9CyEnE$qhdHU)QDwTY`w{o} zcsWJAz)L%O_H0OJ2Sm;oU})h81kPJf0T>kSt`kDA$<{=tK&-QU%q;3{aSg;3 z&&6HM-Ev6Vo?v{*M~a`b5XkXP4tT&f<8!*pNaT2Hz#9VorwYf zSFP$xEb#Qb^&91j+jhWfk8LZ#He%ckl1NjI?Bna#F}xMk9sQP0Hr{?s)smX$ie?9= z0f_#Jx&0atrLaKQVkfvA&TVNzzeQX(0hom-r`XJNW?knWpHBT!2V#d=se~d81|~H* zIZ5m-B(7rse)0+&7wAs*c5i98d{!c;5^d;=sHiPYj*FYZHnroYPMvBtMaY9MOKhCW zzQKS(HlU$jHKPtHp|^0RvVA%dW!jJUDiL46pBVJ3nA8alfaKfhMqO>aZk<;hUR1Y> zTT`{Gs|z7jn*id+;{oFg8tey<4Qe^ShSUSec>q|-<9Yn-?L7(?V5maU)i)0|L8DCx zdMpfxD+qEzq=iiKeK02s%NXF~EP2!Q>(_&tcJ?D9;AddIO_D@mv0B5v<|CdA7!=^= zXQ5I7%7b}H!C+Gzr6eT46 zPLhabuS}OAN^Ttd7ZF>5PGCF~?@yjudvIgd;WD6^r2ysY52Zs1vi>$l4b_ZVUYxwmIM40F!;1rQXbA> z6T+lqn$xpMV;Z*LTC z1bRA~)aJ4FOhaD+{h^Z))JYf-PQW`!60mrREo4ce9uQB5LgJ5z{z{O{2|4cx1;XTK zv`gIQbx_VH&fUijg)|D@g{&@U20!XbheQJ`?JkRee)hn80R5j9eT z&`L7gBcM}6teC-;3JzxGpQ$6gLqlx9{xY=rJ2zCG2jviYvWLfPVPRn}=A0mUAzg(v z;{5z@w}!LUzz1)U{S?0@CwXBc8F8v*eAt-n!J!{r8-Of=U%<_MV$OM9CUh#5gUg@1 z@g@^82SZJ^W>y-7wxTi1A<x`K_TCD>NoPbF$5-=N)HwY6 zxbM6ci=+QO2>k#1nZ*_j2waoy?o&Z>N=x$-6Ad`%?iVcd%{1TtM|)@cQ*{}KaW>~x z!50Iqoh2qb2+H z?W?tjdu3)$yPD%GU!A z9_8nTQ$K`%gs<}qc;pgbMytRAx>!X9gv(3GN0dc=PWVm6RLl9p7nknL$=P*o{iUC? zYv}ZYXGX<940Qb6)>8UGujfXhP&2WrHflBU5zxI0$M6;M;+9~aqlvI z2Q*xXD-uq!8{S}Yf7u01K@CBVIqF|mWS>Z z>z&Yaoy#{D;Qp$cTVK#|N>Ll1oZQ?+bdONW*5}T4?TBesPy7y`fPU6yxR|$bK~%?! z6$Y+>O{dr$c^A-V&a>%1qX2XS^|ACnu5Hc8y8tY8VLIf`t(%Ts$)ym_W&|&&xNaTL z&Ig`uzw?{t9pgi5B6?#=Adq&LkA09HLX;?)pf+3MgdGQD{(~XywtUOA{msUTD9mQeDpg_Vxb-IRsT44xSqMpdJV^xm(w=0TNIpNWAkh7 z(;a>SxPXvWzL%H}M60KAAR=}MP-yv<{gj#!1)OU5{_ z{yowd?j7sb#rV$Z6M^DYoX>{zC&_rq?iX6j{dET<3~?-_atZSeojp^cN{9=)9AL_t zdK0n|vE!>@f9e$16jQ|a-oq(`hmY3dESWGgjDq5?;aAq&#DiGa_5B03fRkQ7kuri? zyGMkY;@OTSAM_7&{SZy~#=9oWi*Yd2>ZFnGG^$Y84_?zf|2jKs9NAEF%NIgdMZ#y~6iWh9i-mp}eSjqx|>T285vnSj5LmkII4bC1AQh}+;@_&(E51H z9$MhduVDJ$R&?&8`_B+K%;%-f#Oc;juJ9GKI?;YcQICtC+XN>dZ?-+Z-W=pd2tmm2Y*XDY8Tl3aoMovZ) z3UwHxa$N_7Vwgsu_8R@YAAYjL-O2}FDtxq04wXo=Fe>*j3lI zLsuIoM@dofi=r1VoPOZu=IkmjCg$+}{0&j3ht^{69vVG_S2^gcV(f}SvD`)e*^{lD zWrISw+{0YIqUV)3H|U)hIx)VxM2l-{jXj}YkyPX)?r~;A-_}o{lrg;eg^EtGp~{)- zg1-I~R%|!Fj?UTiS%q-_cA3WGC5aSTlfxT1tLG;g6BE4AJYk#SuJfBpw2JvSyU9eF zKaS=u7b1QWE&+wAORG}E{rL`c`SO83KRNa{d|>$V6Sary&o?Jer!pYl++q03?avSF zA3b75zIk-`pN>7qHy`)xl}Em!Wd7$x_W$Qa{<9|k*(a#~oSnM=l!E`1N7R2x!T-Th z;C$e?jD74W=HPz$;D-0fXkVXvbrkBf1=4dz;x&`xGdzd=4QWHZg^X+hsRI?Rl=;eq z-b6W9MVu50H7|g)@p;tK{f9fhyykHFR%D3BW;|v%AmlVu#VPgR?ah$m@9XL=qqkSP zIOW}@MZ7gosN!?M;*DO!f8Y%oaplfer-L}mI?^$bjx+1NwE3u*n3($d zdQ(b>xa~lBkwL!Ed)+L}3_2aTx(8PdphjdO+zK~7IrMjDYMh+-7I}*{;Hs5NOXc5~ zEos-Q@$=r;+|09Ti^qPlH-2tba{pU|V9VGzc`pEv z5Z~o*{QB^hLr=-+G#*d7&xj1$kt6Thm3IfIUOzwH&6y`ywj9G7m)w-L>wU+I ztc8$yP1W;5Cq5L`?$Do`@2--&;$w}nv7hT|m*xg)^S6ba#Y0t8&^`XvG z)K~CE320vx!IpM}Deq)|{`~o*k3&{h4lKdU?5`4^-EErF^l?}oJ_JD#-be7MW!lvi zTef5x85Wqzefj)(`uDd;+10*kA8O}^8?U7t`lN1Lzdq0*jzV2lMQ)_ywLN?GaC;Aq zHUv|ivM6=*JJsSn=K8$T51~*4Imnh6_;+V#bAgQlKh1#q$}VvG^tGmVMepM0hfiiI z`O~L+49(j<>%e**0XSD)g2$GrQhM>OXeF=3)SB%z6ZC4wwdJ)nO`1!@%_mO%pYV;L zCp}-QGNCT7H44llZvd+*dgJCzqDDz(3$CY&0_&)^xi%LiX0C)@tYe_V-5vNuDl|9G zP>pI{EoMNy9YmmG`BQ1zZWZcGPuJpTgZoI`9{pTBOo>&SPEW3Wmw$*u|0h9FQB8iW zWW6lSBpv${IoI=mNgt_?Tx>k9#Q_%#TVVE$@Be^MVmZ992H*MfW}c#}si~*;?b~N* z9EU=E{f;cdecb~r+7*ZxwZk``?CM$nmhWDCpwoRyFO;KiTff!rK5?JTegBCYNmp+E%K3i%#*G}c7|{W`?KxQ8(}6o% zuq%6)#+wFvd6WYb+xCn&_K3T}i9aRxFx_Z@LZK`L{*9T{J2t?bE%{Yo=8(iI`KKWt zzMoE;y=|?yO;>sOD1d2wV)B z7EJ{gzVYmC7QQ-jv@3E`X^YSKbkgWt_3fy;#f0Y|H=;O#~ZB5w!pBk#SO zj?^ZLrZ+j*GFxovgK)dI1cn35z!|l$b#9sC> zt%~&!_FQgi3R7EJA}bf9q_A-cv5VUKYhWX|r`pB7e8F&ts{Ht2SaW`N$HzP^Ep%)g zzj}(t33vSK>}!Uhp%W}r@-NbG$_NXbMZ0sU!&lM1Yw9JN-a6BoN4dERR@E_qc#FLU z4|Yzr^+p)9&?;O{C|K3_*#yu39-jK{PhoiA=l2Av%z;7Qg+Mlrsf3#^w6r?3DnxB$ z>OWtnSik#`($xkfl#!_l$qVpxIf!JeK{``r>yEel$Ci*jW3eI)d zXtCO&DcJ|7_45USf=$%EMf8$7ZVMXULKyJRQ+u|B4jw#+CA#IT&HhT0ap)7cbjj$} zn^W2DiSz3=%(ywGog1GC zw_3r&^xz2Q@m5Np&Q4;)@9!e|*{9h>X1Hq#ca#i8+y5fP9w&DT+HKH=g@;qn3B_tV zagqkr&x_97=m&WE#o+l?YFU9 z(W^|KPMhf~%e&R`T%muo!SKmCPoNR5N6b{E!u;jY`MXs&59jI2D!h0V5wP9!{)D8E z-Or0W4g;|s|FkC<=9(Jk>XDmCs!{nv3?9shk01ByRC&=;EJE0OY4c^o>4ko4|6SU! zL7tB-RiL8$lFhfHc9=Z%!zWcOo6~2nrpOjn2PVVX5l-&)k!Xn#BQ-|}Pk8od5TLBf z`>lU$Y7SCRb1P-9izK|sGN^v?G^4LNyzi7Icr$;kq%kRHwy#VWX{_eCKb{mu}f% zH0B3%w&^pr;fw!6j*C>ArmVQm~gt-c9A-fB)qcWOWQ|RH@w+ z_1~#rzeKRdc#Dxx{zQ>O{yR_8W;*B3yJ6Q#B^7*GV!CFMYqufdCb{+a{jNtf?K3Yks z<#pnmw>Rd-D7sd&kq;_rqREA`BB#E11A;SXH@OHmpe`zIfJGpc7JkM zoBv+vnzb6ZYaO(lq)?boTZ!cMyX`Z5M);j!L8AXWSFNs>uL+v#6?^1-1^>j_v%kn+ z!{0sq=`eHwZ{oQ<&fj(94&_Je7p#saC-ivTv|AMZr^(Ht1=d%kcHgCMK_} z3M&o&(ny#c#O{#HJA-*t&Hc{csvh81)}rI3ibPEnmsQqwHtLcJ7oz4VIWJ#FGIOi{ z3JTcvvFZOr;Rz&(27Am2Wlpr{4hN8M23j$BmF+PCXBy?;-Wb1mlnmVBXbQjJ~Ky z-rEeFbMMPL-1KXn;+p3sKkC!g-w5t%+ilUOU zIuo4C$j406Ur0Hpc2`KI-5sZYNK12{?l^P8qIlIkRGjhf;huk=9?<2o;6?lS7nb^& zw~OU{>FF#ZW*CNaqvUl$Yc#`u&j=Y*l)xkwzZAMT^(QsIF4N0zWw`iFF)RE%;^Jc0WO#w0BYd{#|W85`pH*e z1jneCuPc|ggP3~PLR5*U)5aXk3=H^W;edeNpl9iYN=l|iRocn9l0cvpy>)__xpaON zTiRd_JhFfFD7p3XOIB&;k<0KtSB3*N@$w$C*#PmTfd+RniB*k|$Ie8~4U3_7s{nLT zFP*I?780#Av|?IU5;bk+O>KT&w3*Vh!|iN!+7%i2p(4%ki|m`xwhZ zm%`cMs)3#SURX$(*t6{uQdI9@n%Fazyh`ol6O^aZMHU;4uI$zabrXEOF;}ki21zL^ z=VhsN8ag@c^}{L}TO%?D@CrdCrDSj>ez8G)K}bJO;8qcq;0P?S9RiZO zvb21OUSD}NvsV8-VDPib`Cs}+`TH+cn}u6_XWdlI10D9Q;7#!E-YZz;gk&_HWwS1{ zd1k)eW6IfR*VWG--)~#s;r>XAajB@t;8Cp^Q&pGXjT^>lwpG>mTJkBmfriQy9^J3r zjDhD+sG%p3a?T@N1Ja;+F(<_xn4w4TqerxbGO>|rOz&=vQFaBdY zfR?whZhCMrr>u?v%FeCJTIYevW@)7u7#YQ?z7u$_yt97o$dMy`m7Y$~EcVY@*?Gi5 zxg8EaJjP+|KYa?_H#6o~VpeGU{kTn^6geX%pL^exD>}n9z2gHFXq-IC2E#p~OBuY^ z8s$3_8NB|Qqigf;|5_*7HSFHvbMZ! z$qsEc%p+@)&ZZ|W&tvx}l^t7O{cC-F-R^*wa;@;5otb0nvjKP6P>)LCO*a6MOjIcf zwZnEy1!!qA(HvzvR8^7eJ1+-sc!G`f++i-E(360_;>1#4`~CDMw4Ck0>g4pPOuk#pc6Hynu_GJGf1$n> z{5}26%iFsm^e26_ON&CzN?Cgl=WGEul`+M4%_ywDG`uk^hLLU2?VF;QP;&;#Huj_of)Lb^YsSJW3*lvv}BG-YX&aB~Qs*N8hSZ8u4d z_|;Hs*;&%A3b>G`W|Kykywo1v(8b5#T)DJNJUXH~?v^>Umxt9N^m-t=sE9NqcDfE* z;P{wE<%7NFX#Usg{T*8EZVn)fS8v`J06Xe;<7W;&*RaR`zon2&^|%j2cZw|l$yabC z-;)&dKi>J5ZL`p{LI-a`=*`wnTU~5mp@7h52%=w^7K)7MEvt$wG_Xz()QEjh&|=H-rmOek)53! zCoRTBLqc8CXVXec^fxMYhJm5*uk}2Wk&3p1s5^#m>1F{NmhLm%up8d!-MKTO!Q(Vg z?t(BBD0u>ycnQl_$1WJ9o{+Sf1|deRX!qGc*6wr+YxQ!oa74Q7u%9l-N4=nhN?A6u zkpko%{=WU-!L&xqf@ZNxn%nMnC%FsissR(dn&v~)Tl;7e*7?3U5A6p8q;1C1y0W-( zas4?#OsrADG44+HJ)&a;LU|T4-e0Q*)_(i(-kUrDvDuStctHlIrW7r!o)h=+@KPr% ze!I!*4#>OZ@o9v|tG*9?6TSlFvn%xx!&F2`W@%r3C9|Pceb4@p>VqdNv*ay;CF;1K#(({)f+)10DH6#R+i(a} z0Xw|E8Z^+$A*2UPc2JXI}FB~N9a|qX;&}S--KG+Ss#=vI@nejC;v2mYJ5DcqvQ6CTepNo(Hbd) zI$$N}WnwwCfO&ZXALy&9{_g0}s&{WkGSQ zkC?f6mY-juqj`08+hNc5VXt3zfpTAF|4V|8chStFauivYV0qTaWE;7+8Bm<0?0VG; ztCn8@+|Puz_m!60^1CfTmS*%Tf|p0X1~k50Y+0^qj3^$2pqa-9xK)aFAMIfnz)qxn z_yD?v3nKY4E;;i|!7UUqt1^OidgoP&yN~_Q1$XjT8mvXGehX}dr|%SK*9htPgh-8q zbjLwIx3Oo}%{#{iEhDI+kG8PjTfQts=MOWb^82|peqnb99 zFcBzR?ouUtCT&*FI>Luk52)&!+gzW1#A@S7r}Uqb&X?ZB#>UQDOthz%4-E~;2W*x1 z(?N*K^4|4&@ZiDowU~6B3{^pp5c{fqJ#+VbszTz;zcFGe=q-Fo-=nKAvjF$2&Gwbei9sWG2}i*q3qY@z8NG zxl_zLO@o>1)$qaoOQIxy-~1iGDzUP2U0GSVC*N3CE&75sR7PBrIVhGsYqN1Pt}{J( zS$t~I*;*+|1@2IU3=p%xFX>t^Gr2oQSID&7IWuZ-{*!&t{%4ys^BgM;Xjth;a~Eni zA?o@3K0;}*sP^f6vQH;jFi6>nC%|d&Sv4(b?#0iK`YqkNn$0U>DuCW8r;aY}Y-TvsmSL??sJQmg zkJw>dyT^vbD3D+H%j&YMFFl)nKn~{V97+~TaX<6P&4#wf%`H07?Cv_XAGMfo&(p!L znV9W3Nc=WDY|hHBSv6S$mvry>o55UtVFVjrdw%E#hz)vptTb3UU#0&U0N-qYXxa+5URm8lVr_Wkrd8J&0b#;fV~!Q8+zYT`sDuMU4| zV@%=KQkAy-=X+#p0)Lr}h5vEC-$!ib2THR<_M!%SzA^aOh7?(KmJ|0WRa;0W$@BT| z`uFuSu}93;mmp1Z*@491}HH<=}?!(MO_~KccMK zCdmA{`G{vm{T->5Nel!fB4BH&=}^q5=0}GFw_$&fM|G$(&sKOPtyOVEqd~ru04)7? zWU2B$dr`Chj+RS!nJy1$78Uh=!kc7BS_2%(w_GrzP(C?yf+e!M-V|nn&gs<2kJTd- z_o78@Ro?=Ci*kSSQ4L6ddbPp+_QQ^4iTpt;DS^?;lT^1HMW?arIi60GO;KFB zAY9-1Jt%^#lauguKqiB(TFrBy9*3WsAN3zrvP1Qh*%bzET?@|V6X8?CGJK4_K;|B6 zs84_NC_zCMRaa)Y`H0!Kzof}lPC39d*C5onpalmV{oVe<#oV>45mFu(p6=iCF$>`Y za9-pAUS++0*tED*v_D)}`znz(glHLfR5Barms^<{osFR+<(;AG-iYX8OT5b+Ks#dE z^_-#HDCcola4n@<>(r2wvh&f=`vj@;4nZp~{;sbO4PM#9kp3eL-*hb)%QSil)%gi} zFLkcp(1hTq55|Y|gO1Y(ackSQu(0R>U=G+v6HW%HfkinVIz;7M*ZXS++LF-vbXl`)1K-bpPik4Y^P36M9=u# z%#rEW90Vej0o55j$3Ul3DTCaF@MfU@AciKd4XFSJE{Z{0yfj*`ewIQQ&d;d_BJ;Ro zd1A%wrD6;DozL1krL7JhMXQk2qeVFNVu7Q552}jHctWP5&af! zz?~r1;A`~hbM$kAKBi;cFOHlWNS1XRxTK$>a|7IQJ;dHhyTt;Hzw(nOph|FQdZ5|z zVq9EYazP2{BRHiLtbo7%7NMws+l8#ZCnDw2pwLVN(^q001}+YO8Aj+FDrI&g52ok{ zHZW8W)CTb05X>U5+E;;Wk%0}^{`e3CtrZqWA0W~BFLv!I%H~joqQJU5d+wb3;>Z=k z975k=jm`uH27*+o+7QfQc=hTNyUK>qj$obuz-|e`a1DY19yW0Rb`h8awJsNQOvDXK z_8NO>$9yP3(puAh@!taoF1~v0n(iP!_|%|^xQif#J5JKtR5#~5xOp!9V$fX4LInPe zO_PO67WDn<17W8Gw`sMV-;;VbUDZRRm0DSpjTY3lH%(j#Sv~gx%R*0PXembx`RUGd zf8(^y)DxuCKW?}?xI6Uzsgz&2lrE6mFDm-IHAQ*&^Or9fKi4K=8dVy9x7xJ+NaGevcKF=L zx$@JZx4ogiKek;7+3ZtT_x)?`OJg@m9QtKk;SOF1ocsLwy6a@?iAaDGqf??rAM4iX z^mvE>w168K2N)tQ#>__$*Ukk}_(4WS2`$B?XTONb%xea=0KQuRqp|{~+j!gpetZXr)jb)fA$u_SEujnD-`d`8+u7d4HT})R zBBQ~&hzqqZjG02f&4<#7GUM`ii!h=PRg{Fh4JcsymLho z&Pj=NJ9kOoZUFM>&VAdWQwnX8J>eW_EDTi-_O``}H#~dxESd z(O@1K0Gg@&#P`>N;~}b`Tkd>UxA;B=8t6$v%2bcy1|cHuLt5xBVo&TorjR(PZ?OT} zA>Laq7O&V#hFTmwa!g3Enn@wvsCCpT!G|9C3kR8P@HwLcO!Kj2>s276N+9R?EsSGh48wrF8B1nsmm#2zB`L zYTJo76@2KQUcSBwP`=9X=H<@s;}kp#ewDkV8H2Q!V_29hsQuxYNwvzn=D}$aE4yXu zWLu*jElHe}pRp z)axmyXT{;Sc>MLa6g;{?#?a5w76^EXX%=aB&WcxZaSkB6t zVSrEP34FJg0WarpQg+QP=h3VsSlUENL8X+_kRl#eX&@(Y@wz*#I%E!TVNC2oyiQI| zz=|vI3NB;MC`%Aa$wz1&kiu7xK!DX?LNya`nb!f)BOF&|S-R z#W~|1>{_H;FK3GPS-H8DB25_T{WRPNRTY&EpkYXEg|MA#fS)l4z~sWoedP`)(%GGb zwzQ-@*H?zy)XTnorNVWpcWtg8`Irs^<=Vmq`OcJ2$xu|r2JCurCk&zi6Avx#A$3}S zOw*jCKmd1!{AovdcO#6l3AZ{IP$?r5Qx~MUdMaG4S>APXgC|)zUmV??s1Wn;FFM*6 zf&l5dSp|Vx!-0h8?n|e1(r)T(EE~@IYY{nW?p)cA+c)A3G6y2T1Z>lDG!kT9g@wh4 z24+tWRFn+)({NfGVkVI7;*@En2Q1zZmx19KxAp=yo`F#KryQk{+Ior+sD!B!0 zDZGo^UE{ww@MM~5ihdN=FskC7)J1;Bi%3r_%3B|ao(ly>&fOG%P#GP{6>`yEzI>^; znhU@8ie32k+#DWCQC6km9#UK#(Y!VQ$$vD4oJflfoEPfNJ?QJM{zde)A zG($JQKb&Z`oTf!{!I#6l97n<*CWv?bDiOSuI$UjnExP*Z6>+U7G(1|gEV_Gr!&sLT zC6e%vjlQ*Ua1rH*-b=Rxj)%yYNIDLQ*8v|1=ZzA@(Vr{*Gt%g{_}FbWmTI*=;F>HN zI<+;ti;Girpu5$}jU0rA?=u-^-3=T90#Rl-qT3Is1c>fsvg~6=Du{v=ffInk&%$MD z6b)A{)a1dbm?jJd=-$7d4uwAM-ksuZARYQp2)oE#S-K84%!}>?0eDD7ju7!wJZ-K& zMsO|FK*qsW!IjJhWrsOv&zHcx3h}(vXL5fMv!pwT#`}}?6lh5Pka5;q1^Lu zSZh#xde&cv%DG|}zF9V#9-RO33Zt!EBXQpK`;p6Qkojrt?>AvVufF}{G+Y4THlq9J zwcd+2<7L|DJ5*B1f@w~kp&Ag4VV2{k%WTHUNB8{ds(Z`;PN|$m;w5dsT3Dzr4$ut*5`x;{ z-Zl=gEi9t!0e6P=5MUQIHKLFT4FN*v6`G-=L`)0-N0xDW|2_gF{4lrTrEB;R(inx3 z2gxg&vCl7;;WWd#xPwnwxp(vJnQMqnnRor=QJ=Aw{J$qAG9XW73oq_N!4olj$3Bej%VxG1(zd8*E9#HT|`&VxuKeImwO3Jf3-z!PQdpc$^V$qtu?cGLpy;N#_; zhH#;L;7%1Z_R|=sEb;wIpywBWa5__@_L|g*bWk@hqu_m zxtW8dMeJ()dLJwrO5T+B?nM8|9$*Rfang3pWv1nKsJ5F+O~38@`K0wX*9<6uPxph3 z0sPW)zE#Nt<#`t32cCoOF*_SmJW=Wkif6v+1#aD~zOsKmmS$#sE0`H0Vgk|;rY%8@ zv~J2catsN^G=}jM!UF1_iCHMy08qdVX-X&qx8aPpuaZJQq%L<-{H!hkoCy3Ouhy;DzSJAORd(A;0&p|A7@+>4%%cdu!rC=HgOK@A-a z+^rt+8b2J$jmFm0)a2fN|Afek!>eEO^Q-chAIOQdlX?wYMhCiMhniS*`a#z7*+xb8 zkho|6sc9qzmp5e^0g%~fleXZAE(oZo=;gILkM^8TtN~$2BlhB%9+GB!?>wTu+)jNo zUopkKz0|}5-`7dEGZEeZsI@cunEI|ynk!3J-sP+%2c1hH9~Fq6`i?Pl|LGj5T&q`G zBaI*ES*f$0>ZhYjM=w5Smctwq!KQ=KI}Mvf4+xvCn*&P)!;b6C8#e?2h0DYHH?{MF zSjxm9st4;d0lRl9g)m8T93G0NNmaglSv2o!QrXLS$$CQ7?lhOFi+j?)b7$Zdz|q%- z)Z5@Z|93`6o`4R_c<`Xh$;87dulXq<{JREkl{B;5RT zMP&ANBKqK8z5@hy)5HvmZB?Gviqn65BW*$ws7lV^+opdV51OGk+c|a#4-*+JrI~-Z_ zJjl4D9`#Wf&QWNwg+%E~QYTBZmxlIH866 zQfZCOHbyl{SJ{CHxT`(nE=a6`A!@K#dfu|!jjRf$=z=dz^`wkSj55)z3z%L+bme?R zuD)w6t@=JD?-uG>Ez0;QxXPd#u$?%O0|EibO$1^xT|l6bGoAx=Az5^ci(JxP_1g`L z`&Tt)&CRCxjd~S)%5f2hRCU*!2`+M+>%IsWhb9BS1^( zt_=)?$a71YWu@-+wkP-80QvTv)YadS$)Xy)a&9U!zq;rFU;m-Jl6s4~bSkc^O%a`_ zDJcHKr+QYVW?z93)L*cA@<1iZ^;#N3GW_zUQ@M+o8VTKbhGN@bbRcC2JlPEBKb4=F znwsoDenm7%#7P25Vz)Z|1Hyxt+$LWTXr%Adp{VnNCZLzMue$!#dG6xvT%#XY9ab)X zzB>MetGi7+pvS?!kC%q~>qAXfjT)$I4-PizYFSd|{0NpTs7pUU83i+DAAyR*t}nWp z4IXWY;fUfbvyb!7WLJyVnH!MH@o=!ILutkQjdD~5&43)Q>;(kTwl_Zx+4TO9p+#c3 zL^tvWAY#H25)wUANCp_FD>7by09n|DNIyjYGZDvq;eJQz6~HH(${I=X`s_k_e?s>N zL;@nKN6jlB0_ZS@++i39B|Yl$z9YSVGZs5m=9$V-=d`uwUF%F%>P+_Re0~XfQ;!hn zO%2a=K!K_32!3w$9hFiy_sCBFTd8f2YLbE{GEm_Lx(b(~*KKpZr6A-od+{a^E$~e5 z$-Bmf!DGFNyvEkeY#_JiJ3dJ9t4EtW;_?Ue6*yQ5Y`fo|YJhnI(i(J6QMUhi5hNlF z1hu|A%E!jW205f9p%V08wVwwjj1sP3<+Y9j>VcJ&EnuvCM^<%|8+R z*T+PVtAe+%)ezEn05XLkhyfD|3x(jraaGwCWINSv8vwR?3z`$O=L~4O!e`F>TQ&$| zBA{#P!MTf)wEoVfTv@6fCvgMBwLiqK=-v(L>@Qse?<1;+REi{!IiLzQKn8M6D~gi? z=r)Lm$SLcXXqTd;ysZb(6CyNYglpwM=d$f9&1p1~PE`SV*7wOFFGn{^NGn;9EF^iy z@XQ&QC4dv(`C3eMywO;9f?L^9`f{S2h5O^($&+^d@Z+|43>i;viBZ$dQe`-x>d>b` zrKmyT4^L52>fEN8jMGB6McU%d;msUy$1hHT_VhOBkSW~0`(}PiH^!%?tgEW3LSXXc zTll$P0Ow&ZLqn--@l+#_Kn{iBiVn3$>vK50!rR-ty9l znOtM#;IN)-PjLdeqlja>Uk9YJ4SG>4d@b23V`XK9X%=hsd-v`=Pv&4{wLpl>FED0J_|u zA`!T?6io33s~{PE(YzH*m^Ogs>fi-y(B=nm{h%E>!40&8eqj+Jo(bFxBpyZFZdYV- zKj5emzm1100KgHQAQt*3Xhy$2-hGN_hQC0E`&DKaa{@{b%tOc`_=xM=g$u3VK9b<3 zf0^woYjIl`8U*<#7VvN^tWi9irFc;B;(--%W_8(wh+6@#L*x-)OfC7Q6`lFUC5-^3 znt+`TyAQyTiF^C@A4$933y93L0!5tS%GIT_#9mj+8(UBX0%BMRHb*1^Dsmq8a#H2V>WbCP3NfzijTNhvvT8kU;j<=n zUZOKL_4GJh-7D>NZLK(q(gRS78sQ|1yH0Vgkjxts6>X|NCH>6#{!{QI3?ONJ0Y=se z1^drG@Vz1~QQb02xqf$BY)GZ?orqn@ZahKd>JyOROq|Zr2Cdciuk5v7d;Wa2l8b>T z-iR#f)|B$D3z{3uG(m_u3458fz?_T5oja*7RXwQUunnFC2UkXAKt#q0%D)Y_HGIVL z<~4qf6oUd&8$W;lwvm6IhK7ZO8G{%}{63WyrX&H`<4IV1a^LWA3n9Y-`=P*Hf71BJ zj~7>3CELrndC)%HDiTi*uz282AaD?V{|z_VwsqoB05#|ZC;=xiRxsTI5+%3S$R5du zzf}mN9q95JqxC_!FusZ>juBu|#z6zBd=nXuOg&v%dji59FYNiqd%KAAI_yu2E5KC} z@xvghhTlgZ8f-~W_%q@3TLDQuFE0M$j|zGEbREocR3xtq@9vNWkkANG>vFB+($G7( zL``A!-Hj~vf?@Bb*!jUwn}MHHJI-xag5DZtic1yKu=}h{8~nG!v3k zyyt>jfyND3yu6dx5e?=I1@Gni)sR|+Fi7m#vu9TkVJ_2It?hr4xbd(ELB@Wd79n$9 zwjHUDm9`eI0nJ>?Y$hM1^cu^u_GI7X9o&#T7JA|w-qdE$4kkUGzDIeG%Ipk#3UDUD zEIbB3KHLC4e*wwfy>T1*`vf=lyi5QFTmj%kB8B$@w~#TMm-l5IAtKZca~k$5b^xy&u3nu;Yez=zRBtV}a9(mw?GMgfjfFfm0+v8EK%8Ef->L$sw#;=(6S6TEzw97L0jejW z5umpg8|bjG$e0ph&>^Ilmz?A|^aII6`mWhW0ZD|tZvZL{Y)p@Sf7DMxJN>Q6`}(B7 z@s4Pb-Uo9LQUO(L$0h->mM6n<3&RXpir?}}Zb)@I3=;K$Vye(q3c2fAN1fA&-^p&O z5ug%gA>0y}GYLTPJ6R{QZP;uxAksmDFg?>lcC!Up2N`{uUsbpFGroMePMUyd0!aQS zR@_1lEb@JS{WblPKl!$)scHOFlv~l+GIz{W6gQIDSMn=|C|(g;e?%=|qU!PJ?zL;z z+@X^Y+?&F93{_NA;*}%e5N6U7)5kG7kN57A3f%F77%tfOy2wPCneUu5Vjo2y(_|1! zy9!I?%ykZs3aB4lpk=thq^7sz0^3_h=3u?-w`JYA*#V=ng%mx5}{fn4K{cOF}{#=a++`HFnmLAF`sc?}437OF%H`K@Rm2^RQ4 z=Q}~&`yoHpm&OtQwd}!yhEcA!;2PU+8fd$I@ut?n!h+#g#|ndzXbAv6d2 zH*v_iAj7sN7f}&3Q|*Uyy|kN$5lPRo`Q5j7BsF9j5aEdZM1Tl?1S+L0Fn&12?>*~RSwa#Z(93lXtxf_s z16^4NBmr}OYE-h%j3A~HtbtU3&6RWgFpvhrd{+u<=t85XzTP)8>veMoVlsK!f)f$f zmiOrf;b>483U27tKs3Lm5yMzU2GE!KE8ijnryjTG=G<_xKAp-h2?qhRWDSJoah>3|wI}RA-b;g?C1FnP2MfMW`IktgONmSHt1*@^U z+{J>lW?Jzu2M#8r*m@i|I@*)VY9_z?TN6^XQXDk|kFxRlub=69h9X457*;lvwRm~= zT&0cQktTF3td2HFI7o{^gB^?oi4-`(*r#?-hotX+{Rc^+B9$1%SF->XAdS2ry1%sa z|C%z0Sy?MW%yZbAI*@IF3d+9?(eQY%T}+%_0L>voPUHt*GhWOecnlgnGD8A4EccWB zuMeP0m4|NC_)u-RV1f{tnFYuVbFCNUk~B%CWN6(5Lr$27ldU<>@-vKBMtWg>0-Unw z?)gKiDjz3(w1)fxhL!R-ewR&pXgys22z1xRiG?Y0rn{)#cWKN_-kU5Ao%trUEFZP8!jB(E$jd0MF`yQ}(8^?5MNhq>ajuLdP9FNSS@iq^A6 z<2=K~ffY(LekHqEVMo-&T)pR$(n;;vP|?wAKP`|G5HIVLJa55U?YO)x#?l%;b{s>= z)yw$+o5>a^E$78MkB|Xv@FB7RXuu(T_44Hp$#%sw>lA+^@(|fY)`RKSRWK;>aFre) zWRj&#EdBlbN+0|<3x=l_Y;1ivk&vNFML`K|^`vN2GVF2tV`)Dwd*A3P|7GmM2-3p5?EG&fSm{b_p zuc+?rai4~CPG5y965#LJ8hjQMM26|56eN`jisBV0`CX8U%0Iy4*FjB;(x; zG1pwY7!F3y%TSq3qp$oy^(>cs3)q(sPxW6PNEj0BVJn8@_LF8C0 zm~qIcxNiO~v}GqBR8<0K73jVibPAoet4pQuP#+jOM!~EgrE;NJ;KFQTW9X*F(9l-J znN8S9+7BLNL&+9}B%Kxqe*==QU}P7102?J)lyTk}d>SM&0xe}y=71t<^L3c#^l%`9 z+t~Hg{$fujQLan>M9jEOw%6WF*|-2YCNc;L)|o5gK}>bxS8OTF$Uz_^8-yYH##9RK zy$_s7c)>fbHc18iAWkDA^zLut()U;uME72n!EEyTqal}Qav4U*%Yjx$&xjv5bbO@O zK}kqR6$(4)8VmaOl5%~R(sJk8oQ{#vvv?W&{Ey-R#?3X%>tn`&_agbHy05WpH+9`0 z&Ov6^4iXDKkj>CN^l9JbtI$wnQUyjq-c39;=Cp9z+4A0Z;6Rq|DwP*f9zS5SQ6VyG z2ZN#9yD2a8T_oKnt)R^df%gXb{DOn*qMb*hFKdCA8EE}5gt|#nD7Du50GsFDYx&6T z%42I+V5$>pXde*1^IFNXGtYN^T7>1mR2RSmmFI7e1P#c1wjeCq{wmIJtDy8jDeH!N zu9u<8%F*C76TH1yP`UuQ!wnNWtLe2P?CDT49)50Q^S5-c1+` z-{QGCRAsJphM`2miGyz68LOvKJ{wqC%12Vb_UYJNi&@!df;@MX=Y5eJ8;8^-B%2YZ3&ZiB;P zY?j&oqpdqGVFLbe&ZX?S5O@Nd%8!|w=jia+y$qAZmM^~b^Uw3C`tC)ih98Ad;Tx~m zzAZkOvmrx^OZHzghHan*0SW|nxR2KFBRzoDOE5&BJzz}C!^5kR9s2#2KL$mc?jYQbI@NJIbA!M6>Cj#J|P3nhRYY`Ej?NeWr8 z$nYLR#QDD~48e(x(o9y&Idkp#G&quo^8}W|Z~CGK;&JD#Kt&`$zbZfq3J7V4{v!?0 ziSWY=h<+is4%v`zLn3vE;{m{b`qL~U$C11HR^2J{RjZH!>m~K#GazY(<3z=YfGp5; zL*)z@#5tNN@OU*xp1)RGT$_)HnJM;cGPzDPXN-M$O@V+@55VErQ48AaFUdC1IhbbF zD4PGqpVAH9THNFk6bHo2sa|S4c|!E{V0SjWpGLwdRh74X^KbjsyRCl zQ3LfNz}=mIo|bRQK#zB6|IJ zuaTgLi*FruGqXVlj_$K7P(u-Tf*V}gEa533kl`|q@9&bNG`Y5;`5|in7)VU*yI{#y z?JjLs2R#K_E@LARYJDy=5}?nCAQcTXVlhX-!jv8Ix(0!;Xe6O^_AE4<4{Vzn$%@7> zfV>Qkl7Ijk71r8{F1HsoA_yykYXh9&MalqlksiR!cFfOX;(^~2lPItg z*^eLhYJ0>D5j?~XggG2APz-Z7{pjQ@ACTA)e+r>RK-&z#_L7D(O;SRnS|GeV2-1c} zn?MFj8zxH}Ji}p93r3gt;F$;2$TMKN29)5bAP96*Yk7z3&UH6^{P?EtnhHEofZREd zsHIdisl;+P%7+GC2RPO_xijPB6{^&Le@k zhKE~fwLbNB0Cua+AurB<{z(M~6&JwjJl0*e@G$BT4n7@X z^(t>IK85JuL1f<{1|{SWvU>6iDPV_^kSF4Ro#{o4_2doQy-gAM z!J)rtGkH39QnS?lI-Lj7pE-m999zne>>GVNOc{w&U^)>j2jr^Rv?o*mG+$(<4uMn% ze+GEh^#?jb^sWO*{(u&PfRtSAwC8}wGJz&#ZV(|uf+Sf0-{@tivg_v?X@f-F4fNCW zY4m;=s|4gsPUfJ&?{~wV>V_q7n+p_y$G9MJG(1<@v8tOHxHB>WD)WW%`opo zhl6V@U&9($L=cjdX^B~C45x*W#~HyBLYz$sEI}2A$tk^^7TUk?Fd#jM@2QC&M@HTz z=H}*nAnSkEkZA)}c>N7}5?&;%@z4NHPLcp0|)?glU5uKu1K4Ta2i>qT z0kcX7&IEByLtVWaWHNVnF4G2Yf{6r(2~dtYfU1sQJ>7*rHH^OPB3F$*ym{*u@^~oZ zc|tn&!nac|KM(VSmg>Lsg(FU8&ia2ZRuC2M4 zV5kJN&KdH(`^X@&d7wyJ0zZrtNW+s2j%Exmjv8XyE8SQA96d0i+yO};wGQv@2Z{$W z=7=Qw0SKDYE zC}IW5J-`8WIFvx`R1w6>`wh@BV1jPg!KN!j1=wVOmLSF7+ARSO{199{`Bdckb<-0@ zcsoP#tg%*%fd39%jT7BR4_LlGk3LYG2Cf(xdbpz+q!M#CL29LPSvPR@sOK}PmCFy$UdV1Rt|6$B}IBFI`o09}!IakQ9u0f^_eJReR5*Pud(-s!`vf$x42&K1bPsT&-$^(Cg=sfxaQC$mCPQqcjbvf7} zdY7pLvu|6-gxO z0tNdMV{2?AV!JKw-75u!hCm+BDS@y5Ez!B=_CqQ(7kTmy9)uZUu3=#T)W{iRo)JEE zkCd@z&d;Alz;INcS#-j#%Y{ZW1KiS+qTK-{4I+;>Vah}s)Q?hFF91;jU+Uo5Vgm)I zCTH$il)MLonynIkHd*v$^b5!^7MNDJfyEToKF1B)2aJPUo36|PUlGInxAWB`#%iZ( zK0pIM&SnapxnldHe&&sN_FL(4u9g4Q-nBM8bl0|6=Rs>wh`w2JhSb2+w<<9??3N3 zb;{{*jGy1{`~5!8^SMnzOF(Mv{CWrEWef?M(y`2v=gWJ}fmsRgF*2EWZ;zdZrrDV0 z5Im;tgPQu<_alApM}B0y>W-Ser!Z%A=H=};eyExT7D0 zgcf|_yIKtvt{EA!E7d!a@ds45ogk;Gfx+Y*Xg49`4Jli7Fsump7bPW*$ZS}1Zu$53 zCL3aO4gg*1J2K#gfvqvH^>co-b;cY0gHc?6Q|B1tEm8IXjFESHAGU@WiFT*i)m0~b z7AA*DNI=~wT@kQF>4Du|M6{h&ykJW5eavnof}qx61cX5c|J!|mo+avr{h_td zqp>cR!xasK%}1b8*>2AGT6x!574sMkDWz!=He{!?yZ(a(Iwx9HXeKu~Iyxq3%tg}1 zlr1?2ZK$)rZ|Dj-J?oBlM3sda1C3DsuHkGMO-;9H zm3BYRN!gKfswOpZplUUDy_8K;^CYR5N6|NQIc}i?jh@ff_^Mg$5{$QqBAqM3Ok#?4 zIa7H2^UEg|d^e5+VcszfV&h46rR&C3I*dN(xVH>4-5PQqekq91Eeb3E7`dE?lOc&4dC(lI*xutXmoout>=`cw7}^ zEe>!w!~K~^P$}}cR+LXTt=ZIqw&8d07B5@24T*A59FAv9rFxIgDx%Bl>+AJ{BLN<#*XN~WF5Y2kR29~S^WW~nHZvpX*R zjJ&7XS8SEDdB0bvvh`KidWvi#^`3f#Os1MUZ*kdud%Yk$k&Q>j$C55?H1tot&A!v> zMNS#m@;O6qS`w2u?xD}H+vd!ixp4EQES^GRpA-9SU!Nt^09Vo_!cKqn^k0o>7VFXL zt9gMe&O0LN3mN>8VvRUqGgJM&Sx!SI<+E+8B%R~|aG-2|_xMI3Nj)&7!PXQQyFixz z>ftI7)|<_xB+6e;?kwnOf2io&jf@Em8+AT7Ddb~W?OE-;smN>XqiBIiNWiZ1wU6wP zWR)DMyWil5-2m(~fbQ<1c(}7*BWk+V^D!%xUC|kQa3vZ=Cueb&f6uuMoxTnx@_{RJv~# z{Z4;NGi}v^)B7oi)@a4eGq6tcr+kz%_h;-})`_*R#NT&S()O>c!hZ!R8h$OLdR4q3k#{`;>5}|jD1~UyED}gfT-a^rjMK9WS!Vv58aRvHROh&c1 z9c0}{QsrVCmyp97`-*5<0&hz^7TO~Yt}G4RLIb$9m~(Oo;m=m(U`D}r*}`0tZiPrv zn*PZviS8n&xVtwLSE_`TVtYx@Sj|<(yGzy7x)m<@!s25_rV*meG)qP;_5Fl3V5$(Z z_iU-AzJcN;-`BX0bu7-!WVlN9;|1c34i`}~hIz^G@#8ZY)Is7(DeRHo5d1-a1Oms% zz5eQ208|9`4q)PF?#nJsp5_%oQiS5((y3VwNIZaAOl*3pPz3xS7B{0XGadPj{(wL{ z{B0bi=4a|yn;IK00~+s)sVx6$)YV9tR2?X!XaaCX9ME4DnDUF<(iwPAL=v4GGz5Q<$XX;YtAamQ5n`t@*hb zS5M+TSqGxF?7VCoKHJl$K&Q!=J&gHJ7h?(@jzWmZ6#c^RRd~LlP9QfHjo01tyjs`w zG~cu%RTQ`&aPV;(|GX^pMW<;(YXVk-OaTRjwfHszCNDfK*GL2jEg$O*TcCH>ijI=QQbOIKLBM1UmU1f1%6(D<;+&P?0Ex#wmdq&))#4BBL|9GoTgU!cUf zVkle!o59>D*@Od%B9|}cX)JYcaPSWdyz!hSTo9AnksC2OI_lwH*s6z@C%OOxBBg6a zTn5?Ei7^L>r}1CRkX@{)+3>(?qitd^s7Y7Ds^oW4N9{^Knob00-O2e$^1_i{@v*Q+ z6P9^Rvk&Xan5+M$_>wpuW3IX-R$um&+^fg1nF)KxB-8-y5X%jDEv(;pcmytQ=Pyie zDi_7V#pa3uDl0@UB9~RCfo}f1SHg6xFYG$TSEZ2}y}R0embC62o~Qa_K-4N;Apu#D zIE2N@APDKJF~~&f`+0a{s)UXSk4=Yjb104_nxkeVjc6%Mn9?~AF6j7lZfg?=Sg_A^ z-^p2H^~ja&58fF;hKUr^^yJy4&CJIX+wkMuaueFlduRXAt%7p}Mg?W$7Yua2sP5(! zRv!Y8lf387Pp<)|_Pw-OsJ1V+a+=(#nu(Jp#fy-O@){hB*6Oi=NL6N0u76i*iUD45 zNk>)U3K$W@{~{`IhF4$)BkSI2t^3784XkAFQdwgnXz{<6mUgxGZ=WN+W~foxdV1Um zp1qTEL8FYix3jE_Qv=NfoC7(Ut=f2f9@+`I5sh8MR0mhkY{gYMt->@|7|~eG{;! zoGw=)#wi}Gd@RTWnc(2y?MRNOhUt|IlTy+0HhdhhTL*I(mU8~ka&ARZtffH;h+VqE zD45qPe$}hS50qOu?XbJJK=!T_A|)Ds{s*b?9^@%Nl_`=J5gJ>A%-pjvQxF(T?_P0x zD`ka@jSLQgvM9`44P=T^c7glY_YdFjdt-$?f~}&}ULL+4oCHCcc{tnccJ~bo)8zU( z-qX2D>m8OPyDv>LA(YfwAf8By&>?*dPNQlR<3Z>UiiVn?&J&i1f-v1LhU|+rIJib;Bik&6 z5Oiy`Lo{`Q`K%j45r5d~=p)6D-q-NkR-7qCS03gZ> zrZ6w)` zNB61g`3ZRumqAES`$2g(n4pq?6!U?oTY)!azBdDW^n`=*(R_5-er88g7+ zK1cmEr4DX2G&Y6&0ssTcH01(uf@qi<$}QllaY$NiB8~_Pw~s`gxrI2vsahyL>qk0#-zbt)_<#jq3ivhLkDX37TWSCu2tA@AhJBk^fpwuE#bX1*WaN zeIKrm)N<6KVBdOFf#bf;$MCCl|2&YKZy3&qVe)uC8#qZZsLQMesQ$??x+HH5nVaQH z2M38I4QIeXOdcJN>*z&`6o)5QBrkACTNBE;q3Ab?@fE7X=z{#sZS^DHKM`)4|gE~g?D5y$(GFgym zpstV`2NnT2y#pHnnRo@-6A9rX#Q|9=c#rdp4;yA0x-AwPszuyg-==I7$J)W0cUuv@ zbj9q~m94fbTq9{ z-$i{cj+7-D8p5~-p-99+FGMI<{ytP_(gpGCLX))|&z?YY zA_VkDMWyZsVC{=mqEb$RJ2dpW8)1Y~fW!v-(jGGm>9p{f)}c| ziXZzsf{b9zEKo-!EJ@YIBh{z(1qz$;#Wd-KdqbOp-n;Yrj5|mk0g^utFy4;DHm{Y- zfq$a%nqr66ps5f;exOH4ss#RBfw3RrS~^~U=#qO5b{YrJ1De3h%%E+IP@;6LZwv!+ zsbPmuxC_x+)$rif;EZO);6pPVibO*8)L&bVrq7EhU0^|nAnyA>**phVJXOdNzCU%e zqxA0GJowS6tEcW0@`%e&0+D=*=;`idH*Gh!!t|NStuxiW+DJe*G zA!#=A7huU|ub1)VegprCKRj)8zVx3@@AdTQ{jWZ~o!8&stA9XjXs3KyC!f~Ir*-oG dQZkN;r0T9`=Z3BLb{Xwm#zwOZS?l)v^l!*cirN4G literal 0 HcmV?d00001 diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_3.dat b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_3.dat new file mode 100644 index 0000000..b4aaa32 --- /dev/null +++ b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_3.dat @@ -0,0 +1,151 @@ +rib_3 + 1.0000000 0.0000000 + 0.9577769 0.0052581 + 0.9081751 0.0114351 + 0.8585733 0.0176122 + 0.8089715 0.0237892 + 0.7887696 0.0263049 + 0.7593697 0.0299662 + 0.7097680 0.0361432 + 0.6601662 0.0423202 + 0.6105644 0.0484972 + 0.5983523 0.0500180 + 0.5609626 0.0546742 + 0.5113608 0.0608512 + 0.4617590 0.0670282 + 0.4301315 0.0709669 + 0.4121573 0.0732053 + 0.3625555 0.0793823 + 0.3129537 0.0855593 + 0.2944570 0.0878627 + 0.2864751 0.0882233 + 0.2631344 0.0892710 + 0.2431549 0.0901598 + 0.2231755 0.0910486 + 0.2031960 0.0919374 + 0.1832166 0.0928263 + 0.1750000 0.0931918 + 0.1696407 0.0926970 + 0.1632797 0.0921097 + 0.1433727 0.0902628 + 0.1234657 0.0883979 + 0.1222984 0.0882886 + 0.1039075 0.0844087 + 0.0941392 0.0823364 + 0.0842841 0.0802458 + 0.0818568 0.0792930 + 0.0751055 0.0766430 + 0.0658445 0.0729958 + 0.0565836 0.0693432 + 0.0529896 0.0679257 + 0.0478730 0.0646890 + 0.0395115 0.0593849 + 0.0311500 0.0540747 + 0.0292787 0.0528862 + 0.0290956 0.0527055 + 0.0273482 0.0509810 + 0.0256109 0.0492664 + 0.0256009 0.0492565 + 0.0238535 0.0475274 + 0.0221062 0.0457964 + 0.0203588 0.0440654 + 0.0186115 0.0423344 + 0.0168641 0.0406034 + 0.0151168 0.0388725 + 0.0133694 0.0371415 + 0.0132474 0.0370206 + 0.0121593 0.0350404 + 0.0109895 0.0329084 + 0.0098198 0.0307693 + 0.0086500 0.0286303 + 0.0074877 0.0265048 + 0.0066048 0.0242309 + 0.0057275 0.0219660 + 0.0048502 0.0196995 + 0.0039729 0.0174331 + 0.0030956 0.0151667 + 0.0022184 0.0129003 + 0.0021278 0.0123869 + 0.0013159 0.0099952 + 0.0008231 0.0075180 + 0.0003304 0.0050407 + 0.0001652 0.0025204 + 0.0000000 0.0000000 + 0.0001652 -0.0025204 + 0.0003304 -0.0050407 + 0.0008231 -0.0075180 + 0.0013159 -0.0099952 + 0.0021278 -0.0123869 + 0.0029397 -0.0147786 + 0.0040568 -0.0170439 + 0.0051739 -0.0193092 + 0.0065771 -0.0214093 + 0.0079804 -0.0235094 + 0.0096457 -0.0254084 + 0.0113111 -0.0273074 + 0.0132101 -0.0289727 + 0.0151090 -0.0306381 + 0.0172091 -0.0320413 + 0.0193092 -0.0334446 + 0.0215745 -0.0345617 + 0.0238398 -0.0356788 + 0.0262315 -0.0364907 + 0.0286233 -0.0373026 + 0.0311005 -0.0377953 + 0.0335777 -0.0382881 + 0.0360981 -0.0384533 + 0.0386185 -0.0386185 + 0.0411388 -0.0384533 + 0.0436592 -0.0382881 + 0.0461364 -0.0377953 + 0.0486137 -0.0373026 + 0.0510054 -0.0364907 + 0.0533971 -0.0356788 + 0.0556624 -0.0345617 + 0.0579277 -0.0334446 + 0.0600278 -0.0320413 + 0.0621279 -0.0306381 + 0.0640269 -0.0289727 + 0.0659258 -0.0273074 + 0.0675912 -0.0254084 + 0.0692565 -0.0235094 + 0.0706598 -0.0214093 + 0.0720630 -0.0193092 + 0.0731801 -0.0170439 + 0.0742973 -0.0147786 + 0.0751055 -0.0132140 + 0.0818568 -0.0007931 + 0.0842841 0.0031413 + 0.0941392 0.0174898 + 0.1039075 0.0292607 + 0.1222984 0.0466897 + 0.1234657 0.0476151 + 0.1433727 0.0612674 + 0.1632797 0.0712660 + 0.1696407 0.0738449 + 0.1750000 0.0758307 + 0.1832166 0.0785142 + 0.2031960 0.0835748 + 0.2231755 0.0868185 + 0.2431549 0.0885374 + 0.2631344 0.0889779 + 0.2864751 0.0872233 + 0.2944570 0.0868627 + 0.3129537 0.0845593 + 0.3625555 0.0783823 + 0.4121573 0.0722053 + 0.4301315 0.0699669 + 0.4617590 0.0660282 + 0.5113608 0.0598512 + 0.5609626 0.0536742 + 0.5983523 0.0490180 + 0.6105644 0.0474972 + 0.6601662 0.0413202 + 0.7097680 0.0351432 + 0.7593697 0.0289662 + 0.7887696 0.0253049 + 0.8089715 0.0227892 + 0.8585733 0.0166122 + 0.9081751 0.0104351 + 0.9577769 0.0042581 + 1.0000000 -0.0010000 diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_3.png b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_3.png new file mode 100644 index 0000000000000000000000000000000000000000..d0b09ba1d0f859b0a63fd6d7261db3c09d79e732 GIT binary patch literal 28812 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

m2?gnt8Izx(Iow7RFsqu!o0^*P zES?{Un-@7Jb#viEw-IV;lQm`>bvqjb=q%xJWCTo^GDQ>B9@l8rXkh4C91T`kCNAVw ztXa@aL_Fni$XJ`GrwLofi1~FzJoY zQz+Af`gZJ7&0GYvlB|gbM%;MP;R=*nP*7m?mA;nNGzNE4LxYSCffiSzYPh87-bum1 zxjkWfJFSz>V6ZvnxfSNTa#Q-7W#uN zD>;mC+0TYDIsl|2u02k-XC7}L6#2zerRET3C1p|qIGz)|?2uv56o%9_hZKO&%)=(v zYw);PLYyL=jHLkiN(m%by#D+d;`IczMaUt_H{VZadn^20U4ZqJT#g4lyGm3R6oY_4$NkS&Hu!pi#}7@;((0gjjQX^m2UX)5GR=}475EpjZxpSvDhhNba#xYokFoL|j*vz9)nnlyv zAI%*R;IVmT;wYTMapu)}A*3cowI#NRcctl=vwr zDUssRG6TKB?;N$`u&&JN_3a>Wpv7# zh}96ii->!5WySAU2qdRn&N4Xy<(i>1pB0q1fqn_XArb20sAK-n7gpBpvXvm6QrZrV zUSuuqgr@qEdXv5#fS58CXOI7)S9_sNSmHAA?}yX&`Stxr?!5mPA_awi+>yQiXeKt; n`@Qpi@4VkT|6fPJYtx5a&V5p3JZh3uK=UaxCZGAjVf%jpu8VU? literal 0 HcmV?d00001 diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_6.dat b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_6.dat new file mode 100644 index 0000000..2b003ea --- /dev/null +++ b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_6.dat @@ -0,0 +1,150 @@ +rib_6 + 1.0000000 0.0000000 + 0.9577769 0.0048375 + 0.9081751 0.0105205 + 0.8585733 0.0162034 + 0.8089715 0.0218863 + 0.7887696 0.0242009 + 0.7593697 0.0275693 + 0.7097680 0.0332522 + 0.6601662 0.0389351 + 0.6105644 0.0446180 + 0.5983523 0.0460172 + 0.5609626 0.0503010 + 0.5113608 0.0559839 + 0.4617590 0.0616668 + 0.4301315 0.0652904 + 0.4121573 0.0673498 + 0.3625555 0.0730327 + 0.3129537 0.0787156 + 0.2944570 0.0808348 + 0.2864751 0.0811896 + 0.2631344 0.0821921 + 0.2431549 0.0830098 + 0.2231755 0.0838275 + 0.2031960 0.0846453 + 0.1832166 0.0854630 + 0.1750000 0.0857993 + 0.1696407 0.0853780 + 0.1632797 0.0848779 + 0.1433727 0.0832681 + 0.1234657 0.0815686 + 0.1222984 0.0814689 + 0.1039075 0.0780208 + 0.0941392 0.0761323 + 0.0842841 0.0742270 + 0.0818568 0.0733774 + 0.0751055 0.0710146 + 0.0658445 0.0677128 + 0.0565836 0.0643840 + 0.0529896 0.0630922 + 0.0478730 0.0601947 + 0.0395115 0.0553859 + 0.0311500 0.0505464 + 0.0292787 0.0494634 + 0.0290956 0.0493014 + 0.0273482 0.0477561 + 0.0256109 0.0462198 + 0.0256009 0.0462109 + 0.0238535 0.0446427 + 0.0221062 0.0430651 + 0.0203588 0.0414876 + 0.0186115 0.0399101 + 0.0168641 0.0383326 + 0.0151168 0.0367551 + 0.0133694 0.0351775 + 0.0132474 0.0350674 + 0.0121593 0.0333014 + 0.0109895 0.0313870 + 0.0098198 0.0294376 + 0.0086500 0.0274882 + 0.0074877 0.0255511 + 0.0066048 0.0235071 + 0.0057275 0.0214489 + 0.0048502 0.0193834 + 0.0039729 0.0173179 + 0.0030956 0.0152525 + 0.0029780 0.0149716 + 0.0021556 0.0125487 + 0.0013331 0.0101257 + 0.0008339 0.0076161 + 0.0003347 0.0051065 + 0.0001674 0.0025533 + 0.0000000 0.0000000 + 0.0001674 -0.0025533 + 0.0003347 -0.0051065 + 0.0008339 -0.0076161 + 0.0013331 -0.0101257 + 0.0021556 -0.0125487 + 0.0029780 -0.0149716 + 0.0041098 -0.0172665 + 0.0052415 -0.0195614 + 0.0066630 -0.0216889 + 0.0080846 -0.0238164 + 0.0097717 -0.0257402 + 0.0114588 -0.0276640 + 0.0133826 -0.0293511 + 0.0153063 -0.0310382 + 0.0174339 -0.0324598 + 0.0195614 -0.0338813 + 0.0218563 -0.0350130 + 0.0241511 -0.0361447 + 0.0265741 -0.0369672 + 0.0289971 -0.0377897 + 0.0315066 -0.0382889 + 0.0340162 -0.0387881 + 0.0365695 -0.0389554 + 0.0391228 -0.0391228 + 0.0416761 -0.0389554 + 0.0442293 -0.0387881 + 0.0467389 -0.0382889 + 0.0492485 -0.0377897 + 0.0516715 -0.0369672 + 0.0540944 -0.0361447 + 0.0563893 -0.0350130 + 0.0586842 -0.0338813 + 0.0608117 -0.0324598 + 0.0629392 -0.0310382 + 0.0648630 -0.0293511 + 0.0667868 -0.0276640 + 0.0684739 -0.0257402 + 0.0701610 -0.0238164 + 0.0715825 -0.0216889 + 0.0730041 -0.0195614 + 0.0741358 -0.0172665 + 0.0752675 -0.0149716 + 0.0818568 -0.0028308 + 0.0842841 0.0011187 + 0.0941392 0.0151761 + 0.1039075 0.0265060 + 0.1222984 0.0429705 + 0.1234657 0.0438457 + 0.1433727 0.0565655 + 0.1632797 0.0657766 + 0.1696407 0.0681373 + 0.1750000 0.0699475 + 0.1832166 0.0724014 + 0.2031960 0.0770165 + 0.2231755 0.0799503 + 0.2431549 0.0815017 + 0.2631344 0.0818934 + 0.2864751 0.0801896 + 0.2944570 0.0798348 + 0.3129537 0.0777156 + 0.3625555 0.0720327 + 0.4121573 0.0663498 + 0.4301315 0.0642904 + 0.4617590 0.0606668 + 0.5113608 0.0549839 + 0.5609626 0.0493010 + 0.5983523 0.0450172 + 0.6105644 0.0436180 + 0.6601662 0.0379351 + 0.7097680 0.0322522 + 0.7593697 0.0265693 + 0.7887696 0.0232009 + 0.8089715 0.0208863 + 0.8585733 0.0152034 + 0.9081751 0.0095205 + 0.9577769 0.0038375 + 1.0000000 -0.0010000 diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_6.png b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_6.png new file mode 100644 index 0000000000000000000000000000000000000000..23f1b561c12913ae027cebca861dee026cc4cc60 GIT binary patch literal 28785 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 PsF}@MG&kk5RXhI^2rut- literal 0 HcmV?d00001 diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_7.dat b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_7.dat new file mode 100644 index 0000000..546acd6 --- /dev/null +++ b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_7.dat @@ -0,0 +1,150 @@ +rib_7 + 1.0000000 0.0000000 + 0.9577769 0.0046209 + 0.9081751 0.0100494 + 0.8585733 0.0154778 + 0.8089715 0.0209063 + 0.7887696 0.0231172 + 0.7593697 0.0263347 + 0.7097680 0.0317632 + 0.6601662 0.0371916 + 0.6105644 0.0426201 + 0.5983523 0.0439566 + 0.5609626 0.0480485 + 0.5113608 0.0534770 + 0.4617590 0.0589054 + 0.4301315 0.0623667 + 0.4121573 0.0643339 + 0.3625555 0.0697623 + 0.3129537 0.0751908 + 0.2944570 0.0772150 + 0.2864751 0.0775668 + 0.2631344 0.0785461 + 0.2431549 0.0793272 + 0.2231755 0.0801083 + 0.2031960 0.0808894 + 0.1832166 0.0816705 + 0.1750000 0.0819918 + 0.1696407 0.0816080 + 0.1632797 0.0811524 + 0.1433727 0.0796641 + 0.1234657 0.0780503 + 0.1222984 0.0779557 + 0.1039075 0.0747297 + 0.0941392 0.0729364 + 0.0842841 0.0711272 + 0.0818568 0.0703308 + 0.0751055 0.0681158 + 0.0658445 0.0649927 + 0.0565836 0.0618318 + 0.0529896 0.0606051 + 0.0478730 0.0578824 + 0.0395115 0.0533298 + 0.0311500 0.0487344 + 0.0292787 0.0477059 + 0.0290956 0.0475537 + 0.0273482 0.0461008 + 0.0256109 0.0446563 + 0.0256009 0.0446480 + 0.0238535 0.0431630 + 0.0221062 0.0416650 + 0.0203588 0.0401671 + 0.0186115 0.0386691 + 0.0168641 0.0371711 + 0.0151168 0.0356732 + 0.0133694 0.0341752 + 0.0132474 0.0340706 + 0.0121593 0.0324149 + 0.0109895 0.0306127 + 0.0098198 0.0287616 + 0.0086500 0.0269105 + 0.0074877 0.0250712 + 0.0066048 0.0231458 + 0.0057275 0.0211947 + 0.0048502 0.0192333 + 0.0039729 0.0172720 + 0.0030956 0.0153106 + 0.0029999 0.0150816 + 0.0021714 0.0126408 + 0.0013429 0.0102001 + 0.0008400 0.0076721 + 0.0003372 0.0051440 + 0.0001686 0.0025720 + 0.0000000 0.0000000 + 0.0001686 -0.0025720 + 0.0003372 -0.0051440 + 0.0008400 -0.0076721 + 0.0013429 -0.0102001 + 0.0021714 -0.0126408 + 0.0029999 -0.0150816 + 0.0041399 -0.0173933 + 0.0052799 -0.0197050 + 0.0067119 -0.0218482 + 0.0081439 -0.0239913 + 0.0098434 -0.0259292 + 0.0115429 -0.0278671 + 0.0134808 -0.0295666 + 0.0154187 -0.0312661 + 0.0175619 -0.0326981 + 0.0197050 -0.0341301 + 0.0220167 -0.0352701 + 0.0243285 -0.0364101 + 0.0267692 -0.0372386 + 0.0292100 -0.0380672 + 0.0317380 -0.0385700 + 0.0342660 -0.0390729 + 0.0368380 -0.0392414 + 0.0394100 -0.0394100 + 0.0419820 -0.0392414 + 0.0445541 -0.0390729 + 0.0470821 -0.0385700 + 0.0496101 -0.0380672 + 0.0520508 -0.0372386 + 0.0544916 -0.0364101 + 0.0568033 -0.0352701 + 0.0591150 -0.0341301 + 0.0612582 -0.0326981 + 0.0634013 -0.0312661 + 0.0653392 -0.0295666 + 0.0672771 -0.0278671 + 0.0689766 -0.0259292 + 0.0706761 -0.0239913 + 0.0721081 -0.0218482 + 0.0735401 -0.0197050 + 0.0746801 -0.0173933 + 0.0758201 -0.0150816 + 0.0818568 -0.0039728 + 0.0842841 -0.0000056 + 0.0941392 0.0139076 + 0.1039075 0.0250125 + 0.1222984 0.0409883 + 0.1234657 0.0418372 + 0.1433727 0.0540865 + 0.1632797 0.0629056 + 0.1696407 0.0651586 + 0.1750000 0.0668829 + 0.1832166 0.0692235 + 0.2031960 0.0736194 + 0.2231755 0.0764040 + 0.2431549 0.0778748 + 0.2631344 0.0782444 + 0.2864751 0.0765668 + 0.2944570 0.0762150 + 0.3129537 0.0741908 + 0.3625555 0.0687623 + 0.4121573 0.0633339 + 0.4301315 0.0613667 + 0.4617590 0.0579054 + 0.5113608 0.0524770 + 0.5609626 0.0470485 + 0.5983523 0.0429566 + 0.6105644 0.0416201 + 0.6601662 0.0361916 + 0.7097680 0.0307632 + 0.7593697 0.0253347 + 0.7887696 0.0221172 + 0.8089715 0.0199063 + 0.8585733 0.0144778 + 0.9081751 0.0090494 + 0.9577769 0.0036209 + 1.0000000 -0.0010000 diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_7.png b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_7.png new file mode 100644 index 0000000000000000000000000000000000000000..adeb036702be3637c21fa8f43f272440d64a504d GIT binary patch literal 28076 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 Az5oCK literal 0 HcmV?d00001 diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_8.dat b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_8.dat new file mode 100644 index 0000000..f2d3b06 --- /dev/null +++ b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_8.dat @@ -0,0 +1,150 @@ +rib_8 + 1.0000000 0.0000000 + 0.9577769 0.0043617 + 0.9081751 0.0094856 + 0.8585733 0.0146095 + 0.8089715 0.0197335 + 0.7887696 0.0218203 + 0.7593697 0.0248574 + 0.7097680 0.0299813 + 0.6601662 0.0351052 + 0.6105644 0.0402291 + 0.5983523 0.0414907 + 0.5609626 0.0453531 + 0.5113608 0.0504770 + 0.4617590 0.0556009 + 0.4301315 0.0588681 + 0.4121573 0.0607248 + 0.3625555 0.0658488 + 0.3129537 0.0709727 + 0.2944570 0.0728834 + 0.2864751 0.0732316 + 0.2631344 0.0741830 + 0.2431549 0.0749203 + 0.2231755 0.0756576 + 0.2031960 0.0763949 + 0.1832166 0.0771322 + 0.1750000 0.0774354 + 0.1696407 0.0770967 + 0.1632797 0.0766947 + 0.1433727 0.0753533 + 0.1234657 0.0738451 + 0.1222984 0.0737566 + 0.1039075 0.0708010 + 0.0941392 0.0691251 + 0.0842841 0.0674343 + 0.0818568 0.0667027 + 0.0751055 0.0646679 + 0.0658445 0.0617641 + 0.0565836 0.0588100 + 0.0529896 0.0576636 + 0.0478730 0.0551544 + 0.0395115 0.0509167 + 0.0311500 0.0466220 + 0.0292787 0.0456608 + 0.0290956 0.0455204 + 0.0273482 0.0441804 + 0.0256109 0.0428481 + 0.0256009 0.0428404 + 0.0238535 0.0414578 + 0.0221062 0.0400579 + 0.0203588 0.0386579 + 0.0186115 0.0372580 + 0.0168641 0.0358580 + 0.0151168 0.0344581 + 0.0133694 0.0330582 + 0.0132474 0.0329604 + 0.0121593 0.0314392 + 0.0109895 0.0297743 + 0.0098198 0.0280443 + 0.0086500 0.0263144 + 0.0074877 0.0245954 + 0.0066048 0.0228151 + 0.0057275 0.0209957 + 0.0048502 0.0191627 + 0.0039729 0.0173297 + 0.0037273 0.0168126 + 0.0030282 0.0152240 + 0.0021919 0.0127602 + 0.0013555 0.0102964 + 0.0008479 0.0077445 + 0.0003403 0.0051926 + 0.0001702 0.0025963 + 0.0000000 0.0000000 + 0.0001702 -0.0025963 + 0.0003403 -0.0051926 + 0.0008479 -0.0077445 + 0.0013555 -0.0102964 + 0.0021919 -0.0127602 + 0.0030282 -0.0152240 + 0.0041790 -0.0175575 + 0.0053298 -0.0198911 + 0.0067753 -0.0220544 + 0.0082208 -0.0242178 + 0.0099364 -0.0261740 + 0.0116519 -0.0281302 + 0.0136081 -0.0298457 + 0.0155643 -0.0315613 + 0.0177277 -0.0330068 + 0.0198911 -0.0344523 + 0.0222246 -0.0356031 + 0.0245582 -0.0367539 + 0.0270220 -0.0375902 + 0.0294857 -0.0384266 + 0.0320376 -0.0389342 + 0.0345895 -0.0394418 + 0.0371858 -0.0396119 + 0.0397821 -0.0397821 + 0.0423784 -0.0396119 + 0.0449747 -0.0394418 + 0.0475266 -0.0389342 + 0.0500785 -0.0384266 + 0.0525423 -0.0375902 + 0.0550061 -0.0367539 + 0.0573396 -0.0356031 + 0.0596732 -0.0344523 + 0.0618366 -0.0330068 + 0.0639999 -0.0315613 + 0.0659561 -0.0298457 + 0.0679123 -0.0281302 + 0.0696279 -0.0261740 + 0.0713434 -0.0242178 + 0.0727889 -0.0220544 + 0.0742344 -0.0198911 + 0.0753852 -0.0175575 + 0.0765360 -0.0152240 + 0.0818568 -0.0054326 + 0.0842841 -0.0014328 + 0.0941392 0.0123151 + 0.1039075 0.0231541 + 0.1222984 0.0385548 + 0.1234657 0.0393719 + 0.1433727 0.0510680 + 0.1632797 0.0594310 + 0.1696407 0.0615595 + 0.1750000 0.0631851 + 0.1832166 0.0653948 + 0.2031960 0.0695372 + 0.2231755 0.0721527 + 0.2431549 0.0735319 + 0.2631344 0.0738775 + 0.2864751 0.0722316 + 0.2944570 0.0718834 + 0.3129537 0.0699727 + 0.3625555 0.0648488 + 0.4121573 0.0597248 + 0.4301315 0.0578681 + 0.4617590 0.0546009 + 0.5113608 0.0494770 + 0.5609626 0.0443531 + 0.5983523 0.0404907 + 0.6105644 0.0392291 + 0.6601662 0.0341052 + 0.7097680 0.0289813 + 0.7593697 0.0238574 + 0.7887696 0.0208203 + 0.8089715 0.0187335 + 0.8585733 0.0136095 + 0.9081751 0.0084856 + 0.9577769 0.0033617 + 1.0000000 -0.0010000 diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_8.png b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_8.png new file mode 100644 index 0000000000000000000000000000000000000000..9fb8db6b250ff9c0c96fd63d4244cff1322758ae GIT binary patch literal 28054 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 literal 0 HcmV?d00001 diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_9.dat b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_9.dat new file mode 100644 index 0000000..146d5b9 --- /dev/null +++ b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_9.dat @@ -0,0 +1,150 @@ +rib_9 + 1.0000000 0.0000000 + 0.9577769 0.0040550 + 0.9081751 0.0088187 + 0.8585733 0.0135823 + 0.8089715 0.0183459 + 0.7887696 0.0202861 + 0.7593697 0.0231096 + 0.7097680 0.0278732 + 0.6601662 0.0326369 + 0.6105644 0.0374005 + 0.5983523 0.0385733 + 0.5609626 0.0421642 + 0.5113608 0.0469278 + 0.4617590 0.0516915 + 0.4301315 0.0547289 + 0.4121573 0.0564551 + 0.3625555 0.0612187 + 0.3129537 0.0659824 + 0.2944570 0.0677588 + 0.2864751 0.0681027 + 0.2631344 0.0690212 + 0.2431549 0.0697067 + 0.2231755 0.0703921 + 0.2031960 0.0710776 + 0.1832166 0.0717630 + 0.1750000 0.0720449 + 0.1696407 0.0717592 + 0.1632797 0.0714200 + 0.1433727 0.0702522 + 0.1234657 0.0688715 + 0.1222984 0.0687906 + 0.1039075 0.0661569 + 0.0941392 0.0646227 + 0.0842841 0.0630749 + 0.0818568 0.0624205 + 0.0751055 0.0606006 + 0.0658445 0.0579605 + 0.0565836 0.0552562 + 0.0529896 0.0542067 + 0.0478730 0.0519526 + 0.0395115 0.0480937 + 0.0311500 0.0441621 + 0.0292787 0.0432822 + 0.0290956 0.0431560 + 0.0273482 0.0419509 + 0.0256109 0.0407528 + 0.0256009 0.0407458 + 0.0238535 0.0394864 + 0.0221062 0.0382048 + 0.0203588 0.0369232 + 0.0186115 0.0356416 + 0.0168641 0.0343601 + 0.0151168 0.0330785 + 0.0133694 0.0317969 + 0.0132474 0.0317074 + 0.0121593 0.0303466 + 0.0109895 0.0288459 + 0.0098198 0.0272622 + 0.0086500 0.0256785 + 0.0074877 0.0241049 + 0.0066048 0.0224983 + 0.0057275 0.0208377 + 0.0048502 0.0191597 + 0.0039729 0.0174408 + 0.0037728 0.0170180 + 0.0030652 0.0154099 + 0.0022187 0.0129160 + 0.0013721 0.0104221 + 0.0008583 0.0078391 + 0.0003445 0.0052560 + 0.0001722 0.0026280 + 0.0000000 0.0000000 + 0.0001722 -0.0026280 + 0.0003445 -0.0052560 + 0.0008583 -0.0078391 + 0.0013721 -0.0104221 + 0.0022187 -0.0129160 + 0.0030652 -0.0154099 + 0.0042301 -0.0177719 + 0.0053949 -0.0201340 + 0.0068581 -0.0223238 + 0.0083212 -0.0245136 + 0.0100577 -0.0264937 + 0.0117942 -0.0284738 + 0.0137743 -0.0302103 + 0.0157544 -0.0319467 + 0.0179442 -0.0334099 + 0.0201340 -0.0348731 + 0.0224960 -0.0360379 + 0.0248581 -0.0372028 + 0.0273520 -0.0380493 + 0.0298459 -0.0388959 + 0.0324289 -0.0394097 + 0.0350120 -0.0399235 + 0.0376400 -0.0400957 + 0.0402680 -0.0402680 + 0.0428960 -0.0400957 + 0.0455240 -0.0399235 + 0.0481071 -0.0394097 + 0.0506901 -0.0388959 + 0.0531840 -0.0380493 + 0.0556779 -0.0372028 + 0.0580399 -0.0360379 + 0.0604020 -0.0348731 + 0.0625918 -0.0334099 + 0.0647816 -0.0319467 + 0.0667617 -0.0302103 + 0.0687418 -0.0284738 + 0.0704782 -0.0264937 + 0.0722147 -0.0245136 + 0.0736779 -0.0223238 + 0.0751411 -0.0201340 + 0.0763059 -0.0177719 + 0.0774708 -0.0154099 + 0.0818568 -0.0073062 + 0.0842841 -0.0032485 + 0.0941392 0.0103179 + 0.1039075 0.0208497 + 0.1222984 0.0355868 + 0.1234657 0.0363657 + 0.1433727 0.0474230 + 0.1632797 0.0552655 + 0.1696407 0.0572531 + 0.1750000 0.0587679 + 0.1832166 0.0608289 + 0.2031960 0.0646844 + 0.2231755 0.0671125 + 0.2431549 0.0683904 + 0.2631344 0.0687109 + 0.2864751 0.0671027 + 0.2944570 0.0667588 + 0.3129537 0.0649824 + 0.3625555 0.0602187 + 0.4121573 0.0554551 + 0.4301315 0.0537289 + 0.4617590 0.0506915 + 0.5113608 0.0459278 + 0.5609626 0.0411642 + 0.5983523 0.0375733 + 0.6105644 0.0364005 + 0.6601662 0.0316369 + 0.7097680 0.0268732 + 0.7593697 0.0221096 + 0.7887696 0.0192861 + 0.8089715 0.0173459 + 0.8585733 0.0125823 + 0.9081751 0.0078187 + 0.9577769 0.0030550 + 1.0000000 -0.0010000 diff --git a/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_9.png b/data/TUDELFT_V3_KITE/Surfplan_export/profiles/prof_9.png new file mode 100644 index 0000000000000000000000000000000000000000..90ebec86845b9a08e824995607438186fb8c2b6e GIT binary patch literal 27972 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 l=tWU_@oiN97p0*6!.txt' export + and its 'profiles/' directory) switches on the physically-detailed plot. The + export is converted with SurfplanAdapter (cached under the export directory), + and the tube diameters + airfoil top-surfaces are drawn on the wing. The plain + plot above is unaffected when ``surfplan_dir`` is omitted. + """ + # interactive_plot( + # body_aero_CAD_CFD_polars, + # vel=Umag, + # angle_of_attack=angle_of_attack, + # side_slip=side_slip, + # title="TUDELFT_V3_KITE (inflatable tubes + canopy)", + # surfplan_dir=Path(PROJECT_DIR) + # / "data" + # / "TUDELFT_V3_KITE" + # / "Surfplan_export", + # is_with_canopy=True, # curved canopy over the tubes; False -> tubes only + # is_with_tube_rings=False, # True -> also show tube construction rings + # ) + # # Step 5: Plot polar curves for different angles of attack and side slip angles, and save results # """ # Compare the aerodynamic performance of different models by plotting lift, drag, and side force coefficients diff --git a/pyproject.toml b/pyproject.toml index 32412c0..6bd7026 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,7 @@ dependencies = [ "numba", "screeninfo", "odfpy", + "SurfplanAdapter", ] [project.optional-dependencies] dev = [ diff --git a/src/VSM/plot_geometry_plotly.py b/src/VSM/plot_geometry_plotly.py index abf7140..88cf6b7 100644 --- a/src/VSM/plot_geometry_plotly.py +++ b/src/VSM/plot_geometry_plotly.py @@ -1,5 +1,6 @@ import os -from typing import List, Tuple, Dict, Any +from pathlib import Path +from typing import List, Tuple, Dict, Any, Optional import numpy as np import plotly.graph_objects as go from VSM.core.Solver import Solver @@ -435,6 +436,9 @@ def create_3D_plot( forces_of_panels: List[np.ndarray], is_with_aerodynamic_details: bool, is_with_bridles: bool = False, + tube_data: Optional[Dict[str, Any]] = None, + canopy_grid: Optional[Tuple[np.ndarray, np.ndarray, np.ndarray]] = None, + is_with_tube_rings: bool = False, ) -> go.Figure: """ Creates an interactive 3D plot of wing geometry using Plotly. @@ -444,10 +448,19 @@ def create_3D_plot( forces_of_panels: List of force vectors for each panel is_with_aerodynamic_details: Boolean to show/hide aerodynamic visualization details is_with_bridles: Boolean to show/hide bridle system visualization + tube_data: Optional inflatable-tube geometry from + ``VSM.plotly.build_tube_data``; when given, the leading-edge and strut + tubes are drawn as surfaces. + canopy_grid: Optional ``(X, Y, Z)`` curved-canopy surface from + ``VSM.plotly.build_canopy_grid``; when given, it replaces the flat + panel surfaces. + is_with_tube_rings: Also draw the tube construction rings (for inspection). Returns: plotly.graph_objects.Figure """ + from VSM.plotly.tube_geometry import add_tube_surfaces, add_tube_rings + panels = wing_aero.panels chord_average = np.max([panel.chord for panel in panels]) @@ -460,6 +473,8 @@ def create_3D_plot( if is_with_bridles: add_bridle_system(fig, wing_aero, is_first=True) + draw_flat_surface = canopy_grid is None + # Add geometric elements for i, panel in enumerate(panels): is_first = False @@ -473,7 +488,8 @@ def create_3D_plot( add_filaments(fig, panel, is_first) add_panel_edges(fig, panel, is_first, is_last) - add_panel_surface(fig, panel, is_first) + if draw_flat_surface: + add_panel_surface(fig, panel, is_first) add_aerodynamic_vectors( fig, panel, @@ -483,9 +499,39 @@ def create_3D_plot( max_force=max_force, ) + # Curved single-skin canopy replaces the flat panel surfaces. + if canopy_grid is not None: + add_canopy_surface(fig, canopy_grid) + + # Inflatable tubes (leading-edge tube + struts). + if tube_data is not None: + add_tube_surfaces(fig, tube_data) + if is_with_tube_rings: + add_tube_rings(fig, tube_data) + return fig +def add_canopy_surface( + fig: go.Figure, canopy_grid: Tuple[np.ndarray, np.ndarray, np.ndarray] +) -> None: + """Add the curved single-skin canopy as a uniformly coloured surface.""" + x, y, z = canopy_grid + fig.add_trace( + go.Surface( + x=x, + y=y, + z=z, + surfacecolor=np.zeros_like(z), + colorscale=[[0, "lightgrey"], [1, "lightgrey"]], + showscale=False, + opacity=0.85, + name="Canopy", + showlegend=True, + ) + ) + + def compute_kite_geometry_ranges(panels: List[Any]) -> Tuple[Dict[str, float], float]: """Calculate axis ranges and tick spacing.""" all_points = [] @@ -754,6 +800,9 @@ def update_plot( is_with_aerodynamic_details: bool, is_with_bridles: bool, title: str, + tube_data: Optional[Dict[str, Any]] = None, + canopy_grid: Optional[Tuple[np.ndarray, np.ndarray, np.ndarray]] = None, + is_with_tube_rings: bool = False, ): # Update AoA and rerun VSM results = running_VSM( @@ -773,6 +822,9 @@ def update_plot( results["F_distribution"], is_with_aerodynamic_details, is_with_bridles, + tube_data=tube_data, + canopy_grid=canopy_grid, + is_with_tube_rings=is_with_tube_rings, ) fig = add_case_information( fig, @@ -804,6 +856,9 @@ def interactive_plot( is_save: bool = False, filename="wing_geometry", is_show: bool = True, + surfplan_dir: Optional[Path] = None, + is_with_canopy: bool = True, + is_with_tube_rings: bool = False, ): """ Creates and optionally saves multiple views of the wing geometry with interactive AoA slider. @@ -823,8 +878,34 @@ def interactive_plot( is_save: Whether to save the plot filename: Base filename for saved files is_show: Whether to display the plot + surfplan_dir: Optional path to a raw Surfplan export (a ``.txt`` plus + a ``profiles/`` directory). When given, the "fancy" plot is drawn: the + export is converted with SurfplanAdapter (cached) and the inflatable + leading-edge and strut tubes -- and, by default, the curved single-skin + canopy -- are rendered on the wing. Leaving it ``None`` keeps the plain + plot unchanged. + is_with_canopy: When a Surfplan export is given, draw the curved canopy + (lofted airfoil top-surfaces) in place of the flat panel surfaces. + is_with_tube_rings: Also draw the tube construction rings (for inspection). + + Returns: + plotly.graph_objects.Figure: The created figure. """ + # Build the optional fancy-plot geometry once (reused on every slider update). + tube_data = None + canopy_grid = None + if surfplan_dir is not None: + from VSM.plotly.surfplan_runner import ensure_surfplan_processed + from VSM.plotly.tube_geometry import build_tube_data + from VSM.plotly.canopy_geometry import load_contour_table, build_canopy_grid + + processed_dir = ensure_surfplan_processed(Path(surfplan_dir)) + tube_data = build_tube_data(wing_aero.panels, processed_dir) + if is_with_canopy: + contour_table = load_contour_table(processed_dir) + canopy_grid = build_canopy_grid(wing_aero.panels, contour_table) + # Create the figure with a default orientation fig = go.Figure() fig.update_layout( @@ -848,6 +929,9 @@ def interactive_plot( is_with_aerodynamic_details, is_with_bridles, title, + tube_data=tube_data, + canopy_grid=canopy_grid, + is_with_tube_rings=is_with_tube_rings, ) # Save or show the plot if requested @@ -869,3 +953,5 @@ def interactive_plot( fig.write_html(f, include_plotlyjs=True, full_html=True) print(f"Interactive plot saved to: {tmp_path}") webbrowser.open(f"file://{tmp_path}") + + return fig diff --git a/src/VSM/plotly/__init__.py b/src/VSM/plotly/__init__.py new file mode 100644 index 0000000..a204699 --- /dev/null +++ b/src/VSM/plotly/__init__.py @@ -0,0 +1,24 @@ +"""Helpers for the interactive Plotly wing plot. + +The public ``interactive_plot`` entry point lives in ``VSM.plot_geometry_plotly`` +(unchanged location for backward compatibility). This subpackage holds the +supporting machinery for the opt-in "fancy" plot that renders the inflatable +tubes and the curved single-skin canopy of a LEI kite from a raw Surfplan export. +""" + +from VSM.plotly.surfplan_runner import ensure_surfplan_processed +from VSM.plotly.tube_geometry import ( + add_tube_rings, + add_tube_surfaces, + build_tube_data, +) +from VSM.plotly.canopy_geometry import build_canopy_grid, load_contour_table + +__all__ = [ + "ensure_surfplan_processed", + "build_tube_data", + "add_tube_surfaces", + "add_tube_rings", + "build_canopy_grid", + "load_contour_table", +] diff --git a/src/VSM/plotly/canopy_geometry.py b/src/VSM/plotly/canopy_geometry.py new file mode 100644 index 0000000..ae4e98c --- /dev/null +++ b/src/VSM/plotly/canopy_geometry.py @@ -0,0 +1,230 @@ +"""Curved single-skin canopy geometry for the interactive Plotly wing plot. + +The canopy is rendered as a loft of the real airfoil top-surfaces (the ``.dat`` +profiles produced by SurfplanAdapter) placed on the *drawn* wing panels. Only the +upper contour is used (LEI kites are single-skin), so the inflatable tubes stay +visible from below. See ``VSM.plotly.tube_geometry`` for the tubes and +``VSM.plotly.surfplan_runner`` for how the source files are generated. + +All geometry is anchored on the drawn ``Panel`` objects (their LE/TE points and +local frame), so the result is independent of the coordinate frame of the source +Surfplan export. +""" + +from pathlib import Path +from typing import Any, List, Tuple + +import numpy as np +import yaml + + +def read_upper_contour(dat_file_path: Path) -> np.ndarray: + """Read an airfoil ``.dat`` file and return its upper contour, LE-to-TE. + + The ``.dat`` files use the standard ordering starting at the trailing edge + ``(1, 0)``, running forward over the upper surface to the leading edge + ``(0, 0)``, then back over the lower surface to the trailing edge. The upper + surface is the run up to the minimum-x (leading edge) point. + + Args: + dat_file_path (Path): Path to the airfoil coordinate file. The first line + is a name label and is skipped; remaining lines are ``x y`` pairs + normalised by the chord. + + Returns: + np.ndarray: Upper contour as an ``(n, 2)`` array ordered from the leading + edge ``(0, 0)`` to the trailing edge ``(1, 0)``. + """ + points = [] + for line in Path(dat_file_path).read_text().splitlines(): + parts = line.split() + if len(parts) != 2: + continue + try: + points.append((float(parts[0]), float(parts[1]))) + except ValueError: + continue + points = np.asarray(points, dtype=float) + le_index = int(np.argmin(points[:, 0])) + upper = points[: le_index + 1] # TE -> LE + return upper[::-1] # LE -> TE + + +def resample_contour(contour: np.ndarray, n_points: int = 40) -> np.ndarray: + """Resample a contour to a fixed point count by arc length. + + Uses cumulative chord-length parameterisation so that the leading- and + trailing-edge endpoints are preserved. This lets contours from different + airfoils be blended point-by-point. + + Args: + contour (np.ndarray): ``(n, 2)`` contour ordered LE-to-TE. + n_points (int): Number of output points. Defaults to 40. + + Returns: + np.ndarray: ``(n_points, 2)`` resampled contour. + """ + segment_lengths = np.linalg.norm(np.diff(contour, axis=0), axis=1) + arc_length = np.concatenate([[0.0], np.cumsum(segment_lengths)]) + total = arc_length[-1] + if total <= 0: + return np.repeat(contour[:1], n_points, axis=0) + sample = np.linspace(0.0, total, n_points) + x = np.interp(sample, arc_length, contour[:, 0]) + y = np.interp(sample, arc_length, contour[:, 1]) + return np.column_stack([x, y]) + + +def load_contour_table( + processed_dir: Path, n_points: int = 40 +) -> List[Tuple[float, np.ndarray]]: + """Build a span-fraction -> upper-contour table from the generated aero yaml. + + Reads ``aero_geometry.yaml`` written by SurfplanAdapter: each wing section + provides its span position (``LE_y``) and an ``airfoil_id`` whose + ``info_dict`` carries the ``dat_file_path``. Span positions are normalised to + fractions of the half-span so the table can be sampled against any drawn wing + regardless of scale or frame. + + Args: + processed_dir (Path): Directory holding ``aero_geometry.yaml`` and the + ``profiles/`` referenced by relative ``dat_file_path`` entries. + n_points (int): Point count each contour is resampled to. Defaults to 40. + + Returns: + List[Tuple[float, np.ndarray]]: ``(span_fraction, contour)`` pairs sorted + by ascending span fraction, each contour an ``(n_points, 2)`` array. + """ + processed_dir = Path(processed_dir) + aero = yaml.safe_load((processed_dir / "aero_geometry.yaml").read_text()) + sections = aero["wing_sections"] + airfoils = aero["wing_airfoils"] + + section_headers = sections["headers"] + idx_id = section_headers.index("airfoil_id") + idx_le_y = section_headers.index("LE_y") + + airfoil_headers = airfoils["headers"] + a_idx_id = airfoil_headers.index("airfoil_id") + a_idx_info = airfoil_headers.index("info_dict") + dat_by_id = { + row[a_idx_id]: row[a_idx_info]["dat_file_path"] for row in airfoils["data"] + } + + contour_cache = {} + span_y = np.array([abs(row[idx_le_y]) for row in sections["data"]]) + half_span = span_y.max() if span_y.max() > 0 else 1.0 + + table = [] + for row in sections["data"]: + airfoil_id = row[idx_id] + if airfoil_id not in contour_cache: + dat_path = processed_dir / dat_by_id[airfoil_id] + contour_cache[airfoil_id] = resample_contour( + read_upper_contour(dat_path), n_points + ) + fraction = abs(row[idx_le_y]) / half_span + table.append((fraction, contour_cache[airfoil_id])) + + table.sort(key=lambda item: item[0]) + return table + + +def interpolate_contour_at( + span_fraction: float, contour_table: List[Tuple[float, np.ndarray]] +) -> np.ndarray: + """Linearly blend the two contours bracketing a span fraction. + + Args: + span_fraction (float): Target fraction of the half-span, in ``[0, 1]``. + contour_table (List[Tuple[float, np.ndarray]]): Output of + :func:`load_contour_table`; contours share a common point count. + + Returns: + np.ndarray: Interpolated contour, clamped to the end contours outside the + table's fraction range. + """ + fractions = [f for f, _ in contour_table] + if span_fraction <= fractions[0]: + return contour_table[0][1] + if span_fraction >= fractions[-1]: + return contour_table[-1][1] + upper = int(np.searchsorted(fractions, span_fraction)) + lower = upper - 1 + f_lo, c_lo = contour_table[lower] + f_hi, c_hi = contour_table[upper] + weight = (span_fraction - f_lo) / (f_hi - f_lo) if f_hi > f_lo else 0.0 + return (1.0 - weight) * c_lo + weight * c_hi + + +def _edge_stations(panels: List[Any]) -> List[dict]: + """Collect the ``n_panels + 1`` spanwise edge stations of the drawn wing. + + Each station carries its leading- and trailing-edge points, the chord vector + and length, and the local up-vector (averaged across adjacent panels), all + taken from the ``Panel`` objects so the canopy sits exactly on the drawn wing. + """ + stations = [] + + def make_station(le_point, te_point, up_vectors): + chord_vector = np.asarray(te_point) - np.asarray(le_point) + chord_length = float(np.linalg.norm(chord_vector)) + chord_hat = chord_vector / max(chord_length, 1e-12) + up = np.mean(up_vectors, axis=0) + up = up / max(np.linalg.norm(up), 1e-12) + return { + "le_point": np.asarray(le_point, dtype=float), + "te_point": np.asarray(te_point, dtype=float), + "chord_length": chord_length, + "chord_hat": chord_hat, + "up": up, + } + + stations.append( + make_station(panels[0].LE_point_1, panels[0].TE_point_1, [panels[0].x_airf]) + ) + for index, panel in enumerate(panels): + neighbours = [panel.x_airf] + if index + 1 < len(panels): + neighbours.append(panels[index + 1].x_airf) + stations.append(make_station(panel.LE_point_2, panel.TE_point_2, neighbours)) + return stations + + +def build_canopy_grid( + panels: List[Any], contour_table: List[Tuple[float, np.ndarray]] +) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + """Build the lofted canopy surface grid over the drawn wing panels. + + For every panel-edge station the local upper contour (blended from the rib + profiles at that span fraction) is placed on the drawn chord: the contour's + chordwise coordinate runs along the station chord and its height along the + local up-vector, both scaled by the station chord length. + + Args: + panels (List[Any]): Drawn ``Panel`` objects, ordered tip to tip. + contour_table (List[Tuple[float, np.ndarray]]): Output of + :func:`load_contour_table`. + + Returns: + Tuple[np.ndarray, np.ndarray, np.ndarray]: ``X``, ``Y``, ``Z`` arrays of + shape ``(n_panels + 1, n_points)`` suitable for a Plotly surface. + """ + stations = _edge_stations(panels) + all_y = np.array([abs(station["le_point"][1]) for station in stations]) + half_span = all_y.max() if all_y.max() > 0 else 1.0 + + rows = [] + for station in stations: + fraction = abs(station["le_point"][1]) / half_span + contour = interpolate_contour_at(fraction, contour_table) + chord_length = station["chord_length"] + points = ( + station["le_point"][None, :] + + contour[:, 0:1] * chord_length * station["chord_hat"][None, :] + + contour[:, 1:2] * chord_length * station["up"][None, :] + ) + rows.append(points) + + grid = np.stack(rows, axis=0) # (n_stations, n_points, 3) + return grid[:, :, 0], grid[:, :, 1], grid[:, :, 2] diff --git a/src/VSM/plotly/surfplan_runner.py b/src/VSM/plotly/surfplan_runner.py new file mode 100644 index 0000000..f7680fb --- /dev/null +++ b/src/VSM/plotly/surfplan_runner.py @@ -0,0 +1,95 @@ +"""Run SurfplanAdapter to produce the geometry files the fancy plot needs. + +The interactive "fancy" plot needs physical tube diameters and airfoil profiles +that are not present in a plain VSM aero-geometry yaml. They live in a raw +Surfplan export (a ``.txt`` plus a ``profiles/`` directory). This module drives +SurfplanAdapter to convert that export into VSM-readable yaml files, caching the +result so the conversion only runs when the export changes. +""" + +from pathlib import Path +from typing import Optional + + +def _find_surfplan_txt(surfplan_dir: Path) -> Path: + """Return the single ``.txt`` export in ``surfplan_dir`` (error if not one).""" + txt_files = sorted(surfplan_dir.glob("*.txt")) + if not txt_files: + raise FileNotFoundError( + f"No Surfplan '.txt' export found in {surfplan_dir}. Expected a raw " + "Surfplan export (a '.txt' plus a 'profiles/' directory)." + ) + if len(txt_files) > 1: + raise ValueError( + f"Multiple '.txt' files found in {surfplan_dir}: " + f"{[f.name for f in txt_files]}. Keep only the Surfplan export." + ) + return txt_files[0] + + +def ensure_surfplan_processed( + surfplan_dir: Path, output_dir: Optional[Path] = None +) -> Path: + """Convert a raw Surfplan export into VSM geometry yamls, with caching. + + Args: + surfplan_dir (Path): Directory containing the Surfplan ``.txt`` export and + a ``profiles/`` subdirectory of airfoil ``.dat`` files. + output_dir (Optional[Path]): Where to write the generated files. Defaults + to ``surfplan_dir / "vsm_processed"``. + + Returns: + Path: The directory holding the generated ``aero_geometry.yaml``, + ``struc_geometry_all_in_surfplan.yaml`` and ``profiles/``. + + Raises: + ImportError: If SurfplanAdapter is not installed. + FileNotFoundError: If the export ``.txt`` or ``profiles/`` is missing. + """ + surfplan_dir = Path(surfplan_dir) + if output_dir is None: + output_dir = surfplan_dir / "vsm_processed" + output_dir = Path(output_dir) + + txt_path = _find_surfplan_txt(surfplan_dir) + profile_load_dir = surfplan_dir / "profiles" + if not profile_load_dir.is_dir(): + raise FileNotFoundError( + f"No 'profiles/' directory in {surfplan_dir}; the fancy plot needs the " + "airfoil '.dat' files from the Surfplan export." + ) + + aero_yaml = output_dir / "aero_geometry.yaml" + struc_yaml = output_dir / "struc_geometry_all_in_surfplan.yaml" + if ( + aero_yaml.exists() + and struc_yaml.exists() + and aero_yaml.stat().st_mtime >= txt_path.stat().st_mtime + and struc_yaml.stat().st_mtime >= txt_path.stat().st_mtime + ): + return output_dir + + try: + from SurfplanAdapter.process_wing import main_process_wing + from SurfplanAdapter.process_bridle_lines import main_process_bridle_lines + from SurfplanAdapter.generate_yaml import main_generate_yaml + except ImportError as error: + raise ImportError( + "The fancy plot requires SurfplanAdapter to convert the raw Surfplan " + "export. Install it with 'pip install SurfplanAdapter'." + ) from error + + output_dir.mkdir(parents=True, exist_ok=True) + ribs_data = main_process_wing.main( + surfplan_txt_file_path=txt_path, + profile_load_dir=profile_load_dir, + profile_save_dir=output_dir / "profiles", + is_make_plots=False, + ) + bridle_lines = main_process_bridle_lines.main(txt_path) + main_generate_yaml.main( + ribs_data=ribs_data, + bridle_lines=bridle_lines, + yaml_file_path=output_dir / "config_kite.yaml", + ) + return output_dir diff --git a/src/VSM/plotly/tube_geometry.py b/src/VSM/plotly/tube_geometry.py new file mode 100644 index 0000000..f6fad66 --- /dev/null +++ b/src/VSM/plotly/tube_geometry.py @@ -0,0 +1,397 @@ +"""Inflatable-tube geometry for the interactive Plotly wing plot. + +Builds lofted 3D surfaces for the leading-edge tube and the chordwise strut tubes +of a LEI kite, using the physical tube-diameter tables produced by SurfplanAdapter +(``leading_edge_tubes`` and ``strut_tubes`` in +``struc_geometry_all_in_surfplan.yaml``) together with the airfoil top-surface +contours (see ``VSM.plotly.canopy_geometry``). + +Diameters are physical (metres) and sourced from the generated tables; positions +are anchored on the *drawn* ``Panel`` objects, so tubes always sit on the wing +that is actually plotted, independent of the Surfplan export's coordinate frame: + +* Leading-edge tube: centred just aft of the drawn LE polyline, diameter + interpolated from the ``leading_edge_tubes`` table by span fraction (this + captures the tapering tip, which ``t * chord`` does not). +* Struts: placed at the ``is_strut`` rib span fractions, shaped to follow the + local airfoil top-surface and offset downward so the tube top touches the + canopy underside. Per-strut LE/TE diameters come from the ``strut_tubes`` table + paired tip-to-tip with the strut ribs (both are symmetric and span-ordered, so + the pairing does not rely on the table's node indices). +""" + +from pathlib import Path +from typing import Any, Callable, Dict, List, Tuple + +import numpy as np +import plotly.graph_objects as go +import yaml + +from VSM.plotly.canopy_geometry import interpolate_contour_at, load_contour_table + +TUBE_COLOR = "#2c7fb8" +TUBE_OPACITY = 0.5 +RING_COLOR = "#08306b" + + +def circle_points( + center: np.ndarray, axis: np.ndarray, diameter: float, n: int = 24 +) -> np.ndarray: + """Ring of ``n`` points of the given diameter, perpendicular to ``axis``. + + Args: + center (np.ndarray): Ring centre, shape ``(3,)``. + axis (np.ndarray): Tube axis at this station; the ring lies in the plane + normal to it. + diameter (float): Ring diameter. + n (int): Number of points around the ring. Defaults to 24. + + Returns: + np.ndarray: ``(n, 3)`` ring points, closed (first point repeated at end). + """ + axis = np.asarray(axis, dtype=float) + axis = axis / max(np.linalg.norm(axis), 1e-12) + helper = np.array([0.0, 0.0, 1.0]) + if abs(axis @ helper) > 0.9: + helper = np.array([0.0, 1.0, 0.0]) + e1 = np.cross(axis, helper) + e1 = e1 / max(np.linalg.norm(e1), 1e-12) + e2 = np.cross(axis, e1) + theta = np.linspace(0.0, 2.0 * np.pi, n) + radius = 0.5 * float(diameter) + return ( + np.asarray(center, dtype=float)[None, :] + + radius * np.cos(theta)[:, None] * e1[None, :] + + radius * np.sin(theta)[:, None] * e2[None, :] + ) + + +def load_le_diameter_interp(processed_dir: Path) -> Callable[[float], float]: + """Return a leading-edge diameter(span-fraction) interpolator. + + Builds the interpolator from the ``leading_edge_tubes`` segment table: each + segment midpoint gives a ``(|y| fraction, diameter)`` sample. Fractions are + of the half-span so the curve can be sampled against any drawn wing. + + Args: + processed_dir (Path): Directory holding + ``struc_geometry_all_in_surfplan.yaml``. + + Returns: + Callable[[float], float]: Function mapping a span fraction in ``[0, 1]`` + to a leading-edge tube diameter in metres. + """ + struc = yaml.safe_load( + (Path(processed_dir) / "struc_geometry_all_in_surfplan.yaml").read_text() + ) + particles = { + int(row[0]): np.asarray(row[1:4], dtype=float) + for row in struc["wing_particles"]["data"] + } + le = struc["leading_edge_tubes"] + idx_ci = le["headers"].index("ci") + idx_cj = le["headers"].index("cj") + idx_d = le["headers"].index("diameter") + + y_mid = [] + diam = [] + for row in le["data"]: + ci = particles[int(row[idx_ci])] + cj = particles[int(row[idx_cj])] + y_mid.append(0.5 * (ci[1] + cj[1])) + diam.append(float(row[idx_d])) + y_mid = np.abs(np.asarray(y_mid)) + diam = np.asarray(diam) + half_span = y_mid.max() if y_mid.max() > 0 else 1.0 + fractions = y_mid / half_span + + order = np.argsort(fractions) + frac_sorted = fractions[order] + diam_sorted = diam[order] + + def interp(span_fraction: float) -> float: + return float(np.interp(span_fraction, frac_sorted, diam_sorted)) + + return interp + + +def load_strut_diameter_table(processed_dir: Path) -> List[Tuple[float, float]]: + """Per-strut ``(diam_le, diam_te)`` pairs in span order (tip to tip). + + Args: + processed_dir (Path): Directory holding + ``struc_geometry_all_in_surfplan.yaml``. + + Returns: + List[Tuple[float, float]]: Leading- and trailing-edge strut-tube diameters + in metres, in the table's listed (span) order. + """ + struc = yaml.safe_load( + (Path(processed_dir) / "struc_geometry_all_in_surfplan.yaml").read_text() + ) + st = struc["strut_tubes"] + idx_le = st["headers"].index("strut_diam_le") + idx_te = st["headers"].index("strut_diam_te") + return [(float(row[idx_le]), float(row[idx_te])) for row in st["data"]] + + +def load_strut_span_fractions(processed_dir: Path) -> np.ndarray: + """Signed span fractions of the strut ribs, from the generated aero yaml. + + Uses the ``is_strut`` flag in each section's airfoil ``info_dict``. Fractions + are ``LE_y`` normalised by the half-span, returned sorted by descending span + (``+tip`` to ``-tip``) to match the ``strut_tubes`` table order. + + Args: + processed_dir (Path): Directory holding ``aero_geometry.yaml``. + + Returns: + np.ndarray: Signed span fractions of the strut ribs, high to low. + """ + aero = yaml.safe_load((Path(processed_dir) / "aero_geometry.yaml").read_text()) + sections = aero["wing_sections"] + airfoils = aero["wing_airfoils"] + idx_id = sections["headers"].index("airfoil_id") + idx_le_y = sections["headers"].index("LE_y") + a_idx_id = airfoils["headers"].index("airfoil_id") + a_idx_info = airfoils["headers"].index("info_dict") + is_strut_by_id = { + row[a_idx_id]: bool(row[a_idx_info].get("is_strut", False)) + for row in airfoils["data"] + } + + span_y = np.array([abs(row[idx_le_y]) for row in sections["data"]]) + half_span = span_y.max() if span_y.max() > 0 else 1.0 + strut_y = [ + row[idx_le_y] + for row in sections["data"] + if is_strut_by_id.get(row[idx_id], False) + ] + fractions = np.array(sorted(strut_y, reverse=True)) / half_span + return fractions + + +def _drawn_stations(panels: List[Any]) -> Dict[str, np.ndarray]: + """Spanwise edge stations of the drawn wing as stacked arrays. + + Returns leading/trailing points, chord lengths, chord and up unit vectors, and + signed span fractions for the ``n_panels + 1`` panel edges, sorted by span. + """ + le = [panels[0].LE_point_1] + [p.LE_point_2 for p in panels] + te = [panels[0].TE_point_1] + [p.TE_point_2 for p in panels] + up = [panels[0].x_airf] + for index, panel in enumerate(panels): + neighbours = [panel.x_airf] + if index + 1 < len(panels): + neighbours.append(panels[index + 1].x_airf) + up.append(np.mean(neighbours, axis=0)) + + le = np.asarray(le, dtype=float) + te = np.asarray(te, dtype=float) + up = np.asarray(up, dtype=float) + up = up / np.linalg.norm(up, axis=1, keepdims=True).clip(1e-12) + chord_vec = te - le + chord_len = np.linalg.norm(chord_vec, axis=1) + chord_hat = chord_vec / chord_len[:, None].clip(1e-12) + + half_span = np.abs(le[:, 1]).max() + half_span = half_span if half_span > 0 else 1.0 + fraction = le[:, 1] / half_span + + order = np.argsort(fraction) + return { + "le": le[order], + "te": te[order], + "up": up[order], + "chord_len": chord_len[order], + "chord_hat": chord_hat[order], + "fraction": fraction[order], + } + + +def _station_at(stations: Dict[str, np.ndarray], signed_fraction: float) -> dict: + """Interpolate a drawn-wing station at a signed span fraction.""" + frac = stations["fraction"] + le = np.array( + [np.interp(signed_fraction, frac, stations["le"][:, i]) for i in range(3)] + ) + te = np.array( + [np.interp(signed_fraction, frac, stations["te"][:, i]) for i in range(3)] + ) + up = np.array( + [np.interp(signed_fraction, frac, stations["up"][:, i]) for i in range(3)] + ) + up = up / max(np.linalg.norm(up), 1e-12) + chord_vec = te - le + chord_len = float(np.linalg.norm(chord_vec)) + chord_hat = chord_vec / max(chord_len, 1e-12) + return { + "le": le, + "te": te, + "up": up, + "chord_len": chord_len, + "chord_hat": chord_hat, + } + + +def build_le_tube( + panels: List[Any], le_diameter_interp: Callable[[float], float] +) -> List[np.ndarray]: + """Build the leading-edge tube as a list of cross-section rings. + + The tube runs spanwise along the drawn LE polyline; each ring is centred just + aft of the LE point (so the tube front touches the drawn leading edge) and its + diameter is looked up from the LE table at the local span fraction. + + Args: + panels (List[Any]): Drawn ``Panel`` objects, ordered tip to tip. + le_diameter_interp (Callable[[float], float]): Output of + :func:`load_le_diameter_interp`. + + Returns: + List[np.ndarray]: One ``(n, 3)`` ring per spanwise station. + """ + stations = _drawn_stations(panels) + le = stations["le"] + fraction = np.abs(stations["fraction"]) + diameters = np.array([le_diameter_interp(f) for f in fraction]) + centers = le + 0.5 * diameters[:, None] * stations["chord_hat"] + + rings = [] + for index in range(len(centers)): + lo = max(index - 1, 0) + hi = min(index + 1, len(centers) - 1) + tangent = centers[hi] - centers[lo] + rings.append(circle_points(centers[index], tangent, diameters[index])) + return rings + + +def build_strut( + stations: Dict[str, np.ndarray], + contour_table, + signed_fraction: float, + diam_le: float, + diam_te: float, +) -> List[np.ndarray]: + """Build one chordwise strut tube hugging the canopy underside. + + The local airfoil top-surface is placed on the drawn wing at the strut span, + then each chord station is offset downward by the local strut radius so the + tube top touches the canopy. Diameter tapers linearly from ``diam_le`` at the + leading edge to ``diam_te`` at the trailing edge. + + Args: + stations (Dict[str, np.ndarray]): Output of :func:`_drawn_stations`. + contour_table: Output of ``canopy_geometry.load_contour_table``. + signed_fraction (float): Signed span fraction of the strut rib. + diam_le (float): Strut-tube diameter at the leading edge, metres. + diam_te (float): Strut-tube diameter at the trailing edge, metres. + + Returns: + List[np.ndarray]: One ``(n, 3)`` ring per chordwise station. + """ + station = _station_at(stations, signed_fraction) + contour = interpolate_contour_at(abs(signed_fraction), contour_table) + chord_len = station["chord_len"] + + canopy_points = ( + station["le"][None, :] + + contour[:, 0:1] * chord_len * station["chord_hat"][None, :] + + contour[:, 1:2] * chord_len * station["up"][None, :] + ) + chord_fraction = contour[:, 0] + diameters = diam_le + (diam_te - diam_le) * chord_fraction + centers = canopy_points - 0.5 * diameters[:, None] * station["up"][None, :] + + rings = [] + for index in range(len(centers)): + lo = max(index - 1, 0) + hi = min(index + 1, len(centers) - 1) + tangent = centers[hi] - centers[lo] + rings.append(circle_points(centers[index], tangent, diameters[index])) + return rings + + +def build_tube_data(panels: List[Any], processed_dir: Path) -> Dict[str, Any]: + """Assemble leading-edge and strut tube rings for the drawn wing. + + Args: + panels (List[Any]): Drawn ``Panel`` objects, ordered tip to tip. + processed_dir (Path): Directory of SurfplanAdapter-generated files. + + Returns: + Dict[str, Any]: ``{"le": [rings], "struts": [[rings], ...]}``. + """ + processed_dir = Path(processed_dir) + le_interp = load_le_diameter_interp(processed_dir) + contour_table = load_contour_table(processed_dir) + strut_fractions = load_strut_span_fractions(processed_dir) + strut_table = load_strut_diameter_table(processed_dir) + + le_rings = build_le_tube(panels, le_interp) + + stations = _drawn_stations(panels) + struts = [] + for index, signed_fraction in enumerate(strut_fractions): + if index < len(strut_table): + diam_le, diam_te = strut_table[index] + else: + # Fallback: size the strut from the local LE tube diameter. + diam_le = le_interp(abs(signed_fraction)) + diam_te = 0.6 * diam_le + struts.append( + build_strut(stations, contour_table, signed_fraction, diam_le, diam_te) + ) + return {"le": le_rings, "struts": struts} + + +def _rings_to_surface( + rings: List[np.ndarray], +) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + """Stack a list of rings into ``X``, ``Y``, ``Z`` surface arrays.""" + grid = np.stack(rings, axis=0) # (n_rings, n_circle, 3) + return grid[:, :, 0], grid[:, :, 1], grid[:, :, 2] + + +def _add_surface(fig: go.Figure, rings: List[np.ndarray], name: str, show_legend: bool): + """Add one tube as a uniformly coloured, semi-transparent surface.""" + x, y, z = _rings_to_surface(rings) + fig.add_trace( + go.Surface( + x=x, + y=y, + z=z, + surfacecolor=np.zeros_like(z), + colorscale=[[0, TUBE_COLOR], [1, TUBE_COLOR]], + showscale=False, + opacity=TUBE_OPACITY, + name=name, + showlegend=show_legend, + ) + ) + + +def add_tube_surfaces(fig: go.Figure, tube_data: Dict[str, Any]) -> None: + """Add the leading-edge and strut tube surfaces to the figure.""" + _add_surface(fig, tube_data["le"], "Leading-edge tube", show_legend=True) + for index, strut in enumerate(tube_data["struts"]): + _add_surface(fig, strut, "Strut tube", show_legend=(index == 0)) + + +def add_tube_rings(fig: go.Figure, tube_data: Dict[str, Any]) -> None: + """Add the construction rings (wireframe circles) for inspection.""" + all_rings = list(tube_data["le"]) + [ + r for strut in tube_data["struts"] for r in strut + ] + for index, ring in enumerate(all_rings): + fig.add_trace( + go.Scatter3d( + x=ring[:, 0], + y=ring[:, 1], + z=ring[:, 2], + mode="lines", + line=dict(color=RING_COLOR, width=1), + name="Tube rings", + showlegend=(index == 0), + ) + ) diff --git a/tests/plotting/test_tube_geometry.py b/tests/plotting/test_tube_geometry.py new file mode 100644 index 0000000..2bc5cff --- /dev/null +++ b/tests/plotting/test_tube_geometry.py @@ -0,0 +1,196 @@ +"""Tests for the fancy Plotly plot: inflatable tubes and curved canopy. + +Unit tests cover the pure geometry helpers (ring math, contour handling, +interpolation). The integration tests exercise the full SurfplanAdapter +conversion and figure assembly against the bundled TUDELFT V3 Surfplan export; +they are skipped if SurfplanAdapter or the export data are unavailable. +""" + +from pathlib import Path + +import numpy as np +import pytest + +from VSM.plotly.canopy_geometry import ( + interpolate_contour_at, + read_upper_contour, + resample_contour, +) +from VSM.plotly.tube_geometry import circle_points + +REPO_ROOT = Path(__file__).resolve().parents[2] +SURFPLAN_DIR = REPO_ROOT / "data" / "TUDELFT_V3_KITE" / "Surfplan_export" +DRAWN_YAML = ( + REPO_ROOT + / "data" + / "TUDELFT_V3_KITE" + / "CAD_derived_geometry" + / "aero_geometry_CAD_breukels_regression.yaml" +) + + +def _has_surfplan(): + try: + import SurfplanAdapter # noqa: F401 + except ImportError: + return False + return SURFPLAN_DIR.is_dir() + + +requires_surfplan = pytest.mark.skipif( + not _has_surfplan(), reason="SurfplanAdapter or Surfplan export data unavailable" +) + + +# --------------------------------------------------------------------------- # +# Unit tests -- pure geometry helpers +# --------------------------------------------------------------------------- # + + +def test_circle_points_radius_and_orthogonality(): + center = np.array([1.0, 2.0, 3.0]) + axis = np.array([0.0, 1.0, 0.0]) + diameter = 0.4 + ring = circle_points(center, axis, diameter, n=32) + + # Every point lies at the radius from the centre. + radii = np.linalg.norm(ring - center, axis=1) + assert np.allclose(radii, 0.5 * diameter, atol=1e-9) + + # The ring lies in the plane normal to the axis (constant projection). + projections = (ring - center) @ axis + assert np.allclose(projections, 0.0, atol=1e-9) + + +def test_circle_points_axis_normalisation_invariant(): + center = np.zeros(3) + ring_unit = circle_points(center, np.array([0.0, 0.0, 1.0]), 1.0) + ring_scaled = circle_points(center, np.array([0.0, 0.0, 5.0]), 1.0) + assert np.allclose(ring_unit, ring_scaled, atol=1e-9) + + +def test_resample_contour_count_and_endpoints(): + contour = np.array([[0.0, 0.0], [0.3, 0.08], [0.7, 0.05], [1.0, 0.0]]) + resampled = resample_contour(contour, n_points=25) + assert resampled.shape == (25, 2) + assert np.allclose(resampled[0], contour[0], atol=1e-9) + assert np.allclose(resampled[-1], contour[-1], atol=1e-9) + + +def test_interpolate_contour_at_blends_and_clamps(): + c_lo = np.array([[0.0, 0.0], [1.0, 0.0]]) + c_hi = np.array([[0.0, 0.0], [1.0, 1.0]]) + table = [(0.0, c_lo), (1.0, c_hi)] + + # Midpoint is the average of the two contours. + mid = interpolate_contour_at(0.5, table) + assert np.allclose(mid, 0.5 * (c_lo + c_hi)) + + # Outside the range clamps to the end contours. + assert np.allclose(interpolate_contour_at(-1.0, table), c_lo) + assert np.allclose(interpolate_contour_at(2.0, table), c_hi) + + +def test_read_upper_contour_ordering(tmp_path): + # Minimal Selig-style .dat: TE -> upper -> LE -> lower -> TE. + dat = tmp_path / "prof.dat" + dat.write_text( + "test_profile\n" "1.0 0.0\n" "0.5 0.08\n" "0.0 0.0\n" "0.5 -0.04\n" "1.0 0.0\n" + ) + upper = read_upper_contour(dat) + # Ordered LE (x=0) to TE (x=1), all upper-surface points. + assert upper[0, 0] == pytest.approx(0.0) + assert upper[-1, 0] == pytest.approx(1.0) + assert np.all(np.diff(upper[:, 0]) > 0) + assert np.all(upper[:, 1] >= 0.0) + + +# --------------------------------------------------------------------------- # +# Integration tests -- full conversion + figure assembly +# --------------------------------------------------------------------------- # + + +@requires_surfplan +def test_ensure_surfplan_processed_and_cache(): + from VSM.plotly.surfplan_runner import ensure_surfplan_processed + + processed = ensure_surfplan_processed(SURFPLAN_DIR) + aero = processed / "aero_geometry.yaml" + struc = processed / "struc_geometry_all_in_surfplan.yaml" + assert aero.exists() and struc.exists() + + # Second call is a cache hit: the generated file is not rewritten. + first_mtime = aero.stat().st_mtime + ensure_surfplan_processed(SURFPLAN_DIR) + assert aero.stat().st_mtime == first_mtime + + +@requires_surfplan +def test_build_tube_and_canopy_shapes(): + from VSM.core.BodyAerodynamics import BodyAerodynamics + from VSM.plotly.surfplan_runner import ensure_surfplan_processed + from VSM.plotly.tube_geometry import build_tube_data, load_strut_span_fractions + from VSM.plotly.canopy_geometry import build_canopy_grid, load_contour_table + + n_panels = 36 + body = BodyAerodynamics.instantiate( + n_panels=n_panels, + file_path=DRAWN_YAML, + spanwise_panel_distribution="uniform", + ) + panels = body.panels + processed = ensure_surfplan_processed(SURFPLAN_DIR) + + tube = build_tube_data(panels, processed) + n_struts = len(load_strut_span_fractions(processed)) + assert len(tube["le"]) == n_panels + 1 + assert len(tube["struts"]) == n_struts + + x, y, z = build_canopy_grid(panels, load_contour_table(processed)) + assert x.shape[0] == n_panels + 1 + assert x.shape == y.shape == z.shape + + # Tubes track the drawn wing span (within a tube radius at the tips). + wing_le = np.array([p.LE_point_1 for p in panels] + [p.LE_point_2 for p in panels]) + tube_pts = np.vstack(list(tube["le"])) + assert abs(tube_pts[:, 1].max() - wing_le[:, 1].max()) < 0.2 + + +@requires_surfplan +def test_interactive_plot_fancy_traces(): + import plotly.graph_objects as go + from VSM.core.BodyAerodynamics import BodyAerodynamics + from VSM.plot_geometry_plotly import interactive_plot + from VSM.plotly.tube_geometry import load_strut_span_fractions + from VSM.plotly.surfplan_runner import ensure_surfplan_processed + + body = BodyAerodynamics.instantiate( + n_panels=36, + file_path=DRAWN_YAML, + spanwise_panel_distribution="uniform", + ) + n_struts = len(load_strut_span_fractions(ensure_surfplan_processed(SURFPLAN_DIR))) + + # Fancy plot with canopy: surfaces = canopy + LE tube + struts, no flat panels. + fig = interactive_plot(body, surfplan_dir=SURFPLAN_DIR, is_show=False) + surfaces = [t for t in fig.data if isinstance(t, go.Surface)] + flat_panels = [ + t for t in fig.data if isinstance(t, go.Mesh3d) and t.name == "Panel Surface" + ] + assert len(surfaces) == 1 + 1 + n_struts + assert len(flat_panels) == 0 + + # Tubes only: flat panels kept, no canopy surface. + fig2 = interactive_plot( + body, surfplan_dir=SURFPLAN_DIR, is_with_canopy=False, is_show=False + ) + surfaces2 = [t for t in fig2.data if isinstance(t, go.Surface)] + flat_panels2 = [ + t for t in fig2.data if isinstance(t, go.Mesh3d) and t.name == "Panel Surface" + ] + assert len(surfaces2) == 1 + n_struts + assert len(flat_panels2) == len(body.panels) + + # Plain plot is unchanged: no surfaces at all. + fig3 = interactive_plot(body, is_show=False) + assert not [t for t in fig3.data if isinstance(t, go.Surface)] From f3005aa806095098b38a6e8d50f8c0578ae63d84 Mon Sep 17 00:00:00 2001 From: jellepoland Date: Fri, 24 Jul 2026 13:20:48 +0200 Subject: [PATCH 04/13] Refine inflatable tube geometry: strut-to-LE join, tip wrap, framing - Struts now start at the leading-edge tube's aft edge (buried half a tube depth in) instead of passing straight through it - Leading-edge tube wraps around each wingtip and continues to the trailing edge of the outer panel (constant-diameter, tip-to-tip continuous) - Twist-free parallel-transport ring framing (rings_along_centerline) so the swept surfaces do not pinch where the tube turns at the tips - Solid (opaque) tubes so the leading-edge tube reads clearly over the canopy - Split add_tube_surfaces into add_strut_surfaces / add_le_tube_surface so the leading-edge tube can be drawn last (on top) Co-Authored-By: Claude Opus 4.8 (1M context) --- src/VSM/plotly/tube_geometry.py | 190 ++++++++++++++++++++++----- tests/plotting/test_tube_geometry.py | 3 +- 2 files changed, 160 insertions(+), 33 deletions(-) diff --git a/src/VSM/plotly/tube_geometry.py b/src/VSM/plotly/tube_geometry.py index f6fad66..adafdda 100644 --- a/src/VSM/plotly/tube_geometry.py +++ b/src/VSM/plotly/tube_geometry.py @@ -29,13 +29,17 @@ from VSM.plotly.canopy_geometry import interpolate_contour_at, load_contour_table -TUBE_COLOR = "#2c7fb8" -TUBE_OPACITY = 0.5 +TUBE_COLOR = "black" +TUBE_OPACITY = 1.0 RING_COLOR = "#08306b" def circle_points( - center: np.ndarray, axis: np.ndarray, diameter: float, n: int = 24 + center: np.ndarray, + axis: np.ndarray, + diameter: float, + n: int = 24, + ref: np.ndarray = None, ) -> np.ndarray: """Ring of ``n`` points of the given diameter, perpendicular to ``axis``. @@ -45,15 +49,27 @@ def circle_points( normal to it. diameter (float): Ring diameter. n (int): Number of points around the ring. Defaults to 24. + ref (np.ndarray): Optional reference vector fixing the azimuthal origin of + the ring. Passing the *same* ``ref`` for every ring of a tube keeps + consecutive rings consistently oriented, which avoids the surface + twisting where the tube tangent swings (e.g. at a wingtip). Falls back + to an automatic helper when ``ref`` is parallel to the axis. Returns: np.ndarray: ``(n, 3)`` ring points, closed (first point repeated at end). """ axis = np.asarray(axis, dtype=float) axis = axis / max(np.linalg.norm(axis), 1e-12) - helper = np.array([0.0, 0.0, 1.0]) - if abs(axis @ helper) > 0.9: - helper = np.array([0.0, 1.0, 0.0]) + if ref is not None: + helper = np.asarray(ref, dtype=float) + if abs(axis @ (helper / max(np.linalg.norm(helper), 1e-12))) > 0.98: + helper = np.array([0.0, 0.0, 1.0]) + if abs(axis @ helper) > 0.9: + helper = np.array([0.0, 1.0, 0.0]) + else: + helper = np.array([0.0, 0.0, 1.0]) + if abs(axis @ helper) > 0.9: + helper = np.array([0.0, 1.0, 0.0]) e1 = np.cross(axis, helper) e1 = e1 / max(np.linalg.norm(e1), 1e-12) e2 = np.cross(axis, e1) @@ -66,6 +82,61 @@ def circle_points( ) +def rings_along_centerline( + centers: np.ndarray, diameters: np.ndarray, n: int = 24 +) -> List[np.ndarray]: + """Sweep circular rings along a centreline with twist-free framing. + + The ring frame is parallel-transported from one station to the next (the + previous in-plane axis is projected onto the new normal plane) instead of + being chosen independently per ring. This keeps the swept surface from + twisting or pinching where the centreline curves sharply -- e.g. where the + leading-edge tube turns chordwise at a wingtip. + + Args: + centers (np.ndarray): ``(m, 3)`` centreline points. + diameters (np.ndarray): ``(m,)`` local tube diameters. + n (int): Number of points around each ring. Defaults to 24. + + Returns: + List[np.ndarray]: One ``(n, 3)`` ring per centreline station. + """ + centers = np.asarray(centers, dtype=float) + diameters = np.asarray(diameters, dtype=float) + m = len(centers) + + tangents = np.zeros_like(centers) + tangents[1:-1] = centers[2:] - centers[:-2] + tangents[0] = centers[1] - centers[0] + tangents[-1] = centers[-1] - centers[-2] + tangents /= np.linalg.norm(tangents, axis=1, keepdims=True).clip(1e-12) + + def _seed_axis(tangent): + ref = np.array([0.0, 0.0, 1.0]) + if abs(tangent @ ref) > 0.9: + ref = np.array([0.0, 1.0, 0.0]) + axis = ref - (ref @ tangent) * tangent + return axis / max(np.linalg.norm(axis), 1e-12) + + theta = np.linspace(0.0, 2.0 * np.pi, n) + e1 = _seed_axis(tangents[0]) + rings = [] + for index in range(m): + # Parallel-transport the previous in-plane axis onto this normal plane. + e1 = e1 - (e1 @ tangents[index]) * tangents[index] + if np.linalg.norm(e1) < 1e-8: + e1 = _seed_axis(tangents[index]) + e1 /= max(np.linalg.norm(e1), 1e-12) + e2 = np.cross(tangents[index], e1) + radius = 0.5 * diameters[index] + rings.append( + centers[index][None, :] + + radius * np.cos(theta)[:, None] * e1[None, :] + + radius * np.sin(theta)[:, None] * e2[None, :] + ) + return rings + + def load_le_diameter_interp(processed_dir: Path) -> Callable[[float], float]: """Return a leading-edge diameter(span-fraction) interpolator. @@ -234,36 +305,59 @@ def _station_at(stations: Dict[str, np.ndarray], signed_fraction: float) -> dict } +def _tip_extension(le_center, te_point, le_diameter, n=8, taper=1.0): + """Centreline points + diameters continuing from a tip LE to the TE. + + Extends the leading-edge tube at the wingtip so it runs on to the trailing + edge of the outer panel, at (by default) constant diameter for a clean tube. + """ + ts = np.linspace(0.0, 1.0, n + 1)[1:] # exclude 0 (the LE centre already exists) + le_center = np.asarray(le_center, dtype=float) + te_point = np.asarray(te_point, dtype=float) + points = [le_center + t * (te_point - le_center) for t in ts] + diams = [le_diameter * (1.0 - (1.0 - taper) * t) for t in ts] + return points, diams + + def build_le_tube( - panels: List[Any], le_diameter_interp: Callable[[float], float] + panels: List[Any], + le_diameter_interp: Callable[[float], float], + extend_tips: bool = True, ) -> List[np.ndarray]: """Build the leading-edge tube as a list of cross-section rings. The tube runs spanwise along the drawn LE polyline; each ring is centred just aft of the LE point (so the tube front touches the drawn leading edge) and its - diameter is looked up from the LE table at the local span fraction. + diameter is looked up from the LE table at the local span fraction. With + ``extend_tips`` the tube wraps around each wingtip and continues toward the + trailing edge, tapering closed. Args: panels (List[Any]): Drawn ``Panel`` objects, ordered tip to tip. le_diameter_interp (Callable[[float], float]): Output of :func:`load_le_diameter_interp`. + extend_tips (bool): Wrap the tube from each tip toward the trailing edge. Returns: - List[np.ndarray]: One ``(n, 3)`` ring per spanwise station. + List[np.ndarray]: One ``(n, 3)`` ring per centreline station. """ stations = _drawn_stations(panels) le = stations["le"] + te = stations["te"] fraction = np.abs(stations["fraction"]) diameters = np.array([le_diameter_interp(f) for f in fraction]) centers = le + 0.5 * diameters[:, None] * stations["chord_hat"] - rings = [] - for index in range(len(centers)): - lo = max(index - 1, 0) - hi = min(index + 1, len(centers) - 1) - tangent = centers[hi] - centers[lo] - rings.append(circle_points(centers[index], tangent, diameters[index])) - return rings + center_list = list(centers) + diam_list = list(diameters) + if extend_tips: + neg_pts, neg_d = _tip_extension(centers[0], te[0], diameters[0]) + pos_pts, pos_d = _tip_extension(centers[-1], te[-1], diameters[-1]) + # Prepend the -tip wrap (reversed: TE -> LE) and append the +tip wrap. + center_list = neg_pts[::-1] + center_list + pos_pts + diam_list = neg_d[::-1] + diam_list + pos_d + + return rings_along_centerline(np.array(center_list), np.array(diam_list)) def build_strut( @@ -272,6 +366,7 @@ def build_strut( signed_fraction: float, diam_le: float, diam_te: float, + le_tube_diameter: float = 0.0, ) -> List[np.ndarray]: """Build one chordwise strut tube hugging the canopy underside. @@ -280,12 +375,19 @@ def build_strut( tube top touches the canopy. Diameter tapers linearly from ``diam_le`` at the leading edge to ``diam_te`` at the trailing edge. + The strut starts at the aft edge of the leading-edge tube (chord fraction + ``le_tube_diameter / chord`` -- the LE tube spans that far back from the drawn + LE) rather than at the leading edge, so it butts against the LE tube instead + of passing straight through it. + Args: stations (Dict[str, np.ndarray]): Output of :func:`_drawn_stations`. contour_table: Output of ``canopy_geometry.load_contour_table``. signed_fraction (float): Signed span fraction of the strut rib. diam_le (float): Strut-tube diameter at the leading edge, metres. diam_te (float): Strut-tube diameter at the trailing edge, metres. + le_tube_diameter (float): Local leading-edge tube diameter, metres; sets + where the strut starts. Zero means start at the leading edge. Returns: List[np.ndarray]: One ``(n, 3)`` ring per chordwise station. @@ -294,22 +396,27 @@ def build_strut( contour = interpolate_contour_at(abs(signed_fraction), contour_table) chord_len = station["chord_len"] + # Start the strut partway into the leading-edge tube (half its depth) so the + # forward cap is buried inside the LE tube -- no gap, no through-poke. + le_tube_depth_fraction = le_tube_diameter / max(chord_len, 1e-12) + start_fraction = min(0.5 * le_tube_depth_fraction, 0.9) + cx = contour[:, 0] + cy = contour[:, 1] + keep = cx > start_fraction + if start_fraction > 0 and keep.any(): + cx = np.concatenate([[start_fraction], cx[keep]]) + cy = np.concatenate( + [[float(np.interp(start_fraction, contour[:, 0], cy))], cy[keep]] + ) + canopy_points = ( station["le"][None, :] - + contour[:, 0:1] * chord_len * station["chord_hat"][None, :] - + contour[:, 1:2] * chord_len * station["up"][None, :] + + cx[:, None] * chord_len * station["chord_hat"][None, :] + + cy[:, None] * chord_len * station["up"][None, :] ) - chord_fraction = contour[:, 0] - diameters = diam_le + (diam_te - diam_le) * chord_fraction + diameters = diam_le + (diam_te - diam_le) * cx centers = canopy_points - 0.5 * diameters[:, None] * station["up"][None, :] - - rings = [] - for index in range(len(centers)): - lo = max(index - 1, 0) - hi = min(index + 1, len(centers) - 1) - tangent = centers[hi] - centers[lo] - rings.append(circle_points(centers[index], tangent, diameters[index])) - return rings + return rings_along_centerline(centers, diameters) def build_tube_data(panels: List[Any], processed_dir: Path) -> Dict[str, Any]: @@ -339,8 +446,16 @@ def build_tube_data(panels: List[Any], processed_dir: Path) -> Dict[str, Any]: # Fallback: size the strut from the local LE tube diameter. diam_le = le_interp(abs(signed_fraction)) diam_te = 0.6 * diam_le + le_tube_diameter = le_interp(abs(signed_fraction)) struts.append( - build_strut(stations, contour_table, signed_fraction, diam_le, diam_te) + build_strut( + stations, + contour_table, + signed_fraction, + diam_le, + diam_te, + le_tube_diameter=le_tube_diameter, + ) ) return {"le": le_rings, "struts": struts} @@ -367,17 +482,28 @@ def _add_surface(fig: go.Figure, rings: List[np.ndarray], name: str, show_legend opacity=TUBE_OPACITY, name=name, showlegend=show_legend, + lighting=dict(ambient=0.75, diffuse=0.8, specular=0.05, roughness=0.9), ) ) -def add_tube_surfaces(fig: go.Figure, tube_data: Dict[str, Any]) -> None: - """Add the leading-edge and strut tube surfaces to the figure.""" - _add_surface(fig, tube_data["le"], "Leading-edge tube", show_legend=True) +def add_strut_surfaces(fig: go.Figure, tube_data: Dict[str, Any]) -> None: + """Add the strut tube surfaces to the figure.""" for index, strut in enumerate(tube_data["struts"]): _add_surface(fig, strut, "Strut tube", show_legend=(index == 0)) +def add_le_tube_surface(fig: go.Figure, tube_data: Dict[str, Any]) -> None: + """Add the leading-edge tube surface to the figure.""" + _add_surface(fig, tube_data["le"], "Leading-edge tube", show_legend=True) + + +def add_tube_surfaces(fig: go.Figure, tube_data: Dict[str, Any]) -> None: + """Add the leading-edge and strut tube surfaces to the figure.""" + add_le_tube_surface(fig, tube_data) + add_strut_surfaces(fig, tube_data) + + def add_tube_rings(fig: go.Figure, tube_data: Dict[str, Any]) -> None: """Add the construction rings (wireframe circles) for inspection.""" all_rings = list(tube_data["le"]) + [ diff --git a/tests/plotting/test_tube_geometry.py b/tests/plotting/test_tube_geometry.py index 2bc5cff..2d43889 100644 --- a/tests/plotting/test_tube_geometry.py +++ b/tests/plotting/test_tube_geometry.py @@ -143,7 +143,8 @@ def test_build_tube_and_canopy_shapes(): tube = build_tube_data(panels, processed) n_struts = len(load_strut_span_fractions(processed)) - assert len(tube["le"]) == n_panels + 1 + # LE tube has one ring per panel edge, plus the wrap-around tip extensions. + assert len(tube["le"]) >= n_panels + 1 assert len(tube["struts"]) == n_struts x, y, z = build_canopy_grid(panels, load_contour_table(processed)) From 7f4d749e9f4f4aa4724b9e4cd21b8d3d62017a42 Mon Sep 17 00:00:00 2001 From: jellepoland Date: Fri, 24 Jul 2026 13:21:01 +0200 Subject: [PATCH 05/13] Fancy plot: panel toggle, surface-normal force grid, draw order - interactive_plot gains is_with_panels (default off in fancy mode); when off the panel outlines are hidden and drawn thin/blue when on - Replace the per-panel force arrows over the canopy with a grid of equal, surface-normal vectors: one row per panel x 10 chordwise nodes placed at the true 5%-95% chord fractions (clear of the LE tube and trailing edge) - Draw order: struts under the canopy, then canopy, then the vector grid, then the leading-edge tube last so it is never occluded - Canopy recoloured grey; matte surface lighting - Make the figure axis-range helper skip None/NaN gap separators Co-Authored-By: Claude Opus 4.8 (1M context) --- src/VSM/plot_geometry_plotly.py | 297 ++++++++++++++++++++++----- tests/plotting/test_tube_geometry.py | 15 +- 2 files changed, 254 insertions(+), 58 deletions(-) diff --git a/src/VSM/plot_geometry_plotly.py b/src/VSM/plot_geometry_plotly.py index 88cf6b7..99ba553 100644 --- a/src/VSM/plot_geometry_plotly.py +++ b/src/VSM/plot_geometry_plotly.py @@ -7,41 +7,56 @@ import yaml -def add_panel_edges(fig: go.Figure, panel: Any, is_first: bool, is_last: bool) -> None: - """Add panel edges to the figure with different thicknesses.""" - leadinge_edge_line_color = "black" - leadinge_edge_line_width = 15 - # Leading edge - if is_first: - # Include 1-side edge - fig.add_trace( - go.Scatter3d( - x=[panel.LE_point_1[0], panel.TE_point_1[0]], - y=[panel.LE_point_1[1], panel.TE_point_1[1]], - z=[panel.LE_point_1[2], panel.TE_point_1[2]], - mode="lines", - line=dict( - color=leadinge_edge_line_color, width=leadinge_edge_line_width - ), - name="Leading Edge", - showlegend=False, +def add_panel_edges( + fig: go.Figure, + panel: Any, + is_first: bool, + is_last: bool, + is_thin_outline: bool = False, +) -> None: + """Add panel edges to the figure. + + By default the leading edge is drawn as a thick black line. With + ``is_thin_outline`` (used by the fancy plot, where the inflatable leading-edge + tube already marks the leading edge) all panel edges are thin grey outlines + and the thick leading-edge line is omitted. + """ + edge_color = "blue" if is_thin_outline else "black" + if is_thin_outline: + leadinge_edge_line_color = "blue" + leadinge_edge_line_width = 1.5 + else: + leadinge_edge_line_color = "black" + leadinge_edge_line_width = 15 + # Thick tip edges only make sense for the thick leading-edge style. + if is_first: + fig.add_trace( + go.Scatter3d( + x=[panel.LE_point_1[0], panel.TE_point_1[0]], + y=[panel.LE_point_1[1], panel.TE_point_1[1]], + z=[panel.LE_point_1[2], panel.TE_point_1[2]], + mode="lines", + line=dict( + color=leadinge_edge_line_color, width=leadinge_edge_line_width + ), + name="Leading Edge", + showlegend=False, + ) ) - ) - elif is_last: - # Include 1-side edge - fig.add_trace( - go.Scatter3d( - x=[panel.LE_point_2[0], panel.TE_point_2[0]], - y=[panel.LE_point_2[1], panel.TE_point_2[1]], - z=[panel.LE_point_2[2], panel.TE_point_2[2]], - mode="lines", - line=dict( - color=leadinge_edge_line_color, width=leadinge_edge_line_width - ), - name="Leading Edge", - showlegend=False, + elif is_last: + fig.add_trace( + go.Scatter3d( + x=[panel.LE_point_2[0], panel.TE_point_2[0]], + y=[panel.LE_point_2[1], panel.TE_point_2[1]], + z=[panel.LE_point_2[2], panel.TE_point_2[2]], + mode="lines", + line=dict( + color=leadinge_edge_line_color, width=leadinge_edge_line_width + ), + name="Leading Edge", + showlegend=False, + ) ) - ) # Standard leading edge fig.add_trace( go.Scatter3d( @@ -62,7 +77,7 @@ def add_panel_edges(fig: go.Figure, panel: Any, is_first: bool, is_last: bool) - y=[panel.TE_point_1[1], panel.TE_point_2[1]], z=[panel.TE_point_1[2], panel.TE_point_2[2]], mode="lines", - line=dict(color="black", width=2), + line=dict(color=edge_color, width=2), name="Trailing Edge", showlegend=is_first, ) @@ -78,7 +93,7 @@ def add_panel_edges(fig: go.Figure, panel: Any, is_first: bool, is_last: bool) - y=[points[0][1], points[1][1]], z=[points[0][2], points[1][2]], mode="lines", - line=dict(color="black", width=0.8), + line=dict(color=edge_color, width=0.8), name=( "Side Edge" if is_first and i == 0 else None ), # Legend only for the first side edge @@ -176,6 +191,7 @@ def add_aerodynamic_vectors( force_vector: np.ndarray, scale: float = 0.5, max_force: float = 1.0, + origin: np.ndarray = None, ): """ Add aerodynamic force vectors to a given panel on the plot. @@ -185,9 +201,12 @@ def add_aerodynamic_vectors( panel: Panel object force_of_panel: Aerodynamic force vector scale: Scaling factor for the force vector + origin: Optional vector origin. Defaults to the panel aerodynamic centre; + pass a point on the canopy surface to make the vectors emanate from + the surface when the panels are hidden. """ # Compute vector endpoint - aerodynamic_center = panel.aerodynamic_center + aerodynamic_center = panel.aerodynamic_center if origin is None else origin vector = (force_vector / max_force) * scale * 0.5 vector_endpoint = aerodynamic_center + vector @@ -439,6 +458,7 @@ def create_3D_plot( tube_data: Optional[Dict[str, Any]] = None, canopy_grid: Optional[Tuple[np.ndarray, np.ndarray, np.ndarray]] = None, is_with_tube_rings: bool = False, + is_with_panels: bool = True, ) -> go.Figure: """ Creates an interactive 3D plot of wing geometry using Plotly. @@ -455,11 +475,18 @@ def create_3D_plot( ``VSM.plotly.build_canopy_grid``; when given, it replaces the flat panel surfaces. is_with_tube_rings: Also draw the tube construction rings (for inspection). + is_with_panels: Draw the panel outlines. When ``False`` and a canopy is + present, the force vectors are lifted onto the canopy surface instead + of starting at the flat panel aerodynamic centres. Returns: plotly.graph_objects.Figure """ - from VSM.plotly.tube_geometry import add_tube_surfaces, add_tube_rings + from VSM.plotly.tube_geometry import ( + add_strut_surfaces, + add_le_tube_surface, + add_tube_rings, + ) panels = wing_aero.panels @@ -474,6 +501,12 @@ def create_3D_plot( add_bridle_system(fig, wing_aero, is_first=True) draw_flat_surface = canopy_grid is None + # In the fancy plot the inflatable LE tube marks the leading edge, so draw the + # panels as thin grey outlines instead of the thick black leading-edge line. + is_thin_outline = tube_data is not None + # On the canopy the per-panel force vectors are replaced by a set of equal, + # surface-normal vectors distributed across the span (see below). + use_distributed_vectors = canopy_grid is not None # Add geometric elements for i, panel in enumerate(panels): @@ -487,25 +520,39 @@ def create_3D_plot( if is_with_aerodynamic_details: add_filaments(fig, panel, is_first) - add_panel_edges(fig, panel, is_first, is_last) - if draw_flat_surface: + if is_with_panels: + add_panel_edges( + fig, panel, is_first, is_last, is_thin_outline=is_thin_outline + ) + if draw_flat_surface and is_with_panels: add_panel_surface(fig, panel, is_first) - add_aerodynamic_vectors( - fig, - panel, - is_first, - np.array(forces_of_panels[i]), - scale=chord_average, - max_force=max_force, - ) - # Curved single-skin canopy replaces the flat panel surfaces. + if not use_distributed_vectors: + add_aerodynamic_vectors( + fig, + panel, + is_first, + np.array(forces_of_panels[i]), + scale=chord_average, + max_force=max_force, + ) + + # Draw order (so the leading-edge tube ends up rendered on top): struts sit + # under the canopy, then the canopy, then the force vectors, and finally the + # leading-edge tube last so it is never occluded by the semi-transparent canopy. + if tube_data is not None: + add_strut_surfaces(fig, tube_data) + if canopy_grid is not None: add_canopy_surface(fig, canopy_grid) - # Inflatable tubes (leading-edge tube + struts). + if use_distributed_vectors: + add_distributed_surface_vectors( + fig, canopy_grid, forces_of_panels, scale=chord_average + ) + if tube_data is not None: - add_tube_surfaces(fig, tube_data) + add_le_tube_surface(fig, tube_data) if is_with_tube_rings: add_tube_rings(fig, tube_data) @@ -516,6 +563,7 @@ def add_canopy_surface( fig: go.Figure, canopy_grid: Tuple[np.ndarray, np.ndarray, np.ndarray] ) -> None: """Add the curved single-skin canopy as a uniformly coloured surface.""" + canopy_color = "lightgrey" x, y, z = canopy_grid fig.add_trace( go.Surface( @@ -523,11 +571,138 @@ def add_canopy_surface( y=y, z=z, surfacecolor=np.zeros_like(z), - colorscale=[[0, "lightgrey"], [1, "lightgrey"]], + colorscale=[[0, canopy_color], [1, canopy_color]], showscale=False, opacity=0.85, name="Canopy", showlegend=True, + lighting=dict(ambient=0.75, diffuse=0.8, specular=0.05, roughness=0.9), + ) + ) + + +def add_distributed_surface_vectors( + fig: go.Figure, + canopy_grid: Tuple[np.ndarray, np.ndarray, np.ndarray], + forces_of_panels: List[np.ndarray], + scale: float, + n_chord: int = 10, + n_span: int = None, +) -> None: + """Draw a grid of equal, surface-normal force vectors on the canopy. + + One row of ``n_chord`` nodes is placed at each panel centre (uniformly over + the chord); every node carries one equal-length arrow oriented along the local + canopy surface normal (the outward/suction side). This gives a regular + ``n_span x n_chord`` grid of vectors covering the whole canopy. + + Args: + fig: Plotly figure. + canopy_grid: ``(X, Y, Z)`` canopy surface arrays, each ``(S, P)`` with + ``S`` spanwise stations and ``P`` chordwise points. + forces_of_panels: Per-panel aerodynamic force vectors (used only to skip + drawing when there is no force). + scale: Length scale (typically the average chord). + n_chord: Nodes per row, distributed over the chord. Defaults to 10. + n_span: Number of spanwise rows. Defaults to one row per panel (the + midpoints between the ``S`` canopy stations, i.e. ``S - 1`` rows). + """ + x, y, z = canopy_grid + grid = np.stack([x, y, z], axis=-1) # (S, P, 3) + + force_magnitudes = np.linalg.norm(np.asarray(forces_of_panels), axis=1) + if force_magnitudes.size == 0 or np.max(force_magnitudes) <= 0: + return + + # One row per panel: use the midpoints between adjacent canopy stations. + panel_grid = 0.5 * (grid[:-1] + grid[1:]) # (S - 1, P, 3) + n_rows, n_points, _ = panel_grid.shape + + if n_span is None: + n_span = n_rows + span_rows = np.unique( + np.clip(np.round(np.linspace(0, n_rows - 1, n_span)), 0, n_rows - 1).astype(int) + ) + + # Surface-normal field over the panel-centre grid. + d_chord = np.gradient(panel_grid, axis=1) + d_span = np.gradient(panel_grid, axis=0) + normal_field = np.cross(d_chord, d_span) + normal_field /= np.linalg.norm(normal_field, axis=-1, keepdims=True).clip(1e-12) + normal_field[normal_field[:, :, 2] < 0] *= -1 # outward (suction) side + + # Sample each row at the exact target chord fractions (5%-95%). The grid + # columns are arc-length spaced, so interpolate by true chord fraction rather + # than snapping to columns -- this keeps vectors clear of the leading-edge + # tube (near 0%) and the trailing edge (near 100%). + targets = np.linspace(0.05, 0.95, n_chord) + origins = [] + normals = [] + for row in span_rows: + pts = panel_grid[row] # (P, 3) + chord_vector = pts[-1] - pts[0] + chord_length = np.linalg.norm(chord_vector) + chord_hat = chord_vector / max(chord_length, 1e-12) + chord_fraction = ((pts - pts[0]) @ chord_hat) / max(chord_length, 1e-12) + order = np.argsort(chord_fraction) + frac_sorted = chord_fraction[order] + pts_sorted = pts[order] + nrm_sorted = normal_field[row][order] + for target in targets: + point = np.array( + [np.interp(target, frac_sorted, pts_sorted[:, k]) for k in range(3)] + ) + normal = np.array( + [np.interp(target, frac_sorted, nrm_sorted[:, k]) for k in range(3)] + ) + norm = np.linalg.norm(normal) + if norm < 1e-9: + continue + origins.append(point) + normals.append(normal / norm) + + if not origins: + return + origins = np.array(origins) + normals = np.array(normals) + + # Equal arrow length, scaled to the chordwise node spacing so the grid reads + # cleanly without the arrows overlapping badly. + length = 0.9 * scale / max(n_chord, 1) + endpoints = origins + normals * length + + # All stems as one trace (None-separated segments), all heads as one cone trace. + stem_x, stem_y, stem_z = [], [], [] + for start, end in zip(origins, endpoints): + stem_x += [start[0], end[0], None] + stem_y += [start[1], end[1], None] + stem_z += [start[2], end[2], None] + fig.add_trace( + go.Scatter3d( + x=stem_x, + y=stem_y, + z=stem_z, + mode="lines", + line=dict(color="red", width=3), + name="Aerodynamic Vectors", + showlegend=True, + ) + ) + fig.add_trace( + go.Cone( + x=endpoints[:, 0], + y=endpoints[:, 1], + z=endpoints[:, 2], + u=normals[:, 0] * length, + v=normals[:, 1] * length, + w=normals[:, 2] * length, + sizemode="absolute", + sizeref=length / 3, + anchor="tip", + colorscale=[[0, "red"], [1, "red"]], + showscale=False, + name="Arrowheads", + showlegend=False, ) ) @@ -553,14 +728,18 @@ def compute_axis_parameters_from_fig( fig: go.Figure, ) -> Tuple[Dict[str, float], float]: """Calculate axis ranges and tick spacing based on the figure's data.""" - # Extract all data points from the figure traces + # Extract all data points from the figure traces, skipping gap separators + # (``None``/NaN) that traces may use to draw multiple segments at once. all_points = [] for trace in fig.data: if isinstance(trace, go.Scatter3d): - all_points.extend(zip(trace.x, trace.y, trace.z)) + for px, py, pz in zip(trace.x, trace.y, trace.z): + if px is None or py is None or pz is None: + continue + all_points.append((px, py, pz)) # Convert to numpy array for calculations - all_points = np.array(all_points) + all_points = np.array(all_points, dtype=float) # Calculate ranges for each axis ranges = { @@ -803,6 +982,7 @@ def update_plot( tube_data: Optional[Dict[str, Any]] = None, canopy_grid: Optional[Tuple[np.ndarray, np.ndarray, np.ndarray]] = None, is_with_tube_rings: bool = False, + is_with_panels: bool = True, ): # Update AoA and rerun VSM results = running_VSM( @@ -825,6 +1005,7 @@ def update_plot( tube_data=tube_data, canopy_grid=canopy_grid, is_with_tube_rings=is_with_tube_rings, + is_with_panels=is_with_panels, ) fig = add_case_information( fig, @@ -859,6 +1040,7 @@ def interactive_plot( surfplan_dir: Optional[Path] = None, is_with_canopy: bool = True, is_with_tube_rings: bool = False, + is_with_panels: Optional[bool] = None, ): """ Creates and optionally saves multiple views of the wing geometry with interactive AoA slider. @@ -887,10 +1069,16 @@ def interactive_plot( is_with_canopy: When a Surfplan export is given, draw the curved canopy (lofted airfoil top-surfaces) in place of the flat panel surfaces. is_with_tube_rings: Also draw the tube construction rings (for inspection). + is_with_panels: Draw the panel outlines. Defaults to ``False`` for the + fancy plot (a Surfplan export is given) and ``True`` otherwise. When + off, the force vectors are lifted onto the canopy surface. Returns: plotly.graph_objects.Figure: The created figure. """ + # Default: hide the panel outlines for the fancy plot, show them otherwise. + if is_with_panels is None: + is_with_panels = surfplan_dir is None # Build the optional fancy-plot geometry once (reused on every slider update). tube_data = None @@ -932,6 +1120,7 @@ def interactive_plot( tube_data=tube_data, canopy_grid=canopy_grid, is_with_tube_rings=is_with_tube_rings, + is_with_panels=is_with_panels, ) # Save or show the plot if requested diff --git a/tests/plotting/test_tube_geometry.py b/tests/plotting/test_tube_geometry.py index 2d43889..26c4236 100644 --- a/tests/plotting/test_tube_geometry.py +++ b/tests/plotting/test_tube_geometry.py @@ -172,18 +172,24 @@ def test_interactive_plot_fancy_traces(): ) n_struts = len(load_strut_span_fractions(ensure_surfplan_processed(SURFPLAN_DIR))) - # Fancy plot with canopy: surfaces = canopy + LE tube + struts, no flat panels. + # Fancy plot with canopy (panels default off): surfaces = canopy + LE + struts. fig = interactive_plot(body, surfplan_dir=SURFPLAN_DIR, is_show=False) surfaces = [t for t in fig.data if isinstance(t, go.Surface)] flat_panels = [ t for t in fig.data if isinstance(t, go.Mesh3d) and t.name == "Panel Surface" ] + le_outline = [t for t in fig.data if getattr(t, "name", None) == "Leading Edge"] assert len(surfaces) == 1 + 1 + n_struts assert len(flat_panels) == 0 + assert len(le_outline) == 0 # panel outlines hidden by default in fancy mode - # Tubes only: flat panels kept, no canopy surface. + # Tubes only with panels explicitly on: flat panels kept, no canopy surface. fig2 = interactive_plot( - body, surfplan_dir=SURFPLAN_DIR, is_with_canopy=False, is_show=False + body, + surfplan_dir=SURFPLAN_DIR, + is_with_canopy=False, + is_with_panels=True, + is_show=False, ) surfaces2 = [t for t in fig2.data if isinstance(t, go.Surface)] flat_panels2 = [ @@ -192,6 +198,7 @@ def test_interactive_plot_fancy_traces(): assert len(surfaces2) == 1 + n_struts assert len(flat_panels2) == len(body.panels) - # Plain plot is unchanged: no surfaces at all. + # Plain plot is unchanged: panels shown, no surfaces at all. fig3 = interactive_plot(body, is_show=False) assert not [t for t in fig3.data if isinstance(t, go.Surface)] + assert [t for t in fig3.data if getattr(t, "name", None) == "Leading Edge"] From 282cbbc90fc88faa177e68f20431d8d0eb55e846 Mon Sep 17 00:00:00 2001 From: jellepoland Date: Fri, 24 Jul 2026 14:39:03 +0200 Subject: [PATCH 06/13] Fancy plot: Cp-distributed chord-normal force vectors from AoA database Replace the equal surface-normal vector grid with a physically-scaled loading: - New VSM.plotly.cp_distributions: treat data/.../cpx_distributions as a Cp database keyed by AoA; select the closest available angle to the operating point and extract the upper (suction) surface |Cp|(x/c) - Each panel's VSM force is distributed chordwise, weighted by |Cp| (even split without a Cp dir); the node forces of a panel sum back to that panel's force - Vectors point normal to the local chord line (flat-panel up), constant per section, at the true 5%-95% chord fractions - Normalise force->length so the largest panel force plots at the maximum chord length; modest fixed-size arrowheads - interactive_plot gains cp_distributions_dir; add the V3 Cp/Cf data files and Cp-database tests Co-Authored-By: Claude Opus 4.8 (1M context) --- .../cpx_distributions/README.md | 34 +++++ .../cpx_distributions/cf_AOA_2.dat | 78 +++++++++++ .../cpx_distributions/cf_AOA_6.dat | 103 +++++++++++++++ .../cpx_distributions/cf_AOA_8.dat | 104 +++++++++++++++ .../cpx_distributions/cp_AOA_2.dat | 45 +++++++ .../cpx_distributions/cp_AOA_6.dat | 55 ++++++++ .../cpx_distributions/cp_AOA_8.dat | 59 +++++++++ src/VSM/plot_geometry_plotly.py | 124 +++++++++++++----- src/VSM/plotly/cp_distributions.py | 97 ++++++++++++++ tests/plotting/test_tube_geometry.py | 57 ++++++++ 10 files changed, 724 insertions(+), 32 deletions(-) create mode 100644 data/TUDELFT_V3_KITE/cpx_distributions/README.md create mode 100644 data/TUDELFT_V3_KITE/cpx_distributions/cf_AOA_2.dat create mode 100755 data/TUDELFT_V3_KITE/cpx_distributions/cf_AOA_6.dat create mode 100644 data/TUDELFT_V3_KITE/cpx_distributions/cf_AOA_8.dat create mode 100644 data/TUDELFT_V3_KITE/cpx_distributions/cp_AOA_2.dat create mode 100755 data/TUDELFT_V3_KITE/cpx_distributions/cp_AOA_6.dat create mode 100644 data/TUDELFT_V3_KITE/cpx_distributions/cp_AOA_8.dat create mode 100644 src/VSM/plotly/cp_distributions.py diff --git a/data/TUDELFT_V3_KITE/cpx_distributions/README.md b/data/TUDELFT_V3_KITE/cpx_distributions/README.md new file mode 100644 index 0000000..d328489 --- /dev/null +++ b/data/TUDELFT_V3_KITE/cpx_distributions/README.md @@ -0,0 +1,34 @@ +# TU Delft V3 kite — 2D Cp(x/c) distributions + +Chordwise pressure-coefficient (`cp_AOA_*.dat`) and skin-friction (`cf_AOA_*.dat`) +distributions for the **TU Delft V3 LEI kite midspan airfoil section** (rigid 2D), +from RANS CFD (Thijs/Kasper). + +File format: whitespace columns `# x y Cp` (or `... Cf`), i.e. column 0 = x/c, +column 1 = y/c, column 2 = Cp (or Cf). + +## Contents + +| Angle of attack | Cp file | Cf file | +|---|---|---| +| 2° | `cp_AOA_2.dat` | `cf_AOA_2.dat` | +| 6° | `cp_AOA_6.dat` | `cf_AOA_6.dat` | +| 8° | `cp_AOA_8.dat` | `cf_AOA_8.dat` | + +These are the only angles for which the genuine V3 midspan section Cp distribution +is available locally. + +## Provenance + +Copied from `WES_aero_sim_for_kite_design/data/`: +- AoA 2° and 8°: `cp_cf_kasper/` +- AoA 6°: `THIJS_CFD_alpha_6_V3_turbulent/postProcessing/surfaces/624/` + +The V3 section is identified by its airfoil geometry embedded in the files: +~13.4% thickness, max camber y ≈ 0.0953 (chord normalised to 1). + +## Not included + +`Pointwise-Openfoam-toolchain/.../cp_AOA_10.dat` was **excluded**: it is a generic +demo LEI profile from the Pointwise→OpenFOAM toolchain (~12.4% thickness, +`tube_size=0.08, c_x=0.15, c_y=0.08`), **not** the V3 section. diff --git a/data/TUDELFT_V3_KITE/cpx_distributions/cf_AOA_2.dat b/data/TUDELFT_V3_KITE/cpx_distributions/cf_AOA_2.dat new file mode 100644 index 0000000..2d874b9 --- /dev/null +++ b/data/TUDELFT_V3_KITE/cpx_distributions/cf_AOA_2.dat @@ -0,0 +1,78 @@ +# x y Cf +0.04276388755929555 0.06356556861711357 -5.3685492635529e-05 +0.03309840970977389 0.04825624850833229 -3.582704828999522e-08 +0.06326602268098154 0.03073779662338421 6.167786129151704e-05 +0.07384327410599092 0.01793640188203612 -0.0003192609764164146 +0.07660775307689469 0.01104437306879618 -0.000228764133061211 +0.07819996384396966 4.127780662861551e-05 -2.991057168005374e-06 +0.07202763627549755 -0.02108505542368243 -0.0004650481933519364 +0.06030740026776515 -0.03284898406007216 -0.000575092613061699 +0.05197415418742954 -0.03691972333053082 -0.0009533577132157866 +0.04841011551193194 -0.03797539582415126 -0.0012756171634708763 +0.04106259222822374 -0.03905070100038476 -0.002640959490398662 +0.0392041678823387 -0.03909985971367359 -0.002617383818930308 +0.03734550757143083 -0.03906061142774211 -0.0019512487320989223 +0.03549081466558211 -0.03893305307143092 -0.0004661649781977114 +0.02999235773064849 -0.03802447036198644 0.005671899478682422 +0.02819522910180473 -0.03754856560901278 0.007255265503267772 +0.02642274895181671 -0.03698778874565389 0.008437998523398466 +0.02467892578294952 -0.03634339841365394 0.009272998170146079 +0.02296771065963648 -0.03561683055468438 0.009818887434565653 +0.0212929661235993 -0.034809744871696 0.010121505031131855 +0.01965847119772771 -0.03392397792845034 0.010215736624071617 +0.01652493718577972 -0.03192453294918682 0.00988329670009842 +0.01503298224681096 -0.03081537691891334 0.009500925488249457 +0.01221552866592811 -0.02839075860041346 0.008406802228970986 +0.009641067793166569 -0.0257095339280482 0.00700174990074151 +0.0073328241508335 -0.02279597455813496 0.005439446132599986 +0.0004212849451338721 -0.0057241591407998 -9.821423814390917e-05 +0.0001929063854338941 -0.00387916574954884 -0.0001677770129790615 +3.715759961701779e-07 -0.000167057565479412 8.866677575636765e-06 +0.0003737770984357588 0.005393438845063324 0.0009648191807615032 +0.00106093352703043 0.009046469258067052 0.001906286770070402 +0.002091866108723892 0.01261774277269358 0.002862841031091262 +0.002733467311930074 0.01436259362075018 0.003226329655069884 +0.003457264975454637 0.01607498180921493 0.003373864267305056 +0.005118832334056953 0.01943119745211363 0.003244479859277398 +0.01551627563955031 0.03736815990984928 0.00640642847178748 +0.01999837151218267 0.04343539867217488 0.00746451057835034 +0.02363912034760315 0.04776628085227682 0.008134354582056486 +0.03738856887789012 0.0606405766812684 0.009765309341371863 +0.04832587972692123 0.06818926940941658 0.0116451778915289 +0.05399675986092924 0.07144915477192883 0.012011259579708933 +0.06072447947721454 0.07485584093822345 0.012147363864742813 +0.07472263851285585 0.08065345333266476 0.011970806903965334 +0.08887221045490147 0.08513218823611196 0.011442401741641892 +0.1793062895712234 0.09530077013388079 0.006856086486232446 +0.2117339898587441 0.09495067176705572 0.006628380949299338 +0.2581032333418144 0.09215324744689607 0.005192207194632046 +0.2996809993542806 0.08811305015450889 0.004457266541411632 +0.3567078053717327 0.08168243973732783 0.00394706807980882 +0.4309247818702319 0.07270373419804112 0.003581264429982738 +0.859748396615083 0.01806363700433209 0.002415898765630274 +0.990135014604125 0.00127095875812223 0.0018193027298150943 +0.9980660976826226 0.000249320580680477 0.00202920416730058 +0.9995004786446735 6.447374315074194e-05 0.002407312125848668 +0.9998066004065816 2.499749945161417e-05 0.002663732027943266 +1.0 0.0 0.003108088175078052 +1.000175570344442 -0.0003873748714666402 -0.0005394350261954327 +0.9999361185511577 -0.0004959023699215505 0.007227548955785272 +0.9997426931776413 -0.0004710692940610273 0.006070740947186466 +0.9992898615857166 -0.0004127908100313096 0.00523084111183753 +0.9984027748023451 -0.0002985631337174143 0.004596763351448572 +0.9969850747884935 -0.0001159634273836383 0.004125445965553146 +0.9925102286323966 0.0004604996516486448 0.003608634550155278 +0.9828366693570219 0.001706780446830773 0.003290951256256768 +0.8007931164398834 0.02514688172946307 0.0015438641554223705 +0.6227248105276376 0.04799391426786268 -0.000934765929254101 +0.5467873640304859 0.05766507732352074 -0.0017082583043725305 +0.4557607131227709 0.06912084908337753 -0.002057381475396368 +0.3439219432772413 0.08267370547347265 -0.001909630199411214 +0.2878908640215147 0.0888395395812468 -0.0016403223905841638 +0.2472404119045495 0.0925291412707685 -0.0012439644700339358 +0.2284148315498924 0.09375889647752622 -0.0008998106920897766 +0.1938680582880188 0.09475868486167134 6.862163771827565e-05 +0.1565453765113058 0.0943433883278578 3.083217355968188e-05 +0.1049056581150844 0.0884277941269339 0.0003608467874276816 +0.0596424493908252 0.07383844242791567 -7.284237499479304e-05 +0.04571639419151958 0.06586873418008751 -7.147357452011414e-05 diff --git a/data/TUDELFT_V3_KITE/cpx_distributions/cf_AOA_6.dat b/data/TUDELFT_V3_KITE/cpx_distributions/cf_AOA_6.dat new file mode 100755 index 0000000..a415d17 --- /dev/null +++ b/data/TUDELFT_V3_KITE/cpx_distributions/cf_AOA_6.dat @@ -0,0 +1,103 @@ +# x y Cf +0.04276388755929555 0.06356556861711357 -0.0001879585975700373 +0.03309840970977389 0.04825624850833229 -5.458507980308168e-07 +0.04467938624112796 0.03869402682902678 0.0002106766326490412 +0.06326602268098154 0.03073779662338421 0.00010683943074351959 +0.06739054964049444 0.0269898872199461 -5.94499259365639e-05 +0.07295141860002202 0.01956758285390561 -0.000747380127932433 +0.07465658066274587 0.01626466884529217 -0.000804869041349509 +0.07660775307689469 0.01104437306879618 -0.0005439448493615928 +0.07819996384396966 4.127780662861551e-05 -7.537638609578638e-06 +0.07606735331573551 -0.0127367285437733 -0.0003803835335502698 +0.0698766379503123 -0.02411656177727725 5.957956843802754e-05 +0.06744746907549139 -0.0269300909630248 -6.499428398172114e-05 +0.0647621273625294 -0.02950025730032107 -0.0005852397077900648 +0.0618448413245353 -0.03180378944934589 -0.0014060038402179437 +0.06030740026776515 -0.03284898406007216 -0.001682074499723797 +0.05872201224623478 -0.03381991329602371 -0.0016918762225057683 +0.05709225787431717 -0.03471437303988962 -0.0013994803680989652 +0.05371451517243328 -0.03626603972395559 -0.000359979333924828 +0.05197415418742954 -0.03691972333053082 -0.00011495450593594156 +0.05020468513322063 -0.0374899309873329 -0.000475260545252736 +0.04841011551193194 -0.03797539582415126 -0.0011372551904624323 +0.04659450074871872 -0.03837502119181638 -0.0013395688525546072 +0.04476194045990555 -0.03868788115484954 -0.0008744136341935016 +0.04291657957939413 -0.03891327036966102 9.44628992832468e-05 +0.03734550757143083 -0.03906061142774211 0.004816902116601814 +0.03549081466558211 -0.03893305307143092 0.00608226725442228 +0.03364428007162458 -0.03871749253601962 0.007029206136024282 +0.03181007738878976 -0.03841441105310851 0.007703113708221444 +0.02819522910180473 -0.03754856560901278 0.008392342601560906 +0.02642274895181671 -0.03698778874565389 0.008463133797438408 +0.02296771065963648 -0.03561683055468438 0.008153228574274977 +0.0212929661235993 -0.034809744871696 0.007803711062963378 +0.01806792542873962 -0.03296152056688918 0.006792102588333668 +0.0135954264885147 -0.02963656804659672 0.004726720235922244 +0.004414604365619548 -0.01804807451598938 -0.0008341513405588942 +0.002201042597788599 -0.01293351709306329 -0.0017822205566014335 +0.001139618108146739 -0.009371187244292476 -0.0018498189272529702 +0.0007370940576948314 -0.007556212895409316 -0.0017046704323901173 +0.0001929063854338941 -0.00387916574954884 -0.0010666383323894113 +3.662379381053231e-05 0.001691663021906976 0.0006406888791772092 +0.0003737770984357588 0.005393438845063324 0.00210616838314834 +0.00106093352703043 0.009046469258067052 0.003601831254573682 +0.002091866108723892 0.01261774277269358 0.004786120411857886 +0.002733467311930074 0.01436259362075018 0.00505463342148768 +0.003457264975454637 0.01607498180921493 0.00486538638803568 +0.004261644904234816 0.01775102853981628 0.00428531391161938 +0.005118832334056953 0.01943119745211363 0.004020012931537072 +0.007736073021854499 0.02444803701163283 0.005587124190501672 +0.01144677152350567 0.0310164207846188 0.007294117885428082 +0.02118547842718027 0.04490114709827182 0.011234697506728442 +0.02619677866261684 0.05053895293504266 0.012975279747399983 +0.03022079287608805 0.05451623977328293 0.01396098034110171 +0.03591200766144063 0.05946694354457675 0.01489134876295948 +0.04193582911527939 0.06400735561950383 0.01544564631552075 +0.04832587972692123 0.06818926940941658 0.01567609512254134 +0.05835478956095485 0.07370803105654668 0.015522115657526089 +0.07161579828052492 0.07949795292659076 0.014806722607193528 +0.1266732729257711 0.09241218332941833 0.010715983170303985 +0.1793062895712234 0.09530077013388079 0.007118694040138372 +0.2032346417643243 0.09514644933414897 0.00694971658395249 +0.2117339898587441 0.09495067176705572 0.006746221951628926 +0.2483205990229831 0.09294692974194078 0.005363551999315196 +0.2784114042826095 0.09028704743105777 0.00457644175844203 +0.3448969536063716 0.08306230186370865 0.003728681582367628 +0.4695323861056724 0.06790513097604357 0.003075773887487842 +0.9829114078197247 0.002201372895802811 0.0013510704629247409 +0.9925774208648203 0.000956364758547776 0.0014009684429290416 +0.9972419199369482 0.0003555045445397644 0.0016173392531597238 +0.998466960240747 0.0001976703981867997 0.0018179918899748826 +0.9993538313714398 8.3377965966741e-05 0.002157499247928278 +0.9998066004065816 2.499749945161417e-05 0.002622578724939286 +1.0 0.0 0.003238787035356226 +1.000175570344442 -0.0003873748714666402 -0.0006918343168879954 +0.9999361185511577 -0.0004959023699215505 0.00839085568823664 +0.999687684844556 -0.0004639930247993733 0.007152364057113668 +0.999127051009862 -0.000391829623832507 0.006354581004969732 +0.9978535823824828 -0.0002278312280225523 0.005627694100734124 +0.9961081405376435 -3.002163743061166e-06 0.005163856287339768 +0.9900661326324484 0.0007753784135630709 0.004588401666849688 +0.9775963350348024 0.002381866402965103 0.004222134720252326 +0.9160973581448664 0.01030344679663074 0.003731346418568748 +0.6931632727720849 0.03897495614094905 0.00279072966272386 +0.5978723947463842 0.05116649706761253 0.001947719684416495 +0.5208166631398357 0.06095334182358406 0.0006613558809871008 +0.3922624879189146 0.07693223386512749 -0.002536736281764828 +0.3322904223011696 0.08401082699279248 -0.003535543029570178 +0.2878908640215147 0.0888395395812468 -0.003634970950225882 +0.2570330318015585 0.09174362234289557 -0.003128121342685052 +0.237700137204438 0.09320459962989104 -0.002248277989597068 +0.2284148315498924 0.09375889647752622 -0.001515708017509105 +0.2106187027457731 0.09448229827809655 0.0002175146509524326 +0.2021120932873841 0.09466535260951009 0.0005338654385894822 +0.1781651575110249 0.09480029283794308 0.0003016745162148726 +0.1634966219696773 0.09459572113384887 -0.0001420950660125579 +0.1498463100440565 0.09399700027730548 -0.00026465443382224 +0.1433965052540251 0.09356118961842605 -0.00013273574782980165 +0.1200181280886662 0.09102301972975237 0.0012194013974886953 +0.1097221213345114 0.08934836918478271 0.0014867965630896767 +0.1003032010680039 0.08745876557389623 0.0015071913473745859 +0.08772421797878553 0.08431213223263931 0.0012033990065049374 +0.0596424493908252 0.07383844242791567 -0.0001087650154666008 +0.04571639419151958 0.06586873418008751 -0.000230212940186696 diff --git a/data/TUDELFT_V3_KITE/cpx_distributions/cf_AOA_8.dat b/data/TUDELFT_V3_KITE/cpx_distributions/cf_AOA_8.dat new file mode 100644 index 0000000..8b35b73 --- /dev/null +++ b/data/TUDELFT_V3_KITE/cpx_distributions/cf_AOA_8.dat @@ -0,0 +1,104 @@ +# x y Cf +0.04276388755929555 0.06356556861711357 -7.490305597749076e-05 +0.0332272197474035 0.0464048575148447 5.489757479981662e-06 +0.04832943775882318 0.03799302181036906 7.648519645172773e-05 +0.06326602268098154 0.03073779662338421 -0.0004285934373756168 +0.06739054964049444 0.0269898872199461 -0.00010477936600927875 +0.07465658066274587 0.01626466884529217 0.0008238438806884038 +0.07660775307689469 0.01104437306879618 0.0007641332622482074 +0.07779692838209128 0.005599704515728495 0.0003944887276465724 +0.07815773161604739 -0.001817316543405341 5.485397046001178e-06 +0.07299266376766034 -0.01949606863865545 0.0003637595759240324 +0.06869550509015264 -0.02555220766066108 -0.0002755839837928688 +0.0647621273625294 -0.02950025730032107 -0.000300275706798375 +0.06030740026776515 -0.03284898406007216 -0.001077784609359678 +0.05872201224623478 -0.03381991329602371 -0.0011923736195542543 +0.05709225787431717 -0.03471437303988962 -0.0009765999835591377 +0.05371451517243328 -0.03626603972395559 -0.00017016364552131216 +0.05197415418742954 -0.03691972333053082 -0.000316934051102485 +0.05020468513322063 -0.0374899309873329 -0.0007186906753935714 +0.04841011551193194 -0.03797539582415126 -0.0008223291733746979 +0.04659450074871872 -0.03837502119181638 -0.0003321541027406026 +0.04476194045990555 -0.03868788115484954 0.000708909299360625 +0.04106259222822374 -0.03905070100038476 0.003411477405885782 +0.0392041678823387 -0.03909985971367359 0.004562676628395266 +0.03734550757143083 -0.03906061142774211 0.005450530699843342 +0.03549081466558211 -0.03893305307143092 0.006097005211431964 +0.03181007738878976 -0.03841441105310851 0.006796980228486176 +0.02999235773064849 -0.03802447036198644 0.00689567806772039 +0.02642274895181671 -0.03698778874565389 0.006668905710799834 +0.02467892578294952 -0.03634339841365394 0.006369310395537206 +0.0212929661235993 -0.034809744871696 0.005459364209591542 +0.01806792542873962 -0.03296152056688918 0.00422525671759978 +0.01503298224681096 -0.03081537691891334 0.002783959272415512 +0.006285174856966845 -0.02126020489420191 -0.002040075274130408 +0.004414604365619548 -0.01804807451598938 -0.002852651469250982 +0.002857522257900208 -0.01467282465727422 -0.003236282554861442 +0.001627968848605174 -0.01116497425581786 -0.003138053712873696 +0.001139618108146739 -0.009371187244292476 -0.002901560687168934 +0.0004212849451338721 -0.0057241591407998 -0.0020642851403826 +0.0001929063854338941 -0.00387916574954884 -0.001476146818952331 +3.715759961701779e-07 -0.000167057565479412 -1.644386489545432e-05 +0.0003737770984357588 0.005393438845063324 0.002603987753563134 +0.00106093352703043 0.009046469258067052 0.004308258634942198 +0.002091866108723892 0.01261774277269358 0.005527573864036678 +0.002733467311930074 0.01436259362075018 0.005706970896045456 +0.003457264975454637 0.01607498180921493 0.005309936058614546 +0.004261644904234816 0.01775102853981628 0.004473939386915608 +0.005118832334056953 0.01943119745211363 0.004158086084503602 +0.008635223554445109 0.02610611957064 0.007385511866532802 +0.01242751415010598 0.03262757549417115 0.00978259046760362 +0.01770420762625004 0.04044102676627263 0.012222338373033381 +0.02363912034760315 0.04776628085227682 0.0143235871004763 +0.02885514542407597 0.05321522293646556 0.015645352546840394 +0.03445624798895486 0.05826760669966152 0.016566055170825496 +0.04193582911527939 0.06400735561950383 0.017172923889848403 +0.04832587972692123 0.06818926940941658 0.017252283211256866 +0.05611419408811015 0.07257157145324583 0.01696439047089495 +0.06587533521034071 0.07717347429902713 0.016298119454783315 +0.1266732729257711 0.09241218332941833 0.010882410759841941 +0.1793062895712234 0.09530077013388079 0.006945473027203948 +0.2032346417643243 0.09514644933414897 0.006722973839658798 +0.2117339898587441 0.09495067176705572 0.006496660844552578 +0.2581032333418144 0.09215324744689607 0.004671284441683954 +0.2889286523063009 0.08923439218084046 0.003906579283388574 +0.3567078053717327 0.08168243973732783 0.002987260139238476 +0.4565948443542798 0.06951968856030251 0.002289062476511266 +0.9694031190419137 0.003941412459140006 0.0009043310373034418 +0.9889687389773781 0.001421178244630541 0.0009258776436256234 +0.9964097252760367 0.0004627131567309691 0.0011074669809813652 +0.99820673288432 0.0002312005004618359 0.0013175655760859263 +0.9991910520319729 0.0001043590734837622 0.0016309343514974571 +0.999751597840673 3.209277336096525e-05 0.002140587170106048 +1.0 0.0 0.00296276751106508 +1.000175570344442 -0.0003873748714666402 -0.0007331902544534668 +0.9999361185511577 -0.0004959023699215505 0.008659718621007185 +0.9996297267405945 -0.000456535816824477 0.007355613923448616 +0.999039047920008 -0.0003804987101077882 0.00666339865975267 +0.9976974643155097 -0.0002077232855182258 0.00597884279573205 +0.9958589617720149 2.909655168354372e-05 0.005537713550330318 +0.9894974877848076 0.0008486392223196442 0.00499683301592507 +0.9763814569182623 0.002538361569031684 0.004630244339792308 +0.9076252478663309 0.01139458185905065 0.00387386270301527 +0.6469580977353171 0.04489502959157916 0.00241912394509005 +0.5338234109222841 0.05930804868788935 0.0013982687254335613 +0.468715778144382 0.06750438704698408 0.0003543146735410334 +0.4047377992866207 0.0754159191024797 -0.0009780189748020292 +0.3677534071070601 0.07987434616211128 -0.002030172351450874 +0.2986548580999721 0.08772078502814058 -0.004599280878719982 +0.2773623986528947 0.08988862416845661 -0.005113680899771088 +0.2670749342797846 0.09085981280457657 -0.005208437747751682 +0.2472404119045495 0.0925291412707685 -0.004988841818438576 +0.237700137204438 0.09320459962989104 -0.004674488674257146 +0.2193869169365694 0.09418446720371103 -0.003633045552383154 +0.2106187027457731 0.09448229827809655 -0.002856229566245596 +0.2021120932873841 0.09466535260951009 -0.0018243984739640291 +0.18588626433171 0.09479362983589781 0.0004778349305169992 +0.1781651575110249 0.09480029283794308 0.0008614761575344938 +0.1565453765113058 0.0943433883278578 0.000433684342034735 +0.1371926332586585 0.09304091952856827 0.0007608862155223732 +0.1049056581150844 0.0884277941269339 0.0005783583684360126 +0.08772421797878553 0.08431213223263931 0.0010117873864341426 +0.07686484046880157 0.08091124513785357 0.001067302452619815 +0.06477532213063705 0.07619793801987683 0.0008009974026476422 +0.04571639419151958 0.06586873418008751 3.101420874588672e-06 diff --git a/data/TUDELFT_V3_KITE/cpx_distributions/cp_AOA_2.dat b/data/TUDELFT_V3_KITE/cpx_distributions/cp_AOA_2.dat new file mode 100644 index 0000000..547e619 --- /dev/null +++ b/data/TUDELFT_V3_KITE/cpx_distributions/cp_AOA_2.dat @@ -0,0 +1,45 @@ +# x y Cp +0.04276388755929555 0.06356556861711357 -0.04348704157614704 +0.07295141860002202 0.01956758285390561 -0.05140991436041962 +0.07819996384396966 4.127780662861551e-05 -0.03197150713685026 +0.05197415418742954 -0.03691972333053082 -0.06706167387722287 +0.04476194045990555 -0.03868788115484954 -0.093378926915626 +0.02642274895181671 -0.03698778874565389 -0.3570219353414122 +3.715759961701779e-07 -0.000167057565479412 0.9276509910347256 +0.003457264975454637 0.01607498180921493 0.4005836069123416 +0.03445624798895486 0.05826760669966152 -1.2301611547758755 +0.05399675986092924 0.07144915477192883 -1.6426557894762874 +0.06587533521034071 0.07717347429902713 -1.7332221980026814 +0.07799552636294979 0.08179901473482278 -1.754837005226387 +0.09287072676787013 0.0861933468529139 -1.7249851475023648 +0.1159231761269345 0.09090247468259746 -1.6181096392589778 +0.1870216529466714 0.09529101859089956 -1.1515796372398734 +0.2204942894351738 0.09463860823428381 -1.0426813364294585 +0.2996809993542806 0.08811305015450889 -0.7066174109381166 +0.3332789074473506 0.08439957244416332 -0.6088681071632966 +0.3687000900536647 0.08026370487218884 -0.5287719422111052 +0.4056386418146471 0.07580832798897223 -0.4621736253348554 +0.4565948443542798 0.06951968856030251 -0.3890242262205662 +0.5603789570099812 0.0564429374001441 -0.2790317212774662 +0.6709889816587014 0.04232145397591842 -0.19137133392787326 +0.8985775863007591 0.0130638909219138 -0.036892685638763 +0.9944657367345657 0.000713131800744486 0.0471956570293942 +1.000043909301717 -9.851122780894417e-06 0.04151915164349936 +1.000216009864362 -0.000279890227385287 0.04597681465716156 +0.9955965259210384 6.290342386802663e-05 0.0850038637137151 +0.9837355911010561 0.001590970086407752 0.1131781895823859 +0.9160973581448664 0.01030344679663074 0.1857913336595896 +0.8247212136041724 0.02206815331132695 0.2449938402700558 +0.7835335477130784 0.02736682776276132 0.2578459502494688 +0.7458784011853928 0.03220726993377351 0.2591066500658992 +0.704181333714036 0.03756145616169322 0.2463676060298896 +0.6588094047445807 0.04337787974553729 0.2145042006489572 +0.4817165482691285 0.06587661484209784 0.004772282753139308 +0.4300565639732108 0.07230837544542264 -0.04186437951123492 +0.3799297067584973 0.0784194374404138 -0.07216280351532364 +0.3322904223011696 0.08401082699279248 -0.08709921870404717 +0.2878908640215147 0.0888395395812468 -0.08863313732386507 +0.237700137204438 0.09320459962989104 -0.0728984865243349 +0.1938680582880188 0.09475868486167134 -0.03756026982500518 +0.1781651575110249 0.09480029283794308 -0.03241020524223182 +0.04571639419151958 0.06586873418008751 -0.0433295353537881 diff --git a/data/TUDELFT_V3_KITE/cpx_distributions/cp_AOA_6.dat b/data/TUDELFT_V3_KITE/cpx_distributions/cp_AOA_6.dat new file mode 100755 index 0000000..7da2c81 --- /dev/null +++ b/data/TUDELFT_V3_KITE/cpx_distributions/cp_AOA_6.dat @@ -0,0 +1,55 @@ +# x y Cp +0.04276388755929555 0.06356556861711357 0.2831765988277916 +0.06177764393266522 0.03185174700135322 0.280866185252911 +0.07295141860002202 0.01956758285390561 0.25963219479103 +0.07815773161604739 -0.001817316543405341 0.3226553243711208 +0.07606735331573551 -0.0127367285437733 0.3146385901820192 +0.06744746907549139 -0.0269300909630248 0.342684532314071 +0.05709225787431717 -0.03471437303988962 0.234853067095021 +0.05020468513322063 -0.0374899309873329 0.284736464587777 +0.03734550757143083 -0.03906061142774211 0.15553190031453823 +0.03364428007162458 -0.03871749253601962 0.16954883769738988 +0.004414604365619548 -0.01804807451598938 0.9863929885981783 +0.002091866108723892 0.01261774277269358 -0.572820273285277 +0.03160995824607752 0.05579211544351708 -2.432245537380402 +0.04505807310843256 0.06612426369258909 -2.774111357485588 +0.05199666670865696 0.07034321801435707 -2.844549106106646 +0.06072447947721454 0.07485584093822345 -2.867363498348572 +0.07161579828052492 0.07949795292659076 -2.829796764734134 +0.08887221045490147 0.08513218823611196 -2.690124986734862 +0.1870216529466714 0.09529101859089956 -1.6416426798621342 +0.2889286523063009 0.08923439218084046 -1.0436464025123768 +0.3218640927071186 0.08569020328035809 -0.9017119855459382 +0.3567078053717327 0.08168243973732783 -0.7850028154244542 +0.3931790397022614 0.07732341274896151 -0.6877927119270986 +0.4437200012730869 0.07112018838118041 -0.5809462948976902 +0.5085472107892827 0.06300526498795977 -0.4741340541213232 +0.5731865715040237 0.05481455085516451 -0.388873206174795 +0.7259539967148313 0.03527045550632674 -0.232643572741015 +0.9854751216984708 0.0018711616089445 -0.003723889307472904 +0.9964097252760367 0.0004627131567309691 0.002753800976306192 +1.000216009864362 -0.000279890227385287 -0.004826394769678074 +0.9943997089817618 0.0002170811127827793 0.04868571797704922 +0.9808969261659528 0.001956677923828914 0.09055972385002321 +0.9537275624931341 0.005456518791413356 0.13731493299292824 +0.9119523907158397 0.01083728805011287 0.19026388337497901 +0.6469580977353171 0.04489502959157916 0.4433304082027882 +0.5852500607119644 0.05277533327406693 0.4920978858473208 +0.5467873640304859 0.05766507732352074 0.5105806429851942 +0.5208166631398357 0.06095334182358406 0.5137794956559404 +0.4947454515238927 0.06424033472807196 0.5061160251107084 +0.4557607131227709 0.06912084908337753 0.4691184029843818 +0.4173409525953314 0.07387363386197163 0.4012507759782244 +0.3208625976738804 0.08530086466606873 0.17770486868504992 +0.2986548580999721 0.08772078502814058 0.14367991508003292 +0.2773623986528947 0.08988862416845661 0.12653828962832644 +0.2570330318015585 0.09174362234289557 0.12949031449777215 +0.237700137204438 0.09320459962989104 0.16011249221611834 +0.2106187027457731 0.09448229827809655 0.269821412704075 +0.1938680582880188 0.09475868486167134 0.3075080023769274 +0.1634966219696773 0.09459572113384887 0.3147298735128626 +0.1498463100440565 0.09399700027730548 0.305568189083699 +0.1255076224116574 0.09176672636466693 0.270229612624369 +0.1147579180611961 0.0902151563016211 0.2651942739621602 +0.07359886901176031 0.07974335793549313 0.297013291348983 +0.04571639419151958 0.06586873418008751 0.284032411033685 diff --git a/data/TUDELFT_V3_KITE/cpx_distributions/cp_AOA_8.dat b/data/TUDELFT_V3_KITE/cpx_distributions/cp_AOA_8.dat new file mode 100644 index 0000000..ce2f14c --- /dev/null +++ b/data/TUDELFT_V3_KITE/cpx_distributions/cp_AOA_8.dat @@ -0,0 +1,59 @@ +# x y Cp +0.04276388755929555 0.06356556861711357 0.369754792445869 +0.05363791167027467 0.03629681937611275 0.369512825076681 +0.06023799864411321 0.03289369258862966 0.3803300404656592 +0.07295141860002202 0.01956758285390561 0.4525002946292716 +0.07538949147001935 0.0145561611100228 0.4564693840993162 +0.07801937057148131 0.003753985959382214 0.446882922242024 +0.07780867321095557 -0.005517988661109592 0.4783581156255348 +0.06613535838326418 -0.02824710359527738 0.5311348004808492 +0.05709225787431717 -0.03471437303988962 0.484086890575362 +0.05197415418742954 -0.03691972333053082 0.508181593031596 +0.04106259222822374 -0.03905070100038476 0.42315148127032 +0.03734550757143083 -0.03906061142774211 0.428382483023469 +0.0135954264885147 -0.02963656804659672 0.9678991392350302 +0.009641067793166569 -0.0257095339280482 1.0009297902025698 +0.006285174856966845 -0.02126020489420191 0.9426534686983296 +0.00106093352703043 0.009046469258067052 -1.0429979768839335 +0.02885514542407597 0.05321522293646556 -2.97701516742313 +0.04040120622143255 0.06291072902875927 -3.321950362170818 +0.05399675986092924 0.07144915477192883 -3.429003607616636 +0.06587533521034071 0.07717347429902713 -3.376878702524202 +0.07799552636294979 0.08179901473482278 -3.254197144337922 +0.1870216529466714 0.09529101859089956 -1.8327874863459042 +0.2784114042826095 0.09028704743105777 -1.1996806915789198 +0.3106619665783404 0.08692971356968779 -1.0297320028124064 +0.3448969536063716 0.08306230186370865 -0.8915062484382064 +0.3931790397022614 0.07732341274896151 -0.7482650769295596 +0.4437200012730869 0.07112018838118041 -0.6372226271092232 +0.5085472107892827 0.06300526498795977 -0.5230590187758586 +0.5859002138042717 0.05319596110636245 -0.4121258594586978 +0.6824259822772941 0.04085560183950949 -0.298421444448391 +0.8093373719000714 0.02455167460498058 -0.17313674963583736 +0.9660562316502389 0.004372544645865283 -0.0386406821187513 +0.993768270458061 0.000802973252239986 -0.02128853044019792 +1.000216009864362 -0.000279890227385287 -0.03062803960527396 +0.9943997089817618 0.0002170811127827793 0.0244731662116314 +0.9818912176243451 0.001828584093814853 0.06971150300101164 +0.960286674485311 0.004611622190581761 0.11726533977474018 +0.9274984236403104 0.00883501341243748 0.1677274935281196 +0.8530499807859604 0.01842190075726221 0.2484797697022198 +0.6588094047445807 0.04337787974553729 0.4164065158335706 +0.5852500607119644 0.05277533327406693 0.4895377938887346 +0.5208166631398357 0.06095334182358406 0.571407956570911 +0.4173409525953314 0.07387363386197163 0.7448626251611238 +0.3922624879189146 0.07693223386512749 0.7709991898354575 +0.3799297067584973 0.0784194374404138 0.7726402699823532 +0.3677534071070601 0.07987434616211128 0.7634659759410414 +0.3557467275473164 0.08129361982160917 0.741382005954384 +0.3322904223011696 0.08401082699279248 0.654753629482031 +0.2570330318015585 0.09174362234289557 0.14088055807058458 +0.237700137204438 0.09320459962989104 0.05907810318650006 +0.2284148315498924 0.09375889647752622 0.04303112368943488 +0.2193869169365694 0.09418446720371103 0.04419401824806442 +0.2106187027457731 0.09448229827809655 0.06265088115711293 +0.1707025886246218 0.09474637369056492 0.2715407898968978 +0.1565453765113058 0.0943433883278578 0.2872016129793902 +0.09590926783756276 0.08644641392015195 0.2638217468843654 +0.0596424493908252 0.07383844242791567 0.350286588153118 +0.04571639419151958 0.06586873418008751 0.3689025452585844 diff --git a/src/VSM/plot_geometry_plotly.py b/src/VSM/plot_geometry_plotly.py index 99ba553..35ace20 100644 --- a/src/VSM/plot_geometry_plotly.py +++ b/src/VSM/plot_geometry_plotly.py @@ -459,6 +459,7 @@ def create_3D_plot( canopy_grid: Optional[Tuple[np.ndarray, np.ndarray, np.ndarray]] = None, is_with_tube_rings: bool = False, is_with_panels: bool = True, + cp_magnitude_fn=None, ) -> go.Figure: """ Creates an interactive 3D plot of wing geometry using Plotly. @@ -548,7 +549,11 @@ def create_3D_plot( if use_distributed_vectors: add_distributed_surface_vectors( - fig, canopy_grid, forces_of_panels, scale=chord_average + fig, + canopy_grid, + forces_of_panels, + scale=chord_average, + cp_magnitude_fn=cp_magnitude_fn, ) if tube_data is not None: @@ -588,24 +593,34 @@ def add_distributed_surface_vectors( scale: float, n_chord: int = 10, n_span: int = None, + cp_magnitude_fn=None, ) -> None: - """Draw a grid of equal, surface-normal force vectors on the canopy. + """Draw a grid of chord-normal force vectors on the canopy. One row of ``n_chord`` nodes is placed at each panel centre (uniformly over - the chord); every node carries one equal-length arrow oriented along the local - canopy surface normal (the outward/suction side). This gives a regular - ``n_span x n_chord`` grid of vectors covering the whole canopy. + the chord); every node carries one arrow oriented normal to the local chord + line (the flat-panel "up", perpendicular to chord and span), pointing to the + outward/suction side. This gives a regular ``n_span x n_chord`` grid of + vectors covering the whole canopy. + + Each panel's VSM force is distributed over its chord nodes: the arrow lengths + reflect both the spanwise loading (the panel force from VSM) and the chordwise + shape (the ``|Cp|`` weighting, or an even split without a Cp distribution). + The node forces of a panel sum back to that panel's force. Args: fig: Plotly figure. canopy_grid: ``(X, Y, Z)`` canopy surface arrays, each ``(S, P)`` with ``S`` spanwise stations and ``P`` chordwise points. - forces_of_panels: Per-panel aerodynamic force vectors (used only to skip - drawing when there is no force). + forces_of_panels: Per-panel aerodynamic force vectors from VSM; their + magnitudes set the total per panel that is distributed chordwise. scale: Length scale (typically the average chord). n_chord: Nodes per row, distributed over the chord. Defaults to 10. n_span: Number of spanwise rows. Defaults to one row per panel (the midpoints between the ``S`` canopy stations, i.e. ``S - 1`` rows). + cp_magnitude_fn: Optional callable mapping chord fraction ``x/c`` to a + (positive) pressure-coefficient magnitude, used as the chordwise + weighting of each panel's force. Without it the force is spread evenly. """ x, y, z = canopy_grid grid = np.stack([x, y, z], axis=-1) # (S, P, 3) @@ -624,52 +639,75 @@ def add_distributed_surface_vectors( np.clip(np.round(np.linspace(0, n_rows - 1, n_span)), 0, n_rows - 1).astype(int) ) - # Surface-normal field over the panel-centre grid. - d_chord = np.gradient(panel_grid, axis=1) - d_span = np.gradient(panel_grid, axis=0) - normal_field = np.cross(d_chord, d_span) - normal_field /= np.linalg.norm(normal_field, axis=-1, keepdims=True).clip(1e-12) - normal_field[normal_field[:, :, 2] < 0] *= -1 # outward (suction) side + # Spanwise direction field, used to build the per-row chord-normal. + d_span = np.gradient(panel_grid, axis=0) # (R, P, 3) + + # Chordwise weights: how each panel's force is spread over its nodes. With a + # Cp distribution the weights follow the local |Cp| loading; otherwise the + # force is spread evenly. Normalised so the weights sum to one (the node + # forces of a panel add back up to that panel's VSM force). + targets = np.linspace(0.05, 0.95, n_chord) + if cp_magnitude_fn is not None: + chord_weights = np.asarray(cp_magnitude_fn(targets), dtype=float) + else: + chord_weights = np.ones(n_chord) + chord_weights = chord_weights / max(chord_weights.sum(), 1e-12) # Sample each row at the exact target chord fractions (5%-95%). The grid # columns are arc-length spaced, so interpolate by true chord fraction rather # than snapping to columns -- this keeps vectors clear of the leading-edge # tube (near 0%) and the trailing edge (near 100%). - targets = np.linspace(0.05, 0.95, n_chord) origins = [] normals = [] + node_forces = [] # per-node share of the panel's VSM force magnitude for row in span_rows: pts = panel_grid[row] # (P, 3) chord_vector = pts[-1] - pts[0] chord_length = np.linalg.norm(chord_vector) chord_hat = chord_vector / max(chord_length, 1e-12) + + # Chord-normal: perpendicular to the chord line and the span, in the + # airfoil plane (the flat-panel "up"). Constant along the chord, so every + # vector in this row points the same way rather than following the local + # surface curvature. + span_vector = d_span[row].mean(axis=0) + chord_normal = np.cross(chord_vector, span_vector) + cn = np.linalg.norm(chord_normal) + if cn < 1e-9: + continue + chord_normal = chord_normal / cn + if chord_normal[2] < 0: # orient toward the outward (suction) side + chord_normal = -chord_normal + + # Row r is the centre of panel r, so its total is that panel's force. + panel_force = force_magnitudes[row] if row < len(force_magnitudes) else 0.0 + chord_fraction = ((pts - pts[0]) @ chord_hat) / max(chord_length, 1e-12) order = np.argsort(chord_fraction) frac_sorted = chord_fraction[order] pts_sorted = pts[order] - nrm_sorted = normal_field[row][order] - for target in targets: + for k, target in enumerate(targets): point = np.array( - [np.interp(target, frac_sorted, pts_sorted[:, k]) for k in range(3)] + [np.interp(target, frac_sorted, pts_sorted[:, j]) for j in range(3)] ) - normal = np.array( - [np.interp(target, frac_sorted, nrm_sorted[:, k]) for k in range(3)] - ) - norm = np.linalg.norm(normal) - if norm < 1e-9: - continue origins.append(point) - normals.append(normal / norm) + normals.append(chord_normal) + node_forces.append(panel_force * chord_weights[k]) if not origins: return origins = np.array(origins) normals = np.array(normals) + node_forces = np.array(node_forces) - # Equal arrow length, scaled to the chordwise node spacing so the grid reads - # cleanly without the arrows overlapping badly. - length = 0.9 * scale / max(n_chord, 1) - endpoints = origins + normals * length + # Normalise force (N) to plotted length (m): the largest panel force over all + # panels plots at the maximum chord length (``scale``). Each panel's total + # plotted length is then this per-panel length spread across the chord, so a + # panel's node lengths add up to ``|F_panel| / max_force * max_chord``. + max_force = float(force_magnitudes.max()) + newton_to_metre = scale / max(max_force, 1e-12) + lengths = (node_forces * newton_to_metre)[:, None] + endpoints = origins + normals * lengths # All stems as one trace (None-separated segments), all heads as one cone trace. stem_x, stem_y, stem_z = [], [], [] @@ -693,11 +731,11 @@ def add_distributed_surface_vectors( x=endpoints[:, 0], y=endpoints[:, 1], z=endpoints[:, 2], - u=normals[:, 0] * length, - v=normals[:, 1] * length, - w=normals[:, 2] * length, + u=normals[:, 0] * lengths[:, 0], + v=normals[:, 1] * lengths[:, 0], + w=normals[:, 2] * lengths[:, 0], sizemode="absolute", - sizeref=length / 3, + sizeref=0.06 * scale, anchor="tip", colorscale=[[0, "red"], [1, "red"]], showscale=False, @@ -983,6 +1021,7 @@ def update_plot( canopy_grid: Optional[Tuple[np.ndarray, np.ndarray, np.ndarray]] = None, is_with_tube_rings: bool = False, is_with_panels: bool = True, + cp_magnitude_fn=None, ): # Update AoA and rerun VSM results = running_VSM( @@ -1006,6 +1045,7 @@ def update_plot( canopy_grid=canopy_grid, is_with_tube_rings=is_with_tube_rings, is_with_panels=is_with_panels, + cp_magnitude_fn=cp_magnitude_fn, ) fig = add_case_information( fig, @@ -1041,6 +1081,7 @@ def interactive_plot( is_with_canopy: bool = True, is_with_tube_rings: bool = False, is_with_panels: Optional[bool] = None, + cp_distributions_dir: Optional[Path] = None, ): """ Creates and optionally saves multiple views of the wing geometry with interactive AoA slider. @@ -1072,6 +1113,11 @@ def interactive_plot( is_with_panels: Draw the panel outlines. Defaults to ``False`` for the fancy plot (a Surfplan export is given) and ``True`` otherwise. When off, the force vectors are lifted onto the canopy surface. + cp_distributions_dir: Optional directory of ``cp_AOA_.dat`` chordwise + pressure-coefficient files. When given, the closest available angle of + attack is selected and its ``|Cp|(x/c)`` scales the chordwise force + vector magnitudes (the single mid-span distribution is applied at + every span station). Returns: plotly.graph_objects.Figure: The created figure. @@ -1094,6 +1140,19 @@ def interactive_plot( contour_table = load_contour_table(processed_dir) canopy_grid = build_canopy_grid(wing_aero.panels, contour_table) + # Optional Cp-based scaling of the chordwise force vectors. + cp_magnitude_fn = None + if cp_distributions_dir is not None: + from VSM.plotly.cp_distributions import build_cp_magnitude_fn + + cp_magnitude_fn, matched_aoa = build_cp_magnitude_fn( + Path(cp_distributions_dir), angle_of_attack + ) + print( + f"Cp loading from closest available AoA = {matched_aoa:g} deg " + f"(operating AoA = {angle_of_attack:g} deg)" + ) + # Create the figure with a default orientation fig = go.Figure() fig.update_layout( @@ -1121,6 +1180,7 @@ def interactive_plot( canopy_grid=canopy_grid, is_with_tube_rings=is_with_tube_rings, is_with_panels=is_with_panels, + cp_magnitude_fn=cp_magnitude_fn, ) # Save or show the plot if requested diff --git a/src/VSM/plotly/cp_distributions.py b/src/VSM/plotly/cp_distributions.py new file mode 100644 index 0000000..29c1ad0 --- /dev/null +++ b/src/VSM/plotly/cp_distributions.py @@ -0,0 +1,97 @@ +"""Chordwise pressure-coefficient (Cp) database for the fancy Plotly plot. + +A directory of ``cp_AOA_.dat`` files (whitespace columns ``x/c, y/c, Cp`` +around the 2D airfoil section) is treated as a small database keyed by angle of +attack. Given an operating angle of attack, the closest available distribution is +selected and its **upper (suction) surface** ``|Cp|(x/c)`` is used to scale the +magnitude of the chordwise force vectors on the canopy. + +Per the modelling assumption in use, the single mid-span section distribution is +applied to every spanwise station (all airfoils share this geometry). +""" + +import re +from pathlib import Path +from typing import Callable, Dict, Tuple + +import numpy as np + + +def load_upper_surface_cp(dat_file_path: Path) -> Tuple[np.ndarray, np.ndarray]: + """Extract the upper (suction) surface ``Cp(x/c)`` from a Cp ``.dat`` loop. + + The file lists ``x/c, y/c, Cp`` around the closed airfoil section. The upper + surface is the leading-edge-to-trailing-edge branch with the more negative + (suction) mean Cp; it is returned sorted by ``x/c``. + + Args: + dat_file_path (Path): Path to a ``cp_AOA_*.dat`` file. + + Returns: + Tuple[np.ndarray, np.ndarray]: ``x/c`` (ascending) and ``Cp`` arrays. + """ + data = np.loadtxt(dat_file_path) + x = data[:, 0] + cp = data[:, 2] + n = len(x) + i_le = int(np.argmin(x)) + i_te = int(np.argmax(x)) + + def branch(a: int, b: int) -> np.ndarray: + if a <= b: + return np.arange(a, b + 1) + return np.concatenate([np.arange(a, n), np.arange(0, b + 1)]) + + branch_a = branch(i_le, i_te) + branch_b = branch(i_te, i_le) + upper = branch_a if cp[branch_a].mean() < cp[branch_b].mean() else branch_b + + x_upper = x[upper] + cp_upper = cp[upper] + order = np.argsort(x_upper) + return x_upper[order], cp_upper[order] + + +def load_cp_database(cp_dir: Path) -> Dict[float, Tuple[np.ndarray, np.ndarray]]: + """Load all ``cp_AOA_.dat`` files, keyed by angle of attack in degrees. + + Args: + cp_dir (Path): Directory of ``cp_AOA_*.dat`` files. + + Returns: + Dict[float, Tuple[np.ndarray, np.ndarray]]: Mapping ``aoa_deg -> (x/c, Cp)`` + for the upper surface. + """ + database = {} + for path in sorted(Path(cp_dir).glob("cp_AOA_*.dat")): + match = re.search(r"AOA_(-?\d+(?:\.\d+)?)", path.stem) + if not match: + continue + database[float(match.group(1))] = load_upper_surface_cp(path) + if not database: + raise FileNotFoundError(f"No 'cp_AOA_*.dat' files found in {cp_dir}.") + return database + + +def build_cp_magnitude_fn( + cp_dir: Path, angle_of_attack: float +) -> Tuple[Callable[[np.ndarray], np.ndarray], float]: + """Return a ``|Cp|(x/c)`` interpolator for the closest available AoA. + + Args: + cp_dir (Path): Directory of ``cp_AOA_*.dat`` files. + angle_of_attack (float): Operating angle of attack in degrees. + + Returns: + Tuple[Callable, float]: A function mapping ``x/c`` (array) to ``|Cp|`` and + the matched database angle of attack. + """ + database = load_cp_database(cp_dir) + available = np.array(sorted(database)) + matched_aoa = float(available[np.argmin(np.abs(available - angle_of_attack))]) + x_upper, cp_upper = database[matched_aoa] + + def magnitude(x_over_c: np.ndarray) -> np.ndarray: + return np.abs(np.interp(x_over_c, x_upper, cp_upper)) + + return magnitude, matched_aoa diff --git a/tests/plotting/test_tube_geometry.py b/tests/plotting/test_tube_geometry.py index 26c4236..a518998 100644 --- a/tests/plotting/test_tube_geometry.py +++ b/tests/plotting/test_tube_geometry.py @@ -20,6 +20,7 @@ REPO_ROOT = Path(__file__).resolve().parents[2] SURFPLAN_DIR = REPO_ROOT / "data" / "TUDELFT_V3_KITE" / "Surfplan_export" +CP_DIR = REPO_ROOT / "data" / "TUDELFT_V3_KITE" / "cpx_distributions" DRAWN_YAML = ( REPO_ROOT / "data" @@ -202,3 +203,59 @@ def test_interactive_plot_fancy_traces(): fig3 = interactive_plot(body, is_show=False) assert not [t for t in fig3.data if isinstance(t, go.Surface)] assert [t for t in fig3.data if getattr(t, "name", None) == "Leading Edge"] + + +# --------------------------------------------------------------------------- # +# Cp distribution database +# --------------------------------------------------------------------------- # + + +@pytest.mark.skipif(not CP_DIR.is_dir(), reason="Cp distributions unavailable") +def test_load_upper_surface_cp_ordering(): + from VSM.plotly.cp_distributions import load_upper_surface_cp + + x, cp = load_upper_surface_cp(CP_DIR / "cp_AOA_8.dat") + assert np.all(np.diff(x) >= 0) # ascending x/c + assert x.min() >= 0.0 and x.max() <= 1.01 # normalised chord (small CFD overshoot) + assert cp.min() < 0 # suction side has negative Cp + + +@pytest.mark.skipif(not CP_DIR.is_dir(), reason="Cp distributions unavailable") +def test_build_cp_magnitude_fn_closest_aoa(): + from VSM.plotly.cp_distributions import build_cp_magnitude_fn + + # Exact and nearest matches over the available {2, 6, 8} degrees. + assert build_cp_magnitude_fn(CP_DIR, 8)[1] == 8 + assert build_cp_magnitude_fn(CP_DIR, 5)[1] == 6 + assert build_cp_magnitude_fn(CP_DIR, 3)[1] == 2 + assert build_cp_magnitude_fn(CP_DIR, 100)[1] == 8 + + magnitude, _ = build_cp_magnitude_fn(CP_DIR, 8) + values = magnitude(np.array([0.05, 0.5, 0.95])) + assert np.all(values >= 0) + # Loading is front-heavy: near-LE |Cp| exceeds near-TE |Cp|. + assert values[0] > values[-1] + + +@pytest.mark.skipif( + not (_has_surfplan() and CP_DIR.is_dir()), + reason="SurfplanAdapter or data unavailable", +) +def test_interactive_plot_cp_scaled_vectors_run(): + from VSM.core.BodyAerodynamics import BodyAerodynamics + from VSM.plot_geometry_plotly import interactive_plot + + body = BodyAerodynamics.instantiate( + n_panels=36, + file_path=DRAWN_YAML, + spanwise_panel_distribution="uniform", + ) + fig = interactive_plot( + body, + angle_of_attack=8, + surfplan_dir=SURFPLAN_DIR, + cp_distributions_dir=CP_DIR, + is_show=False, + ) + # The vector grid is still drawn (stems + arrowheads present). + assert [t for t in fig.data if getattr(t, "name", None) == "Aerodynamic Vectors"] From 1cfe544053b88474582c128e9ff5815b25fc4e08 Mon Sep 17 00:00:00 2001 From: jellepoland Date: Fri, 24 Jul 2026 17:16:40 +0200 Subject: [PATCH 07/13] Fancy plot: Cp-scaled force field, tube end-caps, high-res render, example - Distribute each panel's VSM force over the chord weighted by the closest-AoA |Cp|(x/c); per-vector size anchored to a base of 10 nodes (extract the shared distributed_surface_vector_field into canopy_geometry) - Cap the open tube ends (leading-edge + struts) so junctions read cleanly - save_high_res_render: render on a square canvas (Plotly only renders a 3D scene undistorted when square), strip legend/axes/annotations, and auto-crop to the kite -> a clean, to-scale PNG - Drop the forced cube aspectratio in the layout so the scene stays true-to-scale - Add examples/TUDELFT_V3_KITE/fancy_interactive_plot.py running the pipeline and writing the high-res render; embed docs/plotly_render.png in the README Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 2 + docs/plotly_render.png | Bin 0 -> 96855 bytes .../TUDELFT_V3_KITE/fancy_interactive_plot.py | 85 ++++++++ src/VSM/plot_geometry_plotly.py | 187 +++++++++--------- src/VSM/plotly/canopy_geometry.py | 102 ++++++++++ src/VSM/plotly/tube_geometry.py | 42 +++- 6 files changed, 326 insertions(+), 92 deletions(-) create mode 100644 docs/plotly_render.png create mode 100644 examples/TUDELFT_V3_KITE/fancy_interactive_plot.py diff --git a/README.md b/README.md index 1d42c90..1bb6935 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ The Vortex Step Method (VSM) is an enhanced lifting line method that improves up A Julia version of this project is available at [VortexStepMethod.jl](https://github.com/Albatross-Kite-Transport/VortexStepMethod.jl) + +![](docs/plotly_render.png) ## Reference Frame diff --git a/docs/plotly_render.png b/docs/plotly_render.png new file mode 100644 index 0000000000000000000000000000000000000000..404ef33f76a4bd039ffccc01c68929b94d84f750 GIT binary patch literal 96855 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.txt`` plus a ``profiles/`` directory, + - (optional) a directory of ``cp_AOA_.dat`` chordwise Cp distributions. + +SurfplanAdapter must be installed (a dependency of VSM) -- it converts the raw +export into the VSM geometry the plot needs (cached on first run). +""" + +from pathlib import Path + +from VSM.core.BodyAerodynamics import BodyAerodynamics +from VSM.plot_geometry_plotly import interactive_plot, save_high_res_render + +PROJECT_DIR = Path(__file__).resolve().parents[2] +DATA_DIR = PROJECT_DIR / "data" / "TUDELFT_V3_KITE" +RESULTS_DIR = PROJECT_DIR / "results" / "TUDELFT_V3_KITE" + +# Set True to also write a high-resolution PNG (results/.../plotly_render.png) +SAVE_HIGH_RES_PNG = True + + +def main(): + # --- inflow / operating point ------------------------------------------- + Umag = 10.0 # [m/s] + angle_of_attack = 8.0 # [deg] + side_slip = 0.0 # [deg] + + # --- build the aerodynamic model ---------------------------------------- + # The Breukels-regression CAD geometry needs no ML models, so it runs + # out of the box. Any other aero_geometry_*.yaml works too. + aero_geometry = ( + DATA_DIR / "CAD_derived_geometry" / "aero_geometry_CAD_breukels_regression.yaml" + ) + body_aero = BodyAerodynamics.instantiate( + n_panels=36, + file_path=aero_geometry, + spanwise_panel_distribution="uniform", + ) + + # --- data for the fancy plot -------------------------------------------- + # Raw Surfplan export (tubes + airfoil profiles) and the Cp database. + surfplan_dir = DATA_DIR / "Surfplan_export" + cp_distributions_dir = DATA_DIR / "cpx_distributions" + + # --- interactive fancy plot --------------------------------------------- + # Passing ``surfplan_dir`` switches on the inflatable tubes + curved canopy. + # Passing ``cp_distributions_dir`` distributes each panel's VSM force over the + # chord using the closest-AoA measured Cp(x/c). Omit either to fall back. + fig = interactive_plot( + body_aero, + vel=Umag, + angle_of_attack=angle_of_attack, + side_slip=side_slip, + title="TUDELFT_V3_KITE", + surfplan_dir=surfplan_dir, + cp_distributions_dir=cp_distributions_dir, + is_with_canopy=True, # curved canopy over the tubes (False -> tubes only) + is_with_tube_rings=False, # True -> also draw the tube construction rings + is_show=True, # open the interactive plot in a browser + # is_save=True, # uncomment to also write an interactive .html + # save_path=str(RESULTS_DIR / "fancy_plot.html"), + ) + + # --- optional: high-resolution PNG (matches the interactive view) -------- + if SAVE_HIGH_RES_PNG: + save_high_res_render( + fig, + RESULTS_DIR / "plotly_render.png", + base_size=2000, # square canvas -> undistorted; then cropped to the kite + scale=4, + show_legend=False, + show_axes=False, + show_annotations=False, + ) + + +if __name__ == "__main__": + main() diff --git a/src/VSM/plot_geometry_plotly.py b/src/VSM/plot_geometry_plotly.py index 35ace20..0d99386 100644 --- a/src/VSM/plot_geometry_plotly.py +++ b/src/VSM/plot_geometry_plotly.py @@ -622,91 +622,19 @@ def add_distributed_surface_vectors( (positive) pressure-coefficient magnitude, used as the chordwise weighting of each panel's force. Without it the force is spread evenly. """ - x, y, z = canopy_grid - grid = np.stack([x, y, z], axis=-1) # (S, P, 3) - - force_magnitudes = np.linalg.norm(np.asarray(forces_of_panels), axis=1) - if force_magnitudes.size == 0 or np.max(force_magnitudes) <= 0: - return - - # One row per panel: use the midpoints between adjacent canopy stations. - panel_grid = 0.5 * (grid[:-1] + grid[1:]) # (S - 1, P, 3) - n_rows, n_points, _ = panel_grid.shape - - if n_span is None: - n_span = n_rows - span_rows = np.unique( - np.clip(np.round(np.linspace(0, n_rows - 1, n_span)), 0, n_rows - 1).astype(int) + from VSM.plotly.canopy_geometry import distributed_surface_vector_field + + origins, normals, lengths_1d = distributed_surface_vector_field( + canopy_grid, + forces_of_panels, + scale, + n_chord=n_chord, + n_span=n_span, + cp_magnitude_fn=cp_magnitude_fn, ) - - # Spanwise direction field, used to build the per-row chord-normal. - d_span = np.gradient(panel_grid, axis=0) # (R, P, 3) - - # Chordwise weights: how each panel's force is spread over its nodes. With a - # Cp distribution the weights follow the local |Cp| loading; otherwise the - # force is spread evenly. Normalised so the weights sum to one (the node - # forces of a panel add back up to that panel's VSM force). - targets = np.linspace(0.05, 0.95, n_chord) - if cp_magnitude_fn is not None: - chord_weights = np.asarray(cp_magnitude_fn(targets), dtype=float) - else: - chord_weights = np.ones(n_chord) - chord_weights = chord_weights / max(chord_weights.sum(), 1e-12) - - # Sample each row at the exact target chord fractions (5%-95%). The grid - # columns are arc-length spaced, so interpolate by true chord fraction rather - # than snapping to columns -- this keeps vectors clear of the leading-edge - # tube (near 0%) and the trailing edge (near 100%). - origins = [] - normals = [] - node_forces = [] # per-node share of the panel's VSM force magnitude - for row in span_rows: - pts = panel_grid[row] # (P, 3) - chord_vector = pts[-1] - pts[0] - chord_length = np.linalg.norm(chord_vector) - chord_hat = chord_vector / max(chord_length, 1e-12) - - # Chord-normal: perpendicular to the chord line and the span, in the - # airfoil plane (the flat-panel "up"). Constant along the chord, so every - # vector in this row points the same way rather than following the local - # surface curvature. - span_vector = d_span[row].mean(axis=0) - chord_normal = np.cross(chord_vector, span_vector) - cn = np.linalg.norm(chord_normal) - if cn < 1e-9: - continue - chord_normal = chord_normal / cn - if chord_normal[2] < 0: # orient toward the outward (suction) side - chord_normal = -chord_normal - - # Row r is the centre of panel r, so its total is that panel's force. - panel_force = force_magnitudes[row] if row < len(force_magnitudes) else 0.0 - - chord_fraction = ((pts - pts[0]) @ chord_hat) / max(chord_length, 1e-12) - order = np.argsort(chord_fraction) - frac_sorted = chord_fraction[order] - pts_sorted = pts[order] - for k, target in enumerate(targets): - point = np.array( - [np.interp(target, frac_sorted, pts_sorted[:, j]) for j in range(3)] - ) - origins.append(point) - normals.append(chord_normal) - node_forces.append(panel_force * chord_weights[k]) - - if not origins: + if len(origins) == 0: return - origins = np.array(origins) - normals = np.array(normals) - node_forces = np.array(node_forces) - - # Normalise force (N) to plotted length (m): the largest panel force over all - # panels plots at the maximum chord length (``scale``). Each panel's total - # plotted length is then this per-panel length spread across the chord, so a - # panel's node lengths add up to ``|F_panel| / max_force * max_chord``. - max_force = float(force_magnitudes.max()) - newton_to_metre = scale / max(max_force, 1e-12) - lengths = (node_forces * newton_to_metre)[:, None] + lengths = lengths_1d[:, None] endpoints = origins + normals * lengths # All stems as one trace (None-separated segments), all heads as one cone trace. @@ -721,7 +649,7 @@ def add_distributed_surface_vectors( y=stem_y, z=stem_z, mode="lines", - line=dict(color="red", width=3), + line=dict(color="red", width=3.9), name="Aerodynamic Vectors", showlegend=True, ) @@ -735,7 +663,7 @@ def add_distributed_surface_vectors( v=normals[:, 1] * lengths[:, 0], w=normals[:, 2] * lengths[:, 0], sizemode="absolute", - sizeref=0.06 * scale, + sizeref=0.048 * scale, anchor="tip", colorscale=[[0, "red"], [1, "red"]], showscale=False, @@ -861,12 +789,15 @@ def update_fig_layout( f"{kite_geometry_ranges['z'][1]:.2f}", ], # Label for the ticks ), - aspectmode="data", # Allow different axis lengths - aspectratio=dict(x=1, y=1, z=1), # Keep aspect ratio 1:1:1 + # "data" keeps the scene true-to-scale (equal units on every axis); + # do NOT force a cube aspectratio here, that squeezes the wide wing. + aspectmode="data", camera=dict( up=dict(x=0, y=0, z=1), center=dict(x=0, y=0, z=0), - eye=dict(x=1.5, y=1.5, z=1.5), + eye=dict( + x=-1.92, y=-1.44, z=0.30 + ), # rotated ~180 deg, low and further out ), bgcolor="white", ), @@ -1204,3 +1135,83 @@ def interactive_plot( webbrowser.open(f"file://{tmp_path}") return fig + + +def save_high_res_render( + fig: go.Figure, + save_path, + base_size: int = 2000, + scale: int = 4, + show_legend: bool = False, + show_axes: bool = False, + show_annotations: bool = False, + transparent_background: bool = False, + pad_frac: float = 0.02, +) -> str: + """Export a clean, to-scale high-resolution PNG of the kite. + + A Plotly 3D scene is only rendered undistorted on a **square** canvas (on a + non-square canvas Plotly stretches it, which squeezes the kite). So this + renders on a square canvas -- guaranteeing correct proportions -- then crops + tightly to the drawn content. The legend, axes and annotations are removed by + default for a clean figure. + + Args: + fig: The Plotly figure (e.g. from :func:`interactive_plot`). + save_path: Output ``.png`` path; parent directories are created. + base_size: Square canvas size in pixels before supersampling. + scale: Supersampling factor (final render is ``base_size * scale`` square, + then cropped). Defaults to 4. + show_legend: Keep the trace legend. Defaults to False. + show_axes: Keep the 3D axes. Defaults to False. + show_annotations: Keep the corner case-info text. Defaults to False. + transparent_background: Transparent instead of white background. + pad_frac: Padding around the cropped content, as a fraction of its size. + + Returns: + str: The path the PNG was written to. + """ + import io + from PIL import Image + + render_fig = go.Figure(fig) + render_fig.update_layout(showlegend=show_legend, margin=dict(l=0, r=0, t=0, b=0)) + if not show_annotations: + render_fig.layout.annotations = () + render_fig.layout.title = None + if not show_axes: + render_fig.update_scenes( + xaxis_visible=False, yaxis_visible=False, zaxis_visible=False + ) + bg = "rgba(0,0,0,0)" if transparent_background else "white" + render_fig.update_layout(paper_bgcolor=bg, plot_bgcolor=bg) + + # Square canvas => Plotly does not distort the 3D scene. + png = render_fig.to_image( + format="png", width=base_size, height=base_size, scale=scale + ) + image = Image.open(io.BytesIO(png)).convert("RGBA") + + # Crop tightly to the drawn content (non-background pixels). + rgb = np.asarray(image)[:, :, :3].astype(int) + alpha = np.asarray(image)[:, :, 3] + if transparent_background: + content = alpha > 0 + else: + content = rgb.sum(axis=2) < 3 * 250 # anything not near-white + ys, xs = np.where(content) + if len(xs): + pad = int(pad_frac * max(xs.max() - xs.min(), ys.max() - ys.min())) + left = max(xs.min() - pad, 0) + right = min(xs.max() + pad, image.width) + top = max(ys.min() - pad, 0) + bottom = min(ys.max() + pad, image.height) + image = image.crop((left, top, right, bottom)) + + save_path = Path(save_path) + save_path.parent.mkdir(parents=True, exist_ok=True) + image.save(str(save_path)) + print( + f"High-resolution render saved to: {save_path} ({image.width} x {image.height} px)" + ) + return str(save_path) diff --git a/src/VSM/plotly/canopy_geometry.py b/src/VSM/plotly/canopy_geometry.py index ae4e98c..419c6bc 100644 --- a/src/VSM/plotly/canopy_geometry.py +++ b/src/VSM/plotly/canopy_geometry.py @@ -228,3 +228,105 @@ def build_canopy_grid( grid = np.stack(rows, axis=0) # (n_stations, n_points, 3) return grid[:, :, 0], grid[:, :, 1], grid[:, :, 2] + + +def distributed_surface_vector_field( + canopy_grid: Tuple[np.ndarray, np.ndarray, np.ndarray], + forces_of_panels: List[np.ndarray], + scale: float, + n_chord: int = 10, + n_span: int = None, + cp_magnitude_fn=None, +) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + """Chord-normal force-vector field over the canopy (origins, directions, lengths). + + One row of ``n_chord`` nodes is placed at each panel centre, uniformly over + the chord (at the true 5%-95% chord fractions). Every node's arrow points + normal to the local chord line (the flat-panel "up", outward/suction side). + Each panel's VSM force is distributed over its chord nodes, weighted by the + ``|Cp|`` chordwise loading (an even split without a Cp distribution) and + normalised to length so the largest panel force plots at the maximum chord + length (``scale``). + + Args: + canopy_grid: ``(X, Y, Z)`` canopy arrays, each ``(S, P)``. + forces_of_panels: Per-panel VSM force vectors. + scale: Length scale (typically the maximum chord); the largest panel + force plots at this length. + n_chord: Nodes per row over the chord. Defaults to 10. + n_span: Number of spanwise rows. Defaults to one per panel (``S - 1``). + cp_magnitude_fn: Optional ``x/c -> |Cp|`` weighting callable. + + Returns: + Tuple[np.ndarray, np.ndarray, np.ndarray]: ``origins`` ``(N, 3)``, + unit ``directions`` ``(N, 3)`` and ``lengths`` ``(N,)``. Empty arrays + if there is no force. + """ + x, y, z = canopy_grid + grid = np.stack([x, y, z], axis=-1) # (S, P, 3) + + force_magnitudes = np.linalg.norm(np.asarray(forces_of_panels), axis=1) + if force_magnitudes.size == 0 or np.max(force_magnitudes) <= 0: + return np.empty((0, 3)), np.empty((0, 3)), np.empty((0,)) + + panel_grid = 0.5 * (grid[:-1] + grid[1:]) # (S - 1, P, 3), one row per panel + n_rows, n_points, _ = panel_grid.shape + + if n_span is None: + n_span = n_rows + span_rows = np.unique( + np.clip(np.round(np.linspace(0, n_rows - 1, n_span)), 0, n_rows - 1).astype(int) + ) + d_span = np.gradient(panel_grid, axis=0) # (R, P, 3) + + targets = np.linspace(0.05, 0.95, n_chord) + if cp_magnitude_fn is not None: + chord_weights = np.asarray(cp_magnitude_fn(targets), dtype=float) + else: + chord_weights = np.ones(n_chord) + chord_weights = chord_weights / max(chord_weights.sum(), 1e-12) + + origins = [] + directions = [] + node_forces = [] + for row in span_rows: + pts = panel_grid[row] + chord_vector = pts[-1] - pts[0] + chord_length = np.linalg.norm(chord_vector) + chord_hat = chord_vector / max(chord_length, 1e-12) + + span_vector = d_span[row].mean(axis=0) + chord_normal = np.cross(chord_vector, span_vector) + cn = np.linalg.norm(chord_normal) + if cn < 1e-9: + continue + chord_normal = chord_normal / cn + if chord_normal[2] < 0: # outward (suction) side + chord_normal = -chord_normal + + panel_force = force_magnitudes[row] if row < len(force_magnitudes) else 0.0 + + chord_fraction = ((pts - pts[0]) @ chord_hat) / max(chord_length, 1e-12) + order = np.argsort(chord_fraction) + frac_sorted = chord_fraction[order] + pts_sorted = pts[order] + for k, target in enumerate(targets): + point = np.array( + [np.interp(target, frac_sorted, pts_sorted[:, j]) for j in range(3)] + ) + origins.append(point) + directions.append(chord_normal) + node_forces.append(panel_force * chord_weights[k]) + + if not origins: + return np.empty((0, 3)), np.empty((0, 3)), np.empty((0,)) + + origins = np.array(origins) + directions = np.array(directions) + node_forces = np.array(node_forces) + newton_to_metre = scale / max(float(force_magnitudes.max()), 1e-12) + # Anchor the per-vector size to a base of 10 chordwise nodes, so adding more + # nodes makes the field denser without shrinking each arrow (at n_chord=10 the + # factor is 1 and the largest panel's arrows still sum to the max chord). + lengths = node_forces * newton_to_metre * (n_chord / 10.0) + return origins, directions, lengths diff --git a/src/VSM/plotly/tube_geometry.py b/src/VSM/plotly/tube_geometry.py index adafdda..0609302 100644 --- a/src/VSM/plotly/tube_geometry.py +++ b/src/VSM/plotly/tube_geometry.py @@ -487,15 +487,49 @@ def _add_surface(fig: go.Figure, rings: List[np.ndarray], name: str, show_legend ) +def _add_ring_cap(fig: go.Figure, ring: np.ndarray) -> None: + """Close an open tube end with a filled (fan-triangulated) cap disc.""" + ring = np.asarray(ring)[:-1] # drop the duplicated closing point + n = len(ring) + center = ring.mean(axis=0) + verts = np.vstack([center[None, :], ring]) # index 0 is the centre + i = [0] * n + j = [1 + t for t in range(n)] + k = [1 + (t + 1) % n for t in range(n)] + fig.add_trace( + go.Mesh3d( + x=verts[:, 0], + y=verts[:, 1], + z=verts[:, 2], + i=i, + j=j, + k=k, + color=TUBE_COLOR, + opacity=TUBE_OPACITY, + name="Tube cap", + showlegend=False, + ) + ) + + +def _add_tube_with_caps( + fig: go.Figure, rings: List[np.ndarray], name: str, show_legend: bool +): + """Add a tube surface and close both open ends with caps.""" + _add_surface(fig, rings, name, show_legend) + _add_ring_cap(fig, rings[0]) + _add_ring_cap(fig, rings[-1]) + + def add_strut_surfaces(fig: go.Figure, tube_data: Dict[str, Any]) -> None: - """Add the strut tube surfaces to the figure.""" + """Add the strut tube surfaces (with end caps) to the figure.""" for index, strut in enumerate(tube_data["struts"]): - _add_surface(fig, strut, "Strut tube", show_legend=(index == 0)) + _add_tube_with_caps(fig, strut, "Strut tube", show_legend=(index == 0)) def add_le_tube_surface(fig: go.Figure, tube_data: Dict[str, Any]) -> None: - """Add the leading-edge tube surface to the figure.""" - _add_surface(fig, tube_data["le"], "Leading-edge tube", show_legend=True) + """Add the leading-edge tube surface (with end caps) to the figure.""" + _add_tube_with_caps(fig, tube_data["le"], "Leading-edge tube", show_legend=True) def add_tube_surfaces(fig: go.Figure, tube_data: Dict[str, Any]) -> None: From 3c680eba4fc3ad3b3e33300d7c2f426f16ca3fda Mon Sep 17 00:00:00 2001 From: jellepoland Date: Fri, 24 Jul 2026 17:28:34 +0200 Subject: [PATCH 08/13] readme --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1bb6935..7028e4c 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,20 @@ The Vortex Step Method (VSM) is an enhanced lifting line method that improves up A Julia version of this project is available at [VortexStepMethod.jl](https://github.com/Albatross-Kite-Transport/VortexStepMethod.jl) + +## Key Features ![](docs/plotly_render.png) +- **Accurate low-aspect-ratio wing modeling** with enhanced lifting line theory +- **Viscous-inviscid coupling** using 2D airfoil polars (inviscid, CFD, ML-based) +- **Complex geometry support**: sweep, dihedral, anhedral, leading-edge inflatable (LEI) kites +- **Rigid-body stability derivatives**: automatic computation of dCx/dα, dCMy/dq, etc. +- **Trim angle solver**: automatic determination of trimmed angle of attack +- **Non-dimensional rate derivatives**: controls-friendly output (hat_p, hat_q, hat_r) +- **Reference point flexibility**: correctly handles moment reference point for rotational velocities +- **Interactive visualization**: Plotly and Matplotlib geometry and results plotting + ## Reference Frame The VSM uses a body-fixed reference frame with the following conventions: @@ -32,17 +43,6 @@ The reference frame is illustrated below for the open-source example kite, the [ **Aircraft Frame Transformation:** For stability derivatives in standard aircraft coordinates (x-forward, y-right, z-down), use `map_derivatives_to_aircraft_frame()` from the `VSM.stability_derivatives` module. See `examples/TUDELFT_V3_KITE/evaluate_stability_derivatives.py` for usage. -## Key Features - -- **Accurate low-aspect-ratio wing modeling** with enhanced lifting line theory -- **Viscous-inviscid coupling** using 2D airfoil polars (inviscid, CFD, ML-based) -- **Complex geometry support**: sweep, dihedral, anhedral, leading-edge inflatable (LEI) kites -- **Rigid-body stability derivatives**: automatic computation of dCx/dα, dCMy/dq, etc. -- **Trim angle solver**: automatic determination of trimmed angle of attack -- **Non-dimensional rate derivatives**: controls-friendly output (hat_p, hat_q, hat_r) -- **Reference point flexibility**: correctly handles moment reference point for rotational velocities -- **Interactive visualization**: Plotly and Matplotlib geometry and results plotting - ## Documentation For detailed documentation, please refer to the following resources. From 3cc38965868081e39dfececc8de214e816bb76bc Mon Sep 17 00:00:00 2001 From: jellepoland Date: Fri, 24 Jul 2026 17:41:07 +0200 Subject: [PATCH 09/13] radme --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7028e4c..2376db6 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,6 @@ A Julia version of this project is available at [VortexStepMethod.jl](https://gi ## Key Features - -![](docs/plotly_render.png) - - **Accurate low-aspect-ratio wing modeling** with enhanced lifting line theory - **Viscous-inviscid coupling** using 2D airfoil polars (inviscid, CFD, ML-based) - **Complex geometry support**: sweep, dihedral, anhedral, leading-edge inflatable (LEI) kites @@ -16,6 +13,8 @@ A Julia version of this project is available at [VortexStepMethod.jl](https://gi - **Non-dimensional rate derivatives**: controls-friendly output (hat_p, hat_q, hat_r) - **Reference point flexibility**: correctly handles moment reference point for rotational velocities - **Interactive visualization**: Plotly and Matplotlib geometry and results plotting + +![](docs/plotly_render.png) ## Reference Frame From c58337d997bc574d1a7f41a2db89b3967558a3b3 Mon Sep 17 00:00:00 2001 From: jellepoland Date: Wed, 2 Sep 2026 11:16:15 +0200 Subject: [PATCH 10/13] Release v2.2.0: fix body_axis in angle-derivative evaluation, drop debug plot, add changelog - stability_derivatives._evaluate_with_angles now passes body_axis so baseline roll/pitch rates are not silently mapped to the z-axis - trim_angle: remove leftover matplotlib debug plot/plt.show() in coarse sweep - changelog: add 2.2.0 entry (rotational-inflow sign fix, body_rates/body_axis API, bridle 'd' key, solver/filament changes, plotly plotting) Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01GmKZURQ7nvjtTzZXXqE6ze --- changelog.md | 52 ++++++++++++++++++++++++++++++++ src/VSM/stability_derivatives.py | 1 + src/VSM/trim_angle.py | 4 --- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index 410043c..3daddb9 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,58 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [2.2.0] - 02/09/2026 + +Release cut from the `improve_plotting` branch. This is the version used to +generate the rigid-body stability derivatives (Table 8) of Poland et al. (2026), +Wind Energ. Sci., "Computational aerodynamics for soft-wing kite design". + +### Fixed +- `BodyAerodynamics.va` setter: the rotational inflow due to body rates is now + `va -= omega x (r - r0)` (apparent wind = freestream minus body-point velocity). + v2.1.2 used `+=`, i.e. the body rotating at `-omega`. **This flips the sign of + every rate derivative** (`d/dp_hat`, `d/dq_hat`, `d/dr_hat`) computed with + `stability_derivatives.py`; angle derivatives and trim angles are unaffected. + Roll, pitch and yaw damping derivatives are now negative, as physics requires. +- `stability_derivatives.py::_evaluate_with_angles` now passes `body_axis`, so a + non-zero baseline roll/pitch rate is no longer silently applied about the z-axis. +- `trim_angle.py`: removed leftover debug `matplotlib` plot/`plt.show()` from the + coarse sweep (blocked headless pipelines). +- `Filament`: vortex-core regularization uses the radial projection of the + evaluation point onto the filament. +- `WingGeometry`: section meshing is orientation-agnostic. +- Elliptical-wing y-offset bug in tests fixed; bridle line index handling fixed. + +### Changed +- **Breaking:** `BodyAerodynamics.va_initialize` / `va` setter no longer accept + `roll_rate`, `pitch_rate`, `yaw_rate`. Use `body_rates` (scalar or list of + magnitudes, rad/s), `body_axis` (matching axes, default +z) and + `rates_in_body_frame`. `compute_trim_angle` and + `compute_rigid_body_stability_derivatives` keep their `roll_rate/pitch_rate/yaw_rate` + keyword arguments. +- **Breaking:** bridle-line dictionaries use header key `d` (line diameter) + instead of `diameter`, matching the SurfPlanAdapter export. +- `BodyAerodynamics.instantiate` accepts `aerodynamic_center_location` and + `control_point_location` (chord fractions, defaults 0.25 / 0.75); AC and CP + chord weights are decoupled. +- `Solver`: opt-in Anderson-accelerated inner circulation loop; opt-in + Li/Gaunaa spanwise artificial viscosity for post-stall stabilization; + `relaxation_factor` default restored to 0.01. +- `Solver.solve` results include `q_ref` (reference dynamic pressure); coefficients + are normalised with an area-weighted reference velocity for distributed inflow. +- Quasi-steady-state solver API and examples added and subsequently removed. + +### Added +- `BodyAerodynamics.rotate(angle_deg|angle_rad, axis, point)` to rotate the full + geometry; body rates follow the geometry unless `rates_in_body_frame=True`. +- `BodyAerodynamics.compute_flat_area`. +- `src/VSM/plotly/`: interactive Plotly geometry plots with inflatable tubes, + curved canopy, Cp-scaled force fields (`fancy_interactive_plot.py` example). +- Examples: `compute_trim_angle.py`, `calculate_max_roll.py`, + `attitude_and_yaw_rate_optimize.py`, `cmz_sensitivity_default.py`, + `varying_spanwise_va.py`. +- Tests for gamma initialization, tube geometry, updated filament/solver tests. + ## [2.1.0] - 11/02/2026 ### Added diff --git a/src/VSM/stability_derivatives.py b/src/VSM/stability_derivatives.py index 0489689..527008c 100644 --- a/src/VSM/stability_derivatives.py +++ b/src/VSM/stability_derivatives.py @@ -183,6 +183,7 @@ def _evaluate_with_angles(alpha_deg: float, beta_deg: float) -> CoeffDict: angle_of_attack=alpha_deg, side_slip=beta_deg, body_rates=[rates["r"], rates["q"], rates["p"]], + body_axis=[[0, 0, 1], [0, 1, 0], [1, 0, 0]], reference_point=reference_point, ) return _solve_and_extract() diff --git a/src/VSM/trim_angle.py b/src/VSM/trim_angle.py index 1207c8c..cb2402e 100644 --- a/src/VSM/trim_angle.py +++ b/src/VSM/trim_angle.py @@ -132,10 +132,6 @@ def _compute_derivative(alpha_deg: float, delta_deg: float) -> float: cmy_coarse.append(cmy_value) cmy_coarse_array = np.asarray(cmy_coarse) - import matplotlib.pyplot as plt - - plt.plot(alpha_coarse, cmy_coarse_array, marker="o") - plt.show() valid_mask = ~np.isnan(cmy_coarse_array) valid_alphas = alpha_coarse[valid_mask] valid_cmy = cmy_coarse_array[valid_mask] From 3cb7bd0c377f3bb6badc47a359914a779ac97804 Mon Sep 17 00:00:00 2001 From: ocayon Date: Tue, 3 Nov 2026 14:54:08 +0100 Subject: [PATCH 11/13] feat(solver): polar tables as inputs of the CasADi panel functions On a deforming wing each panel's blended polar changes slightly every coupled iteration. The CasADi residual and panel-force functions embedded the tables as constants and were cached per table set, so a caller that re-trims deformed shapes (AWETrim's CasadiTrim) rebuilt its graph every iteration and could not afford to SX-expand it. - _casadi_newton_function(table_inputs=False) and casadi_panel_force_function(table_inputs=False): with True the functions take cl_table, cd_table, cm_table, each (n_panels, n_alpha), as extra inputs and are cached per (panel count, alpha grid, settings), not per table values. The lookup is one 1-D parametric linear interpolant on the shared grid, mapped over panels: same nodes and clamping, so the outputs equal the embedded-table ones to round-off. Omitted tables default to NaN, never to a silently zero polar. - Solver.casadi_polar_table_inputs() and casadi_stall_angle_inputs(): the current tables and stall-gate angles in the input layout. - Linear polars on one shared alpha grid only (a parametric cubic spline refits per call, ~0.1 s); other cases raise a clear ValueError. VSM's own loops keep the embedded-table functions and are unchanged: all solve outputs are bit-identical to a801b5b (4 loop types, both is_aoa_corrected settings, viscosity on/off). Expanded to SX, the table function evaluates as fast as the embedded one (+2% at 135 panels). Generated with Claude Code --- src/VSM/core/Solver.py | 210 ++++++++++++++++-- .../Solver/test_casadi_polar_table_inputs.py | 168 ++++++++++++++ 2 files changed, 365 insertions(+), 13 deletions(-) create mode 100644 tests/Solver/test_casadi_polar_table_inputs.py diff --git a/src/VSM/core/Solver.py b/src/VSM/core/Solver.py index 96d3766..bb03468 100644 --- a/src/VSM/core/Solver.py +++ b/src/VSM/core/Solver.py @@ -6,6 +6,26 @@ from VSM.core import panel_kernels + +def _table_symbols(n_panels: int, alpha_tables) -> dict: + """Symbolic ``(n_panels, n_alpha)`` polar tables for ``table_inputs``.""" + import casadi as ca + + n_alpha = int(np.asarray(alpha_tables[0]).size) + return {name: ca.MX.sym(f"{name}_table", n_panels, n_alpha) for name in ("cl", "cd", "cm")} + + +def _with_table_inputs(inputs: list, input_names: list, table_syms): + """Append the table inputs (if any) and default them to NaN, so a call + that omits them (CasADi fills omitted inputs with 0 by default) gives NaN + rather than a silently zero polar.""" + if table_syms is None: + return inputs, input_names, {} + inputs = inputs + [table_syms[k] for k in ("cl", "cd", "cm")] + input_names = input_names + ["cl_table", "cd_table", "cm_table"] + defaults = [0.0] * (len(inputs) - 3) + [float("nan")] * 3 + return inputs, input_names, {"default_in": defaults} + class Solver: """Solver for aerodynamic circulation distribution and force computation. @@ -203,6 +223,7 @@ def __init__( self._newton_reject_ratio = 0.5 self._casadi_newton_cache: dict = {} self._casadi_newton_loop_cache: dict = {} + self._casadi_table_input_cache: dict = {} # polar tables as inputs #: Diagnostic: did the last casadi_newton solve fall back to base? self.last_fallback = False #: Diagnostic: residual evaluations used by the last casadi_newton solve. @@ -1030,12 +1051,91 @@ def _casadi_newton_key(self, alpha_tables, cl_tables, cd_tables, cm_tables) -> t digest.hexdigest(), ) - def _casadi_polar_lookups(self, alpha_tables, cl_tables, cd_tables=None, cm_tables=None): + def _casadi_table_input_key(self, alpha_tables) -> tuple: + """Cache key of the functions that take the polar tables as INPUTS + (``table_inputs=True``): the graph depends on the panel count, the + shared alpha grid, the interpolation and the regularization settings, + not on the table values, so one function serves every shape whose + blended polars change but keep the grid.""" + self._check_table_inputs_supported(alpha_tables) + grid = np.ascontiguousarray(alpha_tables[0], dtype=float) + return ( + "table_inputs", + self.n_panels, + bool(self.is_with_artificial_viscosity), + float(self.artificial_viscosity_factor), + self.polar_interpolation, + hashlib.blake2b(grid.tobytes(), digest_size=16).hexdigest(), + ) + + def _check_table_inputs_supported(self, alpha_tables) -> None: + if self.polar_interpolation != "linear": + # A parametric cubic spline refits its coefficients on every call + # (~0.1 s per evaluation), which defeats the purpose. + raise ValueError( + "Polar tables as CasADi inputs need polar_interpolation='linear' " + f"(got {self.polar_interpolation!r})." + ) + if not all( + table.shape == alpha_tables[0].shape and np.array_equal(table, alpha_tables[0]) + for table in alpha_tables[1:] + ): + raise ValueError( + "Polar tables as CasADi inputs need every panel polar on one " + "shared alpha grid." + ) + + def casadi_polar_table_inputs(self) -> dict: + """The current panels' polar tables in the layout of the ``table_inputs`` + CasADi functions (:meth:`_casadi_newton_function` and + :meth:`casadi_panel_force_function` with ``table_inputs=True``): + ``{"cl_table", "cd_table", "cm_table"}``, each ``(n_panels, n_alpha)``, + row ``i`` = panel ``i``'s coefficients on the shared alpha grid + (``panel.panel_polar_data[:, 0]``, radians). + + Pass them on every call, next to the geometry: a deformed shape blends + its section polars with new weights, so the values change while the + grid, and with it the function, stays the same. + """ + alpha_tables, cl_tables, cd_tables, cm_tables = self._polar_tables() + self._check_table_inputs_supported(alpha_tables) + return { + "cl_table": np.vstack(cl_tables), + "cd_table": np.vstack(cd_tables), + "cm_table": np.vstack(cm_tables), + } + + def casadi_stall_angle_inputs(self) -> dict: + """``{"stall_angles", "stall_angles_neg"}`` [rad] of the current panel + polars, the gate inputs of the residual function as + :meth:`gamma_loop_casadi_newton` passes them (plus / minus infinity + without artificial viscosity). They are derived from the tables, so a + caller passing new tables passes these new values with them.""" + if self.is_with_artificial_viscosity: + return { + "stall_angles": self._panel_stall_angles(), + "stall_angles_neg": self._panel_negative_stall_angles(), + } + return { + "stall_angles": np.full(self.n_panels, np.inf), + "stall_angles_neg": np.full(self.n_panels, -np.inf), + } + + def _casadi_polar_lookups( + self, alpha_tables, cl_tables, cd_tables=None, cm_tables=None, table_symbols=None + ): """Clamped CasADi lookups ``alpha (n) -> cl, cd, cm (n)`` of the per-panel polars, shared by :meth:`_build_casadi_newton_function` and :meth:`_build_casadi_panel_force_function` (see the former for the interpolation). Returns ``(cl_of, cd_of, cm_of, smooth)``, ``smooth`` True for "bspline" polars. + + ``table_symbols`` (``{"cl": MX, "cd": MX, "cm": MX}``, each + ``(n, n_alpha)``) makes the table VALUES symbolic inputs instead of + constants: one 1-D parametric linear interpolant on the shared grid, + mapped over the panels. It reproduces the constant table exactly + (same nodes, same clamping); only the shared-grid linear case is + supported (see :meth:`_check_table_inputs_supported`). """ import casadi as ca @@ -1059,6 +1159,33 @@ def _casadi_polar_lookups(self, alpha_tables, cl_tables, cd_tables=None, cm_tabl kind = self.polar_interpolation smooth = kind == "bspline" + if table_symbols is not None: + self._check_table_inputs_supported(alpha_tables) + grid = np.asarray(alpha_tables[0], dtype=float) + lo, hi = float(grid[0]), float(grid[-1]) + row_interpolant = ca.interpolant("polar_row", "linear", [grid], 1) + alpha_row = ca.MX.sym("alpha_row") + values_row = ca.MX.sym("values_row", grid.size) + # One panel's lookup; .map(n) evaluates it column-wise over panels. + row_lookup = ca.Function( + "polar_row_lookup", + [alpha_row, values_row], + [row_interpolant(ca.fmin(ca.fmax(alpha_row, lo), hi), values_row)], + ).map(n) + + def make_parametric_lookup(table): + def lookup(alpha_vec): + return row_lookup(alpha_vec.T, table.T).T + + return lookup + + return ( + make_parametric_lookup(table_symbols["cl"]), + make_parametric_lookup(table_symbols["cd"]), + make_parametric_lookup(table_symbols["cm"]), + smooth, + ) + def make_lookup(name, value_tables): """Clamped lookup ``alpha_vec (n) -> values (n)`` of one polar column.""" if shared_grid: @@ -1104,7 +1231,9 @@ def lookup(alpha_vec): cm_of = make_lookup("cm", cm_tables) return cl_of, cd_of, cm_of, smooth - def _build_casadi_newton_function(self, alpha_tables, cl_tables, cd_tables=None, cm_tables=None): + def _build_casadi_newton_function( + self, alpha_tables, cl_tables, cd_tables=None, cm_tables=None, table_inputs=False + ): """Build the CasADi function of the PER-PANEL section physics that :meth:`gamma_loop_casadi_newton` assembles its residual and exact Jacobian from. @@ -1154,12 +1283,18 @@ def _build_casadi_newton_function(self, alpha_tables, cl_tables, cd_tables=None, Jacobian), ``cl``, ``cd``, ``cm`` at ``alpha`` and their corner- averaged counterparts ``cl_avg``, ``cd_avg``, ``cm_avg``. With "bspline" polars the averaged outputs equal the exact ones. + + ``table_inputs=True`` appends the inputs ``cl_table``, ``cd_table``, + ``cm_table`` (each ``(n, n_alpha)``, :meth:`casadi_polar_table_inputs`) + and takes the table values from them instead of embedding them; see + :meth:`_casadi_newton_function`. """ import casadi as ca n = self.n_panels + table_syms = _table_symbols(n, alpha_tables) if table_inputs else None cl_of, cd_of, cm_of, smooth = self._casadi_polar_lookups( - alpha_tables, cl_tables, cd_tables, cm_tables + alpha_tables, cl_tables, cd_tables, cm_tables, table_symbols=table_syms ) v_rel = ca.MX.sym("v_rel", n, 3) @@ -1244,9 +1379,12 @@ def averaged(lookup): seeds = [ca.DM(np.eye(3)[k][None, :].repeat(n, axis=0)) for k in range(3)] dh_dvrel = ca.horzcat(*[ca.jtimes(h_for_jacobian, v_rel, seed) for seed in seeds]) + inputs = [v_rel, x_airf, y_airf, z_airf, chord, width, stall_angles, stall_angles_neg, l_gamma] + input_names = ["v_rel", "x_airf", "y_airf", "z_airf", "chord", "width", "stall_angles", "stall_angles_neg", "l_gamma"] + inputs, input_names, options = _with_table_inputs(inputs, input_names, table_syms) return ca.Function( - "vsm_panel_residual", - [v_rel, x_airf, y_airf, z_airf, chord, width, stall_angles, stall_angles_neg, l_gamma], + "vsm_panel_residual_tables" if table_inputs else "vsm_panel_residual", + inputs, [ h, dh_dvrel, @@ -1262,11 +1400,29 @@ def averaged(lookup): averaged(cd_of), averaged(cm_of), ], - ["v_rel", "x_airf", "y_airf", "z_airf", "chord", "width", "stall_angles", "stall_angles_neg", "l_gamma"], + input_names, ["h", "dh_dvrel", "mu", "alpha", "umag", "gate", "h_avg", "cl", "cd", "cm", "cl_avg", "cd_avg", "cm_avg"], + options, ) - def _casadi_newton_function(self): + def _casadi_newton_function(self, table_inputs: bool = False): + """The per-panel residual function (:meth:`_build_casadi_newton_function`) + of the current panels, cached. + + ``table_inputs=False`` (default, what :meth:`gamma_loop_casadi_newton` + uses): the polar tables are constants of the graph, cached per table + set. ``table_inputs=True``: the tables are the extra inputs + ``cl_table, cd_table, cm_table``, cached per alpha grid, so a caller + that re-trims deformed shapes builds the function once and passes + :meth:`casadi_polar_table_inputs` (and :meth:`casadi_stall_angle_inputs`) + per shape. Same outputs to round-off; needs ``polar_interpolation= + "linear"`` and one shared alpha grid. Omitted table inputs default to + NaN, so a call that forgets them fails loudly instead of using zeros. + """ + if table_inputs: + return self._cached_table_input_function( + "newton", self._build_casadi_newton_function + ) alpha_tables, cl_tables, cd_tables, cm_tables = self._polar_tables() key = self._casadi_newton_key(alpha_tables, cl_tables, cd_tables, cm_tables) fn = self._casadi_newton_cache.get(key) @@ -1282,7 +1438,7 @@ def _casadi_newton_function(self): return fn def _build_casadi_panel_force_function( - self, alpha_tables, cl_tables, cd_tables=None, cm_tables=None + self, alpha_tables, cl_tables, cd_tables=None, cm_tables=None, table_inputs=False ): """CasADi function of the per-panel section forces, the force law of ``compute_results`` (``VSM.core.panel_kernels.panel_forces``) on the @@ -1291,8 +1447,9 @@ def _build_casadi_panel_force_function( import casadi as ca n = self.n_panels + table_syms = _table_symbols(n, alpha_tables) if table_inputs else None cl_of, cd_of, cm_of, _ = self._casadi_polar_lookups( - alpha_tables, cl_tables, cd_tables, cm_tables + alpha_tables, cl_tables, cd_tables, cm_tables, table_symbols=table_syms ) v_rel = ca.MX.sym("v_rel", n, 3) v_qc = ca.MX.sym("v_qc", n, 3) @@ -1310,15 +1467,21 @@ def _build_casadi_panel_force_function( alpha, umag, cl, cd, cm, chord, width, x_airf, y_airf, z_airf, rho, alpha_dir=alpha_dir, xp=ca, ) - return ca.Function( - "vsm_panel_forces", + inputs, input_names, options = _with_table_inputs( [v_rel, v_qc, x_airf, y_airf, z_airf, chord, width, rho], - [force, moment_local, alpha, alpha_dir, umag, cl, cd, cm], ["v_rel", "v_qc", "x_airf", "y_airf", "z_airf", "chord", "width", "rho"], + table_syms, + ) + return ca.Function( + "vsm_panel_forces_tables" if table_inputs else "vsm_panel_forces", + inputs, + [force, moment_local, alpha, alpha_dir, umag, cl, cd, cm], + input_names, ["force", "moment_local", "alpha", "alpha_dir", "umag", "cl", "cd", "cm"], + options, ) - def casadi_panel_force_function(self): + def casadi_panel_force_function(self, table_inputs: bool = False): """CasADi function of the per-panel section forces of the current panels (the ones of the last :meth:`solve`), cached per polar set like the ``casadi_newton`` residual function. @@ -1343,9 +1506,16 @@ def casadi_panel_force_function(self): vortex force (``panel_kernels.attached_trailed_forces``) and the viscous drag correction. Taking ``v_qc`` rather than the matrices keeps the graph O(n); the caller forms the dense product. + + ``table_inputs=True`` appends ``cl_table, cd_table, cm_table`` as + inputs (see :meth:`_casadi_newton_function`). """ import casadi # noqa: F401 (clear error when the optional dep is missing) + if table_inputs: + return self._cached_table_input_function( + "panel_forces", self._build_casadi_panel_force_function + ) alpha_tables, cl_tables, cd_tables, cm_tables = self._polar_tables() key = ("panel_forces",) + self._casadi_newton_key( alpha_tables, cl_tables, cd_tables, cm_tables @@ -1360,6 +1530,20 @@ def casadi_panel_force_function(self): self._casadi_newton_cache[key] = fn return fn + def _cached_table_input_function(self, kind: str, builder): + """Function of ``kind`` with the polar tables as inputs, built once + per (panel count, alpha grid, settings) and then reused for any + table values.""" + alpha_tables, cl_tables, cd_tables, cm_tables = self._polar_tables() + key = (kind,) + self._casadi_table_input_key(alpha_tables) + fn = self._casadi_table_input_cache.get(key) + if fn is None: + fn = builder(alpha_tables, cl_tables, cd_tables, cm_tables, table_inputs=True) + if len(self._casadi_table_input_cache) >= 32: + self._casadi_table_input_cache.pop(next(iter(self._casadi_table_input_cache))) + self._casadi_table_input_cache[key] = fn + return fn + def _casadi_newton_loop_function(self): """The residual function restricted to the six outputs the Newton loop reads (``h, dh_dvrel, mu, alpha, umag, gate``): the same graph, minus diff --git a/tests/Solver/test_casadi_polar_table_inputs.py b/tests/Solver/test_casadi_polar_table_inputs.py new file mode 100644 index 0000000..34ed806 --- /dev/null +++ b/tests/Solver/test_casadi_polar_table_inputs.py @@ -0,0 +1,168 @@ +"""Polar tables as inputs of the CasADi panel functions (``table_inputs=True``). + +Contract: with the tables passed as inputs, the residual and panel-force +functions give the same outputs as the functions with the tables embedded; +one function, built once, serves a second shape whose blended polars differ +(no rebuild); omitted tables give NaN, not a silently zero polar; and the +function still expands to SX. +""" + +import sys +from pathlib import Path + +import numpy as np +import pytest + +ca = pytest.importorskip("casadi") + +REPO = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(REPO / "tests" / "BodyAerodynamics")) + +from panel_kernel_cases import V3_FILE # noqa: E402 +from VSM.core.BodyAerodynamics import BodyAerodynamics # noqa: E402 +from VSM.core.Solver import Solver # noqa: E402 + +RTOL = 1e-12 + + +def _v3(distribution): + body = BodyAerodynamics.instantiate( + n_panels=20, file_path=V3_FILE, spanwise_panel_distribution=distribution + ) + body.va_initialize(10.0, 8.0, 3.0) + return body + + +def _solved(body, **solver_kwargs): + solver = Solver(gamma_loop_type="casadi_newton", allowed_error=1e-10, **solver_kwargs) + results = solver.solve(body) + return solver, np.asarray(results["gamma_distribution"], dtype=float) + + +def _newton_inputs(solver, gamma): + v_rel = solver.compute_relative_velocity(gamma) + n = solver.n_panels + laplacian = ( + solver._build_spanwise_laplacian() + if solver.is_with_artificial_viscosity + else np.zeros((n, n)) + ) + return { + "v_rel": v_rel, + "x_airf": solver.x_airf_array, + "y_airf": solver.y_airf_array, + "z_airf": solver.z_airf_array, + "chord": solver.chord_array, + "width": solver.width_array, + "l_gamma": laplacian @ gamma, + **solver.casadi_stall_angle_inputs(), + } + + +def _force_inputs(solver, gamma): + v_rel = solver.compute_relative_velocity(gamma) + return { + "v_rel": v_rel, + "v_qc": v_rel, + "x_airf": solver.x_airf_array, + "y_airf": solver.y_airf_array, + "z_airf": solver.z_airf_array, + "chord": solver.chord_array, + "width": solver.width_array, + "rho": solver.rho, + } + + +def _assert_outputs_equal(out_tables, out_const): + assert set(out_tables) == set(out_const) + for key, value in out_const.items(): + expected = np.array(value, dtype=float) + actual = np.array(out_tables[key], dtype=float) + scale = max(np.max(np.abs(expected)), 1e-300) + np.testing.assert_allclose(actual, expected, rtol=0, atol=RTOL * scale, err_msg=key) + + +@pytest.mark.parametrize("viscosity", [False, True]) +def test_table_inputs_reproduce_embedded_tables(viscosity): + """Residual function (all 13 outputs, incl. the corner-averaged ones and + the viscosity lift slope) and panel-force function, against the + embedded-table functions of the same shape.""" + solver, gamma = _solved(_v3("uniform"), is_with_artificial_viscosity=viscosity) + tables = solver.casadi_polar_table_inputs() + assert tables["cl_table"].shape == (solver.n_panels, solver.panels[0].panel_polar_data.shape[0]) + + args = _newton_inputs(solver, gamma) + if viscosity: # put a panel past stall so the gate and mu are exercised + args["stall_angles"] = args["stall_angles"] - 0.3 + _assert_outputs_equal( + solver._casadi_newton_function(table_inputs=True)(**args, **tables), + solver._casadi_newton_function()(**args), + ) + f_args = _force_inputs(solver, gamma) + _assert_outputs_equal( + solver.casadi_panel_force_function(table_inputs=True)(**f_args, **tables), + solver.casadi_panel_force_function()(**f_args), + ) + + +def test_one_function_serves_a_second_shape_without_rebuild(): + """Shapes A and B have the same panel count and alpha grid but differently + blended polars. The table-input function built for A, given B's tables and + geometry, equals a fresh embedded-table build for B, and no new + table-input function is built.""" + solver_a, gamma_a = _solved(_v3("uniform")) + fn = solver_a._casadi_newton_function(table_inputs=True) + force_fn = solver_a.casadi_panel_force_function(table_inputs=True) + cache_size = len(solver_a._casadi_table_input_cache) + + body_b = _v3("cosine") + tables_a = solver_a.casadi_polar_table_inputs() + solver_a.solve(body_b) # same solver object, now on shape B + gamma_b = solver_a.solve(body_b)["gamma_distribution"] + tables_b = solver_a.casadi_polar_table_inputs() + assert np.max(np.abs(tables_b["cl_table"] - tables_a["cl_table"])) > 1e-3 # the polars differ + + assert solver_a._casadi_newton_function(table_inputs=True) is fn + assert solver_a.casadi_panel_force_function(table_inputs=True) is force_fn + assert len(solver_a._casadi_table_input_cache) == cache_size + + fresh_b, _ = _solved(_v3("cosine")) + _assert_outputs_equal( + fn(**_newton_inputs(solver_a, gamma_b), **tables_b), + fresh_b._casadi_newton_function()(**_newton_inputs(solver_a, gamma_b)), + ) + _assert_outputs_equal( + force_fn(**_force_inputs(solver_a, gamma_b), **tables_b), + fresh_b.casadi_panel_force_function()(**_force_inputs(solver_a, gamma_b)), + ) + + +def test_omitted_tables_give_nan_not_zero(): + solver, gamma = _solved(_v3("uniform")) + out = solver._casadi_newton_function(table_inputs=True)(**_newton_inputs(solver, gamma)) + assert np.all(np.isnan(np.array(out["h"], dtype=float))) + out = solver.casadi_panel_force_function(table_inputs=True)(**_force_inputs(solver, gamma)) + assert np.all(np.isnan(np.array(out["force"], dtype=float))) + + +def test_table_input_functions_expand_to_sx(): + solver, gamma = _solved(_v3("uniform"), is_with_artificial_viscosity=True) + tables = solver.casadi_polar_table_inputs() + for fn, args in ( + (solver._casadi_newton_function(table_inputs=True), _newton_inputs(solver, gamma)), + (solver.casadi_panel_force_function(table_inputs=True), _force_inputs(solver, gamma)), + ): + _assert_outputs_equal(fn.expand()(**args, **tables), fn(**args, **tables)) + + +def test_newton_loop_is_unchanged_and_does_not_build_table_functions(): + solver, _ = _solved(_v3("uniform")) + assert solver._casadi_table_input_cache == {} + + +def test_unsupported_cases_raise_clearly(): + solver, _ = _solved(_v3("uniform"), polar_interpolation="bspline") + with pytest.raises(ValueError, match="polar_interpolation='linear'"): + solver._casadi_newton_function(table_inputs=True) + with pytest.raises(ValueError, match="polar_interpolation='linear'"): + solver.casadi_polar_table_inputs() From 0a051a76a68ff233c75fc4ae06c0a5892af0c873 Mon Sep 17 00:00:00 2001 From: ocayon Date: Tue, 3 Nov 2026 16:29:00 +0100 Subject: [PATCH 12/13] docs(changelog): 2.3.0 entry Lists the default-result changes of the consistent lifting-line coupling (each with the option that restores the old behaviour where one exists), the removed options, the new CasADi APIs, fixes and speedups, and notes that the release includes the 2.2.0 content that had not been merged. Generated with Claude Code --- changelog.md | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/changelog.md b/changelog.md index 3daddb9..707ff01 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,110 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [2.3.0] - 03/11/2026 + +**Default results change.** The lifting-line coupling now follows the consistent +formulation of Gaunaa, Li & Pirrung (TORQUE 2026): circulation from the inner +velocity, span-perpendicular sections, quarter-chord force directions, +attached-trailed-vortex forces and a chord-regularised curved bound vortex. +Forces, moments and coefficients computed with the defaults differ from 2.2.0, +most on swept and curved wings (kites). Each new default can be switched back +individually where an option exists (listed below). See +`docs/aerodynamic_model` for the formulation and its validation. + +This release also contains everything shipped in 2.2.0. `v2.2.0` was tagged on +the `improve_plotting` branch, which had not been merged into `develop`/`main`. + +### Changed (results) +- Circulation target is `Gamma = 0.5 |V_inner| c Cl` with the inner + (span-perpendicular, own-2D-induction-removed) velocity, instead of + `0.5 Umag^2 / Umagw c Cl`. On the V3 kite the old map put the circulation + 2-6 % low. +- Panel frame and chord follow the Crossflow Principle: the chord direction is + perpendicular to the bound vortex, the normal is chord x span, and the panel + chord is the perpendicular one. Unswept panels are unchanged; on swept panels + the chord shrinks (V3 tip panels by 56 % and 19 %). +- `is_aoa_corrected=True` by default: lift and drag directions come from the + flow at the quarter chord (TAT3), with magnitudes from the 3/4-chord angle. + `is_aoa_corrected=False` restores the 3/4-chord directions. +- The quarter-chord flow includes the influence of a curved or swept bound + vortex through a chord-regularised correction (the actual minus a locally + unrolled vortex system). On-blade induced drag matches the Trefftz plane + within about 1 % on arc, swept and kite geometries, independently of the + panel count. It is computed once per shape and rotated under rigid motion. +- Drag acts along the local inner flow, not the global span axis. +- `is_with_attached_trailed_vortex_force=True` by default: Kutta-Joukowski force + on the chordwise attached trailed vortex segments (`F_attached_trailed_distribution` + in the results). +- `relaxation_factor=None` by default: 0.8 x the fixed-point stability limit of + Li et al. (2026), evaluated per solve (`Solver.compute_relaxation_factor_limit`). + 2.2.0 used a fixed 0.01; pass a number to keep a fixed value. +- Each wake ring follows its own panel's apparent velocity. Results in uniform + translating inflow are unchanged to round-off; they change under body rates or + distributed inflow. +- Bridle line segments are charged at their own inflow, not the wing's mean. +- With `is_with_viscous_drag_correction=True`, the Gaunaa 2024 correction receives + the full 3D relative velocity (the spanwise flow it models). +- With `is_with_artificial_viscosity=True`, the regularization is also gated on + negative stall. +- `Wing.preserve_section_order` (default False): skip the section re-sorting; + `update_wing_from_points` sets it, since its arrays already define the arc order. + +### Removed / breaking +- `Solver` options `is_smooth_circulation`, `smoothness_factor`, + `is_artificial_damping`, `artificial_damping`, + `is_with_simonet_artificial_viscosity`, `simonet_artificial_viscosity_fva` + (stored, never read) and the `gamma_loop_type` values `simonet_stall`, + `non_linear_simonet_stall`, `non_linear_simonet_stall_newton_raphson` (they + raised `ImportError`). An unknown `gamma_loop_type` now raises `ValueError`. +- `Solver.compute_aerodynamic_quantities` no longer returns `Umagw`; the relative + velocity is available from `Solver.compute_relative_velocity(gamma)`. +- The Anderson loop no longer falls back to the base loop by default + (`anderson_fallback_to_base=True` restores it). + +### Added +- `gamma_loop_type="casadi_newton"`: Newton solve of the circulation residual + with an exact CasADi Jacobian, globalised by pseudo-transient continuation, + with fallback to the base loop (`newton_max_iterations`, + `newton_pseudo_time_step`, `newton_fallback_to_base`, `polar_interpolation`). + Requires the optional `casadi` package. +- `stagnation_patience` / `stagnation_rtol`: optional early stop of stagnating + circulation loops. +- `BodyAerodynamics(..., leg_direction="rib" | "streamwise" | "perpendicular")` + for the attached-trailed legs (default `"rib"`, unchanged). +- `results["drag_induced_trefftz"]`: Trefftz-plane induced drag as a far-wake + reference (uniform inflow only). +- `VSM.core.panel_kernels`: the section force law as functions over an `xp` + namespace (`numpy` or `casadi`): `relative_flow`, `quarter_chord_velocity`, + `direction_angle`, `panel_forces`, `attached_trailed_forces` and row helpers. + `compute_results` uses them itself. +- `BodyAerodynamics.compute_quarter_chord_AIC_matrices`: the induction of the + force-direction flow at the quarter chords, for callers that freeze it. +- `Solver.casadi_panel_force_function()`: per-panel forces as a CasADi function. + With `table_inputs=True` (also on the residual function) the polar tables are + inputs, so one graph serves every deformed shape; + `Solver.casadi_polar_table_inputs()` and `casadi_stall_angle_inputs()` return + them in the input layout. +- Documentation: aerodynamic model document (formulation, conventions, + validation) and the curved bound-vortex validation report. + +### Fixed +- Circulation loops stop on non-finite circulation instead of running on. +- `Solver`: SyntaxError in the pseudo-transient reject condition (hotfix on `main` + after 2.2.0, now also here). +- `Wing.compute_projected_area` no longer normalises the caller's + `z_plane_vector` in place. + +### Performance +- AIC assembly JIT-compiled with numba, then made allocation-free (vectors as + tuples): about 1 us -> 20 ns per filament evaluation, 7.3 -> 0.1 ms for a + 36-panel AIC. +- Curved bound-vortex correction computed once per shape and rotated under + rigid motion. +- Faster Li/Gaunaa artificial viscosity, vectorised force law, Trefftz drag and + projected area, cheaper `casadi_newton` residual calls. A 36-panel V3 + `casadi_newton` solve takes about 17 ms (about 50 ms before these changes). + ## [2.2.0] - 02/09/2026 Release cut from the `improve_plotting` branch. This is the version used to From b7debded311fd54bb268c1e15d283a5ff7259a5c Mon Sep 17 00:00:00 2001 From: ocayon Date: Wed, 4 Nov 2026 12:24:03 +0100 Subject: [PATCH 13/13] test(masure): skip the regression tests when the model files are absent The models in data/ml_models are git-ignored, so these six tests failed on every clean checkout, including CI. Skip the module when they are missing. test_masure_regression_requires_model_files checked data/models, a path that does not exist, so it was always skipped. The module-level check replaces it and that test now runs when the files are present. No source change. Test bug, no effect on results. Generated with Claude Code --- tests/AirfoilAerodynamics/test_masure.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/AirfoilAerodynamics/test_masure.py b/tests/AirfoilAerodynamics/test_masure.py index 76abfc7..f4e9148 100644 --- a/tests/AirfoilAerodynamics/test_masure.py +++ b/tests/AirfoilAerodynamics/test_masure.py @@ -6,6 +6,14 @@ from pathlib import Path from VSM.core.AirfoilAerodynamics import AirfoilAerodynamics +# The regression models are git-ignored, so they are absent on a clean checkout (CI). +pytestmark = pytest.mark.skipif( + not Path(__file__) + .parent.parent.parent.joinpath("data", "ml_models", "ET_re1e6.pkl") + .exists(), + reason="Real model files not available", +) + class TestMasureRegressionReal: """Test cases for masure_regression with real model file integration.""" @@ -196,12 +204,6 @@ def test_masure_regression_different_parameters( assert airfoil.CD is not None, f"CD should not be None for param set {i}" assert airfoil.CM is not None, f"CM should not be None for param set {i}" - @pytest.mark.skipif( - not Path(__file__) - .parent.parent.parent.joinpath("data", "models", "ET_re1e6.pkl") - .exists(), - reason="Real model files not available", - ) def test_masure_regression_requires_model_files( self, masure_params, small_alpha_range, test_reynolds, masure_config_path ):