From 748dd801c5451dec2fc8218781d289de4b57f36c Mon Sep 17 00:00:00 2001 From: sandstromviktor Date: Thu, 14 Mar 2024 10:52:19 +0100 Subject: [PATCH 1/3] added example dash app to repo --- .github/workflows/example-dash.yml | 66 +++++++ examples/dash/Dockerfile | 20 ++ examples/dash/Procfile | 1 + examples/dash/README.md | 11 ++ examples/dash/airplane.py | 260 ++++++++++++++++++++++++++ examples/dash/app.py | 258 +++++++++++++++++++++++++ examples/dash/requirements.txt | 13 ++ examples/dash/tests.py | 31 +++ serve-filemanager/branding/custom.css | 6 +- 9 files changed, 665 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/example-dash.yml create mode 100644 examples/dash/Dockerfile create mode 100644 examples/dash/Procfile create mode 100644 examples/dash/README.md create mode 100644 examples/dash/airplane.py create mode 100644 examples/dash/app.py create mode 100644 examples/dash/requirements.txt create mode 100644 examples/dash/tests.py diff --git a/.github/workflows/example-dash.yml b/.github/workflows/example-dash.yml new file mode 100644 index 0000000..4370bf9 --- /dev/null +++ b/.github/workflows/example-dash.yml @@ -0,0 +1,66 @@ +name: Dash example workflow + +on: + push: + paths: + - "examples/dash/**" + workflow_dispatch: + inputs: + logLevel: + description: 'Log level' + required: true + default: 'warning' + type: choice + options: + - info + - warning + - debug + tags: + description: 'Manual run' + required: false + type: boolean + + +jobs: + push: + if: | + github.ref == 'refs/heads/main' && + github.repository == 'scilifelabdatacentre/serve-images' + runs-on: ubuntu-latest + concurrency: + group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: true + permissions: + contents: read + packages: write + + steps: + - name: 'Checkout github action' + uses: actions/checkout@main + + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: ghcr.io/scilifelabdatacentre/example-dash + tags: | + type=raw,value={{date 'YYMMDD-HHmm' tz='Europe/Stockholm'}} + + - name: 'Login to GHCR' + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{github.actor}} + password: ${{secrets.GITHUB_TOKEN}} + + - name: Publish image to GHCR + uses: docker/build-push-action@v3 + with: + file: ./examples/dash/Dockerfile + context: ./examples/dash + push: true + build-args: version=${{ github.ref_name }} + tags: | + ${{ steps.meta.outputs.tags }} + ghcr.io/scilifelabdatacentre/example-dash:latest + labels: ${{ steps.meta.outputs.labels }} diff --git a/examples/dash/Dockerfile b/examples/dash/Dockerfile new file mode 100644 index 0000000..f1d8b28 --- /dev/null +++ b/examples/dash/Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.8-slim + +ENV USER=serve +ENV HOME=/home/$USER + +RUN apt-get update -yq \ + && useradd -m $USER \ + && pip install --upgrade --no-cache-dir pip \ + && rm -rf /var/lib/apt/lists/* + +COPY . $HOME/ + +RUN pip install --no-cache-dir --upgrade pip \ + && pip install --no-cache-dir -r $HOME/requirements.txt + +USER $USER +EXPOSE 8000 +WORKDIR $HOME + +ENTRYPOINT ["gunicorn", "app:server", "-b", "0.0.0.0:8000"] diff --git a/examples/dash/Procfile b/examples/dash/Procfile new file mode 100644 index 0000000..2c3403a --- /dev/null +++ b/examples/dash/Procfile @@ -0,0 +1 @@ +web: gunicorn app:server --workers 4 diff --git a/examples/dash/README.md b/examples/dash/README.md new file mode 100644 index 0000000..c4a6db8 --- /dev/null +++ b/examples/dash/README.md @@ -0,0 +1,11 @@ +# AeroSandbox-Interactive-Demo +by Peter Sharpe + +## Description +An interactive demo of AeroSandbox, powered by Dash! Work in progress. + +## Installation and Usage +1. Install all dependencies listed in `requirements.txt` - all packages are pip-installable. In particular, be sure to get a recent version of AeroSandbox (`pip install --upgrade aerosandbox`). +2. Run `app.py` to launch a local Dash server to host the Dash app. A link will appear in your console; click this to use the Dash app. + +## Illustration \ No newline at end of file diff --git a/examples/dash/airplane.py b/examples/dash/airplane.py new file mode 100644 index 0000000..d3a8229 --- /dev/null +++ b/examples/dash/airplane.py @@ -0,0 +1,260 @@ +import aerosandbox as asb +from aerosandbox.library.airfoils import e216 +import numpy as np +import casadi as cas +import copy + +naca0008 = asb.Airfoil("naca0008") + + +def make_airplane( + n_booms, wing_span, +): + # n_booms = 3 + + # wing + # wing_span = 37.126 + wing_root_chord = 2.316 + wing_x_quarter_chord = -0.1 + + # hstab + hstab_span = 2.867 + hstab_chord = 1.085 + hstab_twist_angle = -7 + + # vstab + vstab_span = 2.397 + vstab_chord = 1.134 + + # fuselage + boom_length = 6.181 + nose_length = 1.5 + fuse_diameter = 0.6 + boom_diameter = 0.2 + + wing = asb.Wing( + name="Main Wing", + # x_le=-0.05 * wing_root_chord, # Coordinates of the wing's leading edge # TODO make this a free parameter? + x_le=wing_x_quarter_chord, # Coordinates of the wing's leading edge # TODO make this a free parameter? + y_le=0, # Coordinates of the wing's leading edge + z_le=0, # Coordinates of the wing's leading edge + symmetric=True, + xsecs=[ # The wing's cross ("X") sections + asb.WingXSec( # Root + x_le=-wing_root_chord / 4, + # Coordinates of the XSec's leading edge, relative to the wing's leading edge. + y_le=0, # Coordinates of the XSec's leading edge, relative to the wing's leading edge. + z_le=0, # Coordinates of the XSec's leading edge, relative to the wing's leading edge. + chord=wing_root_chord, + twist=0, # degrees + airfoil=e216, # Airfoils are blended between a given XSec and the next one. + control_surface_type="symmetric", + # Flap # Control surfaces are applied between a given XSec and the next one. + control_surface_deflection=0, # degrees + spanwise_panels=30, + ), + asb.WingXSec( # Tip + x_le=-wing_root_chord * 0.5 / 4, + y_le=wing_span / 2, + z_le=0, # wing_span / 2 * cas.pi / 180 * 5, + chord=wing_root_chord * 0.5, + twist=0, + airfoil=e216, + ), + ], + ) + hstab = asb.Wing( + name="Horizontal Stabilizer", + x_le=boom_length + - vstab_chord * 0.75 + - hstab_chord, # Coordinates of the wing's leading edge + y_le=0, # Coordinates of the wing's leading edge + z_le=0.1, # Coordinates of the wing's leading edge + symmetric=True, + xsecs=[ # The wing's cross ("X") sections + asb.WingXSec( # Root + x_le=0, # Coordinates of the XSec's leading edge, relative to the wing's leading edge. + y_le=0, # Coordinates of the XSec's leading edge, relative to the wing's leading edge. + z_le=0, # Coordinates of the XSec's leading edge, relative to the wing's leading edge. + chord=hstab_chord, + twist=-3, # degrees # TODO fix + airfoil=naca0008, # Airfoils are blended between a given XSec and the next one. + control_surface_type="symmetric", + # Flap # Control surfaces are applied between a given XSec and the next one. + control_surface_deflection=0, # degrees + spanwise_panels=8, + ), + asb.WingXSec( # Tip + x_le=0, + y_le=hstab_span / 2, + z_le=0, + chord=hstab_chord, + twist=-3, # TODO fix + airfoil=naca0008, + ), + ], + ) + vstab = asb.Wing( + name="Vertical Stabilizer", + x_le=boom_length - vstab_chord * 0.75, # Coordinates of the wing's leading edge + y_le=0, # Coordinates of the wing's leading edge + z_le=-vstab_span / 2 + + vstab_span * 0.15, # Coordinates of the wing's leading edge + symmetric=False, + xsecs=[ # The wing's cross ("X") sections + asb.WingXSec( # Root + x_le=0, # Coordinates of the XSec's leading edge, relative to the wing's leading edge. + y_le=0, # Coordinates of the XSec's leading edge, relative to the wing's leading edge. + z_le=0, # Coordinates of the XSec's leading edge, relative to the wing's leading edge. + chord=vstab_chord, + twist=0, # degrees + airfoil=naca0008, # Airfoils are blended between a given XSec and the next one. + control_surface_type="symmetric", + # Flap # Control surfaces are applied between a given XSec and the next one. + control_surface_deflection=0, # degrees + spanwise_panels=8, + ), + asb.WingXSec( # Tip + x_le=0, + y_le=0, + z_le=vstab_span, + chord=vstab_chord, + twist=0, + airfoil=naca0008, + ), + ], + ) + ### Build the fuselage geometry + blend = lambda x: (1 - np.cos(np.pi * x)) / 2 + fuse_x_c = [] + fuse_z_c = [] + fuse_radius = [] + fuse_resolution = 10 + # Nose geometry + fuse_nose_theta = np.linspace(0, np.pi / 2, fuse_resolution) + fuse_x_c.extend( + [ + (wing_x_quarter_chord - wing_root_chord / 4) - nose_length * np.cos(theta) + for theta in fuse_nose_theta + ] + ) + fuse_z_c.extend([-fuse_diameter / 2] * fuse_resolution) + fuse_radius.extend([fuse_diameter / 2 * np.sin(theta) for theta in fuse_nose_theta]) + # Taper + fuse_taper_x_nondim = np.linspace(0, 1, fuse_resolution) + fuse_x_c.extend( + [ + 0.0 * boom_length + (0.6 - 0.0) * boom_length * x_nd + for x_nd in fuse_taper_x_nondim + ] + ) + fuse_z_c.extend( + [ + -fuse_diameter / 2 * blend(1 - x_nd) - boom_diameter / 2 * blend(x_nd) + for x_nd in fuse_taper_x_nondim + ] + ) + fuse_radius.extend( + [ + fuse_diameter / 2 * blend(1 - x_nd) + boom_diameter / 2 * blend(x_nd) + for x_nd in fuse_taper_x_nondim + ] + ) + # Tail + # fuse_tail_x_nondim = np.linspace(0, 1, fuse_resolution)[1:] + # fuse_x_c.extend([ + # 0.9 * boom_length + (1 - 0.9) * boom_length * x_nd for x_nd in fuse_taper_x_nondim + # ]) + # fuse_z_c.extend([ + # -boom_diameter / 2 * blend(1 - x_nd) for x_nd in fuse_taper_x_nondim + # ]) + # fuse_radius.extend([ + # boom_diameter / 2 * blend(1 - x_nd) for x_nd in fuse_taper_x_nondim + # ]) + fuse_straight_resolution = 4 + fuse_x_c.extend( + [ + 0.6 * boom_length + (1 - 0.6) * boom_length * x_nd + for x_nd in np.linspace(0, 1, fuse_straight_resolution)[1:] + ] + ) + fuse_z_c.extend([-boom_diameter / 2] * (fuse_straight_resolution - 1)) + fuse_radius.extend([boom_diameter / 2] * (fuse_straight_resolution - 1)) + + fuse = asb.Fuselage( + name="Fuselage", + x_le=0, + y_le=0, + z_le=0, + xsecs=[ + asb.FuselageXSec(x_c=fuse_x_c[i], z_c=fuse_z_c[i], radius=fuse_radius[i]) + for i in range(len(fuse_x_c)) + ], + ) + + # Assemble the airplane + fuses = [] + hstabs = [] + vstabs = [] + if n_booms == 1: + fuses.append(fuse) + hstabs.append(hstab) + vstabs.append(vstab) + elif n_booms == 2: + boom_location = 0.40 # as a fraction of the half-span + + left_fuse = copy.deepcopy(fuse) + right_fuse = copy.deepcopy(fuse) + left_fuse.xyz_le += cas.vertcat(0, -wing_span / 2 * boom_location, 0) + right_fuse.xyz_le += cas.vertcat(0, wing_span / 2 * boom_location, 0) + fuses.extend([left_fuse, right_fuse]) + + left_hstab = copy.deepcopy(hstab) + right_hstab = copy.deepcopy(hstab) + left_hstab.xyz_le += cas.vertcat(0, -wing_span / 2 * boom_location, 0) + right_hstab.xyz_le += cas.vertcat(0, wing_span / 2 * boom_location, 0) + hstabs.extend([left_hstab, right_hstab]) + + left_vstab = copy.deepcopy(vstab) + right_vstab = copy.deepcopy(vstab) + left_vstab.xyz_le += cas.vertcat(0, -wing_span / 2 * boom_location, 0) + right_vstab.xyz_le += cas.vertcat(0, wing_span / 2 * boom_location, 0) + vstabs.extend([left_vstab, right_vstab]) + + elif n_booms == 3: + boom_location = 0.57 # as a fraction of the half-span + + left_fuse = copy.deepcopy(fuse) + center_fuse = copy.deepcopy(fuse) + right_fuse = copy.deepcopy(fuse) + left_fuse.xyz_le += cas.vertcat(0, -wing_span / 2 * boom_location, 0) + right_fuse.xyz_le += cas.vertcat(0, wing_span / 2 * boom_location, 0) + fuses.extend([left_fuse, center_fuse, right_fuse]) + + left_hstab = copy.deepcopy(hstab) + center_hstab = copy.deepcopy(hstab) + right_hstab = copy.deepcopy(hstab) + left_hstab.xyz_le += cas.vertcat(0, -wing_span / 2 * boom_location, 0) + right_hstab.xyz_le += cas.vertcat(0, wing_span / 2 * boom_location, 0) + hstabs.extend([left_hstab, center_hstab, right_hstab]) + + left_vstab = copy.deepcopy(vstab) + center_vstab = copy.deepcopy(vstab) + right_vstab = copy.deepcopy(vstab) + left_vstab.xyz_le += cas.vertcat(0, -wing_span / 2 * boom_location, 0) + right_vstab.xyz_le += cas.vertcat(0, wing_span / 2 * boom_location, 0) + vstabs.extend([left_vstab, center_vstab, right_vstab]) + + else: + raise ValueError("Bad value of n_booms!") + + airplane = asb.Airplane( + name="Solar1", + x_ref=0, + y_ref=0, + z_ref=0, + wings=[wing] + hstabs + vstabs, + fuselages=fuses, + ) + + return airplane diff --git a/examples/dash/app.py b/examples/dash/app.py new file mode 100644 index 0000000..8583714 --- /dev/null +++ b/examples/dash/app.py @@ -0,0 +1,258 @@ +import plotly.express as px +import plotly.graph_objects as go +import plotly.subplots as sub +import dash +import dash_core_components as dcc +import dash_html_components as html +import dash_bootstrap_components as dbc +from dash.dependencies import Input, Output, State +import aerosandbox as asb +import casadi as cas +from airplane import make_airplane +import numpy as np +import pandas as pd + +app = dash.Dash(external_stylesheets=[dbc.themes.MINTY]) +app.title = "Aircraft CFD" +server = app.server + +app.layout = dbc.Container( + [ + dbc.Row( + [ + dbc.Col( + [ + html.H2("Solar Aircraft Design with AeroSandbox and Dash"), + html.H5("Peter Sharpe"), + ], + width=True, + ), + # dbc.Col([ + # html.Img(src="assets/MIT-logo-red-gray-72x38.svg", alt="MIT Logo", height="30px"), + # ], width=1) + ], + align="end", + ), + html.Hr(), + dbc.Row( + [ + dbc.Col( + [ + html.Div( + [ + html.H5("Key Parameters"), + html.P("Number of booms:"), + dcc.Slider( + id="n_booms", + min=1, + max=3, + step=1, + value=3, + marks={1: "1", 2: "2", 3: "3",}, + ), + html.P("Wing Span [m]:"), + dcc.Input(id="wing_span", value=43, type="number"), + html.P("Angle of Attack [deg]:"), + dcc.Input(id="alpha", value=7.0, type="number"), + ] + ), + html.Hr(), + html.Div( + [ + html.H5("Commands"), + dbc.Button( + "Display (1s)", + id="display_geometry", + color="primary", + style={"margin": "5px"}, + n_clicks_timestamp="0", + ), + dbc.Button( + "LL Analysis (3s)", + id="run_ll_analysis", + color="secondary", + style={"margin": "5px"}, + n_clicks_timestamp="0", + ), + dbc.Button( + "VLM Analysis (15s)", + id="run_vlm_analysis", + color="secondary", + style={"margin": "5px"}, + n_clicks_timestamp="0", + ), + ] + ), + html.Hr(), + html.Div( + [ + html.H5("Aerodynamic Performance"), + dbc.Spinner(html.P(id="output"), color="primary",), + ] + ), + ], + width=3, + ), + dbc.Col( + [ + # html.Div(id='display') + dbc.Spinner( + dcc.Graph(id="display", style={"height": "80vh"}), + color="primary", + ) + ], + width=True, + ), + ] + ), + html.Hr(), + html.P( + [ + html.A( + "Source code", + href="https://github.com/peterdsharpe/AeroSandbox-Interactive-Demo", + ), + ". Aircraft design tools powered by ", + html.A( + "AeroSandbox", href="https://peterdsharpe.github.com/AeroSandbox" + ), + ". Build beautiful UIs for your scientific computing apps with ", + html.A("Plot.ly ", href="https://plotly.com/"), + "and ", + html.A("Dash", href="https://plotly.com/dash/"), + "!", + ] + ), + ], + fluid=True, +) + + +def make_table(dataframe): + return dbc.Table.from_dataframe( + dataframe, bordered=True, hover=True, responsive=True, striped=True, style={} + ) + + +@app.callback( + [Output("display", "figure"), Output("output", "children")], + [ + Input("display_geometry", "n_clicks_timestamp"), + Input("run_ll_analysis", "n_clicks_timestamp"), + Input("run_vlm_analysis", "n_clicks_timestamp"), + ], + [State("n_booms", "value"), State("wing_span", "value"), State("alpha", "value"),], +) +def display_geometry( + display_geometry, run_ll_analysis, run_vlm_analysis, n_booms, wing_span, alpha, +): + ### Figure out which button was clicked + try: + button_pressed = np.argmax( + np.array( + [ + float(display_geometry), + float(run_ll_analysis), + float(run_vlm_analysis), + ] + ) + ) + assert button_pressed is not None + except: + button_pressed = 0 + + ### Make the airplane + airplane = make_airplane(n_booms=n_booms, wing_span=wing_span,) + op_point = asb.OperatingPoint(density=0.10, velocity=20, alpha=alpha,) + if button_pressed == 0: + # Display the geometry + figure = airplane.draw(show=False, colorbar_title=None) + output = "Please run an analysis to display the data." + elif button_pressed == 1: + # Run an analysis + opti = cas.Opti() # Initialize an analysis/optimization environment + ap = asb.Casll1( + airplane=airplane, op_point=op_point, opti=opti, run_setup=False + ) + ap.setup(verbose=False) + # Solver options + p_opts = {} + s_opts = {} + # s_opts["mu_strategy"] = "adaptive" + opti.solver("ipopt", p_opts, s_opts) + # Solve + try: + sol = opti.solve() + except RuntimeError: + sol = opti.debug + raise Exception("An error occurred!") + + figure = ap.draw(show=False) # Generates figure + + output = make_table( + pd.DataFrame( + { + "Figure": ["CL", "CD", "CDi", "CDp", "L/D"], + "Value": [ + sol.value(ap.CL), + sol.value(ap.CD), + sol.value(ap.CDi), + sol.value(ap.CDp), + sol.value(ap.CL / ap.CD), + ], + } + ) + ) + + elif button_pressed == 2: + # Run an analysis + opti = cas.Opti() # Initialize an analysis/optimization environment + ap = asb.Casvlm1( + airplane=airplane, op_point=op_point, opti=opti, run_setup=False + ) + ap.setup(verbose=False) + # Solver options + p_opts = {} + s_opts = {} + # s_opts["mu_strategy"] = "adaptive" + opti.solver("ipopt", p_opts, s_opts) + # Solve + try: + sol = opti.solve() + except RuntimeError: + sol = opti.debug + raise Exception("An error occurred!") + + figure = ap.draw(show=False) # Generates figure + + output = make_table( + pd.DataFrame( + { + "Figure": ["CL", "CDi", "L/Di"], + "Value": [ + sol.value(ap.CL), + sol.value(ap.CDi), + sol.value(ap.CL / ap.CDi), + ], + } + ) + ) + + figure.update_layout( + autosize=True, + # width=1000, + # height=700, + margin=dict(l=0, r=0, b=0, t=0,), + ) + + return (figure, output) + + +try: # wrapping this, since a forum post said it may be deprecated at some point. + app.title = "Aircraft Design with Dash" +except: + print("Could not set the page title!") + + +if __name__ == "__main__": + app.run_server(debug=False) diff --git a/examples/dash/requirements.txt b/examples/dash/requirements.txt new file mode 100644 index 0000000..65f79f5 --- /dev/null +++ b/examples/dash/requirements.txt @@ -0,0 +1,13 @@ +aerosandbox==1.1.20 +plotly>= 4.6.0 +dash>= 1.9 +dash_core_components>=1.8.0 +dash_html_components>=1.0.2 +dash_bootstrap_components>=0.8.0 +numpy>=1.18 +casadi>=3.5 +gunicorn +pandas +seaborn +importlib-resources + diff --git a/examples/dash/tests.py b/examples/dash/tests.py new file mode 100644 index 0000000..d8524ba --- /dev/null +++ b/examples/dash/tests.py @@ -0,0 +1,31 @@ +import aerosandbox as asb +import casadi as cas +from airplane import make_airplane + +n_booms = 1 +wing_span = 40 +alpha = 5 + +airplane = make_airplane(n_booms=n_booms, wing_span=wing_span,) +op_point = asb.OperatingPoint(density=0.10, velocity=20, alpha=alpha,) + +### LL +# Run an analysis +opti = cas.Opti() # Initialize an analysis/optimization environment +# airplane.fuselages=[] +ap = asb.Casvlm1(airplane=airplane, op_point=op_point, opti=opti) +# Solver options +p_opts = {} +s_opts = {} +# s_opts["mu_strategy"] = "adaptive" +opti.solver("ipopt", p_opts, s_opts) +# Solve +try: + sol = opti.solve() +except RuntimeError: + sol = opti.debug + raise Exception("An error occurred!") +# Postprocess +# ap.substitute_solution(sol) +ap.draw(show=False).show() + diff --git a/serve-filemanager/branding/custom.css b/serve-filemanager/branding/custom.css index 8cca3e1..677a9bd 100644 --- a/serve-filemanager/branding/custom.css +++ b/serve-filemanager/branding/custom.css @@ -11,4 +11,8 @@ .button:hover { background-color: var(--serve-lime); box-shadow: 0 0.5rem 1rem rgba(var(--bs-body-color-rgb), .15) !important; -} \ No newline at end of file +} + +.credits { + display: none; +} From 7491ec3537ec660378e3ffc2f6777f756c6ebc20 Mon Sep 17 00:00:00 2001 From: sandstromviktor Date: Thu, 14 Mar 2024 10:55:37 +0100 Subject: [PATCH 2/3] ran black formatting --- examples/dash/airplane.py | 3 ++- examples/dash/app.py | 42 ++++++++++++++++++++++++++++++++------- examples/dash/tests.py | 12 ++++++++--- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/examples/dash/airplane.py b/examples/dash/airplane.py index d3a8229..fc4e89f 100644 --- a/examples/dash/airplane.py +++ b/examples/dash/airplane.py @@ -8,7 +8,8 @@ def make_airplane( - n_booms, wing_span, + n_booms, + wing_span, ): # n_booms = 3 diff --git a/examples/dash/app.py b/examples/dash/app.py index 8583714..7b781cc 100644 --- a/examples/dash/app.py +++ b/examples/dash/app.py @@ -48,7 +48,11 @@ max=3, step=1, value=3, - marks={1: "1", 2: "2", 3: "3",}, + marks={ + 1: "1", + 2: "2", + 3: "3", + }, ), html.P("Wing Span [m]:"), dcc.Input(id="wing_span", value=43, type="number"), @@ -87,7 +91,10 @@ html.Div( [ html.H5("Aerodynamic Performance"), - dbc.Spinner(html.P(id="output"), color="primary",), + dbc.Spinner( + html.P(id="output"), + color="primary", + ), ] ), ], @@ -141,10 +148,19 @@ def make_table(dataframe): Input("run_ll_analysis", "n_clicks_timestamp"), Input("run_vlm_analysis", "n_clicks_timestamp"), ], - [State("n_booms", "value"), State("wing_span", "value"), State("alpha", "value"),], + [ + State("n_booms", "value"), + State("wing_span", "value"), + State("alpha", "value"), + ], ) def display_geometry( - display_geometry, run_ll_analysis, run_vlm_analysis, n_booms, wing_span, alpha, + display_geometry, + run_ll_analysis, + run_vlm_analysis, + n_booms, + wing_span, + alpha, ): ### Figure out which button was clicked try: @@ -162,8 +178,15 @@ def display_geometry( button_pressed = 0 ### Make the airplane - airplane = make_airplane(n_booms=n_booms, wing_span=wing_span,) - op_point = asb.OperatingPoint(density=0.10, velocity=20, alpha=alpha,) + airplane = make_airplane( + n_booms=n_booms, + wing_span=wing_span, + ) + op_point = asb.OperatingPoint( + density=0.10, + velocity=20, + alpha=alpha, + ) if button_pressed == 0: # Display the geometry figure = airplane.draw(show=False, colorbar_title=None) @@ -242,7 +265,12 @@ def display_geometry( autosize=True, # width=1000, # height=700, - margin=dict(l=0, r=0, b=0, t=0,), + margin=dict( + l=0, + r=0, + b=0, + t=0, + ), ) return (figure, output) diff --git a/examples/dash/tests.py b/examples/dash/tests.py index d8524ba..33e1f1c 100644 --- a/examples/dash/tests.py +++ b/examples/dash/tests.py @@ -6,8 +6,15 @@ wing_span = 40 alpha = 5 -airplane = make_airplane(n_booms=n_booms, wing_span=wing_span,) -op_point = asb.OperatingPoint(density=0.10, velocity=20, alpha=alpha,) +airplane = make_airplane( + n_booms=n_booms, + wing_span=wing_span, +) +op_point = asb.OperatingPoint( + density=0.10, + velocity=20, + alpha=alpha, +) ### LL # Run an analysis @@ -28,4 +35,3 @@ # Postprocess # ap.substitute_solution(sol) ap.draw(show=False).show() - From af370d8bfc22a6c00bfcb4cc576b587bbe7b230d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sandstr=C3=B6m?= Date: Thu, 14 Mar 2024 11:26:14 +0100 Subject: [PATCH 3/3] Update examples/dash/README.md Co-authored-by: Nikita Churikov <8545082+churnikov@users.noreply.github.com> --- examples/dash/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/dash/README.md b/examples/dash/README.md index c4a6db8..7f5cf55 100644 --- a/examples/dash/README.md +++ b/examples/dash/README.md @@ -7,5 +7,3 @@ An interactive demo of AeroSandbox, powered by Dash! Work in progress. ## Installation and Usage 1. Install all dependencies listed in `requirements.txt` - all packages are pip-installable. In particular, be sure to get a recent version of AeroSandbox (`pip install --upgrade aerosandbox`). 2. Run `app.py` to launch a local Dash server to host the Dash app. A link will appear in your console; click this to use the Dash app. - -## Illustration \ No newline at end of file