API Documentation
Describes how to use the Engineering Model API. API usage requires a paid plan — see plans.
API Overview
Aerodule’s API allows for scripted use of the engineering models.
Authentication
Create a key on your account page. It is shown only once, at creation. Send it as a bearer token.
Authorization: Bearer ak_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxOne key per account. Creating a key revokes the previous one immediately.
Propulsion Model
Overview
Runs the propulsion system model. The API calls for propulsion data within a grid of user-set airspeeds and input voltages. It can also call for propulsion data at a single airspeed and input voltage. Solves take well under a second.
Inputs
| Field | Required | Description |
|---|---|---|
| sim_params.input_units | required | Sets the units that the inputs in this request is written in. "system" is "imperial" or "metric". |
| sim_params.output_units | required | Sets the units that the outputs in this response is written in. "system" is "imperial" or "metric". |
| sim_params.atmosphere | required | Set either { altitude } and have atmospheric properties looked up from a STD ATM table or set { density } directly. |
| propulsion_params.motor | required | Motor ID from the Motor & Propeller Catalog. Found here. |
| propulsion_params.propeller | required | Propeller ID from the Motor & Propeller Catalog. Found here. |
| airspeed | required | Sets the airspeed to evaluate the propulsion system at. Input either a single airspeed or a range of airspeeds. Must be zero or greater. |
| voltage | required | Sets the voltage to evaluate the propulsion system at. Input either a single voltage or a range of voltages. Must be greater than 0. |
Outputs
| Field | Description |
|---|---|
| model.name | The name of the model. |
| model.version | The model version that produced this result. See Model Version. |
| units.system | The unit system that the outputs are written in. |
| motor.id | The ID of the motor. |
| motor.name | The name of the motor. |
| motor.kv | The kV of the motor. |
| propeller.id | The ID of the propeller. |
| propeller.name | The name of the propeller. |
| propeller.diameter | The diameter of the propeller. |
| propeller.pitch | The pitch of the propeller. |
| atmosphere.density | The atmospheric density that the model was run at. |
| atmosphere.altitude | The STD ATM altitude that the model was run at. |
| grid.voltages | The grid of voltages that correlate with other grids of airspeed, thrust, etc. |
| grid.airspeeds | The grid of airspeeds that correlate with other grids of voltages, thrust, etc. |
| grid.thrust | The grid of thrust values that correlates with the other grids of airspeeds, voltages, etc. |
| grid.amps | The grid of current (as in amps) values that correlates with the other grids of airspeeds, voltages, etc. |
| grid.rpm | The grid of RPMs that correlates with the other grids of airspeeds, voltages, etc. |
| grid.eta_motor | The grid of motor efficiency values that correlates with the other grids of airspeeds, voltages, etc. |
| grid.eta_prop | The grid of propeller efficiency values that correlates with the other grids of airspeeds, voltages, etc. |
| grid.eta_total | The grid of propulsion system efficiency values that correlates with the other grids of airspeeds, voltages, etc. |
| omitted.count | The amount of data points left out of the results. |
| omitted.points | Holds each omitted data point and its reason for omission as { voltage, airspeed, reason } |
| usage.compute_seconds | The amount of API quota this request used. |
| usage.quota_remaining_seconds | The amount of API quota remaining in this period. |
| usage.reserve_remaining_seconds | The amount of reserve API quota remaining. |
Examples
import os
import requests
API = "https://aerodule.com/api/v1/propulsion"
KEY = os.environ["AERODULE_API_KEY"] # export AERODULE_API_KEY=ak_...
payload = {
"sim_params": {
"input_units": {"system": "imperial"},
"output_units": {"system": "imperial"},
"atmosphere": {"altitude": 0},
},
"propulsion_params": {
"motor": "scorpion-s-ii-2205-v2",
"propeller": "apc-9-625x3-75n",
},
"airspeed": {"from": 0, "to": 120, "step": 40},
"voltage": {"from": 7.4, "to": 14.8, "step": 3.7},
}
r = requests.post(API, json=payload,
headers={"Authorization": f"Bearer {KEY}"}, timeout=30)
data = r.json()
if not r.ok:
error = data["error"]
raise SystemExit(f"{r.status_code} {error['code']}: {error['message']}")
if data["units"]["system"] == "imperial":
speed, force, length, density = "ft/s", "lbf", "ft", "slug/ft^3"
else:
speed, force, length, density = "m/s", "N", "m", "kg/m^3"
motor, prop, atm = data["motor"], data["propeller"], data["atmosphere"]
print(f"Model : {data['model']['name']} {data['model']['version']}")
print(f"Units : {data['units']['system']}")
print(f"Motor : {motor['name']} ({motor['id']}), {motor['kv']} kV")
print(f"Propeller : {prop['name']} ({prop['id']}), "
f"diameter {prop['diameter']} {length}, pitch {prop['pitch']} {length}")
line = f"Atmosphere : density {atm['density']} {density}"
if "altitude" in atm:
line += f", altitude {atm['altitude']} {length}"
print(line)
g = data["grid"]
tables = [
("thrust", f"Thrust ({force})"),
("amps", "Current (A)"),
("rpm", "RPM"),
("eta_motor", "Motor efficiency"),
("eta_prop", "Propeller efficiency"),
("eta_total", "Total efficiency"),
]
corner = f"V / {speed}"
for key, title in tables:
# One row per voltage, one column per airspeed. None is an omitted point.
print(f"\n{title}")
print(f"{corner:>10}" + "".join(f"{a:>10g}" for a in g["airspeeds"]))
for v, row in zip(g["voltages"], g[key]):
cells = "".join(f"{'-':>10}" if x is None else f"{x:>10g}" for x in row)
print(f"{v:>10g}" + cells)
print(f"\nOmitted points: {data['omitted']['count']}")
for o in data["omitted"]["points"]:
print(f" {o['voltage']:g} V, {o['airspeed']:g} {speed}: {o['reason']}")
u = data["usage"]
print(f"\nAPI quota used by this request : {u['compute_seconds']} s")
print(f"API quota left this period : {u['quota_remaining_seconds']} s")
print(f"Reserve API quota left : {u['reserve_remaining_seconds']} s")Wing Model
Overview
Runs the wing aerodynamic model. Typical solves take 2–7 seconds with the heaviest around 12. Allow a generous client timeout to avoid timeout errors.
Inputs
| Field | Required | Description |
|---|---|---|
| sim_params.input_units | required | Sets the units that the inputs in this request is written in. "system" is "imperial" or "metric". "angle" is "deg" or "rad". |
| sim_params.output_units | required | Sets the units that the outputs in this response is written in. "system" is "imperial" or "metric". "angle" is "deg" or "rad". "derivative" is "1/deg" or "1/rad". |
| sim_params.airspeed | required | Sets the freestream airspeed. Must be greater than zero. |
| sim_params.atmosphere | required | Set either { altitude } and have atmospheric properties looked up from a STD ATM table or set { density, viscosity } directly. |
| wing_params.airfoil | required | Sets the airfoil. It can be set using { catalog: "…" } with the filename of an airfoil in the Airfoil Catalog or { points: [[x, y], …] }, or { dat: "…" } as Selig-order text. Coordinates need at least four pairs of two columns, chord-normalised (0-1), running from the trailing edge forward over one surface and back along the other. |
| wing_params.span | required | Sets the wingspan. |
| wing_params.root_chord | required | Sets the root chord. |
| wing_params.tip_chord | required | Sets the tip chord. |
| wing_params.sweep | required | Sets the sweep angle and reference location with { angle, reference }, where reference is the chord fraction the angle is measured at: 0 leading edge, 0.25 quarter chord, 1 trailing edge. |
| wing_params.dihedral | required | Sets wing dihedral. Must be at least 0 and less than 90 degrees (1.5708 radians). |
| wing_params.twist | required | Sets wingtip twist. |
| wing_params.tip_device | optional | Omit for a bare tip. { type: "endplate", height, chord } or { type: "winglet", height, taper, sweep: { angle, reference } } for endplates or winglets. |
| wing_params.tip_device.height | if endplate or winglet | Sets the vertical length of the wingtip device. Greater than 0. |
| wing_params.tip_device.chord | if endplate | Sets the horizontal length of the endplate. Greater than 0. |
| wing_params.tip_device.taper | if winglet | Sets the ratio of winglet tip chord to root chord, where the root chord is the wing tip chord. 0 or greater. |
| wing_params.tip_device.sweep | if winglet | Sets the sweep angle of the winglet using { angle, reference }, measured the same way as wing_params.sweep. |
| parasite_drag | optional | Omit and parasite drag is not modelled. Include it and roughness, crud_factor and interference_factor are all required. |
| parasite_drag.roughness | if parasite_drag is present | Characteristic roughness height of the wing and wingtip device surface. Greater than zero. |
| parasite_drag.crud_factor | if parasite_drag is present | Parasitic drag multiplier used to account for miscellaneous drag not modeled. 1 or greater. |
| parasite_drag.interference_factor | if parasite_drag is present | Parasite drag multiplier accounting for interference drag at the junctions with wingtip devices. 1 or greater. |
Outputs
| Field | Description |
|---|---|
| model.name | The name of the model. |
| model.version | The model version that produced this result. See Model Version. |
| units.system | The unit system that the outputs are written in. |
| units.angle | The units that the angles that the outputs are written in (deg or rad). |
| units.derivative | The units that the derivatives are written in (1/deg or 1/rad). |
| geometry.sref | Planform area. |
| geometry.AR | Aspect ratio. Equal to the span squared divided by the planform area. |
| geometry.TR | Taper ratio. The ratio of the tip chord to the root chord. |
| geometry.MAC | Mean aerodynamic chord. The chord for a rectangular planform wing that would mirror the surface’s aerodynamic properties. |
| geometry.span | Wingspan |
| geometry.root_chord | Root chord |
| geometry.tip_chord | Tip chord |
| geometry.sweep_ref | The chordwise point that the sweep angle is measured from. |
| geometry.sweep | Sweep angle from the selected reference point. |
| geometry.dihedral | Dihedral |
| geometry.twist | The difference in incidence from tip chord to root chord. Negative values denote washout. |
| geometry.wingtip_type | Determines the wingtip geometry. Options are none, endplate, or winglet. |
| geometry.wingtip_param_1 | Vertical length of the wingtip device. Returned for an endplate or a winglet. |
| geometry.wingtip_param_2 | Horizontal length of the endplate, or the sweep angle of the winglet about its quarter chord. |
| geometry.wingtip_param_3 | Ratio of winglet tip chord to root chord. Returned for a winglet. |
| geometry.airfoil_x | A vector containing the x coordinates of the airfoil, in Selig order. |
| geometry.airfoil_y | A vector containing the y coordinates of the airfoil, in Selig order. |
| aerodynamics.aoas | A vector containing each AOA point in the output lift and drag polar. |
| aerodynamics.CL | A vector containing the lift coefficient at each AOA. |
| aerodynamics.CD | A vector containing the total drag coefficient at each AOA. Only returned if parasite_drag is included. |
| aerodynamics.CDi | A vector containing the induced drag coefficient at each AOA. |
| aerodynamics.CD0 | A vector containing the parasite drag coefficient at each AOA. Only returned if parasite_drag is included. |
| aerodynamics.CD_friction | A vector containing the skin friction drag coefficient at each AOA. Constant across AOA. Only returned if parasite_drag is included. |
| aerodynamics.CD_form | A vector containing the form drag coefficient at each AOA. Only returned if parasite_drag is included. |
| aerodynamics.CL_alpha | The lift-curve slope at 0 AOA. |
| aerodynamics.CY_beta | The derivative representing the change in sideforce with respect to sideslip angle at 0 AOA. |
| aerodynamics.CMl_beta | The derivative representing the change in rolling moment with respect to sideslip angle at 0 AOA. |
| aerodynamics.CMn_beta | The derivative representing the change in yawing moment with respect to sideslip angle at 0 AOA. |
| aerodynamics.CMm0 | Zero-lift pitching moment. |
| aerodynamics.interference_factor | Parasite drag multiplier accounting for interference drag at the junctions with wingtip devices. |
| aerodynamics.root_le_to_ac | Longitudinal location of the AC with respect to the root LE. |
| aerodynamics.stall.stall_alpha | Stall AOA. |
| aerodynamics.stall.stall_y | The spanwise location where stall occurs. |
| warnings | Present when there are warnings associated with the model inputs or outputs. |
| usage.compute_seconds | The amount of API quota this request used. |
| usage.quota_remaining_seconds | The amount of API quota remaining in this period. |
| usage.reserve_remaining_seconds | The amount of reserve API quota remaining. |
Examples
Wing Model Example - Parasite Drag Omitted
import os
import requests
API = "https://aerodule.com/api/v1/wing"
KEY = os.environ["AERODULE_API_KEY"] # export AERODULE_API_KEY=ak_...
payload = {
"sim_params": {
"input_units": {"system": "imperial", "angle": "deg"},
"output_units": {"system": "imperial", "angle": "deg", "derivative": "1/deg"},
"airspeed": 60,
"atmosphere": {"altitude": 0},
},
"wing_params": {
"span": 8,
"root_chord": 1.25,
"tip_chord": 0.75,
"sweep": {"angle": 0, "reference": 0.25},
"dihedral": 3,
"twist": -2,
"airfoil": {"catalog": "naca2412.dat"},
},
}
r = requests.post(API, json=payload,
headers={"Authorization": f"Bearer {KEY}"}, timeout=120)
data = r.json()
if not r.ok:
error = data["error"]
raise SystemExit(f"{r.status_code} {error['code']}: {error['message']}")
units = data["units"]
length, area = ("ft", "ft^2") if units["system"] == "imperial" else ("m", "m^2")
angle, per = units["angle"], units["derivative"]
print(f"Model : {data['model']['name']} {data['model']['version']}")
print(f"Units : {units['system']}, angles in {angle}, derivatives in {per}")
# Everything solved, then the planform, tip device and section echoed back.
geo = data["geometry"]
print("\nGeometry")
for name, unit in [("sref", area), ("AR", ""), ("TR", ""), ("MAC", length),
("span", length), ("root_chord", length), ("tip_chord", length),
("sweep_ref", ""), ("sweep", angle), ("dihedral", angle), ("twist", angle),
("wingtip_type", "")]:
print(f" {name:<14} {geo[name]} {unit}".rstrip())
# One to three parameters, depending on the device: height and chord for an
# endplate; height, quarter-chord sweep and taper for a winglet; none at all
# when no device is fitted.
for i in (1, 2, 3):
key = f"wingtip_param_{i}"
if key in geo:
print(f" {key:<14} {geo[key]}")
print(f" airfoil {len(geo['airfoil_x'])} coordinate pairs, "
f"first ({geo['airfoil_x'][0]}, {geo['airfoil_y'][0]})")
aero = data["aerodynamics"]
print("\nAerodynamics")
print(f" CL_alpha {aero['CL_alpha']} {per}")
print(f" CY_beta {aero['CY_beta']} {per}")
print(f" CMl_beta {aero['CMl_beta']} {per}")
print(f" CMn_beta {aero['CMn_beta']} {per}")
print(f" CMm0 {aero['CMm0']}")
print(f" interference_factor {aero['interference_factor']}")
print(f" root_le_to_ac {aero['root_le_to_ac']} {length}")
print(f" stall_alpha {aero['stall']['stall_alpha']} {angle}")
print(f" stall_y {aero['stall']['stall_y']} {length}")
# Parallel arrays on one AOA grid, from zero lift up to stall.
print(f"\n{'alpha (' + angle + ')':>12}{'CL':>12}{'CDi':>12}")
for alpha, cl, cdi in zip(aero["aoas"], aero["CL"], aero["CDi"]):
print(f"{alpha:>12g}{cl:>12.6f}{cdi:>12.6f}")
# Present only when the solve has something to flag.
warnings = data.get("warnings", [])
print(f"\nWarnings: {len(warnings)}")
for w in warnings:
print(f" {w}")
u = data["usage"]
print(f"\nAPI quota used by this request : {u['compute_seconds']} s")
print(f"API quota left this period : {u['quota_remaining_seconds']} s")
print(f"Reserve API quota left : {u['reserve_remaining_seconds']} s")Wing Model Example - Parasite Drag Included
import os
import requests
API = "https://aerodule.com/api/v1/wing"
KEY = os.environ["AERODULE_API_KEY"] # export AERODULE_API_KEY=ak_...
payload = {
"sim_params": {
"input_units": {"system": "imperial", "angle": "deg"},
"output_units": {"system": "imperial", "angle": "deg", "derivative": "1/deg"},
"airspeed": 60,
"atmosphere": {"altitude": 0},
},
"wing_params": {
"span": 8,
"root_chord": 1.25,
"tip_chord": 0.75,
"sweep": {"angle": 0, "reference": 0.25},
"dihedral": 3,
"twist": -2,
"airfoil": {"catalog": "naca2412.dat"},
},
"parasite_drag": {
"roughness": 0.0000167,
"crud_factor": 1.2,
"interference_factor": 1.1,
},
}
r = requests.post(API, json=payload,
headers={"Authorization": f"Bearer {KEY}"}, timeout=120)
data = r.json()
if not r.ok:
error = data["error"]
raise SystemExit(f"{r.status_code} {error['code']}: {error['message']}")
units = data["units"]
length, area = ("ft", "ft^2") if units["system"] == "imperial" else ("m", "m^2")
angle, per = units["angle"], units["derivative"]
print(f"Model : {data['model']['name']} {data['model']['version']}")
print(f"Units : {units['system']}, angles in {angle}, derivatives in {per}")
# Everything solved, then the planform, tip device and section echoed back.
geo = data["geometry"]
print("\nGeometry")
for name, unit in [("sref", area), ("AR", ""), ("TR", ""), ("MAC", length),
("span", length), ("root_chord", length), ("tip_chord", length),
("sweep_ref", ""), ("sweep", angle), ("dihedral", angle), ("twist", angle),
("wingtip_type", "")]:
print(f" {name:<14} {geo[name]} {unit}".rstrip())
# One to three parameters, depending on the device: height and chord for an
# endplate; height, quarter-chord sweep and taper for a winglet; none at all
# when no device is fitted.
for i in (1, 2, 3):
key = f"wingtip_param_{i}"
if key in geo:
print(f" {key:<14} {geo[key]}")
print(f" airfoil {len(geo['airfoil_x'])} coordinate pairs, "
f"first ({geo['airfoil_x'][0]}, {geo['airfoil_y'][0]})")
aero = data["aerodynamics"]
print("\nAerodynamics")
print(f" CL_alpha {aero['CL_alpha']} {per}")
print(f" CY_beta {aero['CY_beta']} {per}")
print(f" CMl_beta {aero['CMl_beta']} {per}")
print(f" CMn_beta {aero['CMn_beta']} {per}")
print(f" CMm0 {aero['CMm0']}")
print(f" interference_factor {aero['interference_factor']}")
print(f" root_le_to_ac {aero['root_le_to_ac']} {length}")
print(f" stall_alpha {aero['stall']['stall_alpha']} {angle}")
print(f" stall_y {aero['stall']['stall_y']} {length}")
# Parallel arrays on one AOA grid, from zero lift up to stall. With parasite_drag
# sent, the drag build-up is included: CD = CDi + CD0, CD0 = CD_friction + CD_form.
columns = ["CL", "CD", "CDi", "CD0", "CD_friction", "CD_form"]
print(f"\n{'alpha (' + angle + ')':>12}" + "".join(f"{name:>12}" for name in columns))
for i, alpha in enumerate(aero["aoas"]):
print(f"{alpha:>12g}" + "".join(f"{aero[name][i]:>12.6f}" for name in columns))
# Present only when the solve has something to flag.
warnings = data.get("warnings", [])
print(f"\nWarnings: {len(warnings)}")
for w in warnings:
print(f" {w}")
u = data["usage"]
print(f"\nAPI quota used by this request : {u['compute_seconds']} s")
print(f"API quota left this period : {u['quota_remaining_seconds']} s")
print(f"Reserve API quota left : {u['reserve_remaining_seconds']} s")Aircraft Model
Overview
Runs the aircraft aerodynamic model. Typical solves take 10–30 seconds. Allow a generous client timeout to avoid timeout errors. When modeling a v-tail or a-tail, the API expects many tail inputs to be under horizontal tail inputs. Reference the input/output indices for more detail.
Inputs
sim_params
| Field | Required | Description |
|---|---|---|
| sim_params.input_units | required | Sets the units that the inputs in this request is written in. "system" is "imperial" or "metric". "angle" is "deg" or "rad". "derivative" is "1/deg" or "1/rad". |
| sim_params.output_units | required | Sets the units that the outputs in this response is written in. "system" is "imperial" or "metric". "angle" is "deg" or "rad". "derivative" is "1/deg" or "1/rad". |
| sim_params.parasite_drag | required | Set as "true" to model parasitic drag on all parts. Set as "false" to ignore parasitic drag in the modeling process. |
| sim_params.airspeed | required | Sets the freestream airspeed. Must be greater than zero. |
| sim_params.max_trim_aoa | optional | Sets the maximum AOA of the aircraft sweep. If the set AOA is above the wing stall AOA, then the input will be overridden to the wing stall AOA. If the field is ignored, then the maximum AOA of the sweep will be at wing stall. |
| sim_params.grow_tail_to_trim | required | Set as "true" to grow the horizontal tail to allow the aircraft to trim at every modeled AOA. Set as "false" to ignore this process. |
| sim_params.atmosphere | required | Set either { altitude } and have atmospheric properties looked up from a STD ATM table or set { density, viscosity } directly. |
wing_params
| Field | Required | Description |
|---|---|---|
| wing_params.aerodynamics | required | Sets the source of wing aerodynamics data. "simulate" simulates the wing aerodynamics and "input" allows wing aerodynamics to be input. |
| wing_params.airfoil | if wing_params.aerodynamics=simulate | Sets the airfoil. It can be set using { catalog: "…" } with the filename of an airfoil in the Airfoil Catalog or { points: [[x, y], …] }, or { dat: "…" } as Selig-order text. Coordinates need at least four pairs of two columns, chord-normalised (0-1), running from the trailing edge forward over one surface and back along the other. |
| wing_params.span | required | Sets the wingspan. |
| wing_params.root_chord | required | Sets the root chord. |
| wing_params.tip_chord | required | Sets the tip chord. |
| wing_params.sweep | required | Sets the sweep angle and reference location with { angle, reference }, where reference is the chord fraction the angle is measured at: 0 leading edge, 0.25 quarter chord, 1 trailing edge. |
| wing_params.dihedral | required | Sets wing dihedral. Must be at least 0 and less than 90 degrees (1.5708 radians). |
| wing_params.twist | required | Sets wingtip twist. |
| wing_params.tip_device | optional | { type: "endplate", height, chord } or { type: "winglet", height, taper, sweep: { angle, reference } }. Accepted only when aerodynamics is "simulate". |
| wing_params.tip_device.height | if endplate or winglet | Sets the vertical length of the wingtip device. Greater than 0. |
| wing_params.tip_device.chord | if endplate | Sets the horizontal length of the endplate. Greater than 0. |
| wing_params.tip_device.taper | if winglet | Sets the ratio of winglet tip chord to root chord, where the root chord is the wing tip chord. 0 or greater. |
| wing_params.tip_device.sweep | if winglet | Sets the sweep angle of the winglet using { angle, reference }, measured the same way as wing_params.sweep. |
| wing_params.roughness | if sim_params.parasite_drag=true and wing_params.aerodynamics=simulate | Characteristic roughness height of the wing and wingtip device surface. Greater than zero. |
| wing_params.crud_factor | if sim_params.parasite_drag=true and wing_params.aerodynamics=simulate | Parasitic drag multiplier used to account for miscellaneous drag not modeled. 1 or greater. |
| wing_params.polar | if wing_params.aerodynamics=input | Sets the wing aerodynamic data using { aoa, CL, CD } as three equal-length arrays. Each array must have at least 3 points, be ordered from low AOA to high AOA with all unique AOAs. If sim_params.parasite_drag is set to true, the polar can alternatively be input as { aoa, CL, CD, CDi, CD0 } with five equal-length arrays. This is not required, but allows wing_params.interference_factor to be applied to the parasitic drag coefficient, CD0. If sim_params.parasite_drag is enabled but wing_params.polar is formatted as only { aoa, CL, CD }, then wing_params.interference_factor will have no effect on the model. |
| wing_params.root_le_to_ac | if wing_params.aerodynamics=input | Longitudinal location of the wing AC with respect to the root LE. |
| wing_params.CMm0 | if wing_params.aerodynamics=input | Wing pitching moment coefficient at zero lift. |
| wing_params.CMl_beta | if wing_params.aerodynamics=input | The derivative representing the change in rolling moment with respect to sideslip angle. |
| wing_params.CY_beta | if wing_params.aerodynamics=input | The derivative representing the change in sideforce with respect to sideslip angle. |
| wing_params.interference_factor | if sim_params.parasite_drag=true | Parasite drag multiplier accounting for interference drag at the junctions with wingtip devices and/or with the fuselage. 1 or greater. |
| wing_params.cg_to_ac | required | Longitudinal location of the wing AC with respect to the aircraft CG. |
| wing_params.ac_z_from_cg | required | Height of the wing AC above the CG, positive up. It is the moment arm the wing's drag acts on, so it is not cosmetic. |
| wing_params.incidence | required | Wing incidence to the fuselage reference line. |
tail_params
| Field | Required | Description |
|---|---|---|
| tail_params.configuration | required | Sets the tail configuration. Options are conventional/cruciform, t_tail, v_tail, a_tail, u_tail, inverted_u_tail or h_tail. |
| tail_params.elevator.chord_ratio | required | Ratio of the elevator (or ruddervator) chord to the MAC of the tail surface it is attached to. Must be greater than 0 and less than .33 |
| tail_params.elevator.max_deflection | required | Maximum angle that the elevator (or ruddervator) can deflect in either direction. The total elevator range of motion is twice this value. Must be greater than 0 and less than 40 degrees (.698 radians) |
| tail_params.spanwise_mac_location | if tail_params.vertical.aerodynamics=input (tail_params.horizontal for a v_tail or a_tail), unless tail_params.configuration=h_tail | The spanwise distance from the root chord of the vertical tail to the MAC. For a v-tail or a-tail configuration, this value is the spanwise distance from the root chord of the entire tail to the MAC. For an h-tail configuration, this value is not accepted. Values must be positive. |
tail_params.horizontal
| Field | Required | Description |
|---|---|---|
| tail_params.horizontal.aerodynamics | required | Sets the source of tail aerodynamics data. "simulate" simulates the tail aerodynamics and "input" allows tail aerodynamics to be input. |
| tail_params.horizontal.sizing | required | Set the horizontal tail sizing parameter with { method: "stability", value }, { method: "volume_coefficient", value } or { method: "area", value }. For a v-tail or a-tail, this input is used with tail_params.vertical.sizing to size the tail. |
| tail_params.horizontal.sizing.method | required | Sets how the horizontal tail is sized. Options are stability, volume_coefficient, or area. If "stability" is selected, the horizontal tail is sized based on an aircraft longitudinal static stability requirement. If "volume_coefficient" is selected, the horizontal tail is sized based on a horizontal tail volume coefficient. If "area" is selected, the horizontal tail is sized directly based on the input value. For a v-tail or a-tail, this input is used with tail_params.vertical.sizing.method to size the tail. |
| tail_params.horizontal.sizing.value | required | Sets the value for the associated tail_params.horizontal.sizing.method. If tail_params.horizontal.sizing.method is set to "stability", then this value is the desired longitudinal static stability coefficient. If tail_params.horizontal.sizing.method is set to "volume_coefficient", then this value is the desired horizontal tail volume coefficient. If tail_params.horizontal.sizing.method is set to "area", then this value is the desired horizontal tail area. |
| tail_params.horizontal.AR | required | Sets the horizontal tail aspect ratio. Equal to the wingspan squared divided by the planform area. In the case of a v-tail or a-tail, this sets the aspect ratio of the entire tail. |
| tail_params.horizontal.TR | required | Sets the horizontal tail taper ratio. The ratio of the tip chord to the root chord. In the case of a v-tail or a-tail, this sets the taper ratio of the entire tail. |
| tail_params.horizontal.sweep | required | Sets the horizontal tail sweep angle and reference location with { angle, reference }, where reference is the chord fraction the angle is measured at: 0 leading edge, 0.25 quarter chord, 1 trailing edge. In the case of a v-tail or a-tail, this sets the sweep of the entire tail. |
| tail_params.horizontal.airfoil | if tail_params.horizontal.aerodynamics=simulate | Sets the airfoil. It can be set using { catalog: "…" } with the filename of an airfoil in the Airfoil Catalog or { points: [[x, y], …] }, or { dat: "…" } as Selig-order text. Coordinates need at least four pairs of two columns, chord-normalised (0-1), running from the trailing edge forward over one surface and back along the other. |
| tail_params.horizontal.arm | required | Sets the longitudinal location of the horizontal tail AC with respect to the aircraft CG. In the case of a v-tail or a-tail, this sets the longitudinal AC location of the entire tail. |
| tail_params.horizontal.incidence | required | Sets the horizontal tail incidence to the fuselage reference line. In the case of a v-tail or a-tail, this sets the incidence of the entire tail. |
| tail_params.horizontal.root_z_from_cg | required unless tail_params.configuration=t_tail | Sets the vertical location of the horizontal tail root chord with respect to the aircraft CG. In the case of a v-tail or a-tail, this sets the vertical root location of the entire tail. |
| tail_params.horizontal.roughness | if sim_params.parasite_drag=true and tail_params.horizontal.aerodynamics=simulate | Characteristic roughness height of the tail surface. Greater than zero. |
| tail_params.horizontal.polar | if tail_params.horizontal.aerodynamics=input | Sets the tail aerodynamic data using { aoa, CL, CD } as three equal-length arrays. Each array must have at least 3 points, be ordered from low AOA to high AOA with all unique AOAs. If sim_params.parasite_drag is set to true, the polar can alternatively be input as { aoa, CL, CD, CDi, CD0 } with five equal-length arrays. This is not required, but allows tail_params.horizontal.interference_factor to be applied to the parasitic drag coefficient, CD0. If sim_params.parasite_drag is enabled but tail_params.horizontal.polar is formatted as only { aoa, CL, CD }, then tail_params.horizontal.interference_factor will have no effect on the model. In the case of a v-tail or a-tail, the uploaded polar should be for a flat surface. The model will add dihedral to the surface and properly scale the lift and drag coefficients based on dihedral. |
| tail_params.horizontal.root_le_to_ac | if tail_params.horizontal.aerodynamics=input | Longitudinal location of the tail AC with respect to its root LE. |
| tail_params.horizontal.CMm0 | if tail_params.horizontal.aerodynamics=input | Tail pitching moment coefficient at zero lift. |
| tail_params.horizontal.crud_factor | if sim_params.parasite_drag=true and tail_params.horizontal.aerodynamics=simulate | Parasitic drag multiplier used to account for miscellaneous drag not modeled. 1 or greater. |
| tail_params.horizontal.interference_factor | if sim_params.parasite_drag=true | Parasite drag multiplier accounting for interference drag at the junctions with other tail surfaces and/or with the fuselage. 1 or greater. |
tail_params.vertical
| Field | Required | Description |
|---|---|---|
| tail_params.vertical.aerodynamics | required unless tail_params.configuration=v_tail or a_tail | Sets the source of tail aerodynamics data. "simulate" simulates the tail aerodynamics and "input" allows tail aerodynamics to be input. |
| tail_params.vertical.sizing | required | Set the vertical tail sizing parameter with { method: "stability", value }, { method: "volume_coefficient", value } or { method: "area", value }. For a v-tail or a-tail, the options are { method: "stability", value }, { method: "volume_coefficient", value } or { method: "dihedral", value } and the input is used with tail_params.horizontal.sizing to size the tail. |
| tail_params.vertical.sizing.method | required | Sets how the vertical tail is sized. Options are stability, volume_coefficient, area, or dihedral. If "stability" is selected, the vertical tail is sized based on an aircraft directional static stability requirement. If "volume_coefficient" is selected, the vertical tail is sized based on a vertical tail volume coefficient. If "area" is selected, the vertical tail is sized directly based on the input value. "dihedral" must be selected as an option if tail_params.horizontal.sizing.method is set to "area" and a v-tail or a-tail is being used; it can only be selected under those conditions. For a v-tail or a-tail, this input is used with tail_params.horizontal.sizing.method to size the tail. |
| tail_params.vertical.sizing.value | required | Sets the value for the associated tail_params.vertical.sizing.method. If tail_params.vertical.sizing.method is set to "stability", then this value is the desired directional static stability coefficient. If tail_params.vertical.sizing.method is set to "volume_coefficient", then this value is the desired vertical tail volume coefficient. If tail_params.vertical.sizing.method is set to "area", then this value is the desired vertical tail area. If tail_params.vertical.sizing.method is set to "dihedral", then this value is the v-tail or a-tail dihedral angle. Note that for an a-tail, positive dihedral values result in anhedral. Dihedral angle must be at least 0 and less than 90 degrees (1.5708 radians). |
| tail_params.vertical.AR | required unless tail_params.configuration=v_tail or a_tail | Aspect ratio. Equal to the wingspan squared divided by the planform area. |
| tail_params.vertical.TR | required unless tail_params.configuration=v_tail or a_tail | Taper ratio. The ratio of the tip chord to the root chord. |
| tail_params.vertical.sweep | required unless tail_params.configuration=v_tail or a_tail | Sets the sweep angle and reference location with { angle, reference }, where reference is the chord fraction the angle is measured at: 0 leading edge, 0.25 quarter chord, 1 trailing edge. |
| tail_params.vertical.airfoil | if tail_params.vertical.aerodynamics=simulate | Sets the airfoil. It can be set using { catalog: "…" } with the filename of an airfoil in the Airfoil Catalog or { points: [[x, y], …] }, or { dat: "…" } as Selig-order text. Coordinates need at least four pairs of two columns, chord-normalised (0-1), running from the trailing edge forward over one surface and back along the other. |
| tail_params.vertical.arm | required unless tail_params.configuration=v_tail or a_tail | Longitudinal location of the tail AC with respect to the aircraft CG. |
| tail_params.vertical.root_z_from_cg | required unless tail_params.configuration=v_tail or a_tail | Sets the vertical location of the tail root chord with respect to the aircraft CG. |
| tail_params.vertical.roughness | if sim_params.parasite_drag=true and tail_params.vertical.aerodynamics=simulate | Characteristic roughness height of the tail surface. Greater than zero. |
| tail_params.vertical.CY_beta | if tail_params.vertical.aerodynamics=input | The derivative representing the change in sideforce with respect to sideslip angle. Could also be considered as the vertical tail’s lift-curve slope. |
| tail_params.vertical.CD0 | if tail_params.vertical.aerodynamics=input | Sets the parasitic drag coefficient of the vertical tail. |
| tail_params.vertical.root_le_to_ac | if tail_params.vertical.aerodynamics=input | Longitudinal location of the tail AC with respect to its root LE. |
| tail_params.vertical.crud_factor | if sim_params.parasite_drag=true and tail_params.vertical.aerodynamics=simulate | Parasitic drag multiplier used to account for miscellaneous drag not modeled. 1 or greater. |
| tail_params.vertical.interference_factor | if sim_params.parasite_drag=true | Parasite drag multiplier accounting for interference drag at the junctions with other tail surfaces and/or with the fuselage. 1 or greater. |
fuselage_params
| Field | Required | Description |
|---|---|---|
| fuselage_params.nose | if fuselage_params is present | Sets the { length, angle } of the fuselage nose section. |
| fuselage_params.center | if fuselage_params is present | Sets the { length, angle } of the fuselage center section. |
| fuselage_params.tail | if fuselage_params is present | Sets the { length, angle } of the fuselage tail section. |
| fuselage_params.nose_to_cg | if fuselage_params is present | Sets the longitudinal location of the aircraft CG with respect to the fuselage nose. |
| fuselage_params.fwd_center_diameter | if fuselage_params is present | Sets the diameter at the forward end of the fuselage center section. Also equal to the diameter of the aft end of the fuselage nose section. |
| fuselage_params.aft_center_diameter | if fuselage_params is present | Sets the diameter at the aft end of the fuselage center section. Also equal to the diameter of the forward end of the fuselage tail section. |
| fuselage_params.roughness | if fuselage_params is present and sim_params.parasite_drag=true | Characteristic roughness height of the fuselage surface. Greater than zero. |
| fuselage_params.crud_factor | if fuselage_params is present and sim_params.parasite_drag=true | Parasitic drag multiplier used to account for miscellaneous drag not modeled. 1 or greater. |
| fuselage_params.interference_factor | if fuselage_params is present and sim_params.parasite_drag=true | Parasite drag multiplier accounting for interference drag at the junctions with the wings, tail, etc. 1 or greater. |
propeller_params
| Field | Required | Description |
|---|---|---|
| propeller_params.diameter | if propeller_params is present | Sets the propeller diameter. |
| propeller_params.count | if propeller_params is present | Sets the number of each motor / propeller set on the aircraft. |
| propeller_params.blades | if propeller_params is present | Sets the number of blades on each propeller. Options are 2, 3, 4, and 6. |
| propeller_params.thrust | if propeller_params is present | Sets the thrust per propeller at the reference airspeed. |
| propeller_params.hub_to_cg | if propeller_params is present | Sets the longitudinal location of the propeller hub with respect to the aircraft CG. Positive is aft of the CG. |
| propeller_params.hub_z_from_cg | if propeller_params is present | Sets the vertical location of the propeller hub with respect to the aircraft CG. Positive is up. |
| propeller_params.blanket_ratio.horizontal | if propeller_params is present | Sets the fraction of the horizontal tail area immersed in the propeller slipstream. |
| propeller_params.blanket_ratio.vertical | if propeller_params is present, unless tail_params.configuration=v_tail or a_tail | Sets the fraction of the vertical tail area immersed in the propeller slipstream. |
misc_drag_params
| Field | Required | Description |
|---|---|---|
| misc_drag_params.sources | optional, if sim_params.parasite_drag=true | Add an entry of { cd, area, z_from_cg } for each miscellaneous drag source. cd is its drag coefficient, area is its reference area, and z_from_cg is its vertical location relative to the cg, where positive is above the cg. |
| misc_drag_params.nacelles | optional | Add an entry of { length, diameter } for each nacelle where diameter is the nacelle’s maximum diameter. If parasite_drag is set to "true", the entry must be { length, diameter, roughness, interference_factor } where roughness is the characteristic roughness height of the nacelle surface (greater than 0) and interference_factor is the parasite drag multiplier accounting for interference drag at the junctions with other surfaces (1 or greater). |
| misc_drag_params.nacelle_crud_factor | if sim_params.parasite_drag=true and nacelles is present | Parasitic drag multiplier used to account for miscellaneous drag not modeled. 1 or greater. |
Outputs
| Field | Description |
|---|---|
| model.name | The name of the model. |
| model.version | The model version that produced this result, with the wing solver's version appended when the wing was simulated. See Model Version. |
| units.system | The unit system that the outputs are written in. |
| units.angle | The units that the angles that the outputs are written in (deg or rad). |
| units.derivative | The units that the derivatives are written in (1/deg or 1/rad). |
| geometry.aircraft.Swet_fus | Fuselage wetted area, also defined as the fuselage outer surface area. |
| geometry.aircraft.x_cg_to_wing_ac | Longitudinal location of the wing AC with respect to the aircraft CG. |
| geometry.aircraft.z_wing_ac_from_cg | Height of the wing AC above the CG, positive up. |
| geometry.aircraft.wing_incidence | Wing incidence to the fuselage reference line. |
| geometry.aircraft.tail_configuration | The tail configuration. Options are conventional/cruciform, t_tail, v_tail, a_tail, u_tail, inverted_u_tail or h_tail. |
| geometry.wing.sref | Planform area of the wing. |
| geometry.wing.AR | Aspect ratio. Equal to the span squared divided by the planform area. |
| geometry.wing.TR | Taper ratio. The ratio of the tip chord to the root chord. |
| geometry.wing.MAC | Mean aerodynamic chord. The chord for a rectangular planform wing that would mirror the surface’s aerodynamic properties. |
| geometry.wing.span | Tip-to-tip span of the wing. |
| geometry.wing.root_chord | Chord length at the wing root. |
| geometry.wing.tip_chord | Chord length at the wing tip. |
| geometry.wing.sweep_ref | The chordwise point that the wing sweep angle is measured from. |
| geometry.wing.sweep | Wing sweep angle from the selected reference point. |
| geometry.wing.dihedral | The upward angle of the surface from the horizontal when viewed from the front. |
| geometry.wing.twist | The difference in wing incidence from tip chord to root chord. Negative values create washout. |
| geometry.wing.wingtip_type | Determines the wingtip geometry. Options are none, endplate, or winglet. |
| geometry.wing.wingtip_param_1 | Vertical length of the wingtip device. Returned for an endplate or a winglet. |
| geometry.wing.wingtip_param_2 | Horizontal length of the endplate, or the sweep angle of the winglet about its quarter chord. |
| geometry.wing.wingtip_param_3 | Ratio of winglet tip chord to root chord. Returned for a winglet. |
| geometry.wing.airfoil_x | A vector containing the x coordinates of the airfoil, in Selig order. Returned when the wing aerodynamics are simulated. |
| geometry.wing.airfoil_y | A vector containing the y coordinates of the airfoil, in Selig order. Returned when the wing aerodynamics are simulated. |
| geometry.tail.tail_configuration | The tail configuration. Options are conventional/cruciform, t_tail, v_tail, a_tail, u_tail, inverted_u_tail or h_tail. |
| geometry.tail.dihedral | The v-tail or a-tail dihedral angle. For a v-tail dihedral angle behaves like wing dihedral. For an a-tail the sign of dihedral is reversed and positive values result in anhedral. |
| geometry.tail.horizontal.area | Planform area of the horizontal tail. For a v-tail or a-tail, this is the planform area for the entire tail. |
| geometry.tail.horizontal.span | Tip-to-tip span of the horizontal tail. For a v-tail or a-tail this is not the tip-to-tip span, it is twice the distance from the root chord to the tip chord. |
| geometry.tail.horizontal.root_chord | Chord length at the surface root. For a v-tail or a-tail, this is the root chord for the entire tail. |
| geometry.tail.horizontal.tip_chord | Chord length at the surface tip. For a v-tail or a-tail, this is the tip chord for the entire tail. |
| geometry.tail.horizontal.MAC | Mean aerodynamic chord of the surface. For a v-tail or a-tail, this is the MAC for the entire tail. |
| geometry.tail.horizontal.x_ac_from_root_LE | Longitudinal location of the horizontal tail AC with respect to its root LE. For a v-tail or a-tail, this is the AC location for the entire tail. |
| geometry.tail.horizontal.root_z_from_cg | The vertical location of the horizontal tail root chord with respect to the aircraft CG. For a v-tail or a-tail this sets the vertical location of the entire tail root. |
| geometry.tail.horizontal.AR | The horizontal tail aspect ratio. Equal to the span squared divided by the planform area. For a v-tail or a-tail, this is the aspect ratio of the entire tail. |
| geometry.tail.horizontal.TR | The horizontal tail taper ratio. The ratio of the tip chord to the root chord. For a v-tail or a-tail, this is the taper ratio of the entire tail. |
| geometry.tail.horizontal.sweep_ref | The chordwise point that the horizontal tail sweep angle is measured from. |
| geometry.tail.horizontal.sweep | Horizontal tail sweep angle from the selected reference point. For a v-tail or a-tail, this is the sweep of the entire tail. |
| geometry.tail.horizontal.arm | Longitudinal location of the horizontal tail AC with respect to the aircraft CG. For a v-tail or a-tail, this is the longitudinal AC location of the entire tail. |
| geometry.tail.horizontal.incidence | Horizontal tail incidence to the fuselage reference line. For a v-tail or a-tail, this is the incidence of the entire tail. |
| geometry.tail.horizontal.airfoil_x | A vector containing the x coordinates of the horizontal tail airfoil, in Selig order. Returned when the horizontal tail aerodynamics are simulated. |
| geometry.tail.horizontal.airfoil_y | A vector containing the y coordinates of the horizontal tail airfoil, in Selig order. Returned when the horizontal tail aerodynamics are simulated. |
| geometry.tail.vertical.area | Planform area of the vertical tail. For a v-tail or a-tail, this planform area is not returned. |
| geometry.tail.vertical.span | Root-to-tip span of one vertical fin. For a v-tail or a-tail, this span is not returned. On an h-tail, this value represents the full height of each vertical tail. |
| geometry.tail.vertical.root_chord | Chord length at the surface root. For a v-tail or a-tail, this root chord is not returned. |
| geometry.tail.vertical.tip_chord | Chord length at the surface tip. For a v-tail or a-tail, this tip chord is not returned. |
| geometry.tail.vertical.MAC | Mean aerodynamic chord of the surface. For a v-tail or a-tail, this MAC is not returned. |
| geometry.tail.vertical.x_ac_from_root_LE | Longitudinal location of the vertical tail AC with respect to its root LE. For a v-tail or a-tail, this AC location is not returned. |
| geometry.tail.vertical.root_z_from_cg | The vertical location of the vertical tail root chord with respect to the aircraft CG. For a v-tail or a-tail, this vertical location is not returned. |
| geometry.tail.vertical.AR | The vertical tail aspect ratio. Equal to the span squared divided by the planform area. For a v-tail or a-tail, this aspect ratio is not returned. |
| geometry.tail.vertical.TR | The vertical tail taper ratio. The ratio of the tip chord to the root chord. For a v-tail or a-tail, this taper ratio is not returned. |
| geometry.tail.vertical.sweep_ref | The chordwise point that the vertical tail sweep angle is measured from. For a v-tail or a-tail, this is not returned. |
| geometry.tail.vertical.sweep | Vertical tail sweep angle from the selected reference point. For a v-tail or a-tail, this sweep is not returned. |
| geometry.tail.vertical.arm | Longitudinal location of the vertical tail AC with respect to the aircraft CG. For a v-tail or a-tail, this location is not returned. |
| geometry.tail.vertical.airfoil_x | A vector containing the x coordinates of the vertical tail airfoil, in Selig order. Returned when the vertical tail aerodynamics are simulated, and not for a v-tail or a-tail. |
| geometry.tail.vertical.airfoil_y | A vector containing the y coordinates of the vertical tail airfoil, in Selig order. Returned when the vertical tail aerodynamics are simulated, and not for a v-tail or a-tail. |
| aerodynamics.aircraft.aoas | A vector containing each aircraft AOA point in the output aircraft lift and drag polar. Angles the aircraft cannot trim at are left out. |
| aerodynamics.aircraft.CL | A vector containing the trimmed aircraft lift coefficient at each AOA. |
| aerodynamics.aircraft.CD | A vector containing the aircraft total drag coefficient at each AOA. |
| aerodynamics.aircraft.CD_wing | A vector containing the wing drag coefficient at each AOA. |
| aerodynamics.aircraft.CD_fus | A vector containing the fuselage drag coefficient at each AOA. |
| aerodynamics.aircraft.CD_HT | A vector containing the horizontal tail drag coefficient at each AOA, not including trim drag. For a v-tail or a-tail, this is the drag of the entire tail. |
| aerodynamics.aircraft.CD_VT | A vector containing the vertical tail drag coefficient at each AOA. For a v-tail or a-tail, this value is null. |
| aerodynamics.aircraft.CD_trim | A vector containing the drag coefficient added by elevator (or ruddervator) deflection at each AOA. |
| aerodynamics.aircraft.CD_nac | A vector containing the nacelle drag coefficient at each AOA. |
| aerodynamics.aircraft.CD_misc | A vector containing the drag coefficient of the miscellaneous drag sources at each AOA. |
| aerodynamics.aircraft.de_trim | A vector containing elevator (or ruddervator) deflection needed to trim at each angle. |
| aerodynamics.aircraft.propwash_HT | The average dynamic pressure ratio of the horizontal tail due to being in the propeller slipstream. For a v-tail or a-tail, this is the dynamic pressure ratio of the entire tail. |
| aerodynamics.aircraft.propwash_VT | The average dynamic pressure ratio of the vertical tail due to being in the propeller slipstream. |
| aerodynamics.aircraft.CMm_aoa | The derivative representing the change in aircraft pitching moment with respect to AOA at 0 AOA. Also defined as the aircraft’s longitudinal static stability coefficient at 0 AOA. |
| aerodynamics.aircraft.CMn_beta | The derivative representing the change in aircraft yawing moment with respect to sideslip angle at 0 AOA. Also defined as the aircraft’s directional static stability coefficient at 0 AOA. |
| aerodynamics.aircraft.CMl_beta | The derivative representing the change in aircraft rolling moment with respect to sideslip angle at 0 AOA. Also defined as the aircraft’s lateral static stability coefficient at 0 AOA. |
| aerodynamics.aircraft.SM | Aircraft static margin. Normalized by wing MAC. |
| aerodynamics.aircraft.NP | The longitudinal point on the aircraft where the neutral point is located, measured from the aircraft CG. |
| aerodynamics.wing.aoas | A vector containing each AOA point in the wing lift and drag polar. Returned when the wing aerodynamics are simulated. |
| aerodynamics.wing.CL | A vector containing the wing lift coefficient at each AOA. Returned when the wing aerodynamics are simulated. |
| aerodynamics.wing.CD | A vector containing the wing total drag coefficient at each AOA, the plain sum of its induced and parasite halves. Only returned if parasite_drag is included. Returned when the wing aerodynamics are simulated. |
| aerodynamics.wing.CDi | A vector containing the wing induced drag coefficient at each AOA. Returned when the wing aerodynamics are simulated. |
| aerodynamics.wing.CD0 | A vector containing the wing parasite drag coefficient at each AOA. Only returned if parasite_drag is included. Returned when the wing aerodynamics are simulated. |
| aerodynamics.wing.CD_friction | A vector containing the wing skin friction drag coefficient at each AOA. Constant across AOA. Only returned if parasite_drag is included. Returned when the wing aerodynamics are simulated. |
| aerodynamics.wing.CD_form | A vector containing the wing form drag coefficient at each AOA. Only returned if parasite_drag is included. Returned when the wing aerodynamics are simulated. |
| aerodynamics.wing.CL_alpha | The wing lift-curve slope at 0 AOA. Returned when the wing aerodynamics are simulated. |
| aerodynamics.wing.CY_beta | The derivative representing the change in wing sideforce with respect to sideslip angle at 0 AOA. Returned when the wing aerodynamics are simulated. |
| aerodynamics.wing.CMl_beta | The derivative representing the change in wing rolling moment with respect to sideslip angle at 0 AOA. Returned when the wing aerodynamics are simulated. |
| aerodynamics.wing.CMn_beta | The derivative representing the change in wing yawing moment with respect to sideslip angle at 0 AOA. Returned when the wing aerodynamics are simulated. |
| aerodynamics.wing.CMm0 | Wing zero-lift pitching moment. Returned when the wing aerodynamics are simulated. |
| aerodynamics.wing.interference_factor | Parasite drag multiplier accounting for interference drag at the junctions with wingtip devices. 1 when parasite drag is not modelled. Returned when the wing aerodynamics are simulated. |
| aerodynamics.wing.root_le_to_ac | Longitudinal location of the wing AC with respect to the root LE. Returned when the wing aerodynamics are simulated. |
| warnings | Present when there are warnings associated with the model inputs or outputs. |
| usage.compute_seconds | The amount of API quota this request used. |
| usage.quota_remaining_seconds | The amount of API quota remaining in this period. |
| usage.reserve_remaining_seconds | The amount of reserve API quota remaining. |
Examples
Aircraft Model Example 1
- Wing and tail only
- Parasite drag off
- Wing aerodynamics simulated
- Wing airfoil read from the Airfoil Catalog
- Endplates fitted to the wingtips
- Conventional tail
- Horizontal and vertical tails sized by static stability coefficient
import os
import requests
API = "https://aerodule.com/api/v1/aircraft"
KEY = os.environ["AERODULE_API_KEY"] # export AERODULE_API_KEY=ak_...
units = {"system": "imperial", "angle": "deg", "derivative": "1/deg"}
payload = {
"sim_params": {
"input_units": units,
"output_units": units,
"airspeed": 60,
"atmosphere": {"altitude": 0},
"parasite_drag": False,
"grow_tail_to_trim": False,
},
"wing_params": {
"span": 8,
"root_chord": 1.25,
"tip_chord": 0.75,
"sweep": {"angle": 0, "reference": 0.25},
"dihedral": 3,
"twist": -2,
"incidence": 2,
"cg_to_ac": 0.1,
"ac_z_from_cg": 0,
"aerodynamics": "simulate",
"airfoil": {"catalog": "naca2412.dat"},
"tip_device": {"type": "endplate", "height": 0.45, "chord": 0.5},
},
"tail_params": {
"configuration": "conventional/cruciform",
"elevator": {"chord_ratio": 0.33, "max_deflection": 30},
"horizontal": {
"AR": 4,
"TR": 0.6,
"sweep": {"angle": 0, "reference": 0.25},
"arm": 3.5,
"sizing": {"method": "stability", "value": -0.03},
"incidence": 0,
"root_z_from_cg": 0,
"aerodynamics": "simulate",
"airfoil": {"catalog": "n0012.dat"},
},
"vertical": {
"AR": 1.5,
"TR": 0.5,
"sweep": {"angle": 15, "reference": 0},
"arm": 3.5,
"sizing": {"method": "stability", "value": 0.001},
"root_z_from_cg": 0,
"aerodynamics": "simulate",
"airfoil": {"catalog": "n0012.dat"},
},
},
}
# The wing is simulated first, then the aircraft: allow a generous timeout.
r = requests.post(API, json=payload,
headers={"Authorization": f"Bearer {KEY}"}, timeout=300)
data = r.json()
if not r.ok:
error = data["error"]
raise SystemExit(f"{r.status_code} {error['code']}: {error['message']}")
u = data["units"]
length, area = ("ft", "ft^2") if u["system"] == "imperial" else ("m", "m^2")
angle, per = u["angle"], u["derivative"]
print(f"Model : {data['model']['name']} {data['model']['version']}")
print(f"Units : {u['system']}, angles in {angle}, derivatives in {per}")
# Geometry is grouped the way the exported workbooks are: the aircraft, the wing
# it was built on, then the tail.
geo = data["geometry"]
print("\nGeometry: aircraft")
for name, unit in [("Swet_fus", area), ("x_cg_to_wing_ac", length),
("z_wing_ac_from_cg", length), ("wing_incidence", angle),
("tail_configuration", "")]:
print(f" {name:<18} {geo['aircraft'][name]} {unit}".rstrip())
# The wing geometry always comes back: every figure in it follows from the
# planform that was sent, whether or not the wing was solved here.
w = geo["wing"]
print("\nGeometry: wing")
for name, unit in [("sref", area), ("AR", ""), ("TR", ""), ("MAC", length),
("span", length), ("root_chord", length), ("tip_chord", length),
("sweep_ref", ""), ("sweep", angle), ("dihedral", angle),
("twist", angle), ("wingtip_type", "")]:
print(f" {name:<18} {w[name]} {unit}".rstrip())
# One to three parameters, depending on the device: height and chord for an
# endplate; height, quarter-chord sweep and taper for a winglet; none at all
# when no device is fitted.
for i in (1, 2, 3):
key = f"wingtip_param_{i}"
if key in w:
print(f" {key:<18} {w[key]}")
# The section is there only where one was given: a wing handed in as a polar
# names no airfoil.
if "airfoil_x" in w:
print(f" airfoil {len(w['airfoil_x'])} coordinate pairs, "
f"first ({w['airfoil_x'][0]}, {w['airfoil_y'][0]})")
tail = geo["tail"]
print(f"\nGeometry: tail ({tail['tail_configuration']})")
dims = [("area", area), ("span", length), ("root_chord", length), ("tip_chord", length),
("MAC", length), ("x_ac_from_root_LE", length), ("root_z_from_cg", length),
("AR", ""), ("TR", ""), ("sweep_ref", ""), ("sweep", angle), ("arm", length),
("incidence", angle)]
for surface in ("horizontal", "vertical"):
if surface not in tail: # no vertical block on a v-tail or a-tail
continue
s = tail[surface]
print(f"\nGeometry: {surface} tail")
for name, unit in dims:
if name in s: # incidence is the horizontal tail's alone
print(f" {name:<18} {s[name]} {unit}".rstrip())
if "airfoil_x" in s: # only where a section was given
print(f" airfoil {len(s['airfoil_x'])} coordinate pairs, "
f"first ({s['airfoil_x'][0]}, {s['airfoil_y'][0]})")
if "dihedral" in tail: # v-tail or a-tail only
print(f"\n dihedral {tail['dihedral']} {angle}")
aero = data["aerodynamics"]["aircraft"]
print("\nStability")
print(f" CMm_aoa {aero['CMm_aoa']} {per}")
print(f" CMn_beta {aero['CMn_beta']} {per}")
print(f" CMl_beta {aero['CMl_beta']} {per}")
print(f" SM {aero['SM']}")
print(f" NP {aero['NP']} {length}")
print("\nPropwash")
print(f" propwash_HT {aero['propwash_HT']}")
if "propwash_VT" in aero: # not returned for a v-tail or a-tail
print(f" propwash_VT {aero['propwash_VT']}")
# Parallel arrays on one AOA grid, from zero lift up to the maximum trim AOA.
# The CD_ components sum to CD. CD_VT is null throughout on a v-tail or a-tail.
columns = ["CL", "CD", "CD_wing", "CD_fus", "CD_HT", "CD_VT",
"CD_trim", "CD_nac", "CD_misc", "de_trim"]
print(f"\n{'aoa (' + angle + ')':>11}" + "".join(f"{name:>11}" for name in columns))
for i, alpha in enumerate(aero["aoas"]):
cells = "".join(f"{'-':>11}" if aero[name][i] is None else f"{aero[name][i]:>11.6f}"
for name in columns)
print(f"{alpha:>11g}" + cells)
# The wing the aircraft was built on, on its own AOA grid: it runs below the
# aircraft's, because the aircraft reaches zero lift beneath the wing's own.
wing_aero = data["aerodynamics"].get("wing")
if wing_aero:
print("\nWing aerodynamics")
for name, unit in [("CL_alpha", per), ("CY_beta", per), ("CMl_beta", per),
("CMn_beta", per), ("CMm0", ""), ("interference_factor", ""),
("root_le_to_ac", length)]:
print(f" {name:<20} {wing_aero[name]} {unit}".rstrip())
# The parasite columns are there only when parasite drag was modelled; CD is
# the plain sum of CDi and CD0, with the interference factor left to apply.
wcols = [c for c in ("CL", "CD", "CDi", "CD0", "CD_friction", "CD_form")
if c in wing_aero]
print(f"\n{'aoa (' + angle + ')':>11}" + "".join(f"{name:>13}" for name in wcols))
for i, alpha in enumerate(wing_aero["aoas"]):
print(f"{alpha:>11g}" + "".join(f"{wing_aero[name][i]:>13.6f}" for name in wcols))
# Present only when the solve has something to flag.
warnings = data.get("warnings", [])
print(f"\nWarnings: {len(warnings)}")
for w in warnings:
print(f" {w}")
usage = data["usage"]
print(f"\nAPI quota used by this request : {usage['compute_seconds']} s")
print(f"API quota left this period : {usage['quota_remaining_seconds']} s")
print(f"Reserve API quota left : {usage['reserve_remaining_seconds']} s")Aircraft Model Example 2
- Wing and tail aerodynamics simulated
- Parasite drag on
- Fuselage modelled
- Propeller modelled
- One miscellaneous drag source
- One nacelle
- Winglets fitted to the wingtips
- Wing and tail airfoils read from the Airfoil Catalog
- V-tail sized by horizontal and vertical tail volume coefficients
import os
import requests
API = "https://aerodule.com/api/v1/aircraft"
KEY = os.environ["AERODULE_API_KEY"] # export AERODULE_API_KEY=ak_...
units = {"system": "imperial", "angle": "deg", "derivative": "1/deg"}
payload = {
"sim_params": {
"input_units": units,
"output_units": units,
"airspeed": 70,
"atmosphere": {"altitude": 2000},
"parasite_drag": True,
"max_trim_aoa": 14,
"grow_tail_to_trim": False,
},
"wing_params": {
"span": 9,
"root_chord": 1.3,
"tip_chord": 0.8,
"sweep": {"angle": 3, "reference": 0.25},
"dihedral": 4,
"twist": -2,
"incidence": 2,
"cg_to_ac": 0.12,
"ac_z_from_cg": 0.15,
"aerodynamics": "simulate",
"airfoil": {"catalog": "naca2412.dat"},
"tip_device": {
"type": "winglet",
"height": 0.5,
"taper": 0.45,
"sweep": {"angle": 28, "reference": 0.25},
},
"roughness": 1.67e-05,
"crud_factor": 1.2,
"interference_factor": 1.1,
},
"tail_params": {
"configuration": "v_tail",
"elevator": {"chord_ratio": 0.33, "max_deflection": 30},
"horizontal": {
"AR": 4,
"TR": 0.6,
"sweep": {"angle": 10, "reference": 0.25},
"arm": 3.6,
"sizing": {"method": "volume_coefficient", "value": 0.6},
"incidence": 0,
"root_z_from_cg": 0.3,
"aerodynamics": "simulate",
"airfoil": {"catalog": "n0012.dat"},
"roughness": 1.67e-05,
"crud_factor": 1.2,
"interference_factor": 1.15,
},
"vertical": {
"sizing": {"method": "volume_coefficient", "value": 0.04},
},
},
"fuselage_params": {
"nose": {"length": 1, "angle": 12},
"center": {"length": 3, "angle": 0},
"tail": {"length": 1.5, "angle": 8},
"nose_to_cg": 1.8,
"fwd_center_diameter": 0.55,
"aft_center_diameter": 0.45,
"roughness": 1.67e-05,
"crud_factor": 1.2,
"interference_factor": 1,
},
"propeller_params": {
"diameter": 1.2,
"count": 1,
"blades": 2,
"thrust": 6,
"hub_to_cg": -2.2,
"hub_z_from_cg": 0.1,
"blanket_ratio": {"horizontal": 0.4},
},
"misc_drag_params": {
"sources": [{"cd": 0.8, "area": 0.05, "z_from_cg": 0.3}],
"nacelles": [
{
"length": 0.9,
"diameter": 0.25,
"roughness": 1.67e-05,
"interference_factor": 1.1,
},
],
"nacelle_crud_factor": 1.2,
},
}
# The wing is simulated first, then the aircraft: allow a generous timeout.
r = requests.post(API, json=payload,
headers={"Authorization": f"Bearer {KEY}"}, timeout=300)
data = r.json()
if not r.ok:
error = data["error"]
raise SystemExit(f"{r.status_code} {error['code']}: {error['message']}")
u = data["units"]
length, area = ("ft", "ft^2") if u["system"] == "imperial" else ("m", "m^2")
angle, per = u["angle"], u["derivative"]
print(f"Model : {data['model']['name']} {data['model']['version']}")
print(f"Units : {u['system']}, angles in {angle}, derivatives in {per}")
# Geometry is grouped the way the exported workbooks are: the aircraft, the wing
# it was built on, then the tail.
geo = data["geometry"]
print("\nGeometry: aircraft")
for name, unit in [("Swet_fus", area), ("x_cg_to_wing_ac", length),
("z_wing_ac_from_cg", length), ("wing_incidence", angle),
("tail_configuration", "")]:
print(f" {name:<18} {geo['aircraft'][name]} {unit}".rstrip())
# The wing geometry always comes back: every figure in it follows from the
# planform that was sent, whether or not the wing was solved here.
w = geo["wing"]
print("\nGeometry: wing")
for name, unit in [("sref", area), ("AR", ""), ("TR", ""), ("MAC", length),
("span", length), ("root_chord", length), ("tip_chord", length),
("sweep_ref", ""), ("sweep", angle), ("dihedral", angle),
("twist", angle), ("wingtip_type", "")]:
print(f" {name:<18} {w[name]} {unit}".rstrip())
# One to three parameters, depending on the device: height and chord for an
# endplate; height, quarter-chord sweep and taper for a winglet; none at all
# when no device is fitted.
for i in (1, 2, 3):
key = f"wingtip_param_{i}"
if key in w:
print(f" {key:<18} {w[key]}")
# The section is there only where one was given: a wing handed in as a polar
# names no airfoil.
if "airfoil_x" in w:
print(f" airfoil {len(w['airfoil_x'])} coordinate pairs, "
f"first ({w['airfoil_x'][0]}, {w['airfoil_y'][0]})")
tail = geo["tail"]
print(f"\nGeometry: tail ({tail['tail_configuration']})")
dims = [("area", area), ("span", length), ("root_chord", length), ("tip_chord", length),
("MAC", length), ("x_ac_from_root_LE", length), ("root_z_from_cg", length),
("AR", ""), ("TR", ""), ("sweep_ref", ""), ("sweep", angle), ("arm", length),
("incidence", angle)]
for surface in ("horizontal", "vertical"):
if surface not in tail: # no vertical block on a v-tail or a-tail
continue
s = tail[surface]
print(f"\nGeometry: {surface} tail")
for name, unit in dims:
if name in s: # incidence is the horizontal tail's alone
print(f" {name:<18} {s[name]} {unit}".rstrip())
if "airfoil_x" in s: # only where a section was given
print(f" airfoil {len(s['airfoil_x'])} coordinate pairs, "
f"first ({s['airfoil_x'][0]}, {s['airfoil_y'][0]})")
if "dihedral" in tail: # v-tail or a-tail only
print(f"\n dihedral {tail['dihedral']} {angle}")
aero = data["aerodynamics"]["aircraft"]
print("\nStability")
print(f" CMm_aoa {aero['CMm_aoa']} {per}")
print(f" CMn_beta {aero['CMn_beta']} {per}")
print(f" CMl_beta {aero['CMl_beta']} {per}")
print(f" SM {aero['SM']}")
print(f" NP {aero['NP']} {length}")
print("\nPropwash")
print(f" propwash_HT {aero['propwash_HT']}")
if "propwash_VT" in aero: # not returned for a v-tail or a-tail
print(f" propwash_VT {aero['propwash_VT']}")
# Parallel arrays on one AOA grid, from zero lift up to the maximum trim AOA.
# The CD_ components sum to CD. CD_VT is null throughout on a v-tail or a-tail.
columns = ["CL", "CD", "CD_wing", "CD_fus", "CD_HT", "CD_VT",
"CD_trim", "CD_nac", "CD_misc", "de_trim"]
print(f"\n{'aoa (' + angle + ')':>11}" + "".join(f"{name:>11}" for name in columns))
for i, alpha in enumerate(aero["aoas"]):
cells = "".join(f"{'-':>11}" if aero[name][i] is None else f"{aero[name][i]:>11.6f}"
for name in columns)
print(f"{alpha:>11g}" + cells)
# The wing the aircraft was built on, on its own AOA grid: it runs below the
# aircraft's, because the aircraft reaches zero lift beneath the wing's own.
wing_aero = data["aerodynamics"].get("wing")
if wing_aero:
print("\nWing aerodynamics")
for name, unit in [("CL_alpha", per), ("CY_beta", per), ("CMl_beta", per),
("CMn_beta", per), ("CMm0", ""), ("interference_factor", ""),
("root_le_to_ac", length)]:
print(f" {name:<20} {wing_aero[name]} {unit}".rstrip())
# The parasite columns are there only when parasite drag was modelled; CD is
# the plain sum of CDi and CD0, with the interference factor left to apply.
wcols = [c for c in ("CL", "CD", "CDi", "CD0", "CD_friction", "CD_form")
if c in wing_aero]
print(f"\n{'aoa (' + angle + ')':>11}" + "".join(f"{name:>13}" for name in wcols))
for i, alpha in enumerate(wing_aero["aoas"]):
print(f"{alpha:>11g}" + "".join(f"{wing_aero[name][i]:>13.6f}" for name in wcols))
# Present only when the solve has something to flag.
warnings = data.get("warnings", [])
print(f"\nWarnings: {len(warnings)}")
for w in warnings:
print(f" {w}")
usage = data["usage"]
print(f"\nAPI quota used by this request : {usage['compute_seconds']} s")
print(f"API quota left this period : {usage['quota_remaining_seconds']} s")
print(f"Reserve API quota left : {usage['reserve_remaining_seconds']} s")Aircraft Model Example 3
- Wing and tail aerodynamic data supplied as input
- Parasite drag on
- A-tail sized by area and dihedral
import os
import requests
API = "https://aerodule.com/api/v1/aircraft"
KEY = os.environ["AERODULE_API_KEY"] # export AERODULE_API_KEY=ak_...
units = {"system": "imperial", "angle": "deg", "derivative": "1/deg"}
payload = {
"sim_params": {
"input_units": units,
"output_units": units,
"airspeed": 65,
"atmosphere": {"density": 0.0023769, "viscosity": 3.737e-07},
"parasite_drag": True,
"max_trim_aoa": 10,
"grow_tail_to_trim": False,
},
"wing_params": {
"span": 8.5,
"root_chord": 1.2,
"tip_chord": 0.8,
"sweep": {"angle": 2, "reference": 0.25},
"dihedral": 3,
"twist": -1.5,
"incidence": 1.5,
"cg_to_ac": 0.15,
"ac_z_from_cg": 0.1,
"aerodynamics": "input",
"polar": {
"aoa": [-8, -6, -4, -2, 0, 2, 4, 6, 8, 10, 12, 14],
"CL": [-0.54, -0.36, -0.18, 0, 0.18, 0.36, 0.54, 0.72, 0.9, 1.08, 1.26, 1.44],
"CD": [0.02615, 0.018567, 0.014017, 0.0125, 0.014017, 0.018567, 0.02615, 0.036766, 0.050416, 0.0671, 0.086816, 0.109566],
"CDi": [0.01365, 0.006067, 0.001517, 0, 0.001517, 0.006067, 0.01365, 0.024266, 0.037916, 0.0546, 0.074316, 0.097066],
"CD0": [0.0125, 0.0125, 0.0125, 0.0125, 0.0125, 0.0125, 0.0125, 0.0125, 0.0125, 0.0125, 0.0125, 0.0125],
},
"root_le_to_ac": 0.26,
"CMm0": -0.05,
"CY_beta": -0.005,
"CMl_beta": -0.0012,
"interference_factor": 1.1,
},
"tail_params": {
"configuration": "a_tail",
"elevator": {"chord_ratio": 0.3, "max_deflection": 25},
"spanwise_mac_location": 0.62,
"horizontal": {
"AR": 3.5,
"TR": 0.55,
"sweep": {"angle": 8, "reference": 0.25},
"arm": 3.4,
"sizing": {"method": "area", "value": 2.2},
"incidence": -1,
"root_z_from_cg": -0.2,
"aerodynamics": "input",
"polar": {
"aoa": [-14, -12, -9, -6, -3, 0, 3, 6, 9, 12, 14],
"CL": [-0.98, -0.84, -0.63, -0.42, -0.21, 0, 0.21, 0.42, 0.63, 0.84, 0.98],
"CD": [0.093918, 0.071389, 0.044094, 0.024597, 0.012899, 0.009, 0.012899, 0.024597, 0.044094, 0.071389, 0.093918],
},
"CMm0": 0,
"root_le_to_ac": 0.21,
"interference_factor": 1.15,
},
"vertical": {
"sizing": {"method": "dihedral", "value": 35},
},
},
}
# The wing is simulated first, then the aircraft: allow a generous timeout.
r = requests.post(API, json=payload,
headers={"Authorization": f"Bearer {KEY}"}, timeout=300)
data = r.json()
if not r.ok:
error = data["error"]
raise SystemExit(f"{r.status_code} {error['code']}: {error['message']}")
u = data["units"]
length, area = ("ft", "ft^2") if u["system"] == "imperial" else ("m", "m^2")
angle, per = u["angle"], u["derivative"]
print(f"Model : {data['model']['name']} {data['model']['version']}")
print(f"Units : {u['system']}, angles in {angle}, derivatives in {per}")
# Geometry is grouped the way the exported workbooks are: the aircraft, the wing
# it was built on, then the tail.
geo = data["geometry"]
print("\nGeometry: aircraft")
for name, unit in [("Swet_fus", area), ("x_cg_to_wing_ac", length),
("z_wing_ac_from_cg", length), ("wing_incidence", angle),
("tail_configuration", "")]:
print(f" {name:<18} {geo['aircraft'][name]} {unit}".rstrip())
# The wing geometry always comes back: every figure in it follows from the
# planform that was sent, whether or not the wing was solved here.
w = geo["wing"]
print("\nGeometry: wing")
for name, unit in [("sref", area), ("AR", ""), ("TR", ""), ("MAC", length),
("span", length), ("root_chord", length), ("tip_chord", length),
("sweep_ref", ""), ("sweep", angle), ("dihedral", angle),
("twist", angle), ("wingtip_type", "")]:
print(f" {name:<18} {w[name]} {unit}".rstrip())
# One to three parameters, depending on the device: height and chord for an
# endplate; height, quarter-chord sweep and taper for a winglet; none at all
# when no device is fitted.
for i in (1, 2, 3):
key = f"wingtip_param_{i}"
if key in w:
print(f" {key:<18} {w[key]}")
# The section is there only where one was given: a wing handed in as a polar
# names no airfoil.
if "airfoil_x" in w:
print(f" airfoil {len(w['airfoil_x'])} coordinate pairs, "
f"first ({w['airfoil_x'][0]}, {w['airfoil_y'][0]})")
tail = geo["tail"]
print(f"\nGeometry: tail ({tail['tail_configuration']})")
dims = [("area", area), ("span", length), ("root_chord", length), ("tip_chord", length),
("MAC", length), ("x_ac_from_root_LE", length), ("root_z_from_cg", length),
("AR", ""), ("TR", ""), ("sweep_ref", ""), ("sweep", angle), ("arm", length),
("incidence", angle)]
for surface in ("horizontal", "vertical"):
if surface not in tail: # no vertical block on a v-tail or a-tail
continue
s = tail[surface]
print(f"\nGeometry: {surface} tail")
for name, unit in dims:
if name in s: # incidence is the horizontal tail's alone
print(f" {name:<18} {s[name]} {unit}".rstrip())
if "airfoil_x" in s: # only where a section was given
print(f" airfoil {len(s['airfoil_x'])} coordinate pairs, "
f"first ({s['airfoil_x'][0]}, {s['airfoil_y'][0]})")
if "dihedral" in tail: # v-tail or a-tail only
print(f"\n dihedral {tail['dihedral']} {angle}")
aero = data["aerodynamics"]["aircraft"]
print("\nStability")
print(f" CMm_aoa {aero['CMm_aoa']} {per}")
print(f" CMn_beta {aero['CMn_beta']} {per}")
print(f" CMl_beta {aero['CMl_beta']} {per}")
print(f" SM {aero['SM']}")
print(f" NP {aero['NP']} {length}")
print("\nPropwash")
print(f" propwash_HT {aero['propwash_HT']}")
if "propwash_VT" in aero: # not returned for a v-tail or a-tail
print(f" propwash_VT {aero['propwash_VT']}")
# Parallel arrays on one AOA grid, from zero lift up to the maximum trim AOA.
# The CD_ components sum to CD. CD_VT is null throughout on a v-tail or a-tail.
columns = ["CL", "CD", "CD_wing", "CD_fus", "CD_HT", "CD_VT",
"CD_trim", "CD_nac", "CD_misc", "de_trim"]
print(f"\n{'aoa (' + angle + ')':>11}" + "".join(f"{name:>11}" for name in columns))
for i, alpha in enumerate(aero["aoas"]):
cells = "".join(f"{'-':>11}" if aero[name][i] is None else f"{aero[name][i]:>11.6f}"
for name in columns)
print(f"{alpha:>11g}" + cells)
# The wing the aircraft was built on, on its own AOA grid: it runs below the
# aircraft's, because the aircraft reaches zero lift beneath the wing's own.
wing_aero = data["aerodynamics"].get("wing")
if wing_aero:
print("\nWing aerodynamics")
for name, unit in [("CL_alpha", per), ("CY_beta", per), ("CMl_beta", per),
("CMn_beta", per), ("CMm0", ""), ("interference_factor", ""),
("root_le_to_ac", length)]:
print(f" {name:<20} {wing_aero[name]} {unit}".rstrip())
# The parasite columns are there only when parasite drag was modelled; CD is
# the plain sum of CDi and CD0, with the interference factor left to apply.
wcols = [c for c in ("CL", "CD", "CDi", "CD0", "CD_friction", "CD_form")
if c in wing_aero]
print(f"\n{'aoa (' + angle + ')':>11}" + "".join(f"{name:>13}" for name in wcols))
for i, alpha in enumerate(wing_aero["aoas"]):
print(f"{alpha:>11g}" + "".join(f"{wing_aero[name][i]:>13.6f}" for name in wcols))
# Present only when the solve has something to flag.
warnings = data.get("warnings", [])
print(f"\nWarnings: {len(warnings)}")
for w in warnings:
print(f" {w}")
usage = data["usage"]
print(f"\nAPI quota used by this request : {usage['compute_seconds']} s")
print(f"API quota left this period : {usage['quota_remaining_seconds']} s")
print(f"Reserve API quota left : {usage['reserve_remaining_seconds']} s")Aircraft Model Example 4
- Wing aerodynamics simulated from custom airfoil geometry
- Tail aerodynamic data supplied as input
- Parasite drag on
- Propeller modelled
- T-tail, so the horizontal tail blanket ratio is 0: it sits clear of the slipstream
import os
import requests
API = "https://aerodule.com/api/v1/aircraft"
KEY = os.environ["AERODULE_API_KEY"] # export AERODULE_API_KEY=ak_...
units = {"system": "imperial", "angle": "deg", "derivative": "1/deg"}
payload = {
"sim_params": {
"input_units": units,
"output_units": units,
"airspeed": 75,
"atmosphere": {"altitude": 1000},
"parasite_drag": True,
"max_trim_aoa": 12,
"grow_tail_to_trim": False,
},
"wing_params": {
"span": 9.5,
"root_chord": 1.35,
"tip_chord": 0.85,
"sweep": {"angle": 0, "reference": 0.25},
"dihedral": 2,
"twist": -2,
"incidence": 1.5,
"cg_to_ac": 0.14,
"ac_z_from_cg": 0.12,
"aerodynamics": "simulate",
"airfoil": {
"points": [
[1, 0.0013],
[0.95, 0.0114],
[0.9, 0.0208],
[0.8, 0.0375],
[0.7, 0.0518],
[0.6, 0.0636],
[0.5, 0.0724],
[0.4, 0.078],
[0.3, 0.0788],
[0.25, 0.0767],
[0.2, 0.0726],
[0.15, 0.0661],
[0.1, 0.0563],
[0.075, 0.0496],
[0.05, 0.0413],
[0.025, 0.0299],
[0.0125, 0.0215],
[0, 0],
[0.0125, -0.0165],
[0.025, -0.0227],
[0.05, -0.0301],
[0.075, -0.0346],
[0.1, -0.0375],
[0.15, -0.041],
[0.2, -0.0423],
[0.25, -0.0422],
[0.3, -0.0412],
[0.4, -0.038],
[0.5, -0.0334],
[0.6, -0.0276],
[0.7, -0.0214],
[0.8, -0.015],
[0.9, -0.0082],
[0.95, -0.0048],
[1, -0.0013],
],
},
"roughness": 1.67e-05,
"crud_factor": 1.2,
"interference_factor": 1.1,
},
"tail_params": {
"configuration": "t_tail",
"elevator": {"chord_ratio": 0.32, "max_deflection": 30},
"spanwise_mac_location": 0.58,
"horizontal": {
"AR": 4.2,
"TR": 0.55,
"sweep": {"angle": 6, "reference": 0.25},
"arm": 3.8,
"sizing": {"method": "stability", "value": -0.03},
"incidence": -1,
"aerodynamics": "input",
"polar": {
"aoa": [-14, -12, -9, -6, -3, 0, 3, 6, 9, 12, 14],
"CL": [-0.952, -0.816, -0.612, -0.408, -0.204, 0, 0.204, 0.408, 0.612, 0.816, 0.952],
"CD": [0.089635, 0.068375, 0.042617, 0.024219, 0.01318, 0.0095, 0.01318, 0.024219, 0.042617, 0.068375, 0.089635],
},
"CMm0": 0,
"root_le_to_ac": 0.19,
"interference_factor": 1.15,
},
"vertical": {
"AR": 1.4,
"TR": 0.5,
"sweep": {"angle": 20, "reference": 0.25},
"arm": 3.7,
"sizing": {"method": "volume_coefficient", "value": 0.045},
"root_z_from_cg": 0.25,
"aerodynamics": "input",
"CY_beta": 0.028,
"CD0": 0.011,
"root_le_to_ac": 0.24,
"interference_factor": 1.15,
},
},
"propeller_params": {
"diameter": 1.4,
"count": 2,
"blades": 3,
"thrust": 8,
"hub_to_cg": -1.9,
"hub_z_from_cg": 0.05,
"blanket_ratio": {"horizontal": 0, "vertical": 0.35},
},
}
# The wing is simulated first, then the aircraft: allow a generous timeout.
r = requests.post(API, json=payload,
headers={"Authorization": f"Bearer {KEY}"}, timeout=300)
data = r.json()
if not r.ok:
error = data["error"]
raise SystemExit(f"{r.status_code} {error['code']}: {error['message']}")
u = data["units"]
length, area = ("ft", "ft^2") if u["system"] == "imperial" else ("m", "m^2")
angle, per = u["angle"], u["derivative"]
print(f"Model : {data['model']['name']} {data['model']['version']}")
print(f"Units : {u['system']}, angles in {angle}, derivatives in {per}")
# Geometry is grouped the way the exported workbooks are: the aircraft, the wing
# it was built on, then the tail.
geo = data["geometry"]
print("\nGeometry: aircraft")
for name, unit in [("Swet_fus", area), ("x_cg_to_wing_ac", length),
("z_wing_ac_from_cg", length), ("wing_incidence", angle),
("tail_configuration", "")]:
print(f" {name:<18} {geo['aircraft'][name]} {unit}".rstrip())
# The wing geometry always comes back: every figure in it follows from the
# planform that was sent, whether or not the wing was solved here.
w = geo["wing"]
print("\nGeometry: wing")
for name, unit in [("sref", area), ("AR", ""), ("TR", ""), ("MAC", length),
("span", length), ("root_chord", length), ("tip_chord", length),
("sweep_ref", ""), ("sweep", angle), ("dihedral", angle),
("twist", angle), ("wingtip_type", "")]:
print(f" {name:<18} {w[name]} {unit}".rstrip())
# One to three parameters, depending on the device: height and chord for an
# endplate; height, quarter-chord sweep and taper for a winglet; none at all
# when no device is fitted.
for i in (1, 2, 3):
key = f"wingtip_param_{i}"
if key in w:
print(f" {key:<18} {w[key]}")
# The section is there only where one was given: a wing handed in as a polar
# names no airfoil.
if "airfoil_x" in w:
print(f" airfoil {len(w['airfoil_x'])} coordinate pairs, "
f"first ({w['airfoil_x'][0]}, {w['airfoil_y'][0]})")
tail = geo["tail"]
print(f"\nGeometry: tail ({tail['tail_configuration']})")
dims = [("area", area), ("span", length), ("root_chord", length), ("tip_chord", length),
("MAC", length), ("x_ac_from_root_LE", length), ("root_z_from_cg", length),
("AR", ""), ("TR", ""), ("sweep_ref", ""), ("sweep", angle), ("arm", length),
("incidence", angle)]
for surface in ("horizontal", "vertical"):
if surface not in tail: # no vertical block on a v-tail or a-tail
continue
s = tail[surface]
print(f"\nGeometry: {surface} tail")
for name, unit in dims:
if name in s: # incidence is the horizontal tail's alone
print(f" {name:<18} {s[name]} {unit}".rstrip())
if "airfoil_x" in s: # only where a section was given
print(f" airfoil {len(s['airfoil_x'])} coordinate pairs, "
f"first ({s['airfoil_x'][0]}, {s['airfoil_y'][0]})")
if "dihedral" in tail: # v-tail or a-tail only
print(f"\n dihedral {tail['dihedral']} {angle}")
aero = data["aerodynamics"]["aircraft"]
print("\nStability")
print(f" CMm_aoa {aero['CMm_aoa']} {per}")
print(f" CMn_beta {aero['CMn_beta']} {per}")
print(f" CMl_beta {aero['CMl_beta']} {per}")
print(f" SM {aero['SM']}")
print(f" NP {aero['NP']} {length}")
print("\nPropwash")
print(f" propwash_HT {aero['propwash_HT']}")
if "propwash_VT" in aero: # not returned for a v-tail or a-tail
print(f" propwash_VT {aero['propwash_VT']}")
# Parallel arrays on one AOA grid, from zero lift up to the maximum trim AOA.
# The CD_ components sum to CD. CD_VT is null throughout on a v-tail or a-tail.
columns = ["CL", "CD", "CD_wing", "CD_fus", "CD_HT", "CD_VT",
"CD_trim", "CD_nac", "CD_misc", "de_trim"]
print(f"\n{'aoa (' + angle + ')':>11}" + "".join(f"{name:>11}" for name in columns))
for i, alpha in enumerate(aero["aoas"]):
cells = "".join(f"{'-':>11}" if aero[name][i] is None else f"{aero[name][i]:>11.6f}"
for name in columns)
print(f"{alpha:>11g}" + cells)
# The wing the aircraft was built on, on its own AOA grid: it runs below the
# aircraft's, because the aircraft reaches zero lift beneath the wing's own.
wing_aero = data["aerodynamics"].get("wing")
if wing_aero:
print("\nWing aerodynamics")
for name, unit in [("CL_alpha", per), ("CY_beta", per), ("CMl_beta", per),
("CMn_beta", per), ("CMm0", ""), ("interference_factor", ""),
("root_le_to_ac", length)]:
print(f" {name:<20} {wing_aero[name]} {unit}".rstrip())
# The parasite columns are there only when parasite drag was modelled; CD is
# the plain sum of CDi and CD0, with the interference factor left to apply.
wcols = [c for c in ("CL", "CD", "CDi", "CD0", "CD_friction", "CD_form")
if c in wing_aero]
print(f"\n{'aoa (' + angle + ')':>11}" + "".join(f"{name:>13}" for name in wcols))
for i, alpha in enumerate(wing_aero["aoas"]):
print(f"{alpha:>11g}" + "".join(f"{wing_aero[name][i]:>13.6f}" for name in wcols))
# Present only when the solve has something to flag.
warnings = data.get("warnings", [])
print(f"\nWarnings: {len(warnings)}")
for w in warnings:
print(f" {w}")
usage = data["usage"]
print(f"\nAPI quota used by this request : {usage['compute_seconds']} s")
print(f"API quota left this period : {usage['quota_remaining_seconds']} s")
print(f"Reserve API quota left : {usage['reserve_remaining_seconds']} s")Errors
r = requests.post(url, json=payload, headers=auth, timeout=120)
# Lasts until the billing period resets. Retrying is pointless.
if r.status_code == 402:
raise SystemExit("Quota spent; resets at " + r.headers["X-Quota-Reset"])
if not r.ok:
err = r.json()["error"]
# On a 400, details.field names the exact field that was wrong.
field = err.get("details", {}).get("field")
raise SystemExit(err["code"] + ": " + err["message"]
+ (" [" + field + "]" if field else ""))
data = r.json()| Code | Status | Description |
|---|---|---|
| unauthenticated | 401 | No key, or key was not a bearer token. |
| invalid_key | 401 | Unknown or revoked key. |
| not_entitled | 403 | No active plan, or a payment has failed. |
| invalid_request | 400 | Schema violation: a missing required field, a value of the wrong type or out of range, a field the endpoint does not recognise, or a field that does not apply to the mode chosen. |
| invalid_airfoil | 400 | Airfoil coordinates could not be read. |
| unknown_motor | 400 | No motor with that ID. |
| unknown_propeller | 400 | No propeller with that ID. |
| invalid_atmosphere | 400 | Invalid input for either altitude or density. |
| point_limit | 400 | There are more than 10,000 requested performance points. |
| out_of_operating_range | 422 | Every requested performance point is outside of the motor/propeller operating range. |
| quota_exhausted | 402 | API quota and reserve both spent. |
| billing_unavailable | 503 | Your billing period has ended and the renewal could not be confirmed with Stripe on this request. Retry after the Retry-After header. |
| solver_error | 422 | The geometry was understood but did not solve. On the aircraft endpoint this also covers a wing simulation that failed before the aircraft was reached. |
| upstream_unavailable | 503 | The model service was unreachable. Retry. |
| site_locked | 503 | The deployment is not open to the public yet and is behind a passcode. |
Model Version
wing/1.0.0+solver.1.0.0 propulsion/1.0.0+data.1.0.0
└──┬──┘ └──┬──┘ └──┬──┘ └─┬─┘
conversion the solver conversion the motor
and geometry itself handling and propeller
handling dataset
aircraft/1.0.0+solver.1.0.0+wing.1.0.0
└──┬──┘ └──┬──┘ └──┬──┘
conversion the aircraft the wing solver
and geometry solver
handlingFAQ
What happens if I run out of computing mid sim?
The sim being run will complete.
When does my quota reset?
Quota resets at the start of each billing period, which follows the day of subscription. If the subscription was started on the 20th, the API quota will return on the 20th.
How are computing hours calculated?
Compute time is recorded as time the solver spends on your request. Parsing, validation, and network time are not counted.
What happens if a big sim fails or crashes?
A request that returns solver_error, out_of_operating_range or upstream_unavailable is not counted towards API usage quota.
Is there an API rate limit?
No.
Can I run simulations locally?
No. The models run on Aerodule’s servers and the API is the only programmatic access to them.