4.1.8. Demo 8: Powered Rocket and Thrust Vectoring¶
This example explains how to use powered boundary conditions in Cart3D with pyCart along with a triangulation rotation and intersection as part of a thrust vector model. Primarily, this example seeks to introduce how to simulate rocket engines. In addition, component rotations and intersections are demonstrated, and a detailed report including a customized Tecplot layout are also included.
To get started, clone this repository and run the following easy commands:
$ git clone https://github.com/nasa-ddalle/pycart08-thrust.git $ cd pycart08-thrust $ ./copy-files.py $ cd work/
This will copy all of the files into a newly created work/
folder. Follow
the instructions below by entering that work/
folder; the purpose is that
you can easily delete the work/
folder and restart the tutorial at any
time.
To demonstrate these capabilities, we begin with the arrow example of previous examples as shown in Figure 4.17 and add a stand-alone engine geometry as shown in Figure 4.18.
These are two water-tight closed geometries so that they can be used with
intersect
. In their unmodified positions, the engine intersects the back
plane of the arrow and produces thrust aligned with the centerline of the
arrow. In this example, we add a pitch angle to the engine so that it is
rotated by an amount set in the run matrix, and this rotation is performed
prior to the intersection.
This example can be found in pycart08-thrust
, and as
always the pyCart.json
file in that folder is a good supplement to this
document. Figure 4.19 shows the main product of the
example.
4.1.8.1. Inputs and Run Matrix Description¶
Setting up variables to change the thrust of an example is usually contained
within the "RunMatrix"
section of the JSON file. The following
definitions are used for this example:
"RunMatrix": { "Keys": [ "mach", "alpha", "q", "T", "tilt", "CT", "config", "Label" ], "File": "matrix.csv", "GroupMesh": false, "Definitions": { "mach": { "Format": "%0.2f" }, "tilt": { "Type": "rotation", "Value": "float", "Group": false, "Center": [8.0, 0.0, 0.0], "Axis": [0.0, 1.0, 0.0], "CompID": [ "engine_mount", "noz_exterior", "noz_interior", "noz_bc" ], "Abbreviation": "_t", "Format": "%.1f" }, "CT": { "Type": "SurfCT", "Value": "float", "TotalTemperature": 8500.0, "AreaRatio": 4.0, "RefPressure": "freestream", "RefTemperature": "freestream", "RefDynamicPressure": "freestream", "CompID": "noz_bc", "Abbreviation": "T", "Format": "%.1f" } } }
In addition to our usual mach, alpha, config, and Label parameters that are part of the standard pyCart setup, we have added a few RunMatrix>Keys. The first two are dynamic pressure (q) and freestream static temperature (T). These are both recognized by pyCart as standard variables, and no descriptions are needed in the RunMatrix>Definitions section.
The next trajectory key is tilt, which is defined so that it pitches the
engine block from Figure 4.18 by an angle equal to the
value of this variable. The fact that this is a rotation is set in the Type
option within RunMatrix>Definitions>tilt. The center of rotation is set as
[8.0, 0.0, 0.0]
, which is the center of the back plane of the arrow. The
value of Axis makes this a pitch rotation. CompID is a list of components
that are rotated, which can be either strings or component numbers. This is a
pretty standard rotation, but users are advised that there are many more
rotation & translation options available.
The last key is CT, whose Type of "SurfCT"
tells pyCart that the value
of this key is used to set a surface boundary condition with the intent of
setting a nozzle to attain a desired thrust. The "SurfCT"
and "SurfBC"
types are both targeted at powered boundary conditions, but "SurfBC"
generally targets a desired stagnation pressure while "SurfCT"
targets a
desired thrust.
We should also take this opportunity to discuss the effects of including q
and T. Normally, since Cart3D is an inviscid solver, these dimensional
parameters have no effect at all, and the results are truly nondimensional.
However, introducing an engine partially breaks this symmetry to freestream
conditions. For one thing, a rocket producing an amount of thrust in pounds
will have a different thrust coefficient depending on the freestream dynamic
pressure. Similarly, a particular temperature at the rocket boundary has
different normalized temperatures for different freestream temperatures. While
it is possible in pyCart to use a "SurfCT"
key without q and T, this is
unlikely to be a physically relevant setup.
Going back to the JSON settings for CT, we see a TotalTemperature of
8500.0, which sets the stagnation temperature at the boundary condition plane
to a constant temperature in degrees Rankine. If we wanted to set the
TotalTemperature relative to the freestream temperature instead of a fixed
dimensional value, we would set RefTemperature to 1.0
instead of its
"freestream"
value. It is also possible to use the value of another
variable to change the stagnation temperature from case to case by setting the
value of TotalTemperature to the name of another trajectory key. See the
following example for how this could work.
"CT": { "Type": "SurfCT", "Value": "float", "TotalTemperature": "T0", ... }, "T0": { "Type": "value", "Value": "float" }
We have also set AreaRatio here; for Cart3D thrust setup we usually need this parameter for Cart3D’s internal calculation of anticipated thrust. It is typically recommended to set the boundary condition on a plane where the Mach number is 1.0 in Cart3D, but the Mach number on the plane can be set to a different value using Mach within the Definitions>CT. pyCart then uses this information to calculate the static pressure and density at the boundary condition plane that should give the corresponding thrust.
While pyCart automatically calculates the surface normal of that plane (since the velocity has to be set on that plane including its three components), this simplified thrust calculation is not perfect. In order to get the correct thrust, there is also a PressureCalibration option that can be used to linearly scale the surface pressure.
4.1.8.2. Intersection Process¶
Intersecting closed volumes that each have multiple component IDs marked is a
nontrivial process. Because intersect
is expecting an input triangulation
in which each component is a water-tight surface with one component, pyCart has
to do some extra preprocessing and postprocessing steps. To get things to work
properly, we use two separate tri
files and set the following settings in
the JSON.
"Mesh": { // Surface triangulation "TriFile": ["arrow.tri", "engine.tri"], // Extra refinements "XLev": [ {"n": 2, "compID": "noz_bc"}, {"n": 1, "compID": "noz_interior"} ], // Extra bounding boxes for adaptation regions "BBox": [ {"n": 8, "compID": "noz_exterior", "xp": 2.5} ] }
The key parameter here is that Mesh>TriFile is a list of two files. As a result, pyCart assumes that each individual file is a single closed volume. The XLev descriptions specify additional refinements on any cut cells that intersect specified components, while BBox gives rectangular prisms in which to make a specified number n of refinements.
Figure 4.20 shows the original surface triangulation after
rotations but before performing the intersection operation. It contains the
same component breakdown as the original input files and is labeled
Components.c.tri
in the folder. pyCart also writes the file
Components.tri
which contains the same nodes and triangles but only has
two components, and a visualization is shown in Figure 4.21.
Then a call is made to Cart3D’s intersect
tool such that the input is
Components.tri
, and the output is Components.o.tri
, which is
shown in Figure 4.22.
In order to get the original components requested by the user, pyCart then
performs an additional step of remapping the component IDs to create
Components.i.tri
, shown in Figure 4.23. Each
triangle has the component ID copied from the closest triangle of
Components.c.tri
.
4.1.8.3. Results and Report Generation¶
The run matrix in pycart08-thrust/matrix.csv
has only one
case, which has a Mach number of 1.5, an angle of attack of 2 degrees. The
engine is pitched downward 4.5 degrees and a thrust coefficient of 8.5. A
status while running the case would look something like the following.
$ pycart -c Case Config/Run Directory Status Iterations Que CPU Time ---- -------------------------- ------- ----------- --- -------- 0 poweron/m1.50a2.0_t4.5T8.5 RUN 50/700 . 452.9 RUN=1,
Figure 4.24 shows a flow visualization of this case
that is generated using the "slice-y0-mesh"
subfigure from
pyCart.json
. (The results of the "slice-y0"
subfigure is shown in
Figure 4.19.) These figures show some of the more
advanced procedures from customizing a Tecplot layout.
The process for this example begins with opening the output flow visualization
files created by Cart3D: Components.i.plt
and cutPlanes.plt
.
Actually those files are in the adapt03/
folder in this case, but pyCart
automatically creates symbolic links to the most recent plt
files.
Then, after opening those files, the user should create the desired image and
save it as a layout. A hidden step necessary for this example is that the user
has to customize the color map for the Mach slice. Since layout files do not
have CREATECOLORMAP
commands for built-in color maps, there is no color map
in the layout file to edit. It may be possible without this step, but this
documents one known process. Simply enter the contour details dialouge in
Tecplot and change one of the colors or slide one of the handles in the color
map interface. This needs to be performed for both color maps since we are
using separate contours on the surface and the slice.
The JSON description for the two flow visualization subfigures is shown below:
"TecBase": { "Type": "Tecplot", "FigWidth": 1024, "Width": 0.48, "Caption": "Surface $C_p$ and $y{=}0$ Mach slice", "ContourLevels": [ { "NContour": 1, "MinLevel": -0.4, "MaxLevel": 1.2, "Delta": 0.1 }, { "NContour": 2, "MinLevel": 0.0, "MaxLevel": 4.0, "Delta": 0.1 } ], "ColorMaps": [ { "NContour": 1, "ColorMap": { "-0.4": "blue", "0.0": "white", "1.2": "red" } }, { "NContour": 2, "Constraints": ["mach > 1.25"], "ColorMap": { "0.0": "darkpurple", "1.0": ["#b55fbf", "green"], "$mach": "white", "4.0": "darkorange" } } ] }, // With mesh "slice-y0-mach": { "Type": "TecBase", "Layout": "slice-y0-mach.lay" }, "slice-y0-mach-mesh": { "Type": "TecBase", "Layout": "slice-y0-mach-mesh.lay" }
The two subfigures share most of their options, so they cascade from a common
subfigure called "TecBase"
. Only the name of the layout file is changed.
However, the two layouts are very similar; we could use the following alternate
definition.
"slice-y0-mach-mesh": { "Type": "TecBase", "Layout": "slice-y0-mach.lay", "Keys": { "FIELDLAYERS": { "SHOWMESH": "YES" } }
The minimum and maximum values for the two contour maps are set in the ContourLevels section. Of course, these fixed values could have just been set within Tecplot, but this allows for min and max values to depend on the trajectory keys.
To see how this works, see the more complex ColorMaps section. Here we set
the surface pressure map so that "blue"
is at the minimum pressure of
"-0.4"
, white is at Cp=0, and the maximum value is red. This simplifies
the process of getting white to lie on Cp=0 with an asymmetric range of
values.
The color map for the Mach slice is more complicated. Here we have set
"darkpurple"
at Mach 0, a lighter purple of "#b55fbf"
on the lower side
of Mach 1, "green"
on the upper side of Mach 1. This list of two colors at
Mach 1 leads to a sharp purple/green divide at the sonic line. Then we set
"white"
as the color for "$mach"
; the $
tells pyCart to replace
this with the value of the trajectory key mach for this color. Finally, we
use "darkorange"
for top of the color map.
The result is a very informative color map that clearly identifies subsonic
flow, low supersonic flow, the freestream Mach condition, and high supersonic
flow. Furthermore, this color map setup, by setting "$mach": "white"
, it
applies to a range of conditions. The color map shown above could lead to
problems if the Mach number is lower than about 1.2, so the actual JSON file
contains three different color map specifications. Which one gets applied is
determined by the Constraints key, which is visible in the code snippet show
above.