Blade Mesher

The PGL.components.blademesher.BladeMesher generates a 3D surface mesh around a rotor, combining the classes for blade surface, root or nacelle, and blade tip.

There are two examples that generate a mesh around a rotor; one with a simple cylindrical root connection, and another with a full spinner and nacelle geoemtry.

BladeMesher Simple Root Example

In this example, we will generate a mesh around the three bladed DTU 10MW RWT with a simple cylindrical root connection.

The example is located in PGL/examples/blademesher_example.py.


import numpy as np

from PGL.components.blademesher import BladeMesher
from PGL.main.planform import read_blade_planform

m = BladeMesher()

# path to the planform
m.planform_filename = 'data/DTU_10MW_RWT_blade_axis_prebend.dat'

# spanwise and chordwise number of vertices
m.ni_span = 129
m.ni_chord = 257

# redistribute points chordwise
m.redistribute_flag = True
# number of points on blunt TE
m.chord_nte = 9
# set min TE thickness (which will open TE using AirfoilShape.open_trailing_edge)
# d.minTE = 0.0002

# user defined cell size at LE
# when left empty, ds will be determined based on
# LE curvature
# m.dist_LE = np.array([])

# airfoil family - can also be supplied directly as arrays
m.blend_var = [0.241, 0.301, 0.36, 0.48, 1.0]
m.base_airfoils_path = ['data/ffaw3241.dat',
                        'data/ffaw3301.dat',
                        'data/ffaw3360.dat',
                        'data/ffaw3480.dat',
                        'data/cylinder.dat']

# number of vertices and cell sizes in root region
m.root_type = 'cylinder'
m.ni_root = 8
m.s_root_start = 0.0
m.s_root_end = 0.065
m.ds_root_start = 0.008
m.ds_root_end = 0.005

# add additional dist points to e.g. refine the root
# for placing VGs
# self.pf_spline.add_dist_point(s, ds, index)

# inputs to the tip component
# note that most of these don't need to be changed
m.ni_tip = 11
m.s_tip_start = 0.99
m.s_tip = 0.995
m.ds_tip_start = 0.001
m.ds_tip = 0.00005

m.tip_fLE1 = .5  # Leading edge connector control in spanwise direction.
                 # pointy tip 0 <= fLE1 => 1 square tip.
m.tip_fLE2 = .5  # Leading edge connector control in chordwise direction.
                 # pointy tip 0 <= fLE1 => 1 square tip.
m.tip_fTE1 = .5  # Trailing edge connector control in spanwise direction.
                 # pointy tip 0 <= fLE1 => 1 square tip.
m.tip_fTE2 = .5  # Trailing edge connector control in chordwise direction.
                 # pointy tip 0 <= fLE1 => 1 square tip.
m.tip_fM1 = 1.   # Control of connector from mid-surface to tip.
                 # straight line 0 <= fM1 => orthogonal to starting point.
m.tip_fM2 = 1.   # Control of connector from mid-surface to tip.
                 # straight line 0 <= fM2 => orthogonal to end point.
m.tip_fM3 = .2   # Controls thickness of tip.
                 # 'Zero thickness 0 <= fM3 => 1 same thick as tip airfoil.

m.tip_dist_cLE = 0.0001  # Cell size at tip leading edge starting point.
m.tip_dist_cTE = 0.0001  # Cell size at tip trailing edge starting point.
m.tip_dist_tip = 0.00025  # Cell size of LE and TE connectors at tip.
m.tip_dist_mid0 = 0.00025  # Cell size of mid chord connector start.
m.tip_dist_mid1 = 0.00004  # Cell size of mid chord connector at tip.

m.tip_c0_angle = 40.  # Angle of connector from mid chord to LE/TE

m.tip_nj_LE = 20   # Index along mid-airfoil connector used as starting point for tip connector


# generate the mesh
m.update()

# rotate domain with flow direction in the z+ direction and blade1 in y+ direction
m.domain.rotate_x(-90)
m.domain.rotate_y(180)

# copy blade 1 to blade 2 and 3 and rotate
m.domain.add_group('blade1', list(m.domain.blocks.keys()))
m.domain.rotate_z(-120, groups=['blade1'], copy=True)
m.domain.rotate_z(120, groups=['blade1'], copy=True)

# split blocks to cubes of size 33^3
# m.domain.split_blocks(33)

# Write EllipSys3D ready surface mesh
# write_x2d(m.domain, scale_factor=89.166)
# Write Plot3D surface mesh
m.domain.write_plot3d('DTU_10MW_RWT_mesh.xyz')

BladeMesher With Nacelle Example

To build the rotor mesh including a spinner and nacelle, you need to supply a few extra inputs. The example in PGL/examples/blademesher_nacelle_example.py shows how to add a spinner/nacelle, replacing the simple cylindrical root from the above example.


import numpy as np

from PGL.components.blademesher import BladeMesher
from PGL.main.planform import read_blade_planform
from PGL.main.bezier import BezierCurve
from PGL.main.curve import SegmentedCurve, Curve

m = BladeMesher()

# path to the planform
m.planform_filename = 'data/DTU_10MW_RWT_blade_axis_prebend.dat'

# spanwise and chordwise number of vertices
m.ni_span = 129
m.ni_chord = 257

# redistribute points chordwise
m.redistribute_flag = True
# number of points on blunt TE
m.chord_nte = 9
# set min TE thickness (which will open TE using AirfoilShape.open_trailing_edge)
# d.minTE = 0.0002

# user defined cell size at LE
# when left empty, ds will be determined based on
# LE curvature
# m.dist_LE = np.array([])

# airfoil family - can also be supplied directly as arrays
m.blend_var = [0.241, 0.301, 0.36, 0.48, 1.0]
m.base_airfoils_path = ['data/ffaw3241.dat',
                        'data/ffaw3301.dat',
                        'data/ffaw3360.dat',
                        'data/ffaw3480.dat',
                        'data/cylinder.dat']


# tell the mesher to add a nacelle object and generate a nacelle shape
m.root_type = 'nacelle'

c = BezierCurve()
CPs = np.array([[-6.5, 0],
                [-6.5, 1.5],
                [-4., 2.86],
                [-2.0, 3.],
                [-0.5, 3.25],
                [3.5, 3.5]])
CPs[:,1] *= 4./3.5
CPs /= 89.166
c.CPs = CPs
c.update()
c1 = Curve(points=np.array([np.linspace(3.5,18.5,100),np.linspace(4.,4.,100)]).T)
c1.points /= 89.166
sc = SegmentedCurve()
sc.add_segment(c)
sc.add_segment(c1)
sc.update()

m.nacelle_curve = sc
m.blade_root_radius = 5.38 / 2 / 89.166
m.hub_length = 6.5/ 89.166
m.dr_junction = 0.075
m.ni_root = 16
m.s_root_end = 0.065
m.ds_root_start = 0.001
m.ds_root_end = 0.005
m.base_nv = 7
m.ds_base = 0.0005
m.nacelle_dr = 0.5/89.166
m.ds_nacelle = 0.0012

# add additional dist points to e.g. refine the root
# for placing VGs
# self.pf_spline.add_dist_point(s, ds, index)

# inputs to the tip component
# note that most of these don't need to be changed
m.ni_tip = 11
m.s_tip_start = 0.99
m.s_tip = 0.995
m.ds_tip_start = 0.001
m.ds_tip = 0.00005

m.tip_fLE1 = .5  # Leading edge connector control in spanwise direction.
                 # pointy tip 0 <= fLE1 => 1 square tip.
m.tip_fLE2 = .5  # Leading edge connector control in chordwise direction.
                 # pointy tip 0 <= fLE1 => 1 square tip.
m.tip_fTE1 = .5  # Trailing edge connector control in spanwise direction.
                 # pointy tip 0 <= fLE1 => 1 square tip.
m.tip_fTE2 = .5  # Trailing edge connector control in chordwise direction.
                 # pointy tip 0 <= fLE1 => 1 square tip.
m.tip_fM1 = 1.   # Control of connector from mid-surface to tip.
                 # straight line 0 <= fM1 => orthogonal to starting point.
m.tip_fM2 = 1.   # Control of connector from mid-surface to tip.
                 # straight line 0 <= fM2 => orthogonal to end point.
m.tip_fM3 = .2   # Controls thickness of tip.
                 # 'Zero thickness 0 <= fM3 => 1 same thick as tip airfoil.

m.tip_dist_cLE = 0.0001  # Cell size at tip leading edge starting point.
m.tip_dist_cTE = 0.0001  # Cell size at tip trailing edge starting point.
m.tip_dist_tip = 0.00025  # Cell size of LE and TE connectors at tip.
m.tip_dist_mid0 = 0.00025  # Cell size of mid chord connector start.
m.tip_dist_mid1 = 0.00004  # Cell size of mid chord connector at tip.

m.tip_c0_angle = 40.  # Angle of connector from mid chord to LE/TE

m.tip_nj_LE = 20   # Index along mid-airfoil connector used as starting point for tip connector


# generate the mesh
m.update()

# rotate domain with flow direction in the z+ direction and blade1 in y+ direction
m.domain.rotate_x(-90)
m.domain.rotate_y(180)

# copy blade 1 to blade 2 and 3 and rotate
m.domain.add_group('blade1', list(m.domain.blocks.keys()))
m.domain.rotate_z(-120, groups=['blade1'], copy=True)
m.domain.rotate_z(120, groups=['blade1'], copy=True)

# split blocks to cubes of size 33^3
# m.domain.split_blocks(33)

# Write EllipSys3D ready surface mesh
# write_x2d(m.domain, scale_factor=89.166)
# Write Plot3D surface mesh
m.domain.write_plot3d('DTU_10MW_RWT_nacelle_mesh.xyz')

The nacelle curve is in this example generated manually from a composite curve consisting of a Bezier curve and a straight line. Note that since the nacelle and spinner will be generated as a rotationally symmetric shape, the curve has to describe only the half the geometry, and should be open ended.

_images/nacellecurve.png

Instead of generating the nacelle shape manually, you can load in the shape from a file.

The resulting mesh should look like this:

_images/rotor_with_nacelle.png

BladeMesher One Bladed Example

In this example, we will generate a mesh around a single blade which requires the use of the PGL.components.bladecap.RootCap class.

The example is located in PGL/examples/blademesher_1b_example.py.


import numpy as np

from PGL.components.blademesher import BladeMesher
from PGL.main.planform import read_blade_planform
from PGL.main.domain import write_x2d

m = BladeMesher()

# path to the planform
m.planform_filename = 'data/DTU_10MW_RWT_blade_axis_straight_fine.dat'
# m.planform_filename = 'data/DTU_10MW_RWT_blade_axis_prebend.dat'

# spanwise and chordwise number of vertices
m.ni_span = 185
m.ni_chord = 257

# redistribute points chordwise
m.redistribute_flag = True
# number of points on blunt TE
m.chord_nte = 15
# set min TE thickness (which will open TE using AirfoilShape.open_trailing_edge)
# d.minTE = 0.0002

# user defined cell size at LE
# when left empty, ds will be determined based on
# LE curvature
# m.dist_LE = np.array([])

# airfoil family - can also be supplied directly as arrays
m.blend_var = [0.241, 0.301, 0.36, 0.48, 0.72, 1.0]
m.base_airfoils_path = ['data/ffaw3241.dat',
                        'data/ffaw3301.dat',
                        'data/ffaw3360.dat',
                        'data/ffaw3480.dat',
                        'data/tc72.dat',
                        'data/cylinder.dat']

m.dist_LE = np.array([[0,0.002],[0.06,0.002],[0.2,0.001],[0.5,0.001],[1.,0.001]])

# add Gurney flaps to the inner part of the blade
# array contains <span> <gf_height (h/c)> <gf_lenght factor (l/h)>
m.gf_heights = np.array([[0.00000, 0.0000, 2.000000]
                            ,[0.030000, 0.0000, 1.000000]
                            ,[0.060000, 0.050000, 3.000000]
                            ,[0.080000, 0.100000, 3.000000]
                            ,[0.100000, 0.160000, 3.000000]
                            ,[0.125000, 0.140000, 3.000000]
                            ,[0.150000, 0.100000, 3.000000]
                            ,[0.200000, 0.030000, 3.000000]
                            ,[0.232700, 0.025000, 3.000000]
                            ,[0.314800, 0.012672, 3.000000]
                            ,[0.330000, 0.01000, 3.000000]
                            ,[0.400000, 0.000000, 3.000000]
                            ,[1.00000, 0.000000, 0.000000]])

m.surface_spline = 'cubic'
# number of vertices and cell sizes in root region
m.root_type = 'cap'
m.ni_root = 8
m.s_root_start = 0.0
m.s_root_end = 0.03
m.ds_root_start = 0.001
m.ds_root_end = 0.004
m.cap_cap_radius = .001
m.cap_Fcap = 0.9
m.cap_Fblend = 0.7
# add additional dist points to e.g. refine the root
# for placing VGs
# self.pf_spline.add_dist_point(s, ds, index)

# inputs to the tip component
# note that most of these don't need to be changed
m.ni_tip = 11
m.s_tip_start = 0.99
m.s_tip = 0.995
m.ds_tip_start = 0.001
m.ds_tip = 0.00005

m.tip_fLE1 = .5  # Leading edge connector control in spanwise direction.
                 # pointy tip 0 <= fLE1 => 1 square tip.
m.tip_fLE2 = .5  # Leading edge connector control in chordwise direction.
                 # pointy tip 0 <= fLE1 => 1 square tip.
m.tip_fTE1 = .5  # Trailing edge connector control in spanwise direction.
                 # pointy tip 0 <= fLE1 => 1 square tip.
m.tip_fTE2 = .5  # Trailing edge connector control in chordwise direction.
                 # pointy tip 0 <= fLE1 => 1 square tip.
m.tip_fM1 = 1.   # Control of connector from mid-surface to tip.
                 # straight line 0 <= fM1 => orthogonal to starting point.
m.tip_fM2 = 1.   # Control of connector from mid-surface to tip.
                 # straight line 0 <= fM2 => orthogonal to end point.
m.tip_fM3 = .2   # Controls thickness of tip.
                 # 'Zero thickness 0 <= fM3 => 1 same thick as tip airfoil.

m.tip_dist_cLE = 0.0001  # Cell size at tip leading edge starting point.
m.tip_dist_cTE = 0.0001  # Cell size at tip trailing edge starting point.
m.tip_dist_tip = 0.00025  # Cell size of LE and TE connectors at tip.
m.tip_dist_mid0 = 0.00025  # Cell size of mid chord connector start.
m.tip_dist_mid1 = 0.00004  # Cell size of mid chord connector at tip.

m.tip_c0_angle = 40.  # Angle of connector from mid chord to LE/TE

m.tip_nj_LE = 20   # Index along mid-airfoil connector used as starting point for tip connector


# generate the mesh
m.update()


# rotate domain with flow direction in the z+ direction and blade1 in y+ direction
m.domain.rotate_x(-90)
m.domain.rotate_y(180)
m.domain.translate_y(2.8)

# pitch 90 deg into the wind
m.domain.rotate_y(-90)

# copy blade 1 to blade 2 and 3 and rotate
m.domain.add_group('blade1', list(m.domain.blocks.keys()))

# split blocks to cubes of size 33^3
m.domain.split_blocks(33)

# Write EllipSys3D ready surface mesh
write_x2d(m.domain, scale_factor=86.366)
# Write Plot3D surface mesh
# write_plot3d(m.domain, 'DTU_10MW_RWT_mesh.xyz')