Geometry User Guide
Geometry User Guide
Abstract
Acknowledgement
This document was prepared while PAJ was on Special Studies Program at Oxford
University.
Contents
1 Introduction 1
1.1 Some advice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2 Geometric elements 3
2.1 Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.2 Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2.1 Vector3 objects or Lua tables . . . . . . . . . . . . . . . . . . . . . 7
2.2.2 Simple Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2.3 User-defined-function path . . . . . . . . . . . . . . . . . . . . . 8
2.2.4 Compound Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.2.5 Derived Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.3 Surfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.3.1 Paths on Surfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.4 Volumes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.5 Manipulating elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.5.1 Parametric geometry . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.5.2 The eval function . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.5.3 Subdividing a path . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.5.4 Creating a line normal to a path . . . . . . . . . . . . . . . . . . . 32
3 Grids 35
3.1 Making a simple 2D grid . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.2 StructuredGrid Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.2.1 StructuredGrid Methods . . . . . . . . . . . . . . . . . . . . . . . 39
3.2.2 Importing a Gridpro Grid . . . . . . . . . . . . . . . . . . . . . . 40
3.3 UnstructuredGrid Class . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3.4 Building a multiblock grid . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3.4.1 Improving the grid with clustering . . . . . . . . . . . . . . . . . 42
References 45
iii
1
Introduction
The geometry package supports the construction of geometric elements such as sur-
face patches and volumes within the Eilmer flow simulation program[1]. The flow
solver expects the gas-flow and solid domains to be specified as meshes of finite-
volume cells. You may prepare these meshes in your favourite grid generation pro-
gram and then import them into your simulation or you may prepare a description
of the domain using the elements described in this report and then discretize it using
one of the grid generators that are also included in the geometry package. The proce-
dures described here are most convenient for meshing relatively simple domains but,
with sufficient effort, can be applied to arbitrarily complex situations. They have the
advantage that your simulation description is completely self-contained and you will
not be dependent on external programs to get your simulation going. This report is a
companion to the user’s guide for the overall simulation program[2].
For structured meshes, the top-level geometric elements that are given to the grid
generator are “patches” for 2D flow and “parametric volumes” for 3D flow. These are
regions of space that may be traversed by a set of parametric coordinates 0 ≤ r < 1,
0 ≤ s < 1 in 2D and with the third parameter 0 ≤ t < 1 in 3D. These patches or
volumes can be imported as VTK structured grids or they can be constructed as a
“boundary representation” from lower-dimensional geometric entities such as paths
and points.
For unstructured meshes1 , the boundary representation is provided to the grid
generator as a set of discretized boundary edges in 2D or as a surface mesh in 3D. It is
also possible to construct an unstructured mesh from a structured mesh or to import
the unstructured mesh in VTK or SU2 format.
1
2 Chapter 1. Introduction
1. Start with a rough sketch of your flow domain on paper, labelling key features.
2. Start small, building a script to describe a very simple element from your full
domain.
4. View this artifact to see that it is what you wanted, debugging as required.
We believe that this procedure will result in a far more satisfactory experience than
coding your entire description in one pass. You might be lucky, but chances are that
your mistakes will overpower your luck.
2
Geometric elements
The end goal of working with elements from the geometry package is to construct a
representation of the flow domain that can be passed to the flow solver. For a two-
dimensional flow simulation, this domain will be defined as one or more patches
in the x,y-plane. Within the program code, these patches are represented as sur-
face objects which are topologically quadrilateral and have boundary edges labelled
north, east, south and west. The patches will be body-fitted, meaning that the do-
main boundaries take on the shape one or more bodies that contain the gas flow. For
a three-dimensional flow simulation, the gas-flow domain will be described using
body-fitted volume objects. These volumes and surfaces are constructed from lower-
dimensional geometric objects, specifically points and paths.
2.1 Points
The most fundamental class of geometric object is the Vector3 which represents
a point in 3D space and has the usual behaviour of a geometric vector. The code
for creating and manipulating these objects is in the Vector3 class of the underlying
D-language module. This D code has been wrapped in such a way that the class
methods are accessible from your Lua script.
In your Lua script, you might construct a new Vector3 object as:
Vector3:new{x=0.1, y=0.2, z=0.3}
When building models of 2D regions, you can omit the z-component value and it will
default to zero. Note that we are using the object-oriented convention described in
the Programming in Lua book [3]. In the Lua environment that has been set up by our
program, Vector3 is a table and new is a function within that table. In this particular
case, we will think of the new function as our constructor function for Vector3 ob-
jects. The use of the colon rather than a dot before the word new tells the interpreter
that we want to include the object itself in the arguments passed to the called func-
tion. Finally, note the use of braces to construct a table of parameters that are given
to the function. In the binding code for the new function, we are expecting all of the
arguments to be passed on the stack to be contained in a single table. When construct-
ing other objects within the input script, we have usually chosen a notation that has
all of the elements as named attributes in a single table. We believe that this provides
some benefit by making the order of arguments not significant. It also makes your
script a little more self-documenting at the cost of being a bit more verbose.
3
4 Chapter 2. Geometric elements
1 -- example-1.lua
2
3 a = Vector3:new{x=0.5, y=0.8}
4 x_value = a.x
5 a.y = 0.4
6 b = Vector3:new{x=a.x+0.4, y=a.y+0.2}
7 print("a=", a, "b=", b)
8 print("x_value=", x_value)
9
10 dofile("sketch-example-1.lua")
On line 3, we have constructed a point and bound it to the name a. Lines 4 and 5
show examples of getting and setting components and line 6 constructs a new point
from expressions involving point a. Line 10 calls up another file to make an SVG
rendering of the points. We will discuss the content of that file in a later section. It is
somewhat detailed and unimportant for the moment.
The transcript of running this script through the custom-postprocessing mode of
the Eilmer4 program is:
and the resulting points are displayed graphically in Figure 2.1. Note the output from
the print function calls on lines 6 and 7 of the transcript. The format of the dis-
played Vector3 objects is that of the underlying D-language module rather than the
Lua construction format used on line 3 of the input script. Internally, the coordinates
are stored in fixed-size array, hence the printed format as list of coordinate values
delimited by square brackets.
If you look into the file dgd/src/geom/geom.d, you will see that the Vector3
objects support the usual vector operations of addition, subtraction and the like. Also,
you can construct one Vector3 object from another and there are a small number of
built-in transformations that might be useful for programmatically constructing your
flow domain. For example, to create a point and its mirror image in the (y,z)-plane
going through x=0.5, you could use:
1 -- example-2.lua
2
3 a = Vector3:new{x=0.2, y=0.5}
2.1. Points 5
1.0
0.8
0.6 b
0.4 a
0.2
0.0
0.0 0.2 0.4 0.6 0.8 1.0
x
Figure 2.1: A couple of points defined as Vector3 objects and rendered to SVG.
4 b = Vector3:new{a}
5
6 mi_point = Vector3:new{x=0.5} -- mirror-image plane through this point
7 mi_normal = Vector3:new{x=1.0} -- normal of the mirror-image plane
8 b:mirrorImage(mi_point, mi_normal)
9
10 print("a=", a, "b=", b)
11 print("abs(b)=", b:abs())
12
13 c = a + b
14 print("c=", c)
Note that, on line 8 of the example script, the mirrorImage method call passes
the defining points for the mirror-image plane as unnamed but ordered arguments.
These arguments are delimited by parentheses rather than braces, as would have
been used for building a table. Note also, on line 11 of the script, that the method
call for abs uses a pair of empty parentheses to make the method call with no argu-
6 Chapter 2. Geometric elements
ments. Finally, on line 6 of the transcript, note the round-off error appearing in the
z-component of point b. This error is propagated into point c.
A list of operators and methods provided to the Lua environment is given in Ta-
ble 2.1.
Table 2.1: Operators and methods for Vector3 objects. In the table, Vector3 quanti-
ties are indicated as ⃗a. Scalar quantities are double (or 64-bit) floating-point values.
Some of the methods also appear as unbound functions in the global name space.
2.2 Paths
The next level of dimensionality is the Path class. A path object is a parametric curve
in space, along which points can be specified via the single parameter 0 ≤ t < 1. Path
is a base class in the D-language domain and a number of derived types of paths are
available. Evaluating a path for a parameter value t results in the corresponding
Vector3 value. A path may also be copied or printed as a string.
a = {x=0.2, y=0.3}
The Lua table bound to a may now be supplied where ever a Point is expected.
• Bezier:new{points={⃗b0 , ⃗b1 , ..., ⃗bn }}: a Bezier curve of order n − 1 from ⃗b0
to ⃗bn . You can specify as many points as you like for the curve, however, 4
points for a third-order curve is very commonly used. A convenient feature
of the Bezier curve is that you get direct control of the end points and the tan-
gents at those end points. Note that the table of points is labelled and contained
within the single table passed to the constructor. The individual points are not
labelled and, although the subscripts shown start from zero (to be consistent
with the representation within the core D-language code), the default number-
ing of items in a Lua table starts at 1. We have side-stepped the issue here by
providing a literal construction of the points table, however, there are times
when you need to account for this difference in indexing.
1 -- path-example-1.lua
2
3 -- An arc with a specified pair of end-points and a centre.
4 c = Vector3:new{x=0.4, y=0.1}
5 radius = 0.2
6 a0 = Vector3:new{x=c.x-radius, y=c.y}
7 a1 = Vector3:new{x=c.x, y=c.y+radius}
8 my_arc = Arc:new{p0=a0, p1=a1, centre=c}
9
10 -- A Bezier curve of order 3.
11 b0 = Vector3:new{x=0.1, y=0.1}
12 b3 = Vector3:new{x=0.4, y=0.7}
13 b1 = b0 + Vector3:new{y=0.2}
14 b2 = b3 - Vector3:new{x=0.2}
15 my_bez = Bezier:new{points={b0, b1, b2, b3}}
16
17 -- A line between the Bezier and the arc.
18 my_line = Line:new{p0=b0, p1=a0}
19
20 -- Put an arc through three points.
21 a2 = Vector3:new{x=0.8, y=0.2}
22 a3 = Vector3:new{x=1.0, y=0.3}
23 my_other_arc = Arc3:new{p0=a1, pmid=a2, p1=a3}
24
25 dofile("sketch-path-example-1.lua")
1.0
0.8
b2 b3
0.6
0.4
b1 a1 a3
0.2 a2
c
b0 a0
0.0
0.0 0.2 0.4 0.6 0.8 1.0
x
Figure 2.2: Some simple paths defined from Vector3 objects and rendered to SVG.
definition extracted from the sharp-body example for the simulation code, shows
how to do this for a two-dimensional case. On line 23, a LuaFnPath is constructed
by supplying the name of the Lua function as a string. The function xypath, defined
starting on line 13, accepts values of the parameter, t, (in the range 0.0 to 1.0) and
returns a table with x, y (and maybe z) named components that represent the point in
space for parameter value t. The function y(x), defined starting on line 4, is a helper
function that is used to make the script look more like the formula in the textbook [4]
that originally described the exercise. It is not necessary to split up the functions in
this manner. Figure 2.3 shows a rendering of the path.
1 -- path-example-2.lua
2 -- Extracted from the 2D/sharp-body example.
3
4 function y(x)
5 -- (x,y)-space path for x>=0
6 if x <= 3.291 then
7 return -0.008333 + 0.609425*x - 0.092593*x*x
8 else
9 return 1.0
10 end
11 end
12
13 function xypath(t)
14 -- Parametric path with 0<=t<=1.
15 local x = 10.0 * t
16 local yval = y(x)
17 if yval < 0.0 then
18 yval = 0.0
19 end
20 return {x=x, y=yval}
21 end
22
23 my_path = LuaFnPath:new{luaFnName="xypath"}
24
25 dofile("sketch-path-example-2.lua")
10 Chapter 2. Geometric elements
10.0
8.0
6.0
4.0
2.0
0.0
0.0 2.0 4.0 6.0 8.0 10.0
x
Figure 2.3: A path defined via a Lua function and rendered to SVG. This particular
path is used to represent the profile of the body in the 2D/sharp-body simulation
exercise for Eilmer4.
2.2. Paths 11
• Spline:new{points={⃗b0 , ⃗b1 , ..., ⃗bn }}: a cubic spline from ⃗b0 through ⃗b1 , to ⃗bn .
A Spline is actually a specialized Polyline containing n − 1 cubic Bezier
segments.
• Spline2:new{filename="something.dat"}: a spline constructed from a file
containing x y z coordinates of the interpolation points, one point per line. The
coordinate values are expected to be space separated. If the y or z values are
missing, they are assumed to be zero.
The following script builds a combined path object that looks similar to the first ex-
ample:
1 -- path-example-3.lua
2
3 -- An arc with a specified pair of end-points and a centre.
4 c = Vector3:new{x=0.4, y=0.1}
5 radius = 0.2
6 a0 = Vector3:new{x=c.x-radius, y=c.y}
7 a1 = Vector3:new{x=c.x, y=c.y+radius}
8 my_arc = Arc:new{p0=a0, p1=a1, centre=c}
9
10 -- A Bezier curve of order 3.
11 b3 = Vector3:new{x=0.1, y=0.1}
12 b0 = Vector3:new{x=0.4, y=0.7}
13 b1 = b0 - Vector3:new{x=0.2}
14 b2 = b3 + Vector3:new{y=0.2}
15 my_bez = Bezier:new{points={b0, b1, b2, b3}}
16
17 -- A line between the Bezier and the arc.
18 my_line = Line:new{p0=b3, p1=a0}
19
20 -- Put an arc through three points.
21 a2 = Vector3:new{x=0.8, y=0.2}
22 a3 = Vector3:new{x=1.0, y=0.3}
23 my_other_arc = Arc3:new{p0=a1, pmid=a2, p1=a3}
24
25 one_big_path = Polyline:new{segments={my_bez, my_line, my_arc,
26 my_other_arc}}
27
28 dofile("sketch-path-example-3.lua")
Note, that to make a single path that can be traversed, we needed to define the Bezier
curve using points in the opposite order to the earlier example.
12 Chapter 2. Geometric elements
1.0
0.8
b1 b0
0.6
0.4
b2 a1 a3
0.2 a2
b3 a0 c
0.0
0.0 0.2 0.4 0.6 0.8 1.0
x
Figure 2.4: A single compound path defined from a set of path objects and rendered
to SVG.
If we take the same data points as used to construct the Polyline, we can see the
difference it make to use a Spline constructor in Figure 2.5. Sometimes, it is just what
you want, and other times, not so.
1 -- path-example-4.lua
2 -- A single spline through the same data points.
3 c = Vector3:new{x=0.4, y=0.1}
4 radius = 0.2
5 a0 = Vector3:new{x=c.x-radius, y=c.y}
6 a1 = Vector3:new{x=c.x, y=c.y+radius}
7
8 b3 = Vector3:new{x=0.1, y=0.1}
9 b0 = Vector3:new{x=0.4, y=0.7}
10 b1 = b0 - Vector3:new{x=0.2}
11 b2 = b3 + Vector3:new{y=0.2}
12
13 a2 = Vector3:new{x=0.8, y=0.2}
14 a3 = Vector3:new{x=1.0, y=0.3}
15
16 my_spline = Spline:new{points={b0,b1,b2,b3,a0,a1,a2,a3}}
17
18 dofile("sketch-path-example-4.lua")
2.2. Paths 13
1.0
0.8
b1 b0
0.6
0.4
b2 a1 a3
0.2 a2
c
b3 a0
0.0
0.0 0.2 0.4 0.6 0.8 1.0
x
Figure 2.5: A Spline path defined on the same set of points that we used for the
Polyline.
14 Chapter 2. Geometric elements
Here are some examples of building derived paths, with the results shown in Fig-
ure 2.6:
1 -- path-example-5.lua
2 -- Transformed Paths
3
4 b0 = Vector3:new{x=0.1, y=0.1}
5 b1 = b0 + Vector3:new{x=0.0, y=0.05}
6 b2 = b1 + Vector3:new{x=0.05, y=0.1}
7 b3 = Vector3:new{x=0.8, y=0.1}
8 path0 = Bezier:new{points={b0,b1,b2,b3}}
9
10 path1 = TranslatedPath:new{original_path=path0, shift=Vector3:new{y=0.5}}
11 path1b = MirrorImagePath:new{original_path=path1,
12 point=Vector3:new{x=0.5, y=0.6},
13 normal=Vector3:new{y=1.0}}
14
15 path2 = TranslatedPath:new{original_path=path0, shift=Vector3:new{y=0.5}}
16 path2b = ArcLengthParameterizedPath:new{underlying_path=path2}
17
18 dofile("sketch-path-example-5.lua")
2.2. Paths 15
1.0
0.8
0.6
0.4
b2
0.2
b1
b3
b0
0.0
0.0 0.2 0.4 0.6 0.8 1.0
x
Figure 2.6: The original Bezier curve defined with points b0, b1, b2 and b3 in the
lower part of the figure. A pair of derived paths is shown above it. The dotted points
on all of the paths indicate equal increments in parameter t. Note the more uniform
distribution in x,y-space for the ArcLengthParameterizedPath.
16 Chapter 2. Geometric elements
2.3 Surfaces
The ParametricSurface class represents two-dimensional objects which can be
constructed from Path objects. These can be used as the ParametricSurface ob-
jects that are passed to the StructuredGrid constructor (Sec. 3.2) or they can be
used to form the bounding surfaces of a 3D ParametricVolume object (Sec. 2.4).
ParametricSurface objects can be evaluated as functions of parameter pair (r, s) to
yield corresponding Vector3 values, representing a point in modelling space. Here
0 ≤ r ≤ 1 and 0 ≤ s ≤ 1.
Examples of the most commonly used surface patches are:
starts as CoonsPatch mesh and each iteration should just improve the metrics
of orthogonality and distribution of cell areas.
1 -- surface-example-1.lua
2
3 -- Transfinite interpolated surface
4 a = Vector3:new{x=0.1, y=0.1}; b = Vector3:new{x=0.4, y=0.3}
5 c = Vector3:new{x=0.4, y=0.8}; d = Vector3:new{x=0.1, y=0.8}
6 surf_tfi = CoonsPatch:new{north=Line:new{p0=d, p1=c},
7 south=Line:new{p0=a, p1=b},
8 west=Line:new{p0=a, p1=d},
9 east=Line:new{p0=b, p1=c}}
10
11 -- Knupp’s area-orthogonality surface
12 xshift = Vector3:new{x=0.5}
13 p00 = a + xshift; p10 = b + xshift;
14 p11 = c + xshift; p01 = d + xshift;
15 surf_ao = AOPatch:new{p00=p00, p10=p10, p11=p11, p01=p01}
16
17 dofile("sketch-surface-example-1.lua")
1.0
0.6
0.4
b p10
0.2
a p00
0.0
0.0 0.2 0.4 0.6 0.8 1.0
x
1 -- surface-example-2.lua
2
3 path1 = Arc3:new{p0=Vector3:new{x=0.2, y=0.3},
4 pmid=Vector3:new{x=0.5, y=0.25},
5 p1=Vector3:new{x=0.8, y=0.35}}
6 p = Vector3:new{y=0.2}; n = Vector3:new{y=1.0}
7 path2 = MirrorImagePath:new{original_path=path1,
8 point=p, normal=n}
9 channel = ChannelPatch:new{north=path1, south=path2,
10 ruled=false, pure2D=true}
11 bpath0 = channel:make_bridging_path(0.0)
12 bpath1 = channel:make_bridging_path(1.0)
13
14 yshift = Vector3:new{y=0.5}
15 path3 = TranslatedPath:new{original_path=path1, shift=yshift}
16 path4 = TranslatedPath:new{original_path=path2, shift=yshift}
17 channel_ruled = ChannelPatch:new{north=path3, south=path4,
18 ruled=true, pure2D=true}
19
20 dofile("sketch-surface-example-2.lua")
1.0
0.8
0.6
0.4
0.2
0.0
0.0 0.2 0.4 0.6 0.8 1.0
x
Figure 2.8: Example of ChannelPatch objects. The south-to-north lines drawn over
the patches are for constant values or r and the west-to-east lines are for constant
values of s. The lower patch uses the (default) Bezier curves bridging the north and
south defining paths. The south and north paths are rendered as thick black lines
and the r = 0 and r = 1 bridging paths are shown in blue. The upper example uses
straight-line bridging paths.
20 Chapter 2. Geometric elements
The following script shows the construction of a SweptPathPatch, where the south
path anchors the surface and the west path is swept across it. Note, in Figure 2.9, that
the path constructed for the west path is not actually part of the final surface. The
LuaFnSurface example maps the unit square in (r,s)-space to the region between two
circles in the (x,y)-plane.
1 -- surface-example-3.lua
2
3 path1 = Arc3:new{p0=Vector3:new{x=0.2, y=0.7},
4 pmid=Vector3:new{x=0.5, y=0.65},
5 p1=Vector3:new{x=0.8, y=0.75}}
6 path2 = Line:new{p0=Vector3:new{x=0.1, y=0.7},
7 p1=Vector3:new{x=0.1, y=0.9}}
8 swept = SweptPathPatch:new{south=path1, west=path2}
9
10 function myFun(r, s)
11 -- Map unit square to the region between two circles
12 local theta = math.pi * 2 * r
13 local bigR = 0.05*(1-s) + 0.2*s
14 local x0 = 0.5; local y0 = 0.3
15 return {x=x0+bigR*math.cos(theta), y=y0+bigR*math.sin(theta), z=0.0}
16 end
17 funSurf = LuaFnSurface:new{luaFnName="myFun"}
18
19 dofile("sketch-surface-example-3.lua")
1.0
0.8
0.6
0.4
0.2
0.0
0.0 0.2 0.4 0.6 0.8 1.0
x
1 -- surface-example-4.lua
2 -- BezierPatch in 3D
3 n = 4
4 m = 3
5 Q = {}
6 for i=1,n do Q[i] = {} end
7 Q[1][1] = Vector3:new{x=0.0, y=3.0, z=0.0}
8 Q[1][2] = Vector3:new{x=0.0, y=3.0, z=2.0}
9 Q[1][3] = Vector3:new{x=0.0, y=3.0, z=4.0}
10 Q[2][1] = Vector3:new{x=3.5, y=1.0, z=-1.0}
11 Q[2][2] = Vector3:new{x=5.0, y=2.5, z=2.5}
12 Q[2][3] = Vector3:new{x=5.5, y=4.0, z=6.0}
13 Q[3][1] = Vector3:new{x=8.0, y=4.0, z=1.0}
14 Q[3][2] = Vector3:new{x=8.0, y=1.5, z=4.0}
15 Q[3][3] = Vector3:new{x=8.5, y=2.0, z=7.0}
16 Q[4][1] = Vector3:new{x=10.0, y=1.0, z=-2.0}
17 Q[4][2] = Vector3:new{x=11.0, y=2.0, z=2.0}
18 Q[4][3] = Vector3:new{x=11.0, y=3.0, z=6.0}
19 bezPatch = BezierPatch:new{points=Q}
20
21 dofile("sketch-surface-example-4.lua")
6.0
y
4.0
Q[1][1]
Q[1][2] 2.0
Q[1][3] 0.0
Q[2][1]
2.0 Q[3][1]
0.0
Q[2][2]
4.0 2.0
6.0 Q[2][3] 4.0 Q[4][1]
z 6.0
Q[3][2]
8.0 Q[4][2]
10.0 x
Q[3][3] Q[4][3]
12.0
Figure 2.10: Example of a BezierPatch (green) with control points and control net
(red).
22 Chapter 2. Geometric elements
2.4 Volumes
Working in three dimensions significantly increases the amount of detail needed to
specify a flow domain. In 2D, a simple region may be represented as a single surface
patch with 4 bounding surfaces but, in 3D, the simple volume is now bounded by
6 surfaces, each with 4 edges. With an edge being shared by two surfaces, there
will be 12 distinct edges on the volume. The ParametricVolume object, as shown
in Figure 2.11 maps r,s,t parametric coordinates to x,y,z physical-space coordinates
within the volume. To assist in understanding the orientation of the corners, surfaces
and indices, you can build a model block from the development plan in Appendix A.
This should bring back fond memories of kindergarten and primary school, at least it
did for us.
p7 p4
p4 p6 p5 p7
p5 p6
west north south west
t
t
p3 p0
r s
p0 s bottom p1 bottom
p2 p3
r
p1 p2
Figure 2.11: Two views of the parametric volume defined by its six bounding sur-
faces. These figures are ambiguous but each is supposed to show a hollow box with
the far surfaces in each view being labelled. The near surfaces are transparent and un-
labelled. On the left view, the p1 –p5 edge is closest to the viewer. On the right view,
the p2 –p6 edge is closest to the viewer.
calculation is actually done as path(t) + surf (r, s) − surf (0, 0) such that the path
effectively anchors the volume in x,y,z-space.
function myLuaFunction(r, s, t)
return {x=r, y=s, z=t}
end
Just about the simplest ParametricVolume that can be defined is the regular hexa-
hedral block defined by its vertices. Although the example is simple, there is the same
subtle indexing issue to consider, as we have seen for the construction of Bezier paths
(see page 7). On line 21 of the script, the table of vertices is assembled with number-
ing of the points being implicit. Be aware that, by default, Lua will start numbering
the table items from 1. This is a continuing point of friction with the core program
written in the D programming, where array indices start at zero, so be careful if you
explicitly number the vertices. The Lua interface code for the constructor is expecting
to receive the points with default Lua numbering, even though we have tried to con-
sistently use D-language indexing in our documentation. In this example, we hide
the indexing issue by using a literal expression for the table.
1 -- volume-example-1.lua
2 -- Transfinite interpolated volume
3
4 Lx = 0.2; Ly = 0.1; Lz = 0.5 -- size of box
5 x0 = 0.3; y0 = 0.1; z0 = 0.0 -- location of vertex p000/p0
6
7 -- There is a standard order for the 8 vertices that define
8 -- a volume with straight edges.
9 p000 = Vector3:new{x=x0, y=y0, z=z0} -- p0
10 p100 = Vector3:new{x=x0+Lx, y=y0, z=z0} -- p1
11 p110 = Vector3:new{x=x0+Lx, y=y0+Ly, z=z0} -- p2
12 p010 = Vector3:new{x=x0, y=y0+Ly, z=z0} -- p3
13 p001 = Vector3:new{x=x0, y=y0, z=z0+Lz} -- p4
14 p101 = Vector3:new{x=x0+Lx, y=y0, z=z0+Lz} -- p5
15 p111 = Vector3:new{x=x0+Lx, y=y0+Ly, z=z0+Lz} -- p6
16 p011 = Vector3:new{x=x0, y=y0+Ly, z=z0+Lz} -- p7
2.4. Volumes 25
17
18 -- Assemble a table literal to avoid the issue
19 -- of numeric indices starting at 1 in Lua code
20 -- whilst starting at 0 in D code.
21 vList = {p000, p100, p110, p010, p001, p101, p111, p011}
22
23 my_vol = TFIVolume:new{vertices=vList}
24
25 dofile("sketch-volume-example-1.lua")
Let’s build the same box volume with a Lua function that is defined within the
input script. The function must return a single table with the x, y and z coordinates
labelled as such. The script is actually shorter than the previous example but the
computational machinery that calls back into the Lua interpreter to get the coordinate
values has a cost. For complex geometries with large grids, this might make the
preparation stage run slowly. For simple geometries, this cost is not an issue and your
time preparing scripts is much more valuable, so you should choose the approach that
you find convenient.
1 -- volume-example-2.lua
2 -- Volume defined on a Lua function.
3
4 Lx = 0.2; Ly = 0.1; Lz = 0.5 -- size of box
5 x0 = 0.3; y0 = 0.1; z0 = 0.0 -- location of vertex p000/p0
6
7 function myLuaFn(r, s, t)
8 return {x=x0+Lx*r, y=y0+Ly*s, z=z0+Lz*t}
9 end
10
11 my_vol=LuaFnVolume:new{luaFnName="myLuaFn"}
12
13 dofile("sketch-volume-example-2.lua")
26 Chapter 2. Geometric elements
y y
0.4 0.4
0.2 0.2
p3
0.0 0.0
p0 p2
0.2 0.0 0.2 0.0
p1
0.4 0.2 0.4 0.2
p7
z 0.4 x z 0.4 x
p4 p6
p5
Figure 2.12: Isometric views of a regular hexahedral volume. The left view shows the
volume defined by defined by its vertices. The right view shows the same volume
defined by a Lua function.
2.4. Volumes 27
A very commonly studied 3D configuration is the cylinder with cross flow. The
following script constructs a flow volume around part of a cylindrical surface. We
first construct the (future) bottom surface in the z=0 plane and then sweep out the
volume by following the line b0-d parallel to the z-axis.
1 -- volume-example-3.lua
2 -- Parametric volume construced by sweeping a surface..
3
4 L = 0.4 -- cylinder length
5 R = 0.2 -- cylinder radius
6
7 -- Construct the arc along the edge of the cylinder
8 a0 = Vector3:new{x=R, y=0}; a1 = Vector3:new{x=0, y=R}
9 c = Vector3:new{x=0, y=0}
10 arc0 = Arc:new{p0=a0, p1=a1, centre=c}
11
12 -- Use a Bezier curve for the edge of the outer surface.
13 b0 = Vector3:new{x=1.5*R, y=0}; b1 = Vector3:new{x=b0.x, y=R}
14 b3 = Vector3:new{x=0, y=2.5*R}; b2 = Vector3:new{x=R, y=b3.y-R/2}
15 bez0 = Bezier:new{points={b0, b1, b2, b3}}
16
17 -- Construct the bottom surface
18 surf0 = CoonsPatch:new{west=bez0, east= arc0,
19 south=Line:new{p0=b0, p1=a0},
20 north=Line:new{p0=b3, p1=a1}}
21 -- Construct the edge along which we will sweep the surface.
22 d = Vector3:new{x=b0.x, y=b0.y, z=b0.z+L}
23 line0 = Line:new{p0=b0, p1=d}
24
25 my_vol = SweptSurfaceVolume:new{face0123=surf0, edge04=line0}
26
27 dofile("sketch-volume-example-3.lua")
28 Chapter 2. Geometric elements
y y
b3 b3
0.4 0.4
b2 b2
0.2 0.2
a1 a1
b1 b1
0.0 0.0
c c
0.2 0.0 0.2 0.0
a0 a0
b0 b0
0.4 0.2 0.4 0.2
z 0.4 x z 0.4 x
d
d
Figure 2.13: A volume constructed over part of a cylinder. The left view shows the
surface that will be swept along the line b0-d and the right view shows the con-
structed volume.
2.5. Manipulating elements 29
In the simplest form a number of variables, such as a scaling factor or specific angles
for walls can be defined a-priori. Shown below is a parameterised example for the
sharp-cone simulation as presented in the flow-solver user’s guide. In the first few
lines, several Lua variables are initialized to particular values, either with literals or
algebraic expressions. These variables are then used to define the elements that define
the flow domain in a fully-parametric manner.
1 -- sharp-cone-parameterised-example.lua
2
3 -- Parameters defining cone and flow domain.
4 theta = 32 -- cone half-angle, degrees
5 L = 0.8 -- axial length of cone, metres
6 rbase = L * math.tan(math.pi*theta/180.0)
7 x0 = 0.2 -- upstream distance to cone tip
8 H = 2.0 -- height of flow domain, metres
9
10 -- Set up two quadrilaterals in the (x,y)-plane by first defining
11 -- the corner nodes, then the lines between those corners.
12 a = Vector3:new{x=0.0, y=0.0} -- f----e---------d
13 b = Vector3:new{x=x0, y=0.0} -- | | |
14 c = Vector3:new{x=x0+L, y=rbase} -- |quad| quad |
15 d = Vector3:new{x=x0+L, y=H} -- | 0 | 1 |
16 e = Vector3:new{x=x0, y=H} -- | | ___---c
17 f = Vector3:new{x=0.0, y=H} -- a----b---
18
19 -- Define the two surfaces
20 quad0 = makePatch{p00=a, p10=b, p11=e, p01=f}
21 quad1 = makePatch{p00=b, p10=c, p11=d, p01=e, gridType="ao"}
One of the most useful functions is the eval function. This function evaluates the cur-
rent path, surface or volume element at the parameteric location defined by (t), (s, t),
or (r, s, t) and returns a new point. The function is executed by calling the eval method
on a particular geometry object as per the Lua object-oriented convention described in
Section 2.1: my path:eval(t), my surf:eval(s,t) or my vol:eval(r,s,t).
Alternatively one may call the geometry object directly as, for example, my path(t).
See following sections for how the eval function can be used to manipulate a geom-
etry.
30 Chapter 2. Geometric elements
1 -- split-path-example.lua
2
3 -- Single Bezier curve of order 4.
4 b={}
5 b[1] = Vector3:new{x=0.1, y=0.7}
6 b[2] = Vector3:new{x=0.15, y=0.8}
7 b[3] = Vector3:new{x=0.5, y=0.9}
8 b[4] = Vector3:new{x=0.6, y=0.7}
9 b[5] = Vector3:new{x=0.9, y=0.7}
10 single_bez = Bezier:new{points={b[1], b[2], b[3], b[4], b[5]}}
11
12 -- offset points in y-direction for 2nd and 3rd line
13 c = {}
14 d = {}
15 for i,v in ipairs(b) do
16 c[i] = Vector3:new{x=v.x, y=v.y-0.35} -- offset points by -0.35
17 d[i] = Vector3:new{x=v.x, y=v.y-0.7} -- offset points by -0.7
18 end
19
20 -- create two Bezier curves
21 two_bez_0 = Bezier:new{points={c[1], c[2], c[3]}}
22 two_bez_1 = Bezier:new{points={c[3], c[4], c[5]}}
23
24 -- create split line
25 t = 0.5 -- define parameterised location of split
26 bez = Bezier:new{points={d[1], d[2], d[3], d[4], d[5]}}
27 C = bez:eval(t)
28 line_d1_C = SubRangedPath:new{underlying_path=bez, t0 = 0.0, t1 = t}
29 line_C_d5 = SubRangedPath:new{underlying_path=bez, t0 = t, t1 = 1.0}
30
31 dofile("sketch-split-path-example.lua")
2.5. Manipulating elements 31
1.0
Single Bezier
b3
0.8 b2
b1 b4 b5
d2 C
0.0 d1 d4 d5
0.0 0.2 0.4 0.6 0.8 1.0
x
Figure 2.14: Effect of using different approaches to split a Bezier curve into two paths
elements.
32 Chapter 2. Geometric elements
In the following code, point A is created at normalised position t=0.3. Next the
tangent vector (which equals the gradient of the path) at point A, gradient is deter-
mined using the finite difference method. From this a wall normal vector, normal
is created by swapping the x- and y-components and negating the sign of the x-
component. Finally, the position of point B is determined by adding a scaled version
of the normal vector to point A. The results are shown in Figure 2.15.
1 -- point-normal-to-wall-example.lua
2
3 -- Single Bezier curve of order 4.
4 b={}
5 b[1] = Vector3:new{x=0.1, y=0.1}
6 b[2] = Vector3:new{x=0.15, y=0.2}
7 b[3] = Vector3:new{x=0.5, y=0.3}
8 b[4] = Vector3:new{x=0.6, y=0.1}
9 b[5] = Vector3:new{x=0.9, y=0.1}
10 single_bez = Bezier:new{points={b[1], b[2], b[3], b[4], b[5]}}
11
12 -- Create point along wall
13 t = 0.3
14 A = single_bez:eval(t)
15
16 -- Calculate gradient vector by evaluating path at t +/- dt
17 dt = 0.01
18 gradient = single_bez:eval(t+dt) - single_bez:eval(t-dt)
19
20 -- create wall normal vector and normalise
21 normal = Vector3:new{x=-gradient.y, y=gradient.x}
22 normal:normalize()
23
24 -- Create point B at distance L
25 L = 0.2
26 B = A + L*normal -- add vectors
27
28 -- Create line connecting A and B
29 mypath = Line:new{p0=A, p1=B}
30
31 dofile("sketch-point-normal-to-wall-example.lua")
2.5. Manipulating elements 33
0.4 B
y
0.2
A
b5
b1
0.0
0.0 0.2 0.4 0.6 0.8 1.0
x
Grids
Because the flow solver expects the gas-flow and solid domains to be specified as
meshes of finite-volume cells, we now need to consider the discretizion of the 2D
patches and 3D volumes. Typically, we pass the surface and volume objects, along
with cluster-function objects, to the constructor for the StructuredGrid class and get
back a mesh of points that define the vertices of our finite-volume cells. This mesh,
when combined with boundary conditions and an initial gas state (as described in the
main solver user guide [2]), then defines a flow block.
0.10 I J K L M
y
[3] [4]
[2]
0.05 [0]
[1] F N
DE_b3 E
D GF_b1
[5]
0.00 A B CD_b1 DE_b1 DE_b2 G H
C
-0.10 0.00 0.10 0.20 0.30
x
Figure 3.1: Schematic diagram of Ingo’s beer bottle aligned with the x-axis. This PDF
figure was generated from the SVG file with some edits to move the point and block
labels to nicer positions.
35
36 Chapter 3. Grids
1 -- the-minimal-grid.lua
2
3 -- Create the nodes that define key points for our geometry.
4 E = Vector3:new{x=0.1, y=0.03}; F = Vector3:new{x=0.202, y=0.03}
5 K = Vector3:new{x=0.1, y=0.1}; L = Vector3:new{x=0.202, y=0.1}
6
7 patch3 = CoonsPatch:new{north=Line:new{p0=K, p1=L},
8 east=Line:new{p0=F, p1=L},
9 south=Line:new{p0=E, p1=F},
10 west=Line:new{p0=E, p1=K}}
11
12 grid3 = StructuredGrid:new{psurface=patch3, niv=21, njv=21}
13 grid3:write_to_vtk_file("the-minimal-grid.vtk")
14
15 dofile("sketch-minimal-grid.lua")
0.10 K L
y
[3]
0.05
E F
0.00
Figure 3.2: A single block for a simple subregion from the eventual model of the
Ingo’s beer bottle.
The script for making the SVG sketch was called on the last line of the previous script.
Its content is a bit ugly, especially for drawing the axes, but it allows us to make a
drawing customized to the situation.
1 -- sketch-minimal-grid.lua
2 -- Called by the user input script to make a sketch of the flow domain.
3.2. StructuredGrid Class 37
3 -- PJ, 2016-09-28
4 sk = Sketch:new{renderer="svg", projection="xyortho", canvas_mm={0.0,0.0,120.0,120.0}}
5 sk:set{viewport={0.0,-0.1,0.3,0.2}}
6 sk:start{file_name="the-minimal-grid.svg"}
7
8 -- for flow domain
9 sk:set{line_width=0.1, fill_colour="green"}
10 sk:render{surf=patch3}
11 sk:text{point=0.25*(E+F+K+L), text="[3]", font_size=12}
12
13 -- labelled points of interest
14 sk:dotlabel{point=E, label="E"}; sk:dotlabel{point=F, label="F"}
15 sk:dotlabel{point=K, label="K"}; sk:dotlabel{point=L, label="L"}
16
17 -- axes
18 sk:set{line_width=0.3} -- for drawing rules
19 sk:rule{direction="x", vmin=0.1, vmax=0.2, vtic=0.05,
20 anchor_point=Vector3:new{x=0.1,y=-0.01},
21 tic_mark_size=0.004, number_format="%.2f",
22 text_offset=0.012, font_size=10}
23 sk:text{point=Vector3:new{x=0.15,y=-0.035}, text="x", font_size=12}
24 sk:rule{direction="y", vmin=0.0, vmax=0.1, vtic=0.05,
25 anchor_point=Vector3:new{x=0.09,y=0},
26 tic_mark_size=0.004, number_format="%.2f",
27 text_offset=0.005, font_size=10}
28 sk:text{point=Vector3:new{x=0.07,y=0.075}, text="y", font_size=12}
29
30 sk:finish{}
• path: a Path object along which a number of discrete points will be defined to
represent a one-dimensional grid.
The constructor first looks for a path item and, if not found, tries the psurface item.
If that is not found, it finally tries for a pvolume item. As soon as it has found a valid
geometric item, it proceeds, ignoring the other options. The other essential items for
defining the grid are the number of vertices in each index direction, which are named
niv, njv and nkv. The index directions i, j and k for the grid correspond to the
parametric coordinate directions r, s and t respectively. Remember that the number
of finite-volume cells in each index direction is one less than the number of vertices
in that direction. We will often think of our the size of our grids in terms of numbers
of cells and so will specify the number for the vertices in the call to this constructor
as n + 1, where n is the number of cells that we want.
38 Chapter 3. Grids
With no further information, the grid generator will uniformly distribute the ver-
tices in each parametric direction. This may or may not correspond to a uniform
distribution in x,y,z-space, depending on the definition of the supplied geometric ob-
ject. To get the uniformity in the distribution of the mesh vertices, you may need to
use an ArcLengthParameterizedPath (see page 14), for example, when defining the
edges of your surface patch.
Sometimes you really do want a non-uniform distribution of points along one or
more of the index directions. The distribution of vertices along each edge of the geo-
metric object can be specified via a cluster-function object. These are UnivariateFunction
objects of the following flavours:
function myParabola(t)
s = t*t
return s
end
myClustering = LuaFnClustering:new{luaFnName=’myParabola’}
edge56, edge76, edge47, edge04, edge15, 26 and edge37. Note that the 3D
names use the vertex labels (see Figure 2.11) to indicate which way the r, s or t pa-
rameter varies. Look at your debugging cube from Appendix A to get a good idea of
the arrangement. (You did cut out and stick the edges of your cube together, didn’t
you?) You don’t need to specify a cluster function for every edge. If you don’t specify
a function for a particular edge, it will get a default linear function that results in a
uniformly-distributed set of points.
So, putting all that together for a two-dimensional grid, we have the constructor
signature:
StructuredGrid:new{psurface=myP atch, niv=ni , njv=nj ,
cfList={north=cfN , south=cfS , west=cfW , east=cfE } }
Omitting the longer cluster-function list, the constructor signature for a parametric
volume, will look as simple as:
StructuredGrid:new{pvolume=myV olume, niv=ni , njv=nj , nkv=nk }
• sgrid:get niv() returns the number of vertices in the i-index direction. The
empty parentheses indicate that no argument needs to be supplied.
• sgrid:get nkv() returns the number of vertices in the k-index direction. For a
2D grid, this will be 1.
• sgrid:get vtx(i, j, k) returns the Vector3 object giving the location of the ver-
tex in x,y,z-space. If j and/or k are not supplied, values of zero are assumed.
Index values for each direction start and 0 and range up to (but less than) the
number of vertices in that direction. This is at odds with the usual Lua con-
vention for starting at 1 but it aligns with the D-code implementation of the
StructuredGrid class.
• sgrid:write to vtk file(f ileN ame) writes the grid, in classic VTK format,
to the specified file. This format is a simple ASCII text format that may be
viewed with any text editor or displayed with a visualization program like Par-
aview.
• sgrid:write to gzip file(f ileN ame) writes the grid to the native format
used by Eilmer4. It is a essentially a gzipped text file with a format defined by
40 Chapter 3. Grids
• import the grid in SU2 format, maybe from a file written by Pointwise,
If you rummage through the example set that accompanies the Eilmer source code,
you will find examples of building and importing unstructured grids.
1 -- the-plain-bottle.lua
2
3 -- Create the nodes that define key points for our geometry.
4 A = Vector3:new{x=-0.07, y=0.0}; B = Vector3:new{x=-0.05, y=0.0}
5 C = Vector3:new{x=0.0, y=0.0}; D = Vector3:new{x=0.005, y=0.012}
6 E = Vector3:new{x=0.1, y=0.03}; F = Vector3:new{x=0.202, y=0.03}
7 G = Vector3:new{x=0.207, y=0.0}; H = Vector3:new{x=0.3, y=0.0}
8 I = Vector3:new{x=-0.07, y=0.1}; J = Vector3:new{x=-0.05, y=0.1}
9 K = Vector3:new{x=0.1, y=0.1}; L = Vector3:new{x=0.202, y=0.1}
10 M = Vector3:new{x=0.3, y=0.1}; N = Vector3:new{x=0.3, y=0.03}
3.4. Building a multiblock grid 41
11
12 -- Some interior Bezier control points
13 CD_b1 = Vector3:new{x=0.0, y=0.006}
14 DJ_b1 = Vector3:new{x=-0.008, y=0.075}
15 GF_b1 = Vector3:new{x=0.207, y=0.027}
16 DE_b1 = Vector3:new{x=0.0064, y=0.012}
17 DE_b2 = Vector3:new{x=0.0658, y=0.0164}
18 DE_b3 = Vector3:new{x=0.0727, y=0.0173}
19
20 -- Now, we join our nodes to create lines that will be used to form our blocks.
21 -- lower boundary along x-axis
22 AB = Line:new{p0=A, p1=B}; BC = Line:new{p0=B, p1=C}
23 GH = Line:new{p0=G, p1=H}
24 CD = Bezier:new{points={C, CD_b1, D}} -- top of bottle
25 DE = Bezier:new{points={D, DE_b1, DE_b2, DE_b3, E}} -- neck of bottle
26 EF = Line:new{p0=E, p1=F} -- side of bottle
27 -- bottom of bottle
28 GF = ArcLengthParameterizedPath:new{
29 underlying_path=Bezier:new{points={G, GF_b1, F}}}
30 -- Upper boundary of domain
31 IJ = Line:new{p0=I, p1=J}; JK = Line:new{p0=J, p1=K}
32 KL = Line:new{p0=K, p1=L}; LM = Line:new{p0=L, p1=M}
33 -- Lines to divide the gas flow domain into blocks.
34 AI = Line:new{p0=A, p1=I}; BJ = Line:new{p0=B, p1=J}
35 DJ = Bezier:new{points={D, DJ_b1, J}}
36 JD = ReversedPath:new{underlying_path=DJ}; EK = Line:new{p0=E, p1=K}
37 FL = Line:new{p0=F, p1=L};
38 NM = Line:new{p0=N, p1=M}; HN = Line:new{p0=H, p1=N}
39 FN = Line:new{p0=F, p1=N}
40
41 -- Define the blocks, boundary conditions and set the discretisation.
42 n0 = 10; n1 = 4; n2 = 20; n3 = 20; n4 = 20; n5 = 12; n6 = 8
43
44 patch = {}
45 patch[0] = CoonsPatch:new{north=IJ, east=BJ, south=AB, west=AI}
46 patch[1] = CoonsPatch:new{north=JD, east=CD, south=BC, west=BJ}
47 patch[2] = CoonsPatch:new{north=JK, east=EK, south=DE, west=DJ}
48 patch[3] = CoonsPatch:new{north=KL, east=FL, south=EF, west=EK}
49 patch[4] = CoonsPatch:new{north=LM, east=NM, south=FN, west=FL}
50 patch[5] = CoonsPatch:new{north=FN, east=HN, south=GH, west=GF}
51
52 grid = {}
53 grid[0] = StructuredGrid:new{psurface=patch[0], niv=n1+1, njv=n0+1}
54 grid[1] = StructuredGrid:new{psurface=patch[1], niv=n2+1, njv=n0+1}
55 grid[2] = StructuredGrid:new{psurface=patch[2], niv=n3+1, njv=n2+1}
56 grid[3] = StructuredGrid:new{psurface=patch[3], niv=n4+1, njv=n2+1}
57 grid[4] = StructuredGrid:new{psurface=patch[4], niv=n5+1, njv=n2+1}
58 grid[5] = StructuredGrid:new{psurface=patch[5], niv=n5+1, njv=n6+1}
59
60 for ib = 0, 5 do
61 fileName = string.format("the-plain-bottle-blk-%d.vtk", ib)
62 grid[ib]:write_to_vtk_file(fileName)
63 end
64 dofile("sketch-plain-bottle.lua")
Figure 3.3: A multiple-block model of the region around Ingo’s beer bottle.
ity to ensure that the defining edges that are common to pairs of blocks are consistent
and that the cell-discretization along each of these edges is consistent with the corre-
sponding discretization of any adjacent edge of another block. The first constraint is
easy to meet by defining each edge once only and reusing that path in the definition
of different blocks. Sometimes, the orientation of a pair of blocks and the particular
directions of the paths within each block means that one defining edge needs to be
in the opposite sense to the original. In this case the ReversedPath class may be
useful. For an example of this, note that the script actually constructs such a path
for JD using the path DJ on line 36. Tha path JD is oriented so that it can serve as a
north boundary for patch 1 whereas the path DJ is oriented such that it can be used
as a west boundary for patch 2.
1 -- the-clustered-bottle.lua
2
3 -- Create the nodes that define key points for our geometry.
3.4. Building a multiblock grid 43
[2] Peter A. Jacobs and Rowan J. Gollan. The Eilmer 4.0 flow simulation program:
Guide to the transient flow solver, including some examples to get you started.
School of Mechanical and Mining Engineering Technical Report 2017/26, The
University of Queensland, Brisbane, Australia, February 2018.
[4] M. J. Zucrow and J. D. Hoffman. Gas Dynamics Volume 1. John Wiley & Sons, New
York, 1976.
45
Index
univariate function
47
A
Cut out the development on the reverse of this page, fold along all of the edges and
stick the your own cube together. A pair of cubes is very handy for sorting out the
specification of connections between structured-grid blocks.
49
50
north
7 6
top
west
east
face
j
west
west
east
east
east
east
west
west
face
glue to edge 1-2
3 north 2
glue to edge 2-3
Appendix A. Make your own debugging cube