Sophisticated Communication: From Macro to DSL to API
Swept wing with deflected inboard Fowler flap. CFD-ready mesh generated using a Python script.

Sophisticated Communication: From Macro to DSL to API

How do you tackle complexity? In the world of CAD, complex geometry modeling tasks are usually performed iteratively, with a human operator interacting with CAD software through a series of button presses and mouse drags. There is increasing interest in automating this process to generate of thousands of CAD models, usually for computational analysis. One way to approach this automation is via the macro paradigm: recording a sequence of key presses and mouse drags, and then replaying them with some parameters modified. But such a macro is a black box. It lacks visibility; no human can "read" it and understand what it's doing.

The inevitable paradigm shift follows: why not have the "parameters" describe the modeling process in its entirety? Forget the interactive procedure; let's just pay up front and in full at the beginning, by describing what operations are to be performed and how. In other words, the (human) modeler would like to provide a comprehensive set of directions for what she wants the (computer) modeler to do. A programming language is tautologically the vehicle for this communication of instructions.

While a Domain Specific Language (DSL) is an interesting option, there isn't (as far as I know) a well-established DSL for geometry modeling: there is no lingua franca. So users will need to learn a strange new tongue, and fluency in a new language is always a problem. Instead, by choosing to write an API in a popular programming language, one inherits a wealth of expressivity. One can borrow ideas, idioms, patterns, or even acquire outright whole reams of other people's prose and verse through the use of libraries. This still leaves one free to develop an apposite dialect for the field.

Talk the Talk

At Aerion, we've done just that with our Python API for aerospace geometry modeling. If the vocabulary is expressive, then one can be both succinct and clear.

# Instantiate an airfoil
tip_airfoil = pysage.airfoil.naca_4('0010')
# Incidence: rotate about the quarter-chord point
tip_airfoil.rotate(UNIT_VEC_Y, -3, origin=UNIT_VEC_X * 0.25)
# Scale the section and locate it
tip_airfoil.scale(tip_chord)
tip_airfoil.translate(pysage.get_edge_end(main_le))

The comments in the code above are almost superfluous. And for the myriad aerospace geometry modeling tasks that have nothing to do with aerospace:

# This thing needs to face the other way
cutter = pysage.mirror(cutter, UNIT_VEC_X)
# Move it into position
pysage.translate(cutter, UNIT_VEC_X * 13.125)
# Boolean subtract, et voila: chevrons!
nacelle = pysage.subtract(cutter, nacelle)

And this is all just vanilla Python, which is widely used, and so most competent IDEs will provide auto-complete, thereby facilitating "API discovery" for the human user.

Walk the Walk

Now for some configurations that put these fancy claims to the test by necessitating a sequence of complex modeling operations.

Fowler Flap

Exhibit A is the title image of this article, a typical swept transport wing with a couple of interesting features. One is the split-scimitar winglet, which we will not be discussing here. The other is a deflected inboard Fowler flap. The wing was first modeled without flaps, and then the flap was cut out and moved appropriately. The picture below depicts slices that show that the cutout is not a trivial shape.

In this particular example, the flap sectional geometry is dependent on the wing sectional geometry. So, when the wing, which is modeled parametrically, is modified, the flap will automatically be modified appropriately. Naturally, parameters controlling the flap planform shape and cutout details are independent of the airfoil parametrization.

Excerpts from the flap creation script.

def make_cut(wing, y_slice, flap_chord_frac):
    b = slice_body_with_plane(wing, UNIT_VEC_Y, (0, y_slice, 0))
    ...
    airfoil = af.Airfoil(upper, lower, normalized=False)

    pu = airfoil.upper_point(flap_chord_frac)
    dpu = get_edge_start_tangent(split_edge(upper, pu)[1]) + UNIT_VEC_Z * 0.25 
    pl = airfoil.lower_point(flap_chord_frac)
    dpl = get_edge_start_tangent(split_edge(lower, pl)[1]) + UNIT_VEC_Z * 0.5

    flap_le_profile = bezier_ends_tangents_tensions(pl - UNIT_VEC_Z * 0.25, pu + UNIT_VEC_Z * 0.1, dpl, dpu, 2, 0.75)
    
    return flap_le_profile

...

with open('flap.json', 'r') as f:
    flap_params = json.load(f)

...

flap = intersect(cutter, wing, destructive=False)
wing = subtract(cutter, wing, destructive=False)

Nacelle and Chevrons

The next example is a turbofan nacelle with noise-reducing chevrons. To make things interesting, I emulated the 737's legs-too-short flattening of the bottom of the nacelle.

So what's the big deal?

  • The (longitudinal) sectional shapes at the top, bottom, and side of the nacelle are all different: this thing is not axisymmetric!
  • The fattening and flattening of the nacelle are defined separately and parametrically.
  • The highlight (the intake opening) is tilted to face slightly downwards.
  • The chevrons are all identical, and are defined parametrically.
  • The aft part of the nacelle in the vicinity of the chevrons is modeled realistically: as a legitimate solid representing a sheet with thickness. Consequently, the sheet thickness remains the same as you traverse the wavy trailing edge along the chevrons. See the highlights in the picture below.
  • The surfaces are curvature continuous everywhere except for the forward seam of the chevron sheet, which only has tangent continuity across it.
  • In case you were wondering, yes, it's a valid watertight triangulation shown in these pictures.

Excerpts from the scripts:

# Nacelle outer
nac = loft([highlight, mid_section_nac, nozzle_exit_nac],
           guides=[nac_edge_c, nac_edge_s, mirror(nac_edge_s, UNIT_VEC_Y), nac_edge_k])

# Perform shelling by subtracting a scaled version
cutter = box((params['sheetStartX'], -1000, -1000), (1000, 1000, 1000))
nac_inner = intersect(cutter, nac, destructive=False)
scale(nac_inner, 1, factor_y=0.995, factor_z=0.995)
aft_sheet = subtract(nac_inner, nac, destructive=False)

...

# Now we "array" these patterns in the azimuthal direction
chevron_cutters = []
for i in range(20):
    c = copy(chevron_cutter)
    rotate(c, UNIT_VEC_X, 18 * i)
    chevron_cutters.append(c)

# Combine all the chevron cutters into one tool
cutter = unite_entities(chevron_cutters)

# This thing needs to face the other way
cutter = mirror(cutter, UNIT_VEC_X)
# Move it into position
translate(cutter, UNIT_VEC_X * 13.125)
# Boolean subtract, et voila: chevrons!
nacelle = subtract(cutter, nacelle)
 
  

Wheel and Tire

This one's just for fun, and shows off some edge blends and fillets. The ACIS kernel revels in this sort of thing. Took us no more than a couple of hours to write the associated Python script written from scratch. Note that there's no way for the authors of a geometry tool to divine how the user might want to describe this wheel and tire, so there's essentially no useful a priori data structure for this model. Nevertheless, this geometry is really easy to create, given the right procedural building blocks.

Not that you'd ever want to run CFD directly on this thing, but for what it's worth, the picture above represents a valid watertight triangulation that we can throw directly at the Cart3D solver.

The Takeaway

Aerion's Python API for aerospace geometry sits atop the ACIS CAD kernel and enables sophisticated automated modeling by encoding solid modeling procedures as programming instructions in a script written in a friendly and familiar dialect. By providing functions for both general and domain-specific geometry modeling, it enables users to build up complexity by assembling fundamental building blocks in sophisticated ways.

When switching user to new programming flow it also means responsibility transfer, since user will take big part of responsibility for geometry correctness.  It may not always work perfectly, especially in the flow where user do not have education/understanding in underlying computational models.  So there should be clear who will pay for failure. Another question is who is going to prove that underlying CFD solver will support users geometry? That impose additional complexity for solver developers, they should become paranoid on the input geometry to not miss serious internal limitations. This paranoid view may affect architecture, test cycles, release schedules, and even use models requiring user to carefully revise warnings if any.  That will be additional price for that API to pay I think.

Like
Reply

while we can do complex tasks in less time and more efficient.. automating..we are doing away with human decision making at mandane tasks... its welcome and scary at the sametime !

Like
Reply

Flaps look like magic ... nice!

Like
Reply

To view or add a comment, sign in

More articles by Dev Rajnarayan

  • Full Stack

    As super-specialization becomes pervasive in all professions, our notions of what constitutes an all-round superstar…

    17 Comments
  • SAGE: on Final Approach

    At Aerion Technologies, we’ve been improving and testing SAGE, our Python-based CAD system, while designing the…

  • Got Blob?

    I recently published a short post on the new aerospace geometry tool we've developed at Aerion. I'd like to showcase a…

    2 Comments
  • Python + CAD for Aerospace Design

    We've developed a useful geometry modeling tool for simulation-based design at Aerion. It's a headless, scripted…

    13 Comments

Others also viewed

Explore content categories