Commit 251af981 authored by Huynh Dang Thanh Tam's avatar Huynh Dang Thanh Tam
Browse files

init

parents
No related merge requests found
Showing with 423 additions and 0 deletions
+423 -0
.gitlab-ci.yml merge=ours
.DS_Store
# Stage 1: Build stage
# Use an official Python base image
FROM python:3.10-alpine AS build
# Set a working directory
WORKDIR /app
# Install system dependencies for better compatibility
RUN apk update && apk add --no-cache \
build-base
# Upgrade pip
RUN pip install --upgrade pip
# Copy all file into /app
COPY requirements.txt .
# Install Python dependencies
RUN rm -rf /var/lib/apt/lists/* && \
pip install --no-cache-dir -r requirements.txt && \
pip install dash-bootstrap-components==1.6.0 \
&& pip install scipy \
&& pip install python-dotenv
# Stage 2: Final stage
FROM python:3.10-alpine
# Set a working directory
WORKDIR /app
# Copy only necessary files from the build stage
COPY --from=build /usr/local/lib/python3.10/site-packages /usr/local/lib/python3.10/site-packages
COPY --from=build /usr/local/bin /usr/local/bin
COPY --from=build /app /app
# copy all file into /app
COPY . .
EXPOSE 8085
# Command to run application
CMD ["python3","index.py"]
# KiteModeler - Reboot
![Made with Love in India](https://madewithlove.org.in/badge.svg)
![img](assets/logo.png)
This repository contains the open-source back-end and front-end
code for KiteModeler-Reboot, which is a modern
version of [KiteModeler](https://www.grc.nasa.gov/WWW/K-12/airplane/kiteprog.html) from NASA Glenn.
The entire code has been written in Python (using [Dash](https://plot.ly/dash/) for the front-end)
## How to install and execute
* Using pip, run
```
pip install -r requirements.txt
```
* Run the code using
```
python index.py
```
## Sample
<img src="sample/sample.PNG" width="600">
## Whom to contact?
Please direct your queries to [gpavanb1](http://github.com/gpavanb1)
for any questions.
import dash
import flask
import os
import dash_bootstrap_components as dbc
STATIC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static')
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.title = 'Kite Modeler - Reboot'
server = app.server
app.config.suppress_callback_exceptions = True
@app.server.route('/static/<resource>')
def serve_static(resource):
return flask.send_from_directory(STATIC_PATH, resource)
assets/favicon.ico

355 Bytes

#heading {
text-align: center;
}
\ No newline at end of file
assets/logo.png

8.6 KB

h4 {
font-size: 20px;
font-weight: 200;
}
\ No newline at end of file
from .geom import DiamondGeometry
from .bridle import BridleGeometry
from .fly import FlyParameters
from .composition import Composition
from .dashboard import Dashboard
from .helper import dotdict
def solve(inp):
inp = dotdict(inp)
geom = DiamondGeometry(inp.h1, inp.h2, inp.w1, inp.t)
bridle = BridleGeometry(inp.B, inp.K, geom.h())
fly = FlyParameters(inp.wind_speed, inp.altitude,
inp.line_length, inp.env_name)
compo = Composition(inp.surface,
inp.frame,
inp.tail,
inp.line)
dashboard = Dashboard(geom, bridle, fly, compo)
ret_dict = dashboard.__dict__
# Remove objects from dictionary
toDelete = ["geom", "bridle", "fly", "compo", "min_func"]
for i in toDelete:
ret_dict.pop(i)
return ret_dict
# Thanks to OSI Digital
import math
# https://www.grc.nasa.gov/WWW/k-12/VirtualAero/BottleRocket/airplane/kitebrid.html
def knot_angle(b, k, h):
ret = (k*k + h*h - (b - k)*(b - k))/(2*k*h)
if abs(ret) > 1:
return 0.0
else:
return math.acos(ret)
class BridleGeometry:
def __init__(self, b, k, h):
try:
# TODO : Raise error if negative b, k, h
if b <= k:
raise ValueError("Knot must be larger than bridle")
self.B = b
self.K = k
self.H = h
self.A = knot_angle(b, k, h)
self.Xb = k*math.sin(self.A)
self.Yb = k*math.cos(self.A)
except ValueError as e:
print(e)
from .material_dicts import SurfaceMaterials, FrameMaterials, TailMaterials, LineMaterials
class Composition:
def __init__(self, surface, frame, tail, line):
self.surface = SurfaceMaterials[surface]
self.frame = FrameMaterials[frame]
self.tail = TailMaterials[tail]
self.line = LineMaterials[line]
import math
from scipy.optimize import minimize_scalar
class Dashboard:
def __init__(self, geom, bridle, fly, compo):
self.geom = geom
self.bridle = bridle
self.fly = fly
self.compo = compo
# Get sea level properties
self.pressure = self.fly.env.pressure(self.fly.altitude)
self.temperature = self.fly.env.temperature(self.fly.altitude)
self.density = self.fly.env.density(self.fly.altitude)
# Weight
self._weight = self.kite_weight()
# Center of pressure and gravity
self._cp = self.geom.cp()
# CG calculation uses cp for surface
self._cg = self.cg()
# Geometric parameters
self._surface_area = self.geom.surface_area()
self._frame = self.geom.frame()
# Solve such that torque is zero
self.min_func = lambda x: abs(self.torque(x))
bounds = (math.pi / 180.0, math.pi / 2)
result = minimize_scalar(self.min_func, bounds=bounds, method='bounded')
self._aoa_no_torque = result.x
self._aoa_no_torque_degrees = self._aoa_no_torque * 180 / math.pi
# Calculate corresponding lift and drag
self._lift = self.lift(self._aoa_no_torque)
self._drag = self.drag(self._aoa_no_torque)
# Source : https://www.grc.nasa.gov/WWW/K-12/airplane/kitefor.html
self._vert_tension = self.vertical_tension()
self._horiz_tension = self.horiz_tension()
self._tension = math.sqrt(pow(self._horiz_tension, 2) + pow(self._vert_tension, 2))
# Height and range
self._range = self.range()
self._height = self.height()
def lift(self, a):
cl = self.geom.cl(a)
rho = self.density
# Convert to m2
A = 1.e-4 * self.geom.surface_area()
V = self.fly.wind_speed
# Convert to gram force
f = cl * A * rho * V * V / 2
f = 1.e3 * (f / 9.8)
return f
def drag(self, a):
cd = self.geom.cd(a)
rho = self.density
# Convert to m2
A = 1.e-4 * self.geom.surface_area()
V = self.fly.wind_speed
# Convert to gram force
f = cd * A * rho * V * V / 2
f = 1.e3 * (f / 9.8)
return f
def kite_weight(self):
w_surf = self.compo.surface.density * self.geom.surface_area()
w_frameH = self.compo.frame.density * self.geom.w1
w_frameV = self.compo.frame.density * (self.geom.h1 + self.geom.h2)
w_tail = self.compo.tail.density * self.geom.t
return w_frameH + w_frameV + w_surf + w_tail
def torque(self, a):
L = self.lift(a)
D = self.drag(a)
W = self._weight
cp = self._cp
cg = self._cg
xb = self.bridle.Xb
yb = self.bridle.Yb
T = - L * math.cos(a) * (yb - cp) \
- L * math.sin(a) * xb \
+ D * math.cos(a) * xb \
- D * math.sin(a) * (yb - cp) \
+ W * math.cos(a) * (yb - cg) \
+ W * math.sin(a) * xb
return T
def vertical_tension(self):
# Unit line weight
p = self.compo.line.density
# Line length
s = self.fly.line
g = s * p
ret = self._lift - g - self._weight
if ret >= 0:
return ret
else:
return 0.0
def horiz_tension(self):
if self._vert_tension > 0:
return self._drag
else:
return 0.0
def catenary_equation(self, x):
# Unit line weight
p = self.compo.line.density
# Line length
s = self.fly.line
g = s * p
L = self._lift
D = self._drag
W = self._weight
# Kite won't fly
if L < g + W:
return 0.0
# Integration constants
c1 = math.asin((L - g - W) / D)
c2 = -(D / p) * math.cosh(c1)
# Source: https://www.grc.nasa.gov/WWW/K-12/airplane/kitesag.html
return c2 + (D / p) * math.cosh(p * x / D + c1)
def range(self):
# Unit line weight
p = self.compo.line.density
# Line length
s = self.fly.line
g = s * p
L = self._lift
D = self._drag
W = self._weight
# Kite won't fly
if L < g + W:
return 0.0
# Source: https://www.grc.nasa.gov/WWW/K-12/airplane/kitesag.html
return (D / p) * (math.asinh((L - W) / D) - math.asinh((L - g - W) / D))
def height(self):
return self.catenary_equation(self.range())
def cg(self):
w_surf = self.compo.surface.density * self.geom.surface_area()
# Area-weighted average of triangle centroids
h_surf = (self.geom.h1 + 2 * self.geom.h2) / 3
w_frameH = self.compo.frame.density * self.geom.w1
h_frameH = self.geom.h2
w_frameV = self.compo.frame.density * (self.geom.h1 + self.geom.h2)
h_frameV = (self.geom.h1 + self.geom.h2) / 2
w_tail = self.compo.tail.density * self.geom.t
h_tail = - self.geom.t / 2
dot_prod = w_surf * h_surf + w_frameH * h_frameH + \
w_frameV * h_frameV + w_tail * h_tail
return dot_prod / self._weight
class Environment:
def __init__(self, pressure, temperature):
self.pressure = pressure
self.temperature = temperature
def density(self, h):
# Ideal gas law
p = 1.e3 * self.pressure(h)
T = self.temperature(h)
R = 8.314
M = 28.97e-3
return (p * M) / (R * T)
from .environment import Environment
# Source for Earth
# https://www.grc.nasa.gov/WWW/K-12/airplane/atmosmet.html
def earth_pressure(h):
return 101.29 * pow(earth_temperature(h) / 288.08, 5.256)
def earth_temperature(h):
return 273.1 + 15.04 - 0.00649 * h
Environments = {
"Earth": Environment(earth_pressure, earth_temperature),
# TODO : Add Mars
}
from .environment_dicts import Environments
class FlyParameters:
def __init__(self, wind_speed, altitude, line, env_name):
self.wind_speed = wind_speed
self.altitude = altitude
self.line = line
# TODO : Payload
self.env = Environments[env_name]
import math
class DiamondGeometry:
def __init__(self, h1, h2, w1, t=0):
self.h1 = h1
self.h2 = h2
self.w1 = w1
self.AR = self.aspect_ratio()
# Tail length
self.t = t
def h(self):
return self.h1 + self.h2
def surface_area(self):
return 0.5 * (self.h1 + self.h2) * self.w1
def frame(self):
return self.h1 + self.h2 + self.w1
def aspect_ratio(self):
s = self.w1
A = self.surface_area()
return s * s / A
# Source: https://www.grc.nasa.gov/WWW/K-12/airplane/kitelift.html
def cl(self, a):
Clo = 2 * math.pi * a
return Clo / (1 + Clo / (math.pi * self.AR))
# Source: https://www.grc.nasa.gov/WWW/K-12/airplane/kitedrag.html
def cd(self, a):
Cdo = 1.28 * math.sin(a)
return Cdo + pow(self.cl(a), 2) / (.7 * math.pi * self.AR)
def cp(self):
return 0.5 * (self.h1 + self.h2) + self.h2 / 3
class dotdict(dict):
"""dot.notation access to dictionary attributes"""
__getattr__ = dict.get
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
\ No newline at end of file
class Material:
def __init__(self, density, unit):
self.density = density
self.unit = unit
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment