Creating bodies
The most basic functions of this package create an object of type Body
. There are a variety of such functions, a few of which we will demonstrate here. Generally speaking, we are interesting in creating the object and placing it in a certain position and orientation. We do this in two steps: we create the basic shape, centered at the origin with a default orientation, and then we transform the shape to a desired location and orientation. We will discuss the shapes in this notebook, and the transforms in the following notebook.
It is useful to stress that each body stores two types of points internally: the endpoints of the segments that comprise the body surface, and the midpoints of these segments. The midpoints are intended for use in downstream calculations, e.g. as forcing points in the calculations on immersed layers. The midpoints are simply the geometric averages of the endpoints, so endpoints are the ones that are transformed first, and midpoints are updated next.
using RigidBodyTools
using Plots
Creating a shape
Let's first create a shape. For any shape, we have to make a choice of the geometric dimensions (e.g, radius of the circle, side lengths of a rectangle), as well as the points that we use to discretely represent the surface. For this latter choice, there are two constructor types: we can specify the number of points (as an integer), or we can specify the nominal spacing between points (as a floating-point number).
The second approach is usually preferable when we use these tools for constructing immersed bodies. It is important to stress that the algorithms for placing points attempt to make the spacing as uniform as possible.
Let's create the most basic shape, a circle of radius 1. We will discretize it with 100 points first:
b = Circle(1.0,100)
Circular body with 100 points and radius 1.0
Current position: (0.0,0.0)
Current angle (rad): 0.0
Now we will create the same body with a spacing of 0.02
b = Circle(1.0,0.02)
Circular body with 312 points and radius 1.0
Current position: (0.0,0.0)
Current angle (rad): 0.0
This choice led to 312 points along the circumference. Quick math will tell you that the point spacing is probably not exactly 0.02. In fact, you can find out the actual spacing with dlengthmid
. This function calculates the spacing associated with each point. (It does so by first calculating the spacing between midpoints between each point and its two adjacent points.)
dlengthmid(b)
312-element Vector{Float64}:
0.020139095077842645
0.02013909465345664
0.020139095926384347
0.020139093805429224
0.020139096773551215
0.02013909295946801
0.020139097617965406
0.020139092116941246
0.020139098458258977
0.020139091279220516
⋮
0.020139098876402556
0.02013909169742879
0.020139098038677593
0.020139092537724373
0.02013909719615391
0.020139093382138724
0.020139096350192693
0.020139094229301876
0.020139095502170006
It is just a bit larger than 0.02.
A few other useful functions on the shape. To simply know the number of points,
length(b)
312
To find the outward normal vectors (based on the perpendicular to the line joining the adjacent midpoints):
nx, ny = normalmid(b)
([0.9999999999994525, 0.9997972500513201, 0.9991889560711664, 0.9981756172871938, 0.9967572242069879, 0.9949349397149403, 0.9927087488883317, 0.9900804735231573, 0.9870500969577222, 0.9836200924747993 … 0.9797908571175543, 0.9836197214184416, 0.9870504283171553, 0.9900801823981242, 0.9927089993067586, 0.9949347304092867, 0.9967573920603647, 0.9981754911583587, 0.9991890402708846, 0.9997972079172882], [1.0463939213204075e-6, 0.02013600729582963, 0.04026698480657073, 0.06037745484309014, 0.08046760833515793, 0.10052097161502202, 0.12053771144568437, 0.14050144464794798, 0.16041230032560236, 0.1802540254193159 … -0.20002468924822323, -0.18025605020832927, -0.16041026138910267, -0.14050349612197227, -0.1205356490643909, -0.10052304325576243, -0.0804655290979954, -0.060379540001306534, -0.04026489541211434, -0.020138099234901622])
We can also plot the shape
plot(b)
Sometimes we don't want to fill in the shape (and maybe change the line color). In that case, we can use
plot(b,fill=:false,linecolor=:black)
Other shapes
Let's see some other shapes in action, like a square and an ellipse
b1, b2 = Square(1.0,0.02), Ellipse(0.6,0.1,0.02)
plot(plot(b1), plot(b2))
A NACA 4412 airfoil, with chord length 1, and 0.02 spacing between points.
b = NACA4(0.04,0.4,0.12,0.02)
plot(b)
A flat plate with no thickness
b = Plate(1.0,0.02)
plot(b)
and a flat plate with a 5 percent thickness (and rounded ends)
b = ThickPlate(1.0,0.05,0.01)
plot(b)
There are also some generic tools for creating shapes. A BasicBody
simply consists of points that describe the vertices. The interface for this is very simple.
x = [1.0, 1.2, 0.7, 0.6, 0.2, -0.1, 0.1, 0.4]
y = [0.1, 0.5, 0.8, 1.2, 0.8, 0.6, 0.2, 0.3]
b = BasicBody(x,y)
Basic pointwise-specified body with 8 points
Current position: (0.0,0.0)
Current angle (rad): 0.0
plot(b)
scatter!(b,markersize=3,markercolor=:black)
However, this function does not insert any points along the sides between vertices. We have to do the work of specifying these points in the original call. For this reason, there are a few functions that are more directly useful. For example, we can create a polygon from these vertices, with a specified spacing between points distributed along the polygon sides
b = Polygon(x,y,0.02)
Closed polygon with 8 vertices and 188 points
Current position: (0.0,0.0)
Current angle (rad): 0.0
plot(b)
scatter!(b,markersize=3,markercolor=:black)
Alternatively, we can interpret those original points as control points for splines, with a spacing between points along the splines provided:
b = SplinedBody(x,y,0.02)
Basic pointwise-specified body with 198 points
Current position: (0.0,0.0)
Current angle (rad): 0.0
plot(b)
scatter!(b,markersize=3,markercolor=:black)
Body functions
RigidBodyTools.BasicBody
— TypeBasicBody(x,y[,closuretype=ClosedBody]) <: Body
Construct a body by simply passing in the x
and y
coordinate vectors. The last point will be automatically connected to the first point. The coordinate vectors are assumed to be expressed in the body-fixed coordinate system. The optional closuretype
specifies whether the body is closed (ClosedBody
) or open (OpenBody
). If closed, then the first and last points are assumed joined in operations that require neighbor points.
RigidBodyTools.Polygon
— TypePolygon(x::Vector,y::Vector,n[,closuretype=ClosedBody])
Create a polygon shape with vertices x
and y
, with approximately n
points distributed along the perimeter.
RigidBodyTools.Circle
— FunctionCircle(a,n) <: Body
Construct a circular body with radius a
and with n
points distributed on the body perimeter.
Circle(a,targetsize::Float64) <: Body
Construct a circular body with radius a
with spacing between points set approximately to targetsize
.
RigidBodyTools.Ellipse
— TypeEllipse(a,b,n[,endpointson=false],[shifted=false]) <: Body
Construct an elliptical body with semi-major axis a
and semi-minor axis b
, with n
points distributed on the body perimeter. Can also call this with Ellipse(a,b,ds)
where ds
is the desired spacing between points. If endpointson=true
, then the endpoints will be placed on the surface and the midpoints will lie inside the nominal ellipse. By default, the midpoints lie on the ellipse. If shifted=true
, then the first endpoint lies on an axis of symmetry. By default the first midpoint lies on the axis symmetry.
RigidBodyTools.NACA4
— TypeNACA4(cam,pos,thick,ds::Float64,[len=1.0]) <: Body{N}
Generates points in the shape of a NACA 4-digit airfoil. The relative camber is specified by cam
, the position of maximum camber (as fraction of chord) by pos
, and the relative thickness by thick
. The parameter ds
specifies the distance between points. The optional parameter len
specifies the chord length, which defaults to 1.0.
Example
julia> b = NACA4(0.0,0.0,0.12,0.02);
RigidBodyTools.Plate
— FunctionPlate(a,n) <: Body
Construct a flat plate of zero thickness with length a
, divided into n
equal segments.
Plate(a,ds) <: Body
Construct a flat plate of zero thickness with length a
, with approximate spacing ds
between points.
RigidBodyTools.Rectangle
— FunctionRectangle(a,b,n) <: Body
Construct a rectangular body with x̃ side half-length a
and ỹ side half-length b
, with approximately n
points distributed along the perimeter. The centroid of the rectangle is placed at the origin (so that the lower left corner is at (-a,-b)).
Points are not placed at the corners, but rather, are shifted by half a segment. This ensures that all normals are perpendicular to the sides.
Rectangle(a,b,ds) <: Body
Construct a rectangular body with x̃ side half-length a
and ỹ side half-length b
, with approximate spacing ds
between points.
RigidBodyTools.SplinedBody
— FunctionSplinedBody(X,Δx[,closuretype=ClosedBody]) -> BasicBody
Using control points in X
(assumed to be N x 2, where N is the number of points), create a set of points that are uniformly spaced (with spacing Δx
) on a curve that passes through the control points. A cubic parametric spline algorithm is used. If the optional parameter closuretype
is set to OpenBody
, then the end points are not joined together.
SplinedBody(x,y,Δx[,closuretype=ClosedBody]) -> BasicBody
Using control points in x
and y
, create a set of points that are uniformly spaced (with spacing Δx
) on a curve that passes through the control points. A cubic parametric spline algorithm is used. If the optional parameter closuretype
is set to OpenBody
, then the end points are not joined together.
RigidBodyTools.Square
— FunctionSquare(a,n) <: Body
Construct a square body with side half-length a
and with approximately n
points distributed along the perimeter.
Square(a,ds) <: Body
Construct a square body with side half-length a
, with approximate spacing ds
between points.
Shape utilities
RigidBodyTools.centraldiff
— Functioncentraldiff(body::Body[;axes=:inertial]) -> Tuple{Vector{Float64},Vector{Float64}}
Compute the circular central differences of coordinates on body body
(or on each body in list body
). If axes=:body
, uses the reference coordinates in body-fixed space.
centraldiff(bl::BodyList[;axes=:inertial]) -> Tuple{Vector{Float64},Vector{Float64}}
Compute the centraldiff
on each constituent body in bl
. If axes=:body
, uses the reference coordinates in body-fixed space.
Base.diff
— Methoddiff(body::Body[;axes=:inertial]) -> Tuple{Vector{Float64},Vector{Float64}}
Compute the x and y differences of the faces on the perimeter of body body
, whose ends are at the current x
and y
coordinates (in inertial space) of the body (if axes=:inertial
), or at the reference x̃
and ỹ
coordinates (body-fixed space) if axes=:body
. Face 1 corresponds to the face between points 1 and 2, for example.
If body
is a BodyList
, then it computes the differences separately on each constituent body.
Base.diff
— Methoddiff(bl::BodyList[;axes=:inertial]) -> Tuple{Vector{Float64},Vector{Float64}}
Compute the diff
on each constituent body in bl
.
RigidBodyTools.dlength
— Functiondlength(body::Body/BodyList[;axes=:inertial]) -> Vector{Float64}
Compute the lengths of the faces on the perimeter of body body
, whose ends are at the current xend
and yend
coordinates (in inertial space) of the body. Face 1 corresponds to the face between endpoints 1 and 2, for example. If axes=:body
, uses the reference coordinates in body-fixed space.
RigidBodyTools.dlengthmid
— Functiondlengthmid(body::Body/BodyList[;axes=:inertial]) -> Vector{Float64}
Same as dlength
.
Base.length
— Methodlength(body::Body)
Return the number of points on the body perimeter
RigidBodyTools.midpoints
— Methodmidpoints(body::Body[;axes=:inertial]) -> Tuple{Vector{Float64},Vector{Float64}}
Compute the x and y midpoints of the faces on the perimeter of body body
, whose ends are at the current x
and y
coordinates (in inertial space) of the body (if axes=:inertial
), or at the reference x̃
and ỹ
coordinates (body-fixed space) if axes=:body
. Face 1 corresponds to the face between points 1 and 2, for example.
If body
is a BodyList
, then it computes the differences separately on each constituent body.
RigidBodyTools.midpoints
— Methodmidpoints(bl::BodyList[;axes=:inertial]) -> Tuple{Vector{Float64},Vector{Float64}}
Compute the midpoints
on each constituent body in bl
.
RigidBodyTools.normal
— Functionnormal(body::Body/BodyList[;axes=:inertial]) -> Tuple{Vector{Float64},Vector{Float64}}
Compute the current normals in inertial components (if axes=:inertial
) or body- fixed components (if axes=:body
) of the faces on the perimeter of body body
, whose ends are at the current xend
and yend
coordinates (in inertial space) of the body. Face 1 corresponds to the face between points 1 and 2, for example. For an OpenBody
, this provides a vector that is one element shorter than the number of points.
RigidBodyTools.normalmid
— Functionnormalmid(body::Body/BodyList[;axes=:inertial]) -> Tuple{Vector{Float64},Vector{Float64}}
Compute the current normals in inertial components (if axes=:inertial
) or body- fixed components (if axes=:body
) of the faces formed between endpoints on the perimeter of body body
(or each body in list body
).
RigidBodyTools.arccoord
— Functionarccoord(body::Body/BodyList[;axes=:inertial]) -> Vector{Float64}
Returns a vector containing the arclength coordinate along the surface of body
, evaluated at the second endpoint of each face. So, e.g., the first coordinate would be the length of face 1, the second the length of face 2, and the last would be total length of all of the faces. Use inertial components (if axes=:inertial
) or body-fixed components (if axes=:body
). If this is a body list, restart the origin of the coordinates on each body in the list.
RigidBodyTools.arccoordmid
— Functionarccoordmid(body::Body/BodyList[;axes=:inertial]) -> Vector{Float64}
Returns a vector containing the arclength coordinate along the surface of body
, evaluated at the midpoints between the ends of faces. So, e.g., the first coordinate would be half of the length of face 1, the second would be half of face 2 plus all of face 1, etc. Use inertial components (if axes=:inertial
) or body- fixed components (if axes=:body
). If this is a body list, restart the origin of the coordinates on each body in the list.
RigidBodyTools.arclength
— Methodarclength(body::Body[;axes=:inertial])
Compute the total arclength of body
, from the sum of the lengths of the faces. If axes=:body
, use the body-fixed coordinates.
RigidBodyTools.arclength
— Methodarclength(bl::BodyList[;axes=:inertial]) -> Vector{Float64}
Compute the total arclength of each body in bl
and assemble the results into a vector. If axes=:body
, use the body-fixed coordinates.
This page was generated using Literate.jl.