dragonfly_sim.core.shape_design

Shape design variables as modular layers on an lsdo_geo FFD block.

DG_windtunnel_model only sees an FFDShapeParameterization: it hands it the wall nodes in set_up_sim (setup) and takes the resulting wall displacement into the mesh warper (deform_mesh). Which design variables exist, their bounds, and how they move the control points is decided entirely by the layers given to the parameterization:

  • ControlPointMotions – raw control-point motions (cp_motions).

  • SectionalVariables – the SectionalShape presets (2D camber and thickness per chordwise station; 3D twist, chord, … per spanwise section).

  • WingShape – a trapezoidal wing: quarter-chord (or any chord-line) sweep, aspect ratio, root chord, span and taper ratio, plus chordwise thickness and camber modes per spanwise station.

A driver then reads:

shape = FFDShapeParameterization(ffd_shape, degree, corner_list,
                                 layers=[WingShape(planform={...}, sections={...})])
model = DG_windtunnel_model(mesh, boundary_dict, shape, ...)
model.set_up_sim()
shape_parameters = shape.declare_design_variables()
...
u_vec = model.evaluate(model.deform_mesh(), shape_parameters, alpha)

and constrains geometry through shape.outputs() (layer quantities such as the wing’s planform area) and shape.geometry (WallGeometry: enclosed measure, thickness at stations, planform stations).

The coefficient pipeline is C0 -> one lsdo_geo SectionalParameterization -> additive layers. lsdo_geo freezes axes and stretch origins from the points’ values when the graph is built, so every layer’s sectional operations are collected into a single evaluation on the constant baseline (and must share one principal direction); additive motions (cp motions, sectional modes) are linear and go on top.

A new layer subclasses ShapeLayer and overrides what it needs.

Classes

ControlPointMotions

Raw control-point motions: one design variable cp_motions of shape

FFDShapeParameterization

An FFD block around the wall plus the design-variable layers that move it.

SectionalVariables

SectionalShape presets as design variables.

ShapeLayer

Base of the design-variable layers of an FFDShapeParameterization.

WingShape

Planform and sectional design variables of a trapezoidal half wing.

Functions

_bernstein(degree, xi)

Bernstein polynomials of degree at xi: shape (len(xi), degree + 1).

_bspline_basis(degree, n_coefficients, u)

_invert_monotone_bspline(degree, values, target)

Parametric coordinate u in [0, 1] where the 1D B-spline with control

_section_mode_profiles(kind, num_modes, xi)

Chordwise profiles of the WingShape section modes at chord fractions xi:

Module Contents

class dragonfly_sim.core.shape_design.ControlPointMotions(dv_spec, scaler_mode='bounds', name='cp_motions')

Bases: ShapeLayer

Raw control-point motions: one design variable cp_motions of shape ffd_shape + (n_opt,), with bounds from dv_spec (see ffd_dv_utils).

additive(coefficients)
declare(dvs)

Create this layer’s design variables in dvs (a ShapeDVSet).

describe()

Lines for FFDShapeParameterization.print_design_variables.

coord_idxs
dv_spec
name = 'cp_motions'
scaler_mode = 'bounds'
class dragonfly_sim.core.shape_design.FFDShapeParameterization(shape, degree=2, block_corner_list=None, layers=(), margin=0.0001, projection_newton_tol=1e-12, inner_bdry_rel_tol=1e-06, inner_bdry_overset_margin=0.0001)

An FFD block around the wall plus the design-variable layers that move it.

Parameters:
shapesequence of int

Control points per parametric direction.

degreeint or sequence of int
block_corner_listoptional

Two-surface block definition (shape_parameterization. ffd_corners_from_corner_list); None wraps the wall’s bounding box. Also locates the wall when the model gets no explicit mesh_inner_bdry_function (inner_boundary_function).

layerssequence of ShapeLayer

Applied in order; their design variables are declared in this order.

margin, projection_newton_tolsee WallFFD.
inner_bdry_rel_tol, inner_bdry_overset_margincontainment test of

inner_boundary_function.

__getitem__(name)

A declared design variable by name.

_require_setup()
coefficients()

Deformed FFD coefficients (built once, then cached).

declare_design_variables()

Create every layer’s csdl design variables (with bounds), in layer order. Returns parameter_vector(). Call inside the recorder, after set_up_sim.

inner_boundary_function()

Wall facet marker from the block corners, or None without them.

outputs()

Constraint quantities of the layers: {layer.name: {...}} for the layers that have any (e.g. outputs()['wing']['area']).

parameter_vector()

All shape design variables as one flat vector (declaration order).

print_design_variables(printer=print)
setup(comm, wall_coords, wall_rows, wall_facets=None)

Build the FFD block around the wall and set up the layers. Collective.

wall_coords(M_global, dim) replicated wall node coordinates, in the

rank-block order of the mesh’s wall node set; wall_rows : the rows this rank owns; wall_facets : replicated wall facets (rows of wall node ids), needed for geometry and by layers that measure the baseline wall.

wall_displacement()

Replicated (M_global, dim) wall node displacement (cached).

_coefficients = None
_geometry = None
_wall_displacement = None
_wall_facets = None
block_corner_list = None
degree = 2
property dim
dvs = None
ffd = None
property geometry

WallGeometry of the baseline wall, built on first use.

inner_bdry_overset_margin = 0.0001
inner_bdry_rel_tol = 1e-06
layers = []
margin = 0.0001
projection_newton_tol = 1e-12
shape
class dragonfly_sim.core.shape_design.SectionalVariables(principal_dim, variables, scaler_mode='bounds', name='sectional')

Bases: ShapeLayer

SectionalShape presets as design variables.

variables{kind: spec}, kind one of SectionalShape.add’s presets;

spec is the bounds (anything build_sectional_dv takes) or a dict with keys bounds and optionally num_values (fewer than the number of sections: a B-spline profile), pivot and profile_degree. Each variable is a delta from the baseline.

declare(dvs)

Create this layer’s design variables in dvs (a ShapeDVSet).

sectional(sectional)

Register sectional operations on sectional (a SectionalShape).

name = 'sectional'
principal_dim
scaler_mode = 'bounds'
specs
class dragonfly_sim.core.shape_design.ShapeLayer

Base of the design-variable layers of an FFDShapeParameterization.

A layer with sectional (lsdo_geo) operations sets principal_dim and registers them in sectional; that call happens inside an inline recording, so expressions of the design variables built there have values, which lsdo_geo needs. additive adds motions that are linear in the variables on top of the sectional result.

additive(coefficients)
declare(dvs)

Create this layer’s design variables in dvs (a ShapeDVSet).

describe()

Lines for FFDShapeParameterization.print_design_variables.

outputs()
sectional(sectional)

Register sectional operations on sectional (a SectionalShape).

setup(parameterization)

Numpy precomputation once the FFD block exists (parameterization.ffd, parameterization.geometry).

name = 'layer'
principal_dim = None
class dragonfly_sim.core.shape_design.WingShape(planform=None, sections=None, sweep_chord_fraction=0.25, semi_span=None, derived=None, fit_tol=0.001, scaler_mode='bounds', name='wing')

Bases: ShapeLayer

Planform and sectional design variables of a trapezoidal half wing.

The wall must be a straight-tapered half wing with its root on the y = 0 symmetry plane, x chordwise and z up, inside a lofted FFD block whose parametric axes are (chord, span, vertical) – the corner-list blocks of the wing drivers.

planform{name: (lower, upper)} for any of
sweep sweep angle [rad] of the chord line at

sweep_chord_fraction (0.25: quarter chord)

aspect_ratio full-wing AR = b^2 / S root_chord span full-wing span b (twice the half wing’s semi-span) taper_ratio tip chord / root chord Absolute values: each variable starts at the baseline wing’s value and its bounds must bracket it. A name left out stays at its baseline, except the derived one.

sections{name: spec} for thickness and/or camber. spec is the

bounds, or a dict {‘bounds’: …, ‘modes’: n} for n chordwise modes per FFD spanwise section (default 1). The variable then has n_sections x n entries, section-major (root first: all of the root’s modes, then the next section’s); bounds as for build_sectional_dv – a scalar, a pair, or a pair of arrays of shape (n_sections,) (the same for every mode of a section), (n_sections, n) or (n_sections * n,). Pin an entry with (0., 0.). Deltas from the baseline, starting at zero, with xi the local chord fraction and B_m the Bernstein polynomials of degree n - 1: thickness relative change of the section’s thickness (t/c),

sum_m tau_m B_m(xi) – 0.1 in every mode is 10% thicker

camber change of camber over local chord, sum_m kappa_m

4 xi (1 - xi) B_m(xi), so the leading and trailing edge stay put; kappa in every mode is a parabolic camber line of maximum kappa

With one mode these are a uniform thickness change and a parabolic camber line. The modes are least-squares fits on the block’s chordwise basis; they are exact when it is a Bezier (n_chord = degree + 1) of degree >= n - 1 for thickness and >= n + 1 for camber (describe reports the fit residual).

derivedwhich of aspect_ratio, root_chord and span follows from

the other two and the taper ratio, s = AR c_r (1 + taper) / 4. Default: span, unless span is a variable – then whichever of aspect_ratio and root_chord is not (with only span a variable, name it).

sweep_chord_fractionchord line the sweep is measured on; sections scale

about it, and its root point stays fixed.

semi_spanbaseline semi-span; default the wall’s largest y (which

includes any tip cap).

fit_tolallowed deviation of the baseline leading/trailing edges from

straight lines, relative to the root chord.

The geometry: FFD section j, at baseline span fraction eta_j = y_j / s0, moves to y_j’ = eta_j s’, its chord c_j’ = c_r’ (1 - (1 - taper’) eta_j) scales it by r_j = c_j’/c_j0 about the reference chord line in x and about the chord plane in z (keeping t/c; the thickness modes act on top), and the reference line is placed at x_ref,root + y_j’ tan(sweep’). These are lsdo_geo sectional stretches and translations; thickness and camber are additive sectional modes. With the taper ratio fixed the map is affine, so the deformed wall is exactly the trapezoid the variables describe; a taper change is exact at the FFD sections and B-spline-blended between them.

_derived_size(derived)
_fit_baseline(geometry)
_planform_variable(key)
_section_bounds(bounds, n1, modes)

Broadcast per-section (n1,) or (n1, modes) bound arrays to the variable’s flat (n1 * modes,) layout; scalars pass through.

_setup_sections(ffd)
declare(dvs)

Create this layer’s design variables in dvs (a ShapeDVSet).

describe()

Lines for FFDShapeParameterization.print_design_variables.

outputs()

Planform quantities as csdl variables: sweep, aspect_ratio, root_chord, taper_ratio, tip_chord, semi_span, span, area (full wing) and section_chords (at the FFD sections).

sectional(sectional)

Register sectional operations on sectional (a SectionalShape).

setup(parameterization)

Numpy precomputation once the FFD block exists (parameterization.ffd, parameterization.geometry).

PLANFORM_KEYS = ('sweep', 'aspect_ratio', 'root_chord', 'span', 'taper_ratio')
SECTION_KEYS = ('thickness', 'camber')
SIZE_KEYS = ('aspect_ratio', 'root_chord', 'span')
derived = 'span'
f
fit_tol = 0.001
name = 'wing'
planform_spec
principal_dim = 1
scaler_mode = 'bounds'
section_spec
semi_span_arg = None
dragonfly_sim.core.shape_design._bernstein(degree, xi)

Bernstein polynomials of degree at xi: shape (len(xi), degree + 1).

dragonfly_sim.core.shape_design._bspline_basis(degree, n_coefficients, u)
dragonfly_sim.core.shape_design._invert_monotone_bspline(degree, values, target)

Parametric coordinate u in [0, 1] where the 1D B-spline with control values values equals target.

dragonfly_sim.core.shape_design._section_mode_profiles(kind, num_modes, xi)

Chordwise profiles of the WingShape section modes at chord fractions xi: shape (len(xi), num_modes). Thickness: the Bernstein polynomials of degree num_modes - 1 (they sum to one: equal values scale the section uniformly). Camber: 4 xi (1 - xi) times those (zero at the leading and trailing edge; equal values give the parabolic camber line).