Space Transformation Method

Complex geometries can be defined manually by applying a predefined or user-defined space transformation on a Grid object.

FLOWPanel.GeometricTools.transform!

The following predefined nonlinear orthogonal space transformations are implemented in GeometricTools:

  • cylindrical3D(X)
  • cylindrical2D(X)
  • spherical3D(X)
  • parabolic3D(X)
  • paraboloidal3D(X)
  • elliptic3D(X; a=1)
  • prolate3D(X; a=1)
  • oblate3D(X; a=1)
  • bipolar3D(X; a=1)
  • toroidal3D(X; a=1)
  • conical3D(X; b=2, c=1).

Example — Sphere, predefined transformation

import FLOWPanel as pnl

file_name = "sphere00"
save_path = "./"

# Parameters
R = 1.0                          # (m) radius of sphere
P_min = [0, 0, 0]                # Lower bounds of (theta, phi, dummy)
P_max = [pi, 2*pi, 0]            # Upper bounds of (theta, phi, dummy)
NDIVS = [25, 50, 0]              # Number of divisions (cells) of (theta, phi, dummy)
loop_dim = 2                     # Coordinate to loop (1==theta)

# Generates parametric (theta, phi) grid
grid = pnl.gt.Grid(P_min, P_max, NDIVS, loop_dim)

# Transforms the grid into a spherical cartesian space
my_transform(X) = pnl.gt.spherical3D(vcat(R, X[1:2]))
pnl.gt.transform!(grid, my_transform)

# Splits the quad cells into triangular cells
dimsplit = 1
triang_grid = pnl.gt.GridTriangleSurface(grid, dimsplit)


# Save vtk and call paraview
pnl.gt.save(triang_grid, file_name; path=save_path, format="vtk")

run(`paraview --data=$(joinpath(save_path, file_name)).vtk`)
Vid here

Example — Box, user-defined transformation

import FLOWPanel as pnl

file_name = "box00"
save_path = "./"

# Parameters
L = 1.0                          # (m) side's length
P_min = [-0.95, 0, 0]            # Lower bounds of (x, y, dummy)
P_max = [.95, 4.0, 0]            # Upper bounds of (x, y, dummy)
# NDIVS = [24, 20, 0]            # Number of divisions (cells) of (x, y, dummy)
N = 3
NDIVS = [
            [(0.45, 3*N, 1/2, false),
                (1.0, 6*N, 2.0, true),
                (0.45, 3*N, 2.0, false)],
            [(0.25, 6*N, 2.0, true) for i in 1:4],
            [(1.0, 0, 0.0, false)]
        ]
loop_dim = 2                     # Coordinate to loop (2==y)

# Generates parametric (theta, phi) grid
grid = pnl.gt.Grid(P_min, P_max, NDIVS, loop_dim)

# Axis on every face
Oaxis = [pnl.gt.rotation_matrix2(-a, 0, 0) for a in [90, 0, -90, -180]]

# Center of every face
O = L*[zeros(3), [0, 0, 1.0], [0, 1, 1.0], [0, 1, 0.0]]

# Axis of side sections
Oaxis_sec = [pnl.gt.rotation_matrix2(angles...)*Oaxis[i]
                for (i, angles) in enumerate([(0,-90,0), (0,-90,0), (0,-90,0), (0,-90,0)])]

# Center of side sections
O_sec = [O[i]+L/2*Oaxis[i][2,:] for i in 1:4]

# Transformation function
function my_transform(X)

    # Identifies the face
    face = Int(floor(X[2]+1))

    if face<=0 || face>4
        error("Logic error! Invalid face $face")
    end

    # Identifies the side (-1->left, 0->center, 1->right)
    side = sign(X[1])*(abs(X[1])>0.5)

    if side==0
        return pnl.gt.countertransform(L*[X[1], X[2]-(face-1), 0], inv(Oaxis[face]), O[face])
    else
        mod_X = [abs(X[1])-0.5, X[2]-(face-1)-0.5, 0]
        mod_X[2] *= 1 - mod_X[1]/0.5
        return pnl.gt.countertransform(L*[mod_X[1], side*mod_X[2], 0],
                                        inv([1 0 0; 0 side 0; 0 0 side]*Oaxis_sec[face]),
                                        O_sec[face] + L/2*[side, 0, 0])
    end
end

# Converts the parametric grid into a box
pnl.gt.transform!(grid, my_transform)

# Splits the quad cells into triangular cells
dimsplit = 1
triang_grid = pnl.gt.GridTriangleSurface(grid, dimsplit)

# Save vtk and call paraview
pnl.gt.save(triang_grid, file_name; path=save_path, format="vtk")

run(`paraview --data=$(joinpath(save_path, file_name)).vtk`)
Vid here