Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added example dash app to repo #73

Merged
merged 3 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/example-dash.yml
Original file line number Diff line number Diff line change
@@ -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 }}
20 changes: 20 additions & 0 deletions examples/dash/Dockerfile
Original file line number Diff line number Diff line change
@@ -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"]
1 change: 1 addition & 0 deletions examples/dash/Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: gunicorn app:server --workers 4
9 changes: 9 additions & 0 deletions examples/dash/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# 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.
261 changes: 261 additions & 0 deletions examples/dash/airplane.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
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
Loading
Loading