Skip to content

Commit fb242b6

Browse files
authored
Merge branch 'main' into agent/300-write-section-aero-writes-an-all-nan-def
2 parents 5e78ab6 + 7bb29a1 commit fb242b6

5 files changed

Lines changed: 139 additions & 0 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ Three kinds of input data is needed:
9797

9898
Wing geometry can also be loaded from YAML files or `.obj` files. See the examples for details.
9999

100+
A whole run — the flight condition, each wing and the solver — is configured by a `vsm_settings.yaml`. See [The settings file](https://OpenSourceAWE.github.io/VortexStepMethod.jl/dev/settings/) for an annotated example.
101+
100102
### Example for defining the required input:
101103
```julia
102104

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ makedocs(;
1919
"Home" => "index.md",
2020
"How it works" => "explanation.md",
2121
"CAD mesh to model" => "airfoil_pipeline.md",
22+
"Settings file" => "settings.md",
2223
"Examples" => "examples.md",
2324
"Exported Functions" => "functions.md",
2425
"Exported Types" => "types.md",

docs/src/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ Three kinds of input data is needed:
102102

103103
Wing geometry can also be loaded from YAML files or `.obj` files. See the examples for details.
104104

105+
A whole run — the flight condition, each wing and the solver — is configured by a `vsm_settings.yaml`. See [The settings file](@ref) for an annotated example.
106+
105107
### Example for defining the required input:
106108

107109
```julia

docs/src/settings.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# The settings file
2+
3+
A `vsm_settings.yaml` configures a whole run in one place: the flight condition, each
4+
wing and how it is discretised, and the numerical solver. [`VSMSettings`](@ref) reads
5+
it, and [`Wing`](@ref), [`Solver`](@ref) and [`set_va!`](@ref) are each built from the
6+
object it returns.
7+
8+
```julia
9+
settings = VSMSettings("ram_air_kite/vsm_settings.yaml") # under data/
10+
settings = VSMSettings("my/vsm_settings.yaml"; data_prefix=false) # as written
11+
12+
wing = Wing(settings)
13+
body_aero = BodyAerodynamics([wing])
14+
solver = Solver(body_aero, settings)
15+
set_va!(body_aero, settings)
16+
```
17+
18+
The file has three top-level blocks — `condition:`, `wings:` and `solver_settings:`
19+
and each may be left out, in which case its defaults apply. `wings:` is a list, so a
20+
multi-wing configuration repeats the entry.
21+
22+
## An annotated file
23+
24+
Every key below is optional except a wing's `name`, `n_panels`,
25+
`spanwise_panel_distribution`, `spanwise_direction` and `remove_nan`, and — whenever
26+
`solver_settings:` is present — its `aerodynamic_model_type` and
27+
`type_initial_gamma_distribution`. An omitted key keeps its default: the `condition:`
28+
values shown are those defaults, and the docstrings linked below carry the rest.
29+
30+
```yaml
31+
condition:
32+
wind_speed: 10.0 # free-stream velocity magnitude [m/s]
33+
alpha: 5.0 # angle of attack [°]
34+
beta: 0.0 # sideslip angle [°]
35+
yaw_rate: 0.0 # yaw rate [°/s]
36+
37+
wings:
38+
- name: main_wing # label the wing carries into plots and output
39+
# sections and polars, resolved against the working directory
40+
geometry_file: data/ram_air_kite/geometry.yaml
41+
n_panels: 50 # panels over the span; two sections make a panel
42+
# LINEAR, COSINE, SPLIT_PROVIDED, UNCHANGED or BILLOWING
43+
spanwise_panel_distribution: LINEAR
44+
spanwise_direction: [0, 1, 0] # unit vector along the span, in the CAD frame
45+
remove_nan: true # interpolate over NaN entries in the polar tables
46+
use_prior_polar: false # reuse polars on disk instead of regenerating them
47+
billowing_percentage: 0.0 # trailing-edge billow, as % of arc length
48+
crease_frac: 0.75 # chordwise position of the deflection hinge [-]
49+
50+
mesh: # how the sections were sliced from a CAD mesh
51+
obj_file: data/ram_air_kite/ram_air_kite.obj # the mesh sections come from
52+
n_sections: 45 # sections sliced from the mesh
53+
n_bins: 60 # leading-edge stations marched across the span
54+
# rows of the mesh-to-slicer rotation, whose x = chord, y = span, z = up
55+
rotation: [[0, 0, -1], [-1, 0, 0], [0, 1, 0]]
56+
wingtip_distance: 0.0 # arc length the outermost sections stop short [m]
57+
clearance: 0.006 # shrink-wrap offset outside the cloud [chord fraction]
58+
min_concave_radius: 0.02 # shrink-wrap rolling-ball radius [chord fraction]
59+
60+
airfoil: # the 2D backend and the polars it tabulates
61+
solver: xfoil # section backend: neuralfoil or xfoil
62+
model_size: large # NeuralFoil network size
63+
n_crit: 9.0 # e^N transition criticality; lower transitions earlier
64+
xtr_upper: 0.05 # forced upper-surface transition [chord fraction]
65+
xtr_lower: 0.05 # forced lower-surface transition [chord fraction]
66+
alpha_range: [-180, 1, 180] # angle-of-attack sweep [°] as [first, step, last]
67+
delta_range: [-40, 10, 40] # flap-deflection sweep [°]; null for no flap sweep
68+
# angles off the reference angle a live polar is re-solved at [°]
69+
live_offsets: [-12, -9, -6, -3, 0, 3, 6, 9, 12]
70+
v_app: 25.0 # apparent wind the Reynolds number is taken at [m/s]
71+
chord_ref: 1.0 # reference (maximum panel) chord [m]
72+
table_format: arrow # per-node table format: csv or arrow
73+
74+
solver_settings:
75+
aerodynamic_model_type: VSM # VSM or LLT
76+
type_initial_gamma_distribution: ELLIPTIC # ELLIPTIC or ZEROS
77+
solver_type: LOOP # LOOP or NONLIN
78+
density: 1.225 # air density [kg/m³]
79+
mu: 1.81e-5 # dynamic viscosity [N·s/m²]
80+
rtol: 1e-6 # relative tolerance on the circulation residual [-]
81+
relaxation_factor: 0.01 # under-relaxation of the circulation update [-]
82+
```
83+
84+
[`SolverSettings`](@ref) lists the rest of `solver_settings:`; a key left out keeps its
85+
default. `n_panels` given there is ignored — the total is summed from the wings.
86+
87+
## `mesh:`
88+
89+
[`MeshSettings`](@ref) — which `.obj` mesh a wing's sections were cut from, and how.
90+
The block's `obj_file` is the mesh the sections are *generated from*, an input to the
91+
`geometry_file` the wing then flies; a wing's own top-level `obj_file` is a different
92+
route — straight from a mesh and a `.dat`, with no polar generation — and cannot be
93+
given alongside `geometry_file`.
94+
95+
[`rotation_matrix`](@ref), [`slice_args`](@ref) and [`preview_args`](@ref) turn the
96+
block into the arguments
97+
[`obj_to_yaml`](@ref VortexStepMethod.ObjAdapter.obj_to_yaml) and
98+
[`plot_slices_3d`](@ref) take, and [`ShrinkWrap`](@ref) is built from `clearance` and
99+
`min_concave_radius`. A wing naming no `mesh:` block slices exactly as an
100+
unconfigured `obj_to_yaml` call does. [From CAD mesh to aerodynamic model](@ref)
101+
walks through what those arguments do.
102+
103+
## `airfoil:`
104+
105+
[`AirfoilSettings`](@ref) — the 2D section backend and the polars it tabulates.
106+
`solver:` chooses the viscous panel code (`xfoil`) or the neural surrogate
107+
(`neuralfoil`) for a whole dataset from the file, and [`airfoil_solver`](@ref)
108+
returns the [`XFoilSolver`](@ref) or [`NeuralFoilSolver`](@ref) it names.
109+
110+
One block answers for both the tables a mesh is sliced into and the live polars a
111+
deformed section is re-solved on, so the two cannot be generated at different
112+
transition settings or off different networks. [`alpha_range`](@ref),
113+
[`delta_range`](@ref) and [`reynolds`](@ref) turn the sweeps and the
114+
`density * v_app * chord_ref / mu` reference into what the polar generator takes.

test/settings/test_settings.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,26 @@ end
8383
@test_throws Exception VortexStepMethod.airfoil_settings(Dict("n_crt" => 4.0))
8484
end
8585

86+
@testset "the documented settings file loads" begin
87+
page = joinpath(dirname(dirname(@__DIR__)), "docs", "src", "settings.md")
88+
lines = readlines(page)
89+
opening = findfirst(==("```yaml"), lines)
90+
closing = findnext(==("```"), lines, opening + 1)
91+
path = tempname() * ".yaml"
92+
write(path, join(lines[opening+1:closing-1], "\n"))
93+
94+
set = VSMSettings(path; data_prefix = false)
95+
wing = set.wings[1]
96+
@test rotation_matrix(wing.mesh) == [0 0 -1; -1 0 0; 0 1 0]
97+
@test airfoil_solver(wing.airfoil) isa VortexStepMethod.AirfoilAero.XFoilSolver
98+
@test set.solver_settings.n_panels == wing.n_panels
99+
100+
# a field added to either block is a field the page does not yet describe
101+
block = VortexStepMethod.YAML.load_file(path)["wings"][1]
102+
@test Set(Symbol.(keys(block["mesh"]))) == Set(fieldnames(MeshSettings))
103+
@test Set(Symbol.(keys(block["airfoil"]))) == Set(fieldnames(AirfoilSettings))
104+
end
105+
86106
@testset "an unnamed mesh block slices as an unconfigured call" begin
87107
vertices, faces = ObjAdapter.read_faces(joinpath(ram_air_dir,
88108
"ram_air_kite_body.obj"))

0 commit comments

Comments
 (0)