class BladeStructureBuilderBase(Component): """ base class for components that can interpret the BladeStructure3DVT vartree and generate input for specific types of codes. """ surface = VarTree(BladeSurfaceVT(), iotype='in', desc='Stacked blade surface object') st3d = VarTree(BladeStructureVT3D(), iotype='in', desc='Blade structure definition') def execute(self): raise NotImplementedError( '%s.execute needs to be overwritten by derived classes' % self.get_pathname()) def get_material(self, name): """ retrieve a material by its name parameters ---------- name: string name of material returns ------- mat: object MaterialProps VariableTree object """ # strip integers from name to be safe st = ''.join(i for i in name if i.isalpha()) try: return self.st3d.materials[st] except: return None
class LoftedBladeSurface(Component): pf = VarTree(BladePlanformVT(), iotype='in') base_airfoils = List(iotype='in') blend_var = Array(iotype='in') chord_ni = Int(300, iotype='in') span_ni = Int(300, iotype='in') interp_type = Enum('rthick', ('rthick', 's'), iotype='in') surface_spline = Str('akima', iotype='in', desc='Spline') rot_order = Array(np.array([2, 1, 0]), iotype='in', desc='rotation order of airfoil sections' 'default z,y,x (twist,sweep,dihedral)') surfout = VarTree(BladeSurfaceVT(), iotype='out') surfnorot = VarTree(BladeSurfaceVT(), iotype='out') def execute(self): self.interpolator = BlendAirfoilShapes() self.interpolator.ni = self.chord_ni self.interpolator.spline = self.surface_spline self.interpolator.blend_var = self.blend_var self.interpolator.airfoil_list = self.base_airfoils self.interpolator.initialize() self.span_ni = self.pf.s.shape[0] x = np.zeros((self.chord_ni, self.span_ni, 3)) for i in range(self.span_ni): s = self.pf.s[i] pos_x = self.pf.x[i] pos_y = self.pf.y[i] pos_z = self.pf.z[i] chord = self.pf.chord[i] p_le = self.pf.p_le[i] # generate the blended airfoil shape if self.interp_type == 'rthick': rthick = self.pf.rthick[i] points = self.interpolator(rthick) else: points = self.interpolator(s) points *= chord points[:, 0] += pos_x - chord * p_le # x-coordinate needs to be inverted for clockwise rotating blades x[:, i, :] = (np.array( [-points[:, 0], points[:, 1], x.shape[0] * [pos_z]]).T) # save non-rotated blade (only really applicable for straight blades) x_norm = x.copy() x[:, :, 1] += self.pf.y x = self.rotate(x) self.surfnorot.surface = x_norm self.surfout.surface = x def rotate(self, x): """ rotate blade sections accounting for twist and main axis orientation the blade will be built with a "sheared" layout, ie no rotation around y in the case of sweep. if the main axis includes a winglet, the blade sections will be rotated accordingly. ensure that an adequate point distribution is used in this case to avoid sections colliding in the winglet junction! """ main_axis = Curve(points=np.array([self.pf.x, self.pf.y, self.pf.z]).T) rot_normals = np.zeros((3, 3)) x_rot = np.zeros(x.shape) for i in range(x.shape[1]): points = x[:, i, :] rot_center = main_axis.points[i] # rotation angles read from file angles = np.array([ self.pf.rot_x[i], self.pf.rot_y[i], self.pf.rot_z[i] ]) * np.pi / 180. # compute rotation angles of main_axis t = main_axis.dp[i] rot = np.zeros(3) rot[0] = -np.arctan(t[1] / (t[2] + 1.e-20)) v = np.array([t[2], t[1]]) vt = np.dot(v, v)**0.5 rot[1] = (np.arcsin(t[0] / vt)) angles[0] += rot[0] angles[1] += rot[1] # compute x-y-z normal vectors of rotation n_y = np.cross(t, [1, 0, 0]) n_y = n_y / norm(n_y) rot_normals[0, :] = np.array([1, 0, 0]) rot_normals[1, :] = n_y rot_normals[2, :] = t # compute final rotation matrix rotation_matrix = np.matrix([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]) for n, ii in enumerate(self.rot_order): mat = np.matrix(RotMat(rot_normals[ii], angles[ii])) rotation_matrix = mat * rotation_matrix # apply rotation x_rot[:, i, :] = dotXC(rotation_matrix, points, rot_center) return x_rot
class LoftedBladeSurfaceBase(Component): surfout = VarTree(BladeSurfaceVT(), iotype='out') surfnorot = VarTree(BladeSurfaceVT(), iotype='out')
class BladeStructureCSBuilder(BladeStructureBuilderBase): """ Class that generates a series of 2D cross-sectional property vartrees (CrossSectionStructureVT) used by structural codes like BECAS """ blade_length = Float(1., iotype='in') surface = VarTree(BladeSurfaceVT(), iotype='in', desc='Stacked blade surface object') st3d = VarTree(BladeStructureVT3D(), iotype='in', desc='Blade structure definition') cs2d = List(iotype='out', desc='List of cross-sectional properties' 'vartrees') def execute(self): """ generate cross sections at every spanwise node of the st3d vartree """ # clear list of outputs! self.cs2d = [] ni = self.st3d.x.shape[0] for i in range(ni): x = self.st3d.x[i] # print 'adding section at r/R = %2.2f' % x st2d = CrossSectionStructureVT() st2d.s = x * self.blade_length st2d.DPs = [] try: airfoil = self.surface.interpolate_profile( x)[:, [0, 1]] * self.blade_length st2d.airfoil.initialize(airfoil) except: pass for ir, rname in enumerate(self.st3d.regions): reg = getattr(self.st3d, rname) if reg.thickness[i] == 0.: print 'zero thickness region!', rname # continue DP0 = getattr(self.st3d, 'DP%02d' % ir) DP1 = getattr(self.st3d, 'DP%02d' % (ir + 1)) r = st2d.add_region(rname.upper()) st2d.DPs.append(DP0[i]) r.s0 = DP0[i] r.s1 = DP1[i] for lname in reg.layers: lay = getattr(reg, lname) if lay.thickness[i] > 1.e-5: l = r.add_layer(lname) # try: lnamebase = lname.translate(None, digits) st2d.add_material(lnamebase, self.get_material(lname).copy()) # except: # raise RuntimeError('Material %s not in materials list' % lname) l.materialname = lnamebase l.thickness = max(0., lay.thickness[i]) try: l.angle = lay.angle[i] except: l.angle = 0. st2d.DPs.append(DP1[i]) for ir, rname in enumerate(self.st3d.webs): reg = getattr(self.st3d, rname) if reg.thickness[i] == 0.: continue r = st2d.add_web(rname.upper()) try: DP0 = getattr(self.st3d, 'DP%02d' % self.st3d.iwebs[ir][0]) except: DP0 = getattr( self.st3d, 'DP%02d' % (len(self.st3d.regions) + self.st3d.iwebs[ir][0] + 1)) try: DP1 = getattr(self.st3d, 'DP%02d' % self.st3d.iwebs[ir][1]) except: DP1 = getattr( self.st3d, 'DP%02d' % (len(self.st3d.regions) + self.st3d.iwebs[ir][1] + 1)) r.s0 = DP0[i] r.s1 = DP1[i] for lname in reg.layers: lay = getattr(reg, lname) if lay.thickness[i] > 1.e-5: l = r.add_layer(lname) try: lnamebase = lname.translate(None, digits) st2d.add_material(lnamebase, self.get_material(lname).copy()) except: raise RuntimeError( 'Material %s not in materials list' % lname) l.materialname = lnamebase l.thickness = max(0., lay.thickness[i]) try: l.angle = lay.angle[i] except: l.angle = 0. self.cs2d.append(st2d)