Airfoil Geometry Schema

This document defines the user-facing schema for airfoil geometry definitions. Field definitions and validation rules are normative. Examples are illustrative. Machine-usable GUI and editor constraints are authoritative in the dataclass field metadata exposed through buffalo_wings.airfoil.schema_field_metadata. Buffalo Wings now provides that accessor on top of the shared buffalo_core.schema metadata contract while keeping the Wings dataclasses as the source of truth. Within the schema implementation, those constraints are now attached directly with buffalo_core.schema.SchemaField and explicit SchemaChoice entries where human-readable labels matter, rather than through a Wings-local metadata builder shim. Enum-like choice metadata is exposed as structured {"value": ..., "label": ...} entries so editors can render human-readable option text without losing the serialized schema value.


1. Top-Level Structure

Required Keys

  • schema_version (int): schema version; current version is 1

  • airfoils (object): named airfoil definitions.

  • curves (object): named open section-curve definitions.

Example Skeleton

schema_version: 1

airfoils: {}
curves: {}

2. Common Conventions

Unless otherwise noted, airfoil geometry is defined in a local nondimensional section frame:

  • chordwise coordinate: x

  • thickness/camber coordinate: y

  • leading edge at x = 0

  • trailing edge at x = 1

  • angular quantities are specified in degrees unless otherwise noted

Positive y corresponds to the upper surface side of the airfoil. Unless otherwise specified, airfoil definitions are normalized to unit chord. Unless otherwise specified by a particular airfoil type, all coordinates are interpreted in this normalized local frame.

3. airfoils

airfoils is a mapping from unique airfoil names to airfoil definitions. In the Python schema API, this top-level document is represented by buffalo_wings.airfoil.AirfoilDocumentSpec.

Example:

airfoils:
  root: {type: naca4, designation: "2412"}
  tip:  {type: file, path: "airfoils/tip.dat", format: auto}

Keys must be unique and are case-sensitive.

3.1 curves

curves is a mapping from unique curve names to open section-curve definitions. These curves are intentionally distinct from full closed-body airfoil definitions. They are meant for workflows that need mean lines, walls, or other section-local open geometry. In the Python schema API, this still lives inside buffalo_wings.airfoil.AirfoilDocumentSpec.

Example:

airfoils:
  root: {type: naca6, designation: "65(1)-213"}

curves:
  root_camber:
    type: airfoil_camber
    airfoil: root
    approximation:
      spacing: cosine
      num_points: 81
  wall:
    type: spline_curve
    representation: bezier
    control_points:
      - [0.0, 0.0]
      - [0.3, 0.08]
      - [1.0, 0.0]
  fence:
    type: polyline_curve
    points:
      - [0.0, 0.0]
      - [0.3, 0.08]
      - [1.0, 0.0]

Keys must be unique and are case-sensitive.

3.1.1 airfoil_camber

This curve family derives a camber line from a named source airfoil. It exists so users can reuse an existing airfoil definition directly for mean-line or thin-airfoil workflows without restating family-specific camber parameters.

Fields

  • type: "airfoil_camber"

  • airfoil: name of one entry in the top-level airfoils map

  • approximation: optional fallback controls used only when the referenced airfoil family does not provide exact native camber

If approximation is present, it contains:

  • spacing: "uniform" or "cosine"; default "cosine"

  • num_points: integer sample count for fallback approximation; default 81

Notes

  • This schema captures authoring intent as “camber derived from this airfoil,” not “independent open curve that happens to match it today.”

  • Airfoil families with exact native camber may ignore the approximation controls.

  • Airfoil families without exact native camber may use these controls to build a deterministic approximate camber curve.

  • Future curve families may represent directly authored camber lines or non-airfoil walls without changing the role of airfoil_camber.

3.1.2 spline_curve

This curve family stores directly authored open geometry. The initial schema slice uses Bezier control points so users can define walls or guide curves without going through a closed-body airfoil spec first.

Fields

  • type: "spline_curve"

  • representation: "bezier"

  • control_points: ordered list of at least two (x, y) control points

Notes

  • Control points are interpreted from u = 0 to u = 1.

  • The schema family is intentionally backend-neutral in name even though the current runtime stores Bezier control points directly.

  • Future spline backends may extend the supported representation values without changing the role of spline_curve as a directly authored open curve family.

3.1.3 polyline_curve

This curve family stores explicit vertices for directly authored open geometry. It is the lightest-weight authoring path for simple walls, fences, or guide curves that do not need spline control behavior.

Fields

  • type: "polyline_curve"

  • points: ordered list of at least two (x, y) vertices

Notes

  • Vertices are interpreted from curve start to curve end.

  • The runtime resolves this family to a piecewise-linear open curve parameterized by normalized cumulative arc length.

  • Consecutive duplicate vertices are invalid because they create zero-length segments.

4. Airfoil Definition Types

Airfoils may be defined using analytic parameterizations or point-based representations.

Analytic representations define the airfoil geometry through a parameterized model. Point-based representations define the airfoil geometry through discrete coordinates on the surface.

This schema supports standard NACA designations, externally stored coordinate files, inline coordinate definitions, and parameterized coefficient-based representations.

As of 2026-03-31, AirfoilFactory.from_spec(...) constructs runtime airfoils for naca4, naca4_modified, naca5, naca5_modified, ellipse, cst, and spline. Other families documented in this schema are planned schema targets, but they are not yet constructable through the current runtime factory and are tracked as roadmap work rather than migration-completion work.

4.0 Construction Status

The current migration-complete construction surface is:

  • naca4

  • naca4_modified

  • naca5

  • naca5_modified

  • ellipse

  • spline

  • cst

The following schema families are planned but not yet constructable through AirfoilFactory.from_spec(...):

  • file

  • points

  • parsec

  • naca4bt

  • naca4r

  • naca4f

  • naca16

  • flat_plate

  • biconvex

  • biconvex_parabola

  • polygon

  • circular_arc

  • joukowski

  • naca6

  • naca6a

The following families are still only partially defined in this document and still need their defining designation grammar, explicit parameter form, or validation rules to be completed:

  • naca4bt

  • naca4r

  • naca4f

All other airfoil families currently included in this document have explicit fields, validation rules, and examples.

4.1 NACA Airfoils

These airfoils are all based on the canonical NACA airfoil specifications.

4.1.1 NACA 4-digit

NACA 4-digit airfoils may be specified in either of two ways.

Designation
airfoils:
  naca2412:
    type: naca4
    designation: "2412"
Explicit Parameters
airfoils:
  naca2412_explicit:
    type: naca4
    params:
      m: 0.02
      p: 0.4
      max_thickness: 0.12
      trailing_edge: sharp
      leading_edge_radius: exact
Fields
  • type: "naca4"

  • Exactly one of:

    • designation: 4-digit NACA designation string

    • params: explicit parameter definition

If params is present, it contains:

  • m: maximum camber as a fraction of chord

  • p: location of maximum camber as a fraction of chord

  • max_thickness: maximum thickness as a fraction of chord

  • trailing_edge: "standard" or "sharp"; default "standard"

  • leading_edge_radius: "standard" or "exact"; default "standard"

Designation Syntax
  • The designation must use the form ####.

  • The first digit represents 100 times the maximum camber.

  • The second digit represents 10 times the location of maximum camber.

  • The last two digits combine to represent 100 times the maximum thickness.

  • For example, a 2312 means:

    • maximum thickness 0.12c

    • maximum camber is 0.02c

    • location of maximum camber is at 0.30c

Validation Rules
  • Exactly one of designation and params must be present.

  • If designation is present:

    • must match ^[0-9]{4}$

  • If params is present:

    • 0 <= m < 0.1

    • 0 <= p <= 0.9

    • 0 <= max_thickness <= 0.4

    • if m == 0, require p == 0

    • if m > 0, require p > 0

    • trailing_edge must be one of standard|sharp

    • leading_edge_radius must be one of standard|exact

Examples

Accepted examples:

  • designation: "2412"

  • designation: "0012"

  • params: {m: 0.02, p: 0.4, max_thickness: 0.12}

  • params: {m: 0.0, p: 0.0, max_thickness: 0.12}

Unsupported examples:

  • designation: "24-12"

  • designation: "2412-46"

  • params: {m: 0.02, p: 0.0, max_thickness: 0.12}

  • params: {m: 0.0, p: 0.4, max_thickness: 0.12}

Notes
  • max_thickness = 0 is permitted to support idealized zero-thickness analyses.

  • Airfoil definitions are normalized to unit chord.

  • When designation is used, the airfoil is interpreted as the classical NACA 4-digit designation.

  • When params is used, the airfoil remains within the NACA 4-digit family but allows higher-precision specification of the defining parameters and explicit control over trailing-edge closure and leading-edge radius treatment.

  • In the runtime API, AirfoilFactory.from_spec(...) maps designation-based NACA 4-digit specs to buffalo_wings.airfoil.Naca4AirfoilClassic.

  • In the runtime API, AirfoilFactory.from_spec(...) maps explicit-parameter NACA 4-digit specs to buffalo_wings.airfoil.Naca4AirfoilParams.

4.1.2 NACA Modified 4-digit

Modified NACA 4-digit airfoils may be specified in either of two ways.

Designation
airfoils:
  naca2412_46:
    type: naca4_modified
    designation: "2412-46"
Explicit Parameters
airfoils:
  naca2412_46_explicit:
    type: naca4_modified
    params:
      m: 0.02
      p: 0.4
      max_thickness: 0.12
      leading_edge_index: 4.0
      max_thickness_location: 0.6
      trailing_edge: sharp
Fields
  • type: "naca4_modified"

  • Exactly one of:

    • designation: modified NACA 4-digit designation string

    • params: explicit parameter definition

If params is present, it contains:

  • m: maximum camber as a fraction of chord

  • p: location of maximum camber as a fraction of chord

  • max_thickness: maximum thickness as a fraction of chord

  • leading_edge_index: modified-thickness leading-edge radius index

  • max_thickness_location: location of maximum thickness as a fraction of chord

  • trailing_edge: "standard" or "sharp"; default "standard"

Designation Syntax
  • The designation must use the form ####-##.

  • The first four digits are the standard NACA 4-digit designation.

  • The first digit after the dash is the leading-edge radius index.

  • The second digit after the dash is 10 times the chordwise location of maximum thickness.

  • For example, 2412-46 means:

    • NACA 4-digit camber line 2412

    • leading-edge radius index 4

    • maximum thickness at 0.6c

Validation Rules
  • Exactly one of designation and params must be present.

  • If designation is present:

    • must match ^[0-9]{4}-[1-9][1-9]$

  • If params is present:

    • 0 <= m < 0.1

    • 0 <= p <= 0.9

    • 0 <= max_thickness <= 0.4

    • 1 <= leading_edge_index < 10

    • 0.1 <= max_thickness_location < 1.0

    • if m == 0, require p == 0

    • if m > 0, require p > 0

    • trailing_edge must be one of standard|sharp

Examples

Accepted examples:

  • designation: "2412-46"

  • designation: "0012-34"

  • params: {m: 0.02, p: 0.4, max_thickness: 0.12, leading_edge_index: 4.0, max_thickness_location: 0.6}

  • params: {m: 0.0, p: 0.0, max_thickness: 0.10, leading_edge_index: 3.0, max_thickness_location: 0.4}

Unsupported examples:

  • designation: "2412"

  • designation: "2412-06"

  • designation: "2412-4A"

  • params: {m: 0.02, p: 0.4, max_thickness: 0.12, leading_edge_index: 0.0, max_thickness_location: 0.6}

Notes
  • The modified NACA 4-digit family reuses the standard NACA 4-digit camber definition.

  • The modified designation selects the modified thickness law through the dash suffix.

  • In the runtime API, AirfoilFactory.from_spec(...) maps designation-based modified NACA 4-digit specs to buffalo_wings.airfoil.Naca4ModifiedAirfoilClassic.

  • In the runtime API, AirfoilFactory.from_spec(...) maps explicit-parameter modified NACA 4-digit specs to buffalo_wings.airfoil.Naca4ModifiedAirfoilParams.

4.1.3 NACA 4-digit B/T

This family is reserved for a future NACA 4-digit B/T specification.

The supported designation syntax, explicit parameter form, and validation rules are not yet defined in this document.

This family is planned, but it is not yet constructable through AirfoilFactory.from_spec(...).

4.1.4 NACA 4-digit R

This family is reserved for a future NACA 4-digit R specification.

The supported designation syntax, explicit parameter form, and validation rules are not yet defined in this document.

This family is planned, but it is not yet constructable through AirfoilFactory.from_spec(...).

4.1.5 NACA 4-digit F

This family is reserved for a future NACA 4-digit F specification.

The supported designation syntax, explicit parameter form, and validation rules are not yet defined in this document.

This family is planned, but it is not yet constructable through AirfoilFactory.from_spec(...).

4.1.6 NACA 5-digit

NACA 5-digit airfoils may be specified in either of two ways.

Designation
airfoils:
  naca23015:
    type: naca5
    designation: "23015"
Explicit Parameters
airfoils:
  naca23015_explicit:
    type: naca5
    params:
      ideal_lift_coefficient: 0.3
      max_camber_location: 0.15
      reflexed: false
      max_thickness: 0.15
      trailing_edge: sharp
      leading_edge_radius: exact
Fields
  • type: "naca5"

  • Exactly one of:

    • designation: 5-digit NACA designation string

    • params: explicit parameter definition

If params is present, it contains:

  • ideal_lift_coefficient: ideal lift coefficient

  • max_camber_location: location of maximum camber as a fraction of chord

  • reflexed: whether the camber line has a reflexed trailing edge

  • max_thickness: maximum thickness as a fraction of chord

  • trailing_edge: "standard" or "sharp"; default "standard"

  • leading_edge_radius: "standard" or "exact"; default "standard"

Designation Syntax
  • The designation must use the form #####.

  • The first digit represents 10 times two thirds of the ideal lift coefficient.

  • The second digit represents 10 times twice the location of maximum camber.

  • The third digit selects the camber-line family:

    • 0: standard camber

    • 1: reflexed camber

  • The last two digits combine to represent 100 times the maximum thickness.

  • For example, 23015 means:

    • ideal lift coefficient 0.3

    • maximum camber location 0.15c

    • standard non-reflexed camber

    • maximum thickness 0.15c

Validation Rules
  • Exactly one of designation and params must be present.

  • If designation is present:

    • must match ^[0-9]{5}$

    • the third digit must be 0 or 1

  • If params is present:

    • 0.15 <= ideal_lift_coefficient < 0.6

    • 0.05 <= max_camber_location < 0.3

    • 0 <= max_thickness <= 0.4

    • trailing_edge must be one of standard|sharp

    • leading_edge_radius must be one of standard|exact

Examples

Accepted examples:

  • designation: "23015"

  • designation: "21012"

  • designation: "23112"

  • params: {ideal_lift_coefficient: 0.3, max_camber_location: 0.15, reflexed: false, max_thickness: 0.15}

  • params: {ideal_lift_coefficient: 0.3, max_camber_location: 0.15, reflexed: true, max_thickness: 0.15}

Unsupported examples:

  • designation: "23015-46"

  • designation: "23215"

  • designation: "23-015"

  • params: {ideal_lift_coefficient: 0.3, max_camber_location: 0.15, max_thickness: 0.15}

Notes
  • designation represents the traditional encoded NACA 5-digit form.

  • params is more general than designation.

  • The params form may describe airfoils that do not correspond to a historical published NACA 5-digit designation.

  • The designation digits are encoded indices, not the physical values stored in params.

  • For the first digit, digit = 10 * (2 / 3) * ideal_lift_coefficient. Equivalently, ideal_lift_coefficient = 0.15 * first_digit.

  • For the second digit, digit = 10 * 2 * max_camber_location. Equivalently, max_camber_location = 0.05 * second_digit.

  • For example, 23015 maps to:

    • ideal_lift_coefficient = 0.3

    • max_camber_location = 0.15

    • reflexed = false

    • max_thickness = 0.15

  • The reflex behavior selects which camber implementation is used.

  • The explicit-parameter thickness path exposes the same trailing-edge and leading-edge-radius controls as the NACA 4-digit params schema.

  • In the runtime API, AirfoilFactory.from_spec(...) maps designation-based NACA 5-digit specs to buffalo_wings.airfoil.Naca5AirfoilClassic.

  • In the runtime API, AirfoilFactory.from_spec(...) maps explicit-parameter NACA 5-digit specs to buffalo_wings.airfoil.Naca5AirfoilParams.

4.1.7 NACA Modified 5-digit

Modified NACA 5-digit airfoils may be specified in either of two ways.

Designation
airfoils:
  naca23015_46:
    type: naca5_modified
    designation: "23015-46"
Explicit Parameters
airfoils:
  naca23015_46_explicit:
    type: naca5_modified
    params:
      ideal_lift_coefficient: 0.3
      max_camber_location: 0.15
      reflexed: false
      max_thickness: 0.15
      leading_edge_index: 4.0
      max_thickness_location: 0.6
      trailing_edge: sharp
Fields
  • type: "naca5_modified"

  • Exactly one of:

    • designation: modified NACA 5-digit designation string

    • params: explicit parameter definition

If params is present, it contains:

  • ideal_lift_coefficient: ideal lift coefficient

  • max_camber_location: location of maximum camber as a fraction of chord

  • reflexed: whether the camber line has a reflexed trailing edge

  • max_thickness: maximum thickness as a fraction of chord

  • leading_edge_index: modified-thickness leading-edge radius index

  • max_thickness_location: location of maximum thickness as a fraction of chord

  • trailing_edge: "standard" or "sharp"; default "standard"

Designation Syntax
  • The designation must use the form #####-##.

  • The first five digits are the standard NACA 5-digit designation.

  • The first digit after the dash is the modified-thickness leading-edge radius index.

  • The second digit after the dash is 10 times the chordwise location of maximum thickness.

  • For example, 23015-46 means:

    • ideal lift coefficient 0.3

    • maximum camber location 0.15c

    • standard non-reflexed 5-digit camber

    • maximum thickness 0.15c

    • leading-edge radius index 4

    • maximum thickness at 0.6c

Validation Rules
  • Exactly one of designation and params must be present.

  • If designation is present:

    • must match ^[0-9]{5}-[1-9][1-9]$

    • the third digit of the 5-digit designation must be 0 or 1

  • If params is present:

    • 0.15 <= ideal_lift_coefficient < 0.6

    • 0.05 <= max_camber_location < 0.3

    • 0 <= max_thickness <= 0.4

    • 1 <= leading_edge_index < 10

    • 0.1 <= max_thickness_location < 1.0

    • trailing_edge must be one of standard|sharp

Examples

Accepted examples:

  • designation: "23015-46"

  • designation: "23115-46"

  • params: {ideal_lift_coefficient: 0.3, max_camber_location: 0.15, reflexed: false, max_thickness: 0.15, leading_edge_index: 4.0, max_thickness_location: 0.6}

  • params: {ideal_lift_coefficient: 0.3, max_camber_location: 0.15, reflexed: true, max_thickness: 0.15, leading_edge_index: 4.0, max_thickness_location: 0.6}

Unsupported examples:

  • designation: "23015"

  • designation: "23215-46"

  • designation: "23015-06"

  • params: {ideal_lift_coefficient: 0.3, max_camber_location: 0.15, reflexed: false, max_thickness: 0.15, leading_edge_index: 0.0, max_thickness_location: 0.6}

Notes
  • The modified NACA 5-digit family reuses the NACA 5-digit camber definition.

  • The camber implementation is selected by reflex behavior:

    • designation third digit 0 or params.reflexed = false: standard 5-digit camber

    • designation third digit 1 or params.reflexed = true: reflexed 5-digit camber

  • The designation digits are encoded indices, not the physical values stored in params.

  • For the first digit of the 5-digit designation: digit = 10 * (2 / 3) * ideal_lift_coefficient. Equivalently, ideal_lift_coefficient = 0.15 * first_digit.

  • For the second digit of the 5-digit designation: digit = 10 * 2 * max_camber_location. Equivalently, max_camber_location = 0.05 * second_digit.

  • designation represents the traditional encoded modified NACA 5-digit form.

  • params is more general than designation.

  • The params form may describe airfoils that do not correspond to a historical published modified NACA 5-digit designation.

  • In the runtime API, AirfoilFactory.from_spec(...) maps designation-based modified NACA 5-digit specs to buffalo_wings.airfoil.Naca5ModifiedAirfoilClassic.

  • In the runtime API, AirfoilFactory.from_spec(...) maps explicit-parameter modified NACA 5-digit specs to buffalo_wings.airfoil.Naca5ModifiedAirfoilParams.

4.1.8 NACA 16-Series

NACA 16-series airfoils may be specified in either of two ways.

Designation
airfoils:
  naca16_312:
    type: naca16
    designation: "16-312"
Explicit Parameters
airfoils:
  naca16_312_explicit:
    type: naca16
    params:
      ideal_lift_coefficient: 0.3
      max_thickness: 0.12
Fields
  • type: "naca16"

  • Exactly one of:

    • designation: NACA 16-series designation string

    • params: explicit parameter definition

If params is present, it contains:

  • ideal_lift_coefficient: ideal lift coefficient

  • max_thickness: maximum thickness as a fraction of chord

Designation Syntax
  • The designation must use the form 16-###.

  • The first two digits are fixed as 16.

  • The first digit after the dash represents 10 times the ideal lift coefficient.

  • The last two digits combine to represent 100 times the maximum thickness.

  • For example, 16-312 means:

    • ideal lift coefficient 0.3

    • maximum thickness 0.12c

Validation Rules
  • Exactly one of designation and params must be present.

  • If designation is present:

    • must match ^16-[0-9][0-9]{2}$

    • the encoded ideal lift coefficient digit must be nonnegative

    • the encoded thickness digits must represent a positive thickness

  • If params is present:

    • 0 <= ideal_lift_coefficient <= 0.9

    • 0 < max_thickness <= 0.4

Examples

Accepted examples:

  • designation: "16-312"

  • designation: "16-212"

  • params: {ideal_lift_coefficient: 0.3, max_thickness: 0.12}

  • params: {ideal_lift_coefficient: 0.2, max_thickness: 0.15}

Unsupported examples:

  • designation: "16312"

  • designation: "16-31"

  • designation: "16A-312"

  • params: {ideal_lift_coefficient: 0.3, max_thickness: 0.0}

Notes
  • NACA 16-series airfoils are planned schema targets, but they are not yet constructable through AirfoilFactory.from_spec(...).

  • designation represents the traditional encoded NACA 16-series form.

  • params is more general than designation.

  • The params form may describe airfoils that do not correspond to a historical published NACA 16-series designation.

  • The designation digits are encoded indices, not the physical values stored in params.

  • For the first digit after the dash, digit = 10 * ideal_lift_coefficient. Equivalently, ideal_lift_coefficient = 0.1 * digit.

  • For the last two digits, digits = 100 * max_thickness. Equivalently, max_thickness = 0.01 * digits.

4.1.9 NACA 6-Series

NACA 6-series airfoils may be specified in either of two ways.

Designation
airfoils:
  naca65_213:
    type: naca6
    designation: "65(1)-213"
Explicit Parameters
airfoils:
  naca65_explicit:
    type: naca6
    params:
      thickness:
        series: "65"
        max_thickness: 0.12
      camber:
        components:
          - ideal_lift_coefficient: 0.3
            mean_line_shape_parameter: 0.8
Fields
  • type: "naca6"

  • Exactly one of:

    • designation: NACA 6-series designation string

    • params: explicit parameter definition

If params is present, it contains:

  • thickness.series: thickness-family designation such as "63", "64", "65", "66", or "67"

  • thickness.max_thickness: maximum thickness as a fraction of chord

  • camber.components: one or more additive 6-series mean-line components

Each camber.components entry contains:

  • ideal_lift_coefficient: ideal lift coefficient for that mean-line component

  • mean_line_shape_parameter: historical 6-series mean-line shape parameter often labeled a; optional only when camber.components contains exactly one component, in which case it defaults to 1.0

Designation Syntax
  • The designation syntax is not yet fully finalized in this schema.

  • The schema is expected to support common published 6-series forms such as 65-210, 65(1)-212, and 65(3)-218.

  • In that common form:

    • the leading 65 identifies the family designation

    • an optional low-drag-range digit may appear as a historical subscript or as an ASCII parenthetical form such as (1)

    • the first of the trailing three digits encodes the nominal ideal lift coefficient in tenths

    • the last two of the trailing three digits encode the maximum thickness in hundredths of chord

  • For example, 65(1)-213 denotes a 65-series section with low-drag-range digit 1, nominal ideal lift coefficient 0.2, and nominal maximum thickness 0.13.

  • When historical notation omits the low-drag-range subscript, ASCII designations may omit the parenthetical form as well, as in 65-210.

  • Historical a=... suffixes are not part of the supported designation surface and should be represented with params.

  • Additional compact or normalized designation variants may be added once the supported parsing surface is finalized.

Validation Rules
  • Exactly one of designation and params must be present.

  • If designation is present:

    • must match one of the supported 6-series designation forms once that parsing surface is finalized

    • if a low-drag-range digit is present in the designation, it must be a positive integer digit

    • the encoded nominal ideal lift coefficient digit must be nonnegative

    • the encoded thickness digits must represent a positive thickness

  • If params is present:

    • thickness.series must be one of 63|64|65|66|67

    • 0 <= thickness.max_thickness <= 0.4

    • camber.components must contain at least one component

    • if camber.components contains exactly one component, that component’s ideal_lift_coefficient must satisfy 0 <= value <= 0.5

    • if camber.components contains exactly one component and mean_line_shape_parameter is omitted, it defaults to 1.0

    • if camber.components contains more than one component, each component’s ideal_lift_coefficient must satisfy -0.5 <= value <= 0.5

    • if camber.components contains more than one component, each component must explicitly provide mean_line_shape_parameter

    • each provided camber.components[*].mean_line_shape_parameter must satisfy 0 <= value <= 1

Examples

Accepted examples:

  • designation: "65-210"

  • designation: "65(1)-212"

  • designation: "65(3)-218"

  • params: {thickness: {series: "65", max_thickness: 0.12}, camber: {components: [{ideal_lift_coefficient: 0.3}]}}

  • params: {thickness: {series: "65", max_thickness: 0.18}, camber: {components: [{ideal_lift_coefficient: 0.3, mean_line_shape_parameter: 0.8}, {ideal_lift_coefficient: -0.1, mean_line_shape_parameter: 1.0}]}}

Unsupported examples:

  • designation: "65(1)213"

  • designation: "65-210 a=0.8"

  • designation: "65(1.5)-212"

  • params: {thickness: {series: "65", max_thickness: 0.12}, camber: {components: [{ideal_lift_coefficient: 0.3}, {ideal_lift_coefficient: -0.1, mean_line_shape_parameter: 1.0}]}}

  • params: {thickness: {series: "65", max_thickness: 0.12}, camber: {components: [{ideal_lift_coefficient: -0.1}]}}

Notes
  • NACA 6-series airfoils are planned schema targets, but they are not yet constructable through AirfoilFactory.from_spec(...).

  • The explicit params form is more general than designation.

  • The grouped thickness and camber structure reflects the historical separation between thickness-family inputs and additive camber-line inputs in the bundled 6-series references.

  • The bundled NASA and PDAS lineage supports summing multiple 6-series mean-line components.

  • In additive definitions, component ideal_lift_coefficient values may be positive or negative so that the resulting camber line is the algebraic sum of the component mean lines.

  • For single-component definitions, the supported schema surface expects a nonnegative ideal_lift_coefficient.

  • When a 6-series designation omits an explicit mean-line statement, the supported schema surface assumes the historical uniform-load mean line, corresponding to mean_line_shape_parameter = 1.0.

  • For single-component params definitions, mean_line_shape_parameter may likewise be omitted and defaults to 1.0.

  • Additive params definitions require explicit mean_line_shape_parameter values for each component.

  • The low-drag-range digit that appears in historical 6-series designations is treated here as designation metadata tied to the named thickness form rather than as an independent explicit schema parameter.

  • The designation form is intended to cover common published single-designation strings such as 65-210 and 65(1)-212, including ASCII parenthetical notation used in place of historical subscript notation.

  • The params form is intended for explicit or non-canonical definitions, including additive camber definitions with more than one mean-line component and when more precision is needed on specifying the camber parameters.

  • Historical scaled-thickness designation variants are currently out of scope for the supported designation surface and should be represented with params if needed.

4.1.10 NACA 6A-Series

NACA 6A-series airfoils may be specified in either of two ways.

Designation
airfoils:
  naca65a_212:
    type: naca6a
    designation: "65A-212"
Explicit Parameters
airfoils:
  naca65a_explicit:
    type: naca6a
    params:
      thickness:
        series: "65A"
        max_thickness: 0.12
      camber:
        components:
          - ideal_lift_coefficient: 0.3
Fields
  • type: "naca6a"

  • Exactly one of:

    • designation: NACA 6A-series designation string

    • params: explicit parameter definition

If params is present, it contains:

  • thickness.series: thickness-family designation such as "63A", "64A", or "65A"

  • thickness.max_thickness: maximum thickness as a fraction of chord

  • camber.components: one or more additive 6A-series mean-line components

Each camber.components entry contains:

  • ideal_lift_coefficient: ideal lift coefficient for that mean-line component

Designation Syntax
  • The designation syntax is not yet fully finalized in this schema.

  • The schema is expected to support common published 6A-series forms such as 64A-212 once the supported parsing surface is finalized.

  • The bundled references clearly identify 6A-series family tokens such as 63A, 64A, and 65A.

  • The schema is expected to interpret the trailing three digits analogously to the 6-series form:

    • the first trailing digit encodes the nominal ideal lift coefficient in tenths

    • the last two trailing digits encode the maximum thickness in hundredths of chord

  • Historical a=... suffixes are not part of the supported designation surface and should be represented with params if such an extension is ever needed.

  • Additional designation guidance should be finalized together with the supported parsing rules.

Validation Rules
  • Exactly one of designation and params must be present.

  • If designation is present:

    • must match one of the supported 6A-series designation forms once that parsing surface is finalized

    • the encoded nominal ideal lift coefficient digit must be nonnegative

    • the encoded thickness digits must represent a positive thickness

  • If params is present:

    • thickness.series must be one of 63A|64A|65A

    • 0 <= thickness.max_thickness <= 0.4

    • camber.components must contain at least one component

    • if camber.components contains exactly one component, that component’s ideal_lift_coefficient must satisfy 0 <= value <= 0.5

    • if camber.components contains more than one component, each component’s ideal_lift_coefficient must satisfy -0.5 <= value <= 0.5

Examples

Accepted examples:

  • designation: "64A-212"

  • designation: "65A-218"

  • params: {thickness: {series: "65A", max_thickness: 0.12}, camber: {components: [{ideal_lift_coefficient: 0.3}]}}

  • params: {thickness: {series: "64A", max_thickness: 0.15}, camber: {components: [{ideal_lift_coefficient: 0.3}, {ideal_lift_coefficient: -0.1}]}}

Unsupported examples:

  • designation: "64A212"

  • designation: "64A-212 a=0.8"

  • params: {thickness: {series: "65A", max_thickness: 0.12}, camber: {components: [{ideal_lift_coefficient: 0.3, mean_line_shape_parameter: 0.8}]}}

  • params: {thickness: {series: "65A", max_thickness: 0.12}, camber: {components: [{ideal_lift_coefficient: -0.1}]}}

Notes
  • NACA 6A-series airfoils are planned schema targets, but they are not yet constructable through AirfoilFactory.from_spec(...).

  • The explicit params form is more general than designation.

  • The grouped thickness and camber structure reflects the historical separation between thickness-family inputs and additive camber-line inputs in the bundled 6A-series references.

  • The bundled NASA and PDAS lineage supports summing multiple 6A-series mean-line components.

  • In additive definitions, component ideal_lift_coefficient values may be positive or negative so that the resulting camber line is the algebraic sum of the component mean lines.

  • For single-component definitions, the supported schema surface expects a nonnegative ideal_lift_coefficient.

  • For 6A-series airfoils, the bundled NASA and PDAS lineage fixes the historical a parameter to 0.80 rather than treating it as a user-specified field.

  • Accordingly, mean_line_shape_parameter is intentionally omitted from the naca6a explicit parameter form.

  • The designation form is intended to cover common published single-designation strings and implies the historical modified 6A mean line rather than a user-specified a value.

  • The params form is intended for explicit or non-canonical definitions, including additive camber definitions with more than one mean-line component and when more precision is needed on specifying the mean-line component.

  • Historical scaled-thickness designation variants are currently out of scope for the supported designation surface and should be represented with params if needed.

4.2 Coordinate Files

Coordinate-defined airfoils may be specified using the following file formats.

4.2.1 Selig Format

Selig format stores the airfoil as a single ordered surface curve.

The first non-ignored line is the airfoil name or title.

Subsequent non-ignored lines contain coordinate pairs.

Blank lines are ignored.

Lines whose first non-whitespace character is # are ignored.

The supported coordinate-row syntax is a numeric x y pair separated by whitespace or a comma.

The ordered points are expected to run from the trailing edge along one surface to the leading edge and then back to the trailing edge along the other surface.

The schema treats this as a single surface-curve topology rather than separate upper and lower blocks.

4.2.2 Lednicer Format

Lednicer format stores the airfoil as separate upper and lower surface blocks.

The first non-ignored line is the airfoil name or title.

The next non-ignored numeric line gives the declared upper-surface and lower-surface point counts.

Subsequent non-ignored lines contain coordinate pairs for the upper block followed by the lower block.

Blank lines are ignored.

Lines whose first non-whitespace character is # are ignored.

The supported coordinate-row syntax is a numeric x y pair separated by whitespace or a comma.

The upper and lower surface blocks are both expected to run from the leading edge to the trailing edge.

4.2.3 Native Structured Point Formats

In addition to Selig and Lednicer files, type: file may reference native structured point files.

The supported native structured point file formats are surface_curve and upper_lower.

These formats mirror the corresponding type: points schema forms and are stored as YAML or JSON files rather than inline YAML mappings.

A native structured point file contains only the point-format payload itself rather than a top-level airfoils mapping.

Coordinate-file airfoils may be specified as:

airfoils:
  myfoil:
    type: file
    path: "airfoils/myfoil.yaml"
    format: auto

4.2.4 Fields

  • type: "file"

  • path: path to the coordinate file

  • format: "auto", "selig", "lednicer", "surface_curve", or "upper_lower"; default "auto"

4.2.5 Format Detection

  • If format: "selig", the file is parsed using the Selig rules.

  • If format: "lednicer", the file is parsed using the Lednicer rules.

  • If format: "surface_curve" or format: "upper_lower", the file is parsed as a native structured point file using the corresponding type: points schema shape.

  • If format: "auto", the parser should detect the format from the file structure.

  • File extensions may be used as hints during auto detection, but they do not override an explicitly declared format.

  • If a YAML or JSON file clearly declares format: surface_curve or format: upper_lower, auto detection should treat it as a native structured point file.

  • Otherwise, if a valid Lednicer two-count line is present immediately after the title, ignoring blank lines and comment lines, auto detection should treat the file as lednicer.

  • Otherwise, auto detection should treat the file as selig.

4.2.6 Validation Rules

  • path is required.

  • format must be one of auto|selig|lednicer|surface_curve|upper_lower.

  • If format: "selig":

    • the file must contain at least one non-ignored title line

    • the file must contain at least 3 coordinate rows after the title

    • each coordinate row must be a numeric pair [x, y]

  • If format: "lednicer":

    • the file must contain at least one non-ignored title line

    • the file must contain exactly one declared point-count line before the coordinate blocks

    • the point-count line must contain exactly two positive integers: upper count and lower count

    • the file must contain exactly the declared number of upper and lower coordinate rows after ignored blank and comment lines are removed

    • each coordinate row must be a numeric pair [x, y]

  • If format: "surface_curve" or format: "upper_lower":

    • the file contents must satisfy the corresponding type: points schema form

    • YAML and JSON serializations are supported

    • the file contains only the point-format payload rather than a top-level airfoils mapping

  • For all supported file formats:

    • for selig and lednicer, blank lines are ignored

    • for selig and lednicer, lines whose first non-whitespace character is # are ignored

    • for native structured YAML or JSON files, comments and whitespace follow the rules of the host serialization format

    • finite trailing-edge thickness is allowed

    • the imported coordinates must define a valid nonzero chord extent so they can be normalized to unit chord

4.2.7 Examples

Accepted examples:

  • type: file, path: "airfoils/clarky.dat"

  • type: file, path: "airfoils/clarky.dat", format: auto

  • type: file, path: "airfoils/uiuc/mh60.dat", format: selig

  • type: file, path: "airfoils/uiuc/mh60_lednicer.dat", format: lednicer

  • type: file, path: "airfoils/root.yaml", format: upper_lower

  • type: file, path: "airfoils/tip.json", format: surface_curve

Unsupported examples:

  • type: file

  • type: file, path: "airfoils/clarky.dat", format: csv

  • type: file, path: "airfoils/clarky.dat", format: lednicer with no valid point-count line

  • type: file, path: "airfoils/clarky.dat", format: selig with fewer than 3 coordinate rows

  • type: file, path: "airfoils/root.yaml", format: upper_lower with file contents that do not satisfy the upper_lower point schema

4.2.8 Notes

  • Coordinate files define the airfoil through discrete surface points rather than an analytic parameterization.

  • Imported coordinates are interpreted in the local airfoil frame unless otherwise specified by the file format.

  • Imported airfoils are normalized internally to unit chord.

  • The first non-ignored line is treated as file metadata rather than as a coordinate row.

  • That in-file title or header line is not part of the geometry definition.

  • The schema-level airfoil identity comes from the airfoils mapping key and the imported source path rather than from a title embedded inside the external file.

  • Ignoring the in-file title for geometry and identity avoids hidden schema changes when a file header is edited without changing the coordinates.

  • type: file supports Selig and Lednicer import files as well as native structured point files that mirror the type: points schema.

  • By convention, Selig and Lednicer examples use .dat paths, while native structured point-file examples use .yaml or .json paths.

  • format: auto is intended for convenience when reading common UIUC-style coordinate files, while explicit format may be used when detection would otherwise be ambiguous.

  • If preservation of original imported scale, translation, or orientation is needed in the future, that information should be treated as import provenance rather than as part of the canonical airfoil geometry definition.

  • Coordinate-file airfoils are planned, but they are not yet constructable through AirfoilFactory.from_spec(...).

4.3 Point Coordinates

Coordinate-defined airfoils may be specified directly in the YAML file.

4.3.1 Surface curve format

airfoils:
  flat_plate:
    type: points
    format: surface_curve
    leading_edge: 3
    points:
      - [1.0,  0.0]
      - [0.7, -0.02]
      - [0.3, -0.03]
      - [0.0,  0.0]
      - [0.3,  0.03]
      - [0.7,  0.02]
      - [1.0,  0.0]

This format specifies a single ordered list of points that traces the airfoil surface starting and ending at the trailing edge.

4.3.2 Upper/Lower surface format

airfoils:
  example_foil:
    type: points
    format: upper_lower
    upper:
      - [0.0, 0.0]
      - [0.3, 0.06]
      - [0.7, 0.03]
      - [1.0, 0.0]
    lower:
      - [0.0, 0.0]
      - [0.3, -0.02]
      - [0.7, -0.01]
      - [1.0, 0.0]

This format specifies the upper and lower surfaces separately.

4.3.3 Fields

  • type: "points"

  • format: "surface_curve" or "upper_lower"

If format: "surface_curve":

  • leading_edge: 0-based index of the point that represents the leading edge

  • points: ordered list of [x, y] coordinate pairs

If format: "upper_lower":

  • upper: ordered list of [x, y] coordinate pairs

  • lower: ordered list of [x, y] coordinate pairs

4.3.4 Supported Format Values

  • "surface_curve": points proceed around the airfoil surface starting and ending at the trailing edge

  • "upper_lower": points are given as separate upper and lower surfaces, each starting from the leading edge and going to the trailing edge

4.3.5 Validation Rules

  • If format: "surface_curve":

    • points required

      • must contain at least 3 points

      • each point must be a numeric pair [x, y]

    • leading_edge is required and must be a valid index into the points list

    • upper and lower not allowed

  • If format: "upper_lower":

    • upper and lower required

      • each must contain at least 2 points

      • each point must be a numeric pair [x, y]

      • the first point of both lists (the leading edge) must be coincident

      • the last point of each list defines the trailing-edge point for that surface

        • these may be coincident for a sharp trailing edge

    • points and leading_edge not allowed

  • The input geometry must define a valid nonzero chord extent so it can be normalized to unit chord internally

4.3.6 Notes

  • Point-based airfoils define the geometry directly through discrete coordinates on the surface.

  • Point-defined airfoils are normalized to unit chord.

  • In surface_curve format, the trailing-edge point must be represented for both surfaces, even if the two points are coincident.

  • In upper_lower format, the upper and lower surfaces are specified independently and must share the same leading-edge point.

  • The leading_edge index is required in surface_curve format to identify the surface split and avoid ambiguity in point sets with multiple candidate leading-edge points or nonuniform sampling.

  • If preservation of original point-set scale, translation, or orientation is needed in the future, that information should be treated as provenance rather than as part of the canonical normalized airfoil geometry definition.

  • Point-defined airfoils are planned, but they are not yet constructable through AirfoilFactory.from_spec(...).

4.4 Spline Airfoils

Spline-backed airfoils provide the natural persisted representation for non-analytic airfoils whose authoritative geometry is stored as editable control-point curves rather than as source points or analytic parameters.

The first implemented spline schema subtype is a Bezier-backed airfoil.

airfoils:
  edited_tip:
    type: spline
    representation: bezier
    upper:
      control_points:
        - [0.0, 0.0]
        - [0.2, 0.06]
        - [0.7, 0.03]
        - [1.0, 0.002]
    lower:
      control_points:
        - [0.0, 0.0]
        - [0.2, -0.03]
        - [0.7, -0.02]
        - [1.0, -0.002]

4.4.1 Fields

  • type: "spline"

  • representation: spline representation subtype

  • upper: upper-surface spline definition

  • lower: lower-surface spline definition

  • provenance: optional source and construction metadata

For the initial implemented Bezier subtype:

  • representation: "bezier"

  • upper.control_points: ordered list of planar [x, y] control points

  • lower.control_points: ordered list of planar [x, y] control points

The Bezier degree is derived from the number of control points and is therefore not stored explicitly in the first implemented Bezier-backed schema form.

4.4.2 Validation Rules

For the initial implemented Bezier subtype:

  • representation must be "bezier"

  • upper.control_points and lower.control_points must each contain at least 2 points

  • each control point must be a numeric pair [x, y]

  • the first upper and lower control points must coincide at the leading edge

  • the last upper and lower control points define the upper and lower trailing-edge endpoints

The first implemented spline schema does not require higher-order leading-edge continuity such as G1 or G2.

The first implemented spline schema also does not require coincident upper and lower trailing-edge endpoints, which allows both sharp and blunt trailing edges.

4.4.3 Notes

  • In this repository, “spline” means geometric parametric spline curves such as Bezier, B-spline, and eventually NURBS, not interpolation splines such as PCHIP.

  • The current implementation supports only the Bezier subtype.

  • For the Bezier subtype, both upper and lower surfaces use their own natural parameterization from leading edge to trailing edge.

  • The schema should remain backend-neutral even if the first runtime implementation uses Splipy internally for broader spline families later.

  • type: spline is the natural persisted representation for edited or fitted non-analytic airfoils once their authoritative geometry is no longer just the original source points or analytic definition.

  • The current implementation constructs type: spline through AirfoilFactory.from_spec(...) for representation: bezier.

  • Canonical type: cst airfoils may be exported exactly into this persisted Bezier-backed spline form through buffalo_wings.airfoil.cst_airfoil_to_spline_spec(...).

  • That exact export path is intended for cases where CST remains the source definition but spline geometry becomes the authoritative stored representation for downstream editing or interchange.

4.5 CST Coefficients

CST airfoils are defined directly through separate upper-surface and lower-surface Class-Shape Transformation coefficient sets.

airfoils:
  cst_foil:
    type: cst
    trailing_edge_thickness: 0.0025
    upper: {a: [0.2, 0.1, 0.0]}
    lower: {a: [-0.1, -0.05, 0.0]}

4.5.1 Fields

  • type: "cst"

  • trailing_edge_thickness: trailing-edge thickness as a fraction of chord; default 0.0

  • upper: upper-surface CST definition; default {n1: 0.5, n2: 1.0, a: []}

  • lower: lower-surface CST definition; default {n1: 0.5, n2: 1.0, a: []}

Each surface definition contains:

  • n1: leading-edge class-function exponent; optional, default 0.5

  • n2: trailing-edge class-function exponent; optional, default 1.0

  • a: ordered list of shape-function coefficients; default []

4.5.2 Validation Rules

  • upper.n1 must be numeric

  • upper.n2 must be numeric

  • lower.n1 must be numeric

  • lower.n2 must be numeric

  • trailing_edge_thickness must be numeric

  • trailing_edge_thickness >= 0

  • upper.n1 >= 0

  • upper.n2 >= 0

  • lower.n1 >= 0

  • lower.n2 >= 0

  • upper.a must be a list of numeric coefficients

  • lower.a must be a list of numeric coefficients

4.5.3 Examples

Accepted examples:

  • type: cst

  • type: cst, trailing_edge_thickness: 0.0025, upper: {a: [0.2, 0.1, 0.0]}, lower: {a: [-0.1, -0.05, 0.0]}

  • type: cst, upper: {n1: 0.5, n2: 1.0, a: [0.1, 0.0]}, lower: {n1: 0.5, n2: 1.0, a: [-0.1, 0.0]}

Unsupported examples:

  • type: cst, trailing_edge_thickness: -0.001, upper: {a: [0.2]}, lower: {a: [-0.2]}

  • type: cst, upper: {n1: -0.5, n2: 1.0, a: [0.2]}, lower: {n1: 0.5, n2: 1.0, a: [-0.2]}

  • type: cst, upper: {n1: 0.5, n2: 1.0, a: "0.2,0.1"}, lower: {n1: 0.5, n2: 1.0, a: [-0.1]}

  • type: cst, upper: {n1: 0.5}, lower: {a: [-0.1]}

4.5.4 Notes

  • CST defines the airfoil geometry through separate coefficient sets for the upper and lower surfaces.

  • The class function exponents and coefficient arrays determine the surface shape in the normalized local airfoil frame.

  • Omitting n1 and n2 selects the standard airfoil CST class function with n1 = 0.5 and n2 = 1.0.

  • Nondefault n1 or n2 values are allowed, but they describe more general CST class-function shapes rather than the typical rounded-leading-edge, cusped-trailing-edge airfoil form.

  • trailing_edge_thickness explicitly controls the trailing-edge gap in the airfoil definition.

  • Airfoil definitions are normalized to unit chord.

  • The coefficient ordering in a is the schema order and should be preserved exactly as given.

  • AirfoilFactory.from_spec(...) constructs runtime CstAirfoil objects for canonical n1 = 0.5, n2 = 1.0 CST specs and runtime CstGeometry objects for more general CST exponent choices.

  • Canonical CstAirfoil and general CstGeometry can represent the same canonical CST shape, but they do not share the same curve parameterization.

  • CstAirfoil uses the airfoil-specific curve parameterization x = u^2, which keeps canonical curve-parameter derivatives finite at the leading edge.

  • CstGeometry keeps the general full-airfoil curve parameterization x = |u|, so leading-edge curve-parameter derivatives may remain singular for exponent choices such as n1 = 0.5.

  • Curve-parameter derivatives, breakpoint-side values, and coordinates sampled at the same u can therefore differ between CstAirfoil and CstGeometry even when they describe the same geometric shape under the mapping tau = sign(u) * u^2.

4.6 PARSEC Coefficients

This uses a 12-parameter PARSEC specification with independently specified upper and lower leading-edge radii.

airfoils:
  parsec_foil:
    type: parsec
    leading_edge_radius:
      upper: 0.005
      lower: 0.005
    trailing_edge:
      thickness: 0.0025
      location: -0.006
      direction_angle: 7.0
      wedge_angle: 10.0
    upper_surface_max:
      location: [0.41, 0.11]
      curvature: -0.9
    lower_surface_min:
      location: [0.20, -0.023]
      curvature: 0.05

4.6.1 Fields

  • type: "parsec"

  • leading_edge_radius.upper: upper-surface leading-edge radius

  • leading_edge_radius.lower: lower-surface leading-edge radius

  • trailing_edge.thickness: trailing-edge thickness

  • trailing_edge.location: vertical coordinate of the midpoint between the upper and lower trailing-edge endpoints

  • trailing_edge.direction_angle: angle of the bisector of the upper and lower trailing-edge surface directions

  • trailing_edge.wedge_angle: included angle between the upper and lower surface directions at the trailing edge

  • upper_surface_max.location: [x, y] location of the upper-surface maximum

  • upper_surface_max.curvature: curvature of the upper surface at the upper-surface maximum

  • lower_surface_min.location: [x, y] location of the lower-surface minimum

  • lower_surface_min.curvature: curvature of the lower surface at the lower-surface minimum

4.6.2 Validation Rules

  • leading_edge_radius must contain both upper and lower

  • leading_edge_radius.upper > 0

  • leading_edge_radius.lower > 0

  • trailing_edge.thickness >= 0

  • upper_surface_max.location must be a numeric 2-tuple [x, y]

  • lower_surface_min.location must be a numeric 2-tuple [x, y]

  • 0 < upper_surface_max.location[0] < 1

  • 0 < lower_surface_min.location[0] < 1

  • upper_surface_max.location[1] > 0

  • lower_surface_min.location[1] < 0

4.6.3 Examples

Accepted examples:

  • type: parsec, leading_edge_radius: {upper: 0.005, lower: 0.005}, trailing_edge: {thickness: 0.0025, location: -0.006, direction_angle: 7.0, wedge_angle: 10.0}, upper_surface_max: {location: [0.41, 0.11], curvature: -0.9}, lower_surface_min: {location: [0.20, -0.023], curvature: 0.05}

  • type: parsec, leading_edge_radius: {upper: 0.004, lower: 0.004}, trailing_edge: {thickness: 0.0, location: 0.0, direction_angle: 0.0, wedge_angle: 8.0}, upper_surface_max: {location: [0.4, 0.08], curvature: -0.8}, lower_surface_min: {location: [0.25, -0.02], curvature: 0.04}

Unsupported examples:

  • type: parsec, leading_edge_radius: {upper: -0.005, lower: 0.005}, trailing_edge: {thickness: 0.0025, location: -0.006, direction_angle: 7.0, wedge_angle: 10.0}, upper_surface_max: {location: [0.41, 0.11], curvature: -0.9}, lower_surface_min: {location: [0.20, -0.023], curvature: 0.05}

  • type: parsec, leading_edge_radius: {upper: 0.005, lower: 0.005}, trailing_edge: {thickness: 0.0025, location: -0.006, direction_angle: 7.0, wedge_angle: 10.0}, upper_surface_max: {location: [1.2, 0.11], curvature: -0.9}, lower_surface_min: {location: [0.20, -0.023], curvature: 0.05}

  • type: parsec, leading_edge_radius: {upper: 0.005}, trailing_edge: {thickness: 0.0025, location: -0.006, direction_angle: 7.0, wedge_angle: 10.0}, upper_surface_max: {location: [0.41, 0.11], curvature: -0.9}, lower_surface_min: {location: [0.20, -0.023], curvature: 0.05}

4.6.4 Notes

  • All length quantities are nondimensionalized by the chord.

  • Angular quantities are specified in degrees.

  • Airfoil definitions are normalized to unit chord.

  • This representation defines the airfoil through geometric constraints rather than directly through surface points.

  • Upper and lower leading-edge radii are specified independently.

  • PARSEC airfoils are planned, but they are not yet constructable through AirfoilFactory.from_spec(...).

4.7 Simple Analytic Airfoils

These airfoils are defined by simple geometric shapes rather than by a historical designation system.

4.7.1 Flat Plate

Flat-plate airfoils are specified directly by type.

Example
airfoils:
  plate:
    type: flat_plate
Fields
  • type: "flat_plate"

Validation Rules
  • No additional fields are required.

Examples

Accepted examples:

  • type: flat_plate

Unsupported examples:

  • type: flat_plate, params: {}

Notes
  • This represents an idealized zero-thickness flat plate in the normalized local frame.

  • Flat-plate airfoils are planned, but they are not yet constructable through AirfoilFactory.from_spec(...).

4.7.2 Biconvex

Biconvex airfoils are specified through the maximum thickness only.

Example
airfoils:
  biconvex_12:
    type: biconvex
    params:
      max_thickness: 0.12
Fields
  • type: "biconvex"

  • params.max_thickness: maximum thickness as a fraction of chord

Validation Rules
  • params must be present.

  • 0 <= params.max_thickness <= 1.0

Examples

Accepted examples:

  • params: {max_thickness: 0.12}

  • params: {max_thickness: 0.04}

Unsupported examples:

  • type: biconvex

  • params: {}

  • params: {max_thickness: -0.1}

Notes
  • In this schema, biconvex means the circular-arc form defined by two intersecting circular arcs.

  • Parabolic-arc variants also appear in the literature and in benchmark configurations, but they are treated as a distinct approximation rather than as the canonical meaning of biconvex here.

  • Use biconvex_parabola when the intended shape is the parabolic variant.

  • Biconvex airfoils are planned, but they are not yet constructable through AirfoilFactory.from_spec(...).

4.7.3 Biconvex Parabola

Parabolic biconvex airfoils are specified through the maximum thickness only.

Example
airfoils:
  biconvex_parabola_12:
    type: biconvex_parabola
    params:
      max_thickness: 0.12
Fields
  • type: "biconvex_parabola"

  • params.max_thickness: maximum thickness as a fraction of chord

Validation Rules
  • params must be present.

  • 0 <= params.max_thickness <= 1.0

Examples

Accepted examples:

  • params: {max_thickness: 0.12}

  • params: {max_thickness: 0.04}

Unsupported examples:

  • type: biconvex_parabola

  • params: {}

  • params: {max_thickness: -0.1}

Notes
  • This type represents the parabolic-arc variant sometimes used in analytical approximations and benchmark problems.

  • Use biconvex when the intended shape is the circular-arc form.

  • Parabolic biconvex airfoils are planned, but they are not yet constructable through AirfoilFactory.from_spec(...).

4.7.4 Polygon

Polygonal airfoils are specified by ordered vertices connected by straight segments.

Example
airfoils:
  diamond:
    type: polygon
    params:
      vertices:
        - [1.0, 0.0]
        - [0.5, 0.05]
        - [0.0, 0.0]
        - [0.5, -0.05]
Fields
  • type: "polygon"

  • params.vertices: ordered polygon vertices in the normalized local frame

Validation Rules
  • params must be present.

  • params.vertices must contain at least 3 points.

  • Each vertex must be a numeric 2-tuple [x, y].

Examples

Accepted examples:

  • params: {vertices: [[1.0, 0.0], [0.5, 0.05], [0.0, 0.0], [0.5, -0.05]]}

  • params: {vertices: [[1.0, 0.01], [0.0, 0.06], [0.0, -0.04], [1.0, -0.01]]}

Unsupported examples:

  • type: polygon

  • params: {vertices: []}

  • params: {vertices: [[1.0, 0.0], [0.0, 0.0]]}

Notes
  • The airfoil boundary is closed by connecting the last vertex back to the first.

  • Open trailing edges are allowed by specifying distinct upper and lower trailing-edge-adjacent vertices.

  • Polygonal airfoils are planned, but they are not yet constructable through AirfoilFactory.from_spec(...).

4.7.5 Ellipse

Elliptical airfoils are specified through the maximum thickness only.

Example
airfoils:
  ellipse_12:
    type: ellipse
    params:
      max_thickness: 0.12
Fields
  • type: "ellipse"

  • params.max_thickness: maximum thickness as a fraction of chord

Validation Rules
  • params must be present.

  • 0 < params.max_thickness <= 1.0

Examples

Accepted examples:

  • params: {max_thickness: 0.12}

  • params: {max_thickness: 1.0}

Unsupported examples:

  • type: ellipse

  • params: {max_thickness: 0.0}

  • params: {max_thickness: 1.2}

Notes
  • max_thickness = 1.0 corresponds to the circle case in the normalized local frame.

  • Elliptical airfoils are planned, but they are not yet constructable through AirfoilFactory.from_spec(...).

4.7.6 Circular Arc

Circular-arc airfoils are specified through the maximum thickness only.

Example
airfoils:
  arc_08:
    type: circular_arc
    params:
      max_thickness: 0.08
Fields
  • type: "circular_arc"

  • params.max_thickness: maximum thickness as a fraction of chord

Validation Rules
  • params must be present.

  • 0 <= params.max_thickness <= 1.0

Examples

Accepted examples:

  • params: {max_thickness: 0.08}

  • params: {max_thickness: 0.0}

Unsupported examples:

  • type: circular_arc

  • params: {max_thickness: -0.05}

Notes
  • Circular-arc airfoils are planned, but they are not yet constructable through AirfoilFactory.from_spec(...).

4.7.7 Joukowski

Joukowski airfoils are specified by the classical transformation parameters nu and epsilon.

Example
airfoils:
  joukowski_example:
    type: joukowski
    params:
      nu: 0.1
      epsilon: 0.05
Fields
  • type: "joukowski"

  • params.nu: camber-related Joukowski shape parameter

  • params.epsilon: thickness-related Joukowski shape parameter

Validation Rules
  • params must be present.

  • params.nu must be numeric.

  • params.epsilon must be numeric.

Examples

Accepted examples:

  • params: {nu: 0.1, epsilon: 0.05}

  • params: {nu: 0.0, epsilon: 0.03}

Unsupported examples:

  • type: joukowski

  • params: {nu: 0.1}

  • params: {epsilon: 0.05}

Notes
  • nu and epsilon are shape-control parameters and do not directly equal the resulting geometric camber or thickness.

  • Joukowski airfoils are planned, but they are not yet constructable through AirfoilFactory.from_spec(...).

5. Normalization

Normalization defines how coordinate-based airfoil definitions are transformed into the canonical local airfoil frame used by this schema.

This section applies to imported coordinate files and inline point-coordinate airfoils. Analytic airfoil definitions, such as NACA, CST, and PARSEC, are already defined in the normalized local frame unless otherwise specified.

5.1 Canonical Normalized Frame

A normalized airfoil is represented in the local airfoil frame with the following properties:

  • leading-edge reference at [0.0, 0.0]

  • trailing-edge reference at [1.0, 0.0]

  • chord line aligned with the positive x axis

  • positive y directed toward the upper surface side of the airfoil

  • chord length equal to 1

5.2 Reference Definitions

Normalization is based on a leading-edge reference and a trailing-edge reference.

  • The leading-edge reference is the geometric leading-edge point used to define the start of the chord line.

  • The trailing-edge reference is the point used to define the end of the chord line.

For airfoils with distinct upper and lower trailing-edge endpoints, the trailing-edge reference is the midpoint of those two endpoints.

This definition avoids ambiguity for blunt trailing edges, where the upper and lower trailing-edge points may not coincide.

5.3 Reference Extraction by Representation

5.3.1 Coordinate Files

For file-based coordinate definitions, the leading-edge and trailing-edge references are determined from the parsed file format and the file topology.

  • For lednicer files, the leading-edge reference is the shared leading-edge endpoint of the parsed upper and lower surfaces.

  • For lednicer files, the trailing-edge reference is the midpoint of the upper and lower trailing-edge endpoints.

  • For selig files, the trailing-edge reference is determined from the first and last surface points.

  • For selig files, the leading-edge reference is determined from the parsed surface-curve topology as the leading-edge point implied by the turn between the two surfaces.

5.3.2 Point Coordinates

For type: "points" airfoils:

  • If format: "surface_curve":

    • the first and last points define the two trailing-edge endpoints

    • the trailing-edge reference is the midpoint of those two endpoints

    • the leading_edge index identifies the leading-edge reference point

  • If format: "upper_lower":

    • the first point of both upper and lower defines the shared leading-edge reference

    • the last points of upper and lower define the upper and lower trailing-edge endpoints

    • the trailing-edge reference is the midpoint of the two trailing-edge endpoints

5.4 Normalization Transform

For imported coordinate files and inline point-coordinate airfoils, the following transform is applied:

  1. Translate the airfoil so that the leading-edge reference is at the origin.

  2. Rotate the airfoil so that the line from the leading-edge reference to the trailing-edge reference lies on the positive x axis.

  3. Scale the airfoil so that the distance from the leading-edge reference to the trailing-edge reference is 1.

After normalization, the leading-edge reference is at [0.0, 0.0] and the trailing-edge reference is at [1.0, 0.0].

5.5 Orientation

If the input geometry is oriented such that the upper surface lies at negative y after normalization, the implementation must either:

  • reject the geometry as invalid, or

  • apply a reflection about the x axis if such behavior is explicitly supported by the implementation

Recommended behavior: reject the geometry unless automatic reflection is explicitly documented.

5.6 Validation Rules

  • normalization requires a valid leading-edge reference

  • normalization requires a valid trailing-edge reference

  • the distance between the leading-edge reference and trailing-edge reference must be greater than zero

  • for airfoils with separate upper and lower trailing-edge endpoints, both endpoints must be valid before the trailing-edge midpoint is computed

  • if normalization is disabled for a coordinate-based airfoil, the geometry is preserved as provided, but it must still define a valid nonzero chord extent

5.7 Notes

  • Normalization must not define the leading edge purely as the point with minimum x, because valid airfoil definitions may contain points with x < 0 relative to the normalized leading-edge reference.

  • Normalization must not define the trailing edge purely as the point with maximum x, because blunt trailing edges may have distinct upper and lower endpoints.

  • For blunt trailing edges, the trailing-edge midpoint is the canonical chord endpoint used for normalization.

  • This normalization procedure is intended to make coordinate-defined airfoils consistent with analytic airfoil definitions in this schema.