Shape parameterization
The wall shape is controlled by a B-spline FFD block around the wall nodes (WallFFD, created by
DG_windtunnel_model.set_up_sim() as model.ffd). Design variables change the block’s control points; the wall
moves with them, and the volume mesh follows through IDWarp.
FFD block
ffd_shape sets the number of control points per parametric direction (2 entries in 2D, 3 in 3D) and
ffd_degree the B-spline degree. By default the block is the axis-aligned bounding box of the wall. For a
tapered or swept wing, pass ffd_block_corner_list to DG_windtunnel_model: two rectangular end surfaces (root
and tip), each given by two opposite corners, e.g.
[[(-0.1, 0., 0.32), (5.1, 0., -0.3)], [(7.4, 14.1, 0.32), (9.1, 14.1, -0.3)]].
Control-point motions
cp_motions moves control points individually. Its directions and bounds come from one dictionary keyed by
spatial direction (0 = x, 1 = y, 2 = z):
dv_spec = {1: 0.01} # 2D: vertical motions within +/-0.01
dv_spec = {0: 0.02, 2: (-0.05, 0.10)} # 3D: x symmetric, z asymmetric
dv_spec = {2: spanwise_linear_bounds(base_bound=0.1, scale_at_root=1.0, scale_at_ref=0.5, y_ref=14.1)}
A bound is a symmetric half-range, a (lower, upper) pair, or a callable of the control-point coordinates such
as spanwise_linear_bounds, which varies the bound linearly along the span.
cp_dv_directions(dv_spec)gives thecp_coord_opt_idxsthatDG_windtunnel_modelneeds.build_cp_motion_dv(model.ffd.baseline_coefficients, dv_spec)builds the variable’s value, bounds and scaling (each direction’s box maps to \(\pm 1\)).model.ffd.apply_cp_motions(coefficients, cp_motions, cp_coord_opt_idxs)adds the motions to the control points.
Sectional variables
SectionalShape(model.ffd, principal_dim) views the block as a stack of sections: principal_dim=0 in 2D
(chordwise stations), 1 for a wing (spanwise stations). Variables are added with add(kind, values):
Kind |
Operation |
Dimensions |
|---|---|---|
|
translation along the vertical axis |
2D |
|
stretch along the vertical axis |
2D, 3D |
|
rotation about the spanwise axis (radians) |
3D |
|
stretch along the chordwise axis |
3D |
|
translation along x |
3D |
|
translation along z |
3D |
|
translation along y |
3D |
A variable has one value per section, or fewer values, which are then B-spline coefficients of a profile over the
sections. Create its bounds with build_sectional_dv(num_values, bounds).
Apply the sectional layer to the constant baseline first, then the control-point motions:
sectional = SectionalShape(model.ffd, principal_dim=1)
twist = shape_dvs.add('twist', build_sectional_dv(5, np.radians(2.)))
sectional.add('twist', twist)
coefficients = sectional.apply(model.ffd.baseline_variable())
coefficients = model.ffd.apply_cp_motions(coefficients, cp_motions, cp_coord_opt_idxs)
Design-variable set
ShapeDVSet collects all shape variables in declaration order; shape_dvs.flat_variable() is the vector
passed to the mesh warper, flow model and postprocessor.
Geometric constraints
model.wall_geometry() returns a WallGeometry for the deformed wall:
Method |
Quantity |
|---|---|
|
enclosed area (2D) or volume (3D) |
|
deformed over baseline thickness at the stations |
|
section chords, span and planform area (3D) |
geometry = model.wall_geometry()
area = geometry.enclosed_measure(model.ffd.wall_displacement(coefficients))
area.set_as_constraint(lower=geometry.baseline_measure)
stations = geometry.add_thickness_stations(np.linspace(0.1, 0.9, 9))
geometry.thickness_ratio(coefficients, stations).set_as_constraint(lower=0.9)